Remove the RCSID macro
[openafs.git] / src / kauth / kaprocs.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13
14 #include <afs/stds.h>
15 #include <errno.h>
16 #include "kauth.h"
17 #include <sys/types.h>
18 #include <time.h>
19 #ifdef AFS_NT40_ENV
20 #include <afs/afsutil.h>
21 #else
22 #include <sys/resource.h>
23 #include <sys/file.h>
24 #endif
25 #include <stdio.h>
26 #include <lock.h>
27 #include <ubik.h>
28 #include <lwp.h>
29 #include <rx/xdr.h>
30 #include <rx/rx.h>
31 #include <rx/rxkad.h>
32 #ifdef AFS_NT40_ENV
33 #include <winsock2.h>
34 #else
35 #include <netinet/in.h>
36 #endif
37 #include <string.h>
38 #include <des.h>
39 #include <des_prototypes.h>
40 #include <afs/cellconfig.h>
41 #include <afs/auth.h>
42 #include <afs/com_err.h>
43 #include "kautils.h"
44 #include "kaserver.h"
45 #include "kalog.h"
46 #include "kaport.h"
47 #include "kauth_internal.h"
48 #include "afs/audit.h"
49
50 extern struct ubik_dbase *KA_dbase;
51 struct kaheader cheader;
52 Date cheaderReadTime;           /* time cheader last read in */
53 extern struct afsconf_dir *KA_conf;     /* for getting cell info */
54
55 afs_int32 kamCreateUser(struct rx_call *call, char *aname, char *ainstance, 
56                         EncryptionKey ainitpw);
57 afs_int32 ChangePassWord(struct rx_call *call, char *aname, char *ainstance, 
58                          ka_CBS *arequest, ka_BBS *oanswer);
59 afs_int32 kamSetPassword(struct rx_call *call, char *aname, char *ainstance,
60                          afs_int32 akvno, EncryptionKey apassword);
61 afs_int32 kamSetFields(struct rx_call *call, char *aname, char *ainstance,
62                        afs_int32 aflags, Date aexpiration, 
63                        afs_int32 alifetime, afs_int32 amaxAssociates,
64                        afs_uint32 misc_auth_bytes, afs_int32 spare2);
65 afs_int32 kamDeleteUser(struct rx_call *call, char *aname, char *ainstance);
66 afs_int32 kamGetEntry(struct rx_call *call, char *aname, char *ainstance,
67                       afs_int32 aversion, kaentryinfo *aentry);
68 afs_int32 kamListEntry(struct rx_call *call, afs_int32 previous_index,
69                        afs_int32 *index, afs_int32 *count, kaident *name);
70 afs_int32 kamGetStats(struct rx_call *call, afs_int32 version, 
71                       afs_int32 *admin_accounts, kasstats *statics,
72                       kadstats *dynamics);
73 afs_int32 kamGetPassword(struct rx_call *call, char *name, 
74                          EncryptionKey *password);
75 afs_int32 kamGetRandomKey(struct rx_call *call, EncryptionKey *key);
76 afs_int32 kamDebug(struct rx_call *call, afs_int32 version,
77                    int checkDB, struct ka_debugInfo *info);
78
79 char lrealm[MAXKTCREALMLEN];
80
81 #ifndef EXPIREPW                /* password expiration default yes */
82 #define EXPIREPW
83 #endif
84
85 #ifndef AUTOCPWINTERVAL
86 #define AUTOCPWINTERVAL (24*3600)
87 #endif
88 #ifndef AUTOCPWUPDATES
89 #define AUTOCPWUPDATES 128
90 #endif
91
92 extern int npwSums;
93
94 static afs_int32 autoCPWInterval;
95 static afs_int32 autoCPWUpdates;
96
97 static afs_int32 set_password(struct ubik_trans *tt, char *name, 
98                               char *instance, EncryptionKey *password, 
99                               afs_int32 kvno, afs_int32 caller);
100
101 extern afs_int32 InitAuthServ(struct ubik_trans **tt, int lock,
102                               int *this_op);
103 static afs_int32 impose_reuse_limits(EncryptionKey *password, 
104                                      struct kaentry *tentry);
105 static int create_user(struct ubik_trans *tt, char *name, char *instance,
106                        EncryptionKey *key, afs_int32 caller, 
107                        afs_int32 flags);
108
109 /* This routine is called whenever an RPC interface needs the time.  It uses
110    the current time to randomize a 128 bit value that is used to change the
111    AuthServer Admin and TGS keys automatically. */
112
113 static Date nextAutoCPWTime = 0;
114 static afs_int32 totalUpdates = 0;
115
116 /* This routine is ostensibly to get the current time, but basically its job is
117    to periodically update a random number.  It also periodically updates the
118    keys for the builtin servers.  This is why it needs a transaction pointer
119    and returns an error code.  If the caller is in a read transaction, the tt
120    ptr should be zero and the return code need not be checked. */
121
122 static afs_int32
123 get_time(Date *timeP,
124          struct ubik_trans *tt, /* tt != 0: a write transaction */
125          int admin)             /* the caller is an admin user */
126 {
127     /* random value used to change Admin & TGS keys, this is at risk during
128      * multi-threaded operation, but I think the consequences are fairly
129      * harmless. */
130     static afs_uint32 random_value[4];
131
132     struct timeval time;
133     unsigned int bit, nbit;
134     int i;
135     afs_int32 to;
136
137     gettimeofday(&time, 0);
138     bit = (random_value[3] >> 31) & 1;  /* get high bit of high word */
139     for (i = 0; i < 4; i++) {
140         nbit = random_value[i] >> 31;
141         random_value[i] = (random_value[i] << 1) + bit;
142         bit = nbit & 1;
143     }
144     /* get 60ths from usec.  This is all the real randomness there is. */
145     random_value[0] += time.tv_usec / 16667;
146
147     if (nextAutoCPWTime == 0) { /* initialize things */
148         nextAutoCPWTime = time.tv_sec + autoCPWInterval;
149         memcpy(&random_value[0], &time, 8);
150         memcpy(&random_value[2], &time, 8);
151     }
152
153     if ((++totalUpdates >= autoCPWUpdates) && tt &&     /* a write transaction */
154         ((admin && (time.tv_sec >= nextAutoCPWTime))
155          || (time.tv_sec >= nextAutoCPWTime + autoCPWInterval))) {
156         struct ktc_encryptionKey key;
157         char buf[4 * sizeof(key) + 1];
158         struct kaentry tentry;
159         afs_int32 code;
160         char bob[KA_TIMESTR_LEN];
161
162         ka_timestr(time.tv_sec, bob, KA_TIMESTR_LEN);
163         es_Report("Auto CPW at %s\n", bob);
164         if (!admin)
165             es_Report(" ... even though no ADMIN user\n");
166
167         code = FindBlock(tt, KA_ADMIN_NAME, KA_ADMIN_INST, &to, &tentry);
168         if (code)
169             return code;
170         if (to) {               /* check if auto cpw is disabled */
171             if (!(ntohl(tentry.flags) & KAFNOCPW)) {
172                 memcpy(&key, &random_value[0], sizeof(key));
173                 des_fixup_key_parity(&key);
174                 code =
175                     set_password(tt, KA_ADMIN_NAME, KA_ADMIN_INST, &key, 0,
176                                  0);
177                 if (code == 0) {
178                     des_init_random_number_generator(&key);
179                     ka_ConvertBytes(buf, sizeof(buf), (char *)&key,
180                                     sizeof(key));
181                     es_Report("New Admin key is %s\n", buf);
182                 } else {
183                     es_Report
184                         ("in get_time: set_password failed because: %d\n",
185                          code);
186                     return code;
187                 }
188             }
189         }
190
191         code = FindBlock(tt, KA_TGS_NAME, lrealm, &to, &tentry);
192         if (code)
193             return code;
194         if (to) {               /* check if auto cpw is disabled */
195             if (!(ntohl(tentry.flags) & KAFNOCPW)) {
196                 memcpy(&key, &random_value[2], sizeof(key));
197                 des_fixup_key_parity(&key);
198                 code = set_password(tt, KA_TGS_NAME, lrealm, &key, 0, 0);
199                 if (code == 0) {
200                     ka_ConvertBytes(buf, sizeof(buf), (char *)&key,
201                                     sizeof(key));
202                     es_Report("New TGS key is %s\n", buf);
203                 } else {
204                     es_Report
205                         ("in get_time: set_password failed because: %s\n",
206                          afs_error_message(code));
207                     return code;
208                 }
209             }
210         }
211         code = ka_FillKeyCache(tt);     /* ensure in-core copy is uptodate */
212         if (code)
213             return code;
214
215         nextAutoCPWTime = time.tv_sec + autoCPWInterval;
216         totalUpdates = 0;
217     }
218     if (timeP)
219         *timeP = time.tv_sec;
220     return 0;
221 }
222
223 static int noAuthenticationRequired;    /* global state */
224 static int recheckNoAuth;       /* global state */
225
226 /* kaprocsInited is sort of a lock: during a transaction only one process runs
227    while kaprocsInited is false. */
228
229 static int kaprocsInited = 0;
230
231 /* This variable is protected by the kaprocsInited flag. */
232
233 static int (*rebuildDatabase) (struct ubik_trans *);
234
235 /* This is called to initialize the database */
236
237 static int
238 initialize_database(struct ubik_trans *tt)
239 {
240     struct ktc_encryptionKey key;
241     int code;
242
243     gettimeofday((struct timeval *)&key, 0);    /* this is just a cheap seed key */
244     des_fixup_key_parity(&key);
245     des_init_random_number_generator(&key);
246     if ((code = des_random_key(&key))
247         || (code =
248             create_user(tt, KA_ADMIN_NAME, KA_ADMIN_INST, &key, 0,
249                         KAFNORMAL | KAFNOSEAL | KAFNOTGS)))
250         return code;
251     if ((code = des_random_key(&key))
252         || (code =
253             create_user(tt, KA_TGS_NAME, lrealm, &key, 0,
254                         KAFNORMAL | KAFNOSEAL | KAFNOTGS)))
255         return code;
256     return 0;
257 }
258
259 /* This routine handles initialization required by this module.  The initFlags
260    parameter passes some information about the command line arguments. */
261
262 afs_int32
263 init_kaprocs(char *lclpath, int initFlags)
264 {
265     int code;
266     struct ubik_trans *tt;
267     struct ktc_encryptionKey key;
268     afs_int32 kvno;
269
270     kaprocsInited = 0;
271     if (myHost == 0)
272         return KAINTERNALERROR;
273     if (KA_conf == 0)
274         return KAINTERNALERROR;
275     code = afsconf_GetLocalCell(KA_conf, lrealm, sizeof(lrealm));
276     if (code) {
277         printf("** Can't determine local cell name!\n");
278         return KANOCELLS;
279     }
280     ucstring(lrealm, lrealm, sizeof(lrealm));
281
282     recheckNoAuth = 1;
283     if (initFlags & 1)
284         noAuthenticationRequired = 1;
285     if (initFlags & 2)
286         recheckNoAuth = 0;
287     if (recheckNoAuth)
288         noAuthenticationRequired = afsconf_GetNoAuthFlag(KA_conf);
289     if (noAuthenticationRequired)
290         printf("Running server with security disabled\n");
291
292     if (initFlags & 4) {
293         autoCPWInterval = 10;
294         autoCPWUpdates = 10;
295     } else {
296         autoCPWInterval = AUTOCPWINTERVAL;
297         autoCPWUpdates = AUTOCPWUPDATES;
298     }
299
300     init_kadatabase(initFlags);
301     rebuildDatabase = initialize_database;
302
303     if ((code = InitAuthServ(&tt, LOCKREAD, 0))) {
304         printf("init_kaprocs: InitAuthServ failed: code = %d\n", code);
305         return code;
306     }
307     code = ka_LookupKey(tt, KA_ADMIN_NAME, KA_ADMIN_INST, &kvno, &key);
308     if (code) {
309         ubik_AbortTrans(tt);
310         printf
311             ("init_kaprocs: ka_LookupKey (code = %d) DB not initialized properly?\n",
312              code);
313         return code;
314     }
315     des_init_random_number_generator(&key);
316
317     code = ubik_EndTrans(tt);
318     if (code) {
319         printf("init_kaprocs: ubik_EndTrans failed: code = %d\n", code);
320         return code;
321     }
322
323     kaux_opendb(lclpath);       /* aux database stores failure counters */
324     rebuildDatabase = 0;        /* only do this during init */
325     kaprocsInited = 1;
326     return 0;
327 }
328
329 /* These variable are for returning debugging info about the state of the
330    server.  If they get trashed during multi-threaded operation it doesn't
331    matter. */
332
333 /* this is global so COUNT_REQ in krb_udp.c can refer to it. */
334 char *lastOperation = 0;        /* name of last operation */
335 static Date lastTrans;          /* time of last transaction */
336
337 static char adminPrincipal[256];
338 static char authPrincipal[256];
339 static char tgsPrincipal[256];
340 static char tgsServerPrincipal[256];
341
342 void
343 save_principal(char *p, char *n, char *i, char *c)
344 {
345     int s = 255;
346     int l;
347
348     l = strlen(n);
349     if (l > s)
350         return;
351     strcpy(p, n);
352     s -= l;
353     if (i && strlen(i)) {
354         if (s-- <= 0)
355             return;
356         strcat(p, ".");
357         l = strlen(i);
358         if (l > s)
359             return;
360         strcat(p, i);
361         s -= l;
362     }
363     if (c && strlen(c)) {
364         if (s-- <= 0)
365             return;
366         strcat(p, "@");
367         l = strlen(c);
368         if (l > s)
369             return;
370         strcat(p, c);
371     }
372 }
373
374 static afs_int32
375 check_auth(struct rx_call *call,
376            struct ubik_trans *at,
377            int admin,                   /* require caller to be ADMIN */
378            afs_int32 *acaller_id)
379 {
380     rxkad_level level;
381     char name[MAXKTCNAMELEN];
382     char instance[MAXKTCNAMELEN];
383     char cell[MAXKTCREALMLEN];
384     afs_int32 kvno;
385     Date expiration;            /* checked by Security Module */
386     struct kaentry tentry;
387     int code;
388     int si;
389
390     *acaller_id = 0;
391
392     if (recheckNoAuth)
393         noAuthenticationRequired = afsconf_GetNoAuthFlag(KA_conf);
394
395     si = rx_SecurityClassOf(rx_ConnectionOf(call));
396     if (si == RX_SCINDEX_VAB) {
397         printf("No support for VAB security module yet.\n");
398         return -1;
399     } else if (si == RX_SCINDEX_NULL) {
400         code = KANOAUTH;
401         goto no_auth;
402     } else if (si != RX_SCINDEX_KAD) {
403         es_Report("Unknown security index %d\n", si);
404         return -1;
405     }
406
407     code =
408         rxkad_GetServerInfo(rx_ConnectionOf(call), &level, &expiration, name,
409                             instance, cell, &kvno);
410     if (code) {
411         goto no_auth;
412     }
413     if (level != rxkad_crypt) {
414         es_Report("Incorrect security level = %d\n", level);
415         code = KANOAUTH;
416         goto no_auth;
417     }
418
419     if (!name_instance_legal(name, instance))
420         return KABADNAME;
421     if (strlen(cell)) {
422         ka_PrintUserID
423             ("Authorization rejected because we don't understand intercell stuff yet: ",
424              name, instance, "");
425         printf("@%s\n", cell);
426         return KANOAUTH;
427     }
428
429     code = FindBlock(at, name, instance, acaller_id, &tentry);
430     if (code)
431         return code;
432     if (*acaller_id == 0) {
433         ka_PrintUserID("User ", name, instance, " unknown.\n");
434         return KANOENT;
435     }
436     save_principal(adminPrincipal, name, instance, 0);
437
438     if (admin) {
439         if (!(ntohl(tentry.flags) & KAFADMIN)) {
440             if (noAuthenticationRequired) {
441                 ka_PrintUserID("Authorization approved for ", name, instance,
442                                " because there is no authentication required\n");
443                 osi_auditU(call, UnAuthEvent, code, AUD_STR, name, AUD_STR,
444                            instance, AUD_STR, cell, AUD_END);
445                 return 0;
446             }
447             ka_PrintUserID("User ", name, instance, " is not ADMIN.\n");
448             return KANOAUTH;
449         }
450         osi_auditU(call, UseOfPrivilegeEvent, code, AUD_STR, name, AUD_STR,
451                    instance, AUD_STR, cell, AUD_END);
452     }
453     return 0;
454
455   no_auth:
456     if (noAuthenticationRequired) {
457         es_Report
458             ("Caller w/o authorization approved no authentication required\n");
459         osi_auditU(call, UnAuthEvent, code, AUD_STR, name, AUD_STR, instance,
460                    AUD_STR, cell, AUD_END);
461         return 0;
462     }
463     return code;                /* no auth info */
464 }
465
466 afs_int32
467 AwaitInitialization(void)
468 {
469     afs_int32 start = 0;
470     while (!kaprocsInited) {
471         if (!start)
472             start = time(0);
473         else if (time(0) - start > 5)
474             return UNOQUORUM;
475         IOMGR_Sleep(1);
476     }
477     return 0;
478 }
479
480 /* This is called by every RPC interface to create a Ubik transaction and read
481    the database header into core */
482
483 afs_int32
484 InitAuthServ(struct ubik_trans **tt,
485              int lock,          /* indicate read/write transaction */
486              int *this_op)      /* opcode of RPC proc, for COUNT_ABO */
487 {
488     int code;
489     afs_int32 start = 0;        /* time started waiting for quorum */
490     float wait = 0.91;          /* start waiting for 1 second */
491
492     /* Wait for server initialization to finish if not during init_kaprocs */
493     if (this_op)
494         if ((code = AwaitInitialization()))
495             return code;
496
497     for (code = UNOQUORUM; code == UNOQUORUM;) {
498         if (lock == LOCKREAD)
499             code = ubik_BeginTransReadAny(KA_dbase, UBIK_READTRANS, tt);
500         else
501             code = ubik_BeginTrans(KA_dbase, UBIK_WRITETRANS, tt);
502         if (code == UNOQUORUM) {        /* no quorum elected */
503             if (!start)
504                 start = time(0);
505             else {
506                 int delay = time(0) - start;
507                 if (this_op) {  /* punt quickly, if RPC call */
508                     if (delay > 5)
509                         return code;
510                 } else {        /* more patient during init. */
511                     if (delay > 500)
512                         return code;
513                 }
514             }
515             printf("Waiting for quorum election.\n");
516             if (wait < 15.0)
517                 wait *= 1.1;
518             IOMGR_Sleep((int)wait);
519         }
520     }
521     if (code)
522         return code;
523     if ((code = ubik_SetLock(*tt, 1, 1, lock))) {
524         if (this_op)
525             COUNT_ABO;
526         ubik_AbortTrans(*tt);
527         return code;
528     }
529     /* check that dbase is initialized and setup cheader */
530     if (lock == LOCKREAD) {
531         /* init but don't fix because this is read only */
532         code = CheckInit(*tt, 0);
533         if (code) {
534             ubik_AbortTrans(*tt);       /* abort, since probably I/O error */
535             /* we did the check under a ReadAny transaction, but now, after
536              * getting a write transaction (and thus some real guarantees
537              * about what databases are really out there), we will check again
538              * in CheckInit before nuking the database.  Since this may now get
539              * a UNOQUORUM we'll just do this from the top.
540              */
541             if ((code = InitAuthServ(tt, LOCKWRITE, this_op)))
542                 return code;
543             if ((code = ubik_EndTrans(*tt)))
544                 return code;
545
546             /* now open the read transaction that was originally requested. */
547             return InitAuthServ(tt, lock, this_op);
548         }
549     } else {
550         if ((code = CheckInit(*tt, rebuildDatabase))) {
551             if (this_op)
552                 COUNT_ABO;
553             ubik_AbortTrans(*tt);
554             return code;
555         }
556     }
557     lastTrans = time(0);
558     ka_FillKeyCache(*tt);       /* ensure in-core copy is uptodate */
559     return 0;
560 }
561
562 /* returns true if name is specially known by AuthServer */
563
564 static int
565 special_name(char *name, char *instance)
566
567 {
568     return ((!strcmp(name, KA_TGS_NAME) && !strcmp(instance, lrealm))
569             || (strcmp(name, KA_ADMIN_NAME) == 0));
570 }
571
572 static int
573 create_user(struct ubik_trans *tt, char *name, char *instance,
574             EncryptionKey *key, afs_int32 caller, afs_int32 flags)
575 {
576     register int code;
577     afs_int32 to;
578     struct kaentry tentry;
579     afs_int32 maxLifetime;
580
581     code = FindBlock(tt, name, instance, &to, &tentry);
582     if (code)
583         return code;
584     if (to)
585         return KAEXIST;         /* name already exists, we fail */
586
587     to = AllocBlock(tt, &tentry);
588     if (to == 0)
589         return KACREATEFAIL;
590
591     /* otherwise we have a block */
592     strncpy(tentry.userID.name, name, sizeof(tentry.userID.name));
593     strncpy(tentry.userID.instance, instance, sizeof(tentry.userID.instance));
594     tentry.flags = htonl(flags);
595     if (special_name(name, instance)) { /* this overrides key & version */
596         tentry.flags = htonl(ntohl(tentry.flags) | KAFSPECIAL);
597         tentry.key_version = htonl(-1); /* don't save this key */
598         if ((code = ka_NewKey(tt, to, &tentry, key)))
599             return code;
600     } else {
601         memcpy(&tentry.key, key, sizeof(tentry.key));
602         tentry.key_version = htonl(0);
603     }
604     tentry.user_expiration = htonl(NEVERDATE);
605     code = get_time(&tentry.modification_time, tt, 1);
606     if (code)
607         return code;
608
609     /* time and addr of entry for guy changing this entry */
610     tentry.modification_time = htonl(tentry.modification_time);
611     tentry.modification_id = htonl(caller);
612     tentry.change_password_time = tentry.modification_time;
613
614     if (strcmp(name, KA_TGS_NAME) == 0)
615         maxLifetime = MAXKTCTICKETLIFETIME;
616     else if (strcmp(name, KA_ADMIN_NAME) == 0)
617         maxLifetime = 10 * 3600;
618     else if (strcmp(name, AUTH_SUPERUSER) == 0)
619         maxLifetime = 100 * 3600;
620     else
621         maxLifetime = 25 * 3600;        /* regular users */
622     tentry.max_ticket_lifetime = htonl(maxLifetime);
623
624     code = ThreadBlock(tt, to, &tentry);
625     return code;
626 }
627
628 /* Put actual stub routines here */
629
630 afs_int32
631 SKAM_CreateUser(struct rx_call *call, char *aname, char *ainstance,
632                 EncryptionKey ainitpw)
633 {
634     afs_int32 code;
635
636     code = kamCreateUser(call, aname, ainstance, ainitpw);
637     osi_auditU(call, AFS_KAM_CrUserEvent, code, AUD_STR, aname, AUD_STR,
638                ainstance, AUD_END);
639     return code;
640 }
641
642
643 afs_int32
644 kamCreateUser(struct rx_call *call, char *aname, char *ainstance, 
645               EncryptionKey ainitpw)
646 {
647     register int code;
648     struct ubik_trans *tt;
649     afs_int32 caller;           /* Disk offset of caller's entry */
650
651     COUNT_REQ(CreateUser);
652     if (!des_check_key_parity(&ainitpw) || des_is_weak_key(&ainitpw))
653         return KABADKEY;
654     if (!name_instance_legal(aname, ainstance))
655         return KABADNAME;
656     if ((code = InitAuthServ(&tt, LOCKWRITE, this_op)))
657         return code;
658     code = check_auth(call, tt, 1, &caller);
659     if (code) {
660         COUNT_ABO;
661         ubik_AbortTrans(tt);
662         return code;
663     }
664     code = create_user(tt, aname, ainstance, &ainitpw, caller, KAFNORMAL);
665     if (code) {
666         COUNT_ABO;
667         ubik_AbortTrans(tt);
668         return code;
669     }
670     code = ubik_EndTrans(tt);
671     KALOG(aname, ainstance, NULL, NULL, NULL, call->conn->peer->host,
672           LOG_CRUSER);
673     return code;
674 }
675
676 afs_int32
677 SKAA_ChangePassword(struct rx_call *call, char *aname, char *ainstance,
678                     ka_CBS *arequest, ka_BBS *oanswer)
679 {
680     afs_int32 code;
681
682     code = ChangePassWord(call, aname, ainstance, arequest, oanswer);
683     osi_auditU(call, AFS_KAA_ChPswdEvent, code, AUD_STR, aname, AUD_STR,
684                ainstance, AUD_END);
685     return code;
686 }
687
688 afs_int32
689 ChangePassWord(struct rx_call *call, char *aname, char *ainstance, 
690                ka_CBS *arequest, ka_BBS *oanswer)
691 {
692     register int code;
693     struct ubik_trans *tt;
694     afs_int32 to;               /* offset of block */
695     struct kaentry tentry;
696     struct ka_cpwRequest request;       /* request after decryption */
697     char *answer;               /* where answer is to be put */
698     int answer_len;             /* length of answer packet */
699     afs_int32 kvno;             /* requested key version number */
700     des_key_schedule user_schedule;     /* key schedule for user's key */
701     Date request_time;          /* time request originated */
702
703     COUNT_REQ(ChangePassword);
704     if (!name_instance_legal(aname, ainstance))
705         return KABADNAME;
706     if (strcmp(ainstance, KA_ADMIN_NAME) == 0)
707         return KABADNAME;
708     if ((code = InitAuthServ(&tt, LOCKWRITE, this_op)))
709         return code;
710
711     code = FindBlock(tt, aname, ainstance, &to, &tentry);
712     if (code) {
713         goto abort;
714     }
715     if (to == 0) {              /* no such user */
716         code = KANOENT;
717         goto abort;
718     }
719     if (ntohl(tentry.flags) & KAFNOCPW) {
720         code = KABADCPW;
721         goto abort;
722     }
723
724     /* decrypt request w/ user password */
725     if ((code = des_key_sched(&tentry.key, user_schedule)))
726         es_Report("In KAChangePassword: key_sched returned %d\n", code);
727     des_pcbc_encrypt(arequest->SeqBody, &request,
728                      min(arequest->SeqLen, sizeof(request)), user_schedule,
729                      &tentry.key, DECRYPT);
730
731     /* validate the request */
732     request_time = ntohl(request.time); /* reorder date */
733     kvno = ntohl(request.kvno);
734     if ((abs(request_time - time(0)) > KTC_TIME_UNCERTAINTY) || strncmp(request.label, KA_CPW_REQ_LABEL, sizeof(request.label)) || (request.spare) || (kvno > MAXKAKVNO)) {     /* these are reseved */
735         code = KABADREQUEST;
736         goto abort;
737     }
738
739     /* check to see if the new password was used before, or if there has
740      * not been sufficient time since the last password change
741      */
742     code = impose_reuse_limits(&request.newpw, &tentry);
743     if (code) {
744         goto abort;
745     }
746
747     /* Create the Answer Packet */
748     answer_len = sizeof(Date) + KA_LABELSIZE;
749     if (oanswer->MaxSeqLen < answer_len) {
750         code = KAANSWERTOOLONG;
751         goto abort;
752     }
753     oanswer->SeqLen = answer_len;
754     answer = oanswer->SeqBody;
755     request.time = htonl(request_time + 1);
756     memcpy(answer, (char *)&request.time, sizeof(Date));
757     answer += sizeof(Date);
758     memcpy(answer, KA_CPW_ANS_LABEL, KA_LABELSIZE);
759
760     des_pcbc_encrypt(oanswer->SeqBody, oanswer->SeqBody, answer_len,
761                      user_schedule, &tentry.key, ENCRYPT);
762
763     code = set_password(tt, aname, ainstance, &request.newpw, kvno, 0);
764     if (code) {
765         code = KAIO;
766         goto abort;
767     }
768
769     cheader.stats.cpws = htonl(ntohl(cheader.stats.cpws) + 1);
770     code =
771         kawrite(tt, DOFFSET(0, &cheader, &cheader.stats.cpws),
772                 (char *)&cheader.stats.cpws, sizeof(afs_int32));
773     if (code) {
774         code = KAIO;
775         goto abort;
776     }
777
778     code = ubik_EndTrans(tt);
779     return code;
780
781   abort:
782     COUNT_ABO;
783     ubik_AbortTrans(tt);
784     return code;
785 }
786
787 static afs_int32
788 impose_reuse_limits(EncryptionKey *password, struct kaentry *tentry)
789 {
790     int code;
791     Date now;
792     int i;
793     extern int MinHours;
794     afs_uint32 newsum;
795
796     if (!tentry->pwsums[0] && npwSums > 1 && !tentry->pwsums[1])
797         return 0;               /* password reuse limits not in effect */
798
799     code = get_time(&now, 0, 0);
800     if (code)
801         return code;
802
803     if ((now - ntohl(tentry->change_password_time)) < MinHours * 60 * 60)
804         return KATOOSOON;
805
806     if (!memcmp(password, &(tentry->key), sizeof(EncryptionKey)))
807         return KAREUSED;
808
809     code = ka_KeyCheckSum((char *)password, &newsum);
810     if (code)
811         return code;
812
813     newsum = newsum & 0x000000ff;
814     for (i = 0; i < npwSums; i++) {
815         if (newsum == tentry->pwsums[i])
816             return KAREUSED;
817     }
818
819     return 0;
820 }
821
822
823 static afs_int32
824 set_password(struct ubik_trans *tt, char *name, char *instance, 
825              EncryptionKey *password, afs_int32 kvno, afs_int32 caller)
826 {
827     afs_int32 code;
828     afs_int32 to;               /* offset of block */
829     struct kaentry tentry;
830     Date now;
831     int i;
832     extern int npwSums;
833     afs_uint32 newsum;
834
835     code = FindBlock(tt, name, instance, &to, &tentry);
836     if (code)
837         return code;
838     if (to == 0)
839         return KANOENT;         /* no such user */
840
841     /* if password reuse limits in effect, set the checksums, the hard way */
842     if (!tentry.pwsums[0] && npwSums > 1 && !tentry.pwsums[1]) {
843         /* do nothing, no limits */ ;
844     } else {
845         code = ka_KeyCheckSum((char *)&(tentry.key), &newsum);
846         if (code)
847             return code;
848         for (i = npwSums - 1; i; i--)
849             tentry.pwsums[i] = tentry.pwsums[i - 1];
850         tentry.pwsums[0] = newsum & 0x000000ff;
851     }
852
853
854     if (special_name(name, instance)) { /* set key over rides key_version */
855         tentry.flags = htonl(ntohl(tentry.flags) | KAFSPECIAL);
856         if ((code = ka_NewKey(tt, to, &tentry, password)))
857             return (code);
858     } else {
859         memcpy(&tentry.key, password, sizeof(tentry.key));
860         if (!kvno) {
861             kvno = ntohl(tentry.key_version);
862             if ((kvno < 1) || (kvno >= MAXKAKVNO))
863                 kvno = 1;
864             else
865                 kvno++;
866         }
867         tentry.key_version = htonl((afs_int32) kvno);   /* requested key version */
868     }
869
870
871
872     /* no-write prevents recursive call to set_password by AuthCPW code. */
873     code = get_time(&now, 0, 0);
874     if (code)
875         return code;
876     if (caller) {
877         tentry.modification_time = htonl(now);
878         tentry.modification_id = htonl(caller);
879     }
880
881     tentry.change_password_time = htonl(now);
882
883     if (code = kawrite(tt, to, &tentry, sizeof(tentry)))
884         return (KAIO);
885     return (0);
886 }
887
888 afs_int32
889 SKAM_SetPassword(struct rx_call *call, char *aname, char *ainstance,
890                  afs_int32 akvno, EncryptionKey apassword)
891 {
892     afs_int32 code;
893
894     code = kamSetPassword(call, aname, ainstance, akvno, apassword);
895     osi_auditU(call, AFS_KAM_SetPswdEvent, code, AUD_STR, aname, AUD_STR,
896                ainstance, AUD_END);
897     return code;
898 }
899
900 afs_int32
901 kamSetPassword(struct rx_call *call, char *aname, char *ainstance,
902                afs_int32 akvno, EncryptionKey apassword)
903 {
904     register int code;
905     struct ubik_trans *tt;
906     afs_int32 caller;           /* Disk offset of caller's entry */
907     struct kaentry tentry;
908
909     COUNT_REQ(SetPassword);
910     if (akvno > MAXKAKVNO)
911         return KABADARGUMENT;
912     if (!des_check_key_parity(&apassword) || des_is_weak_key(&apassword))
913         return KABADKEY;
914
915     if (!name_instance_legal(aname, ainstance))
916         return KABADNAME;
917     if ((code = InitAuthServ(&tt, LOCKWRITE, this_op)))
918         return code;
919     code = check_auth(call, tt, 0, &caller);
920     if (code) {
921         goto abort;
922     }
923     if ((code = karead(tt, caller, &tentry, sizeof(tentry)))) {
924         code = KAIO;
925         goto abort;
926     }
927     /* if the user is changing his own password or ADMIN then go ahead. */
928     if ((strcmp(tentry.userID.name, aname) == 0)
929         && (strcmp(tentry.userID.instance, ainstance) == 0)) {
930         if (ntohl(tentry.flags) & KAFNOCPW)
931             code = KABADCPW;
932         else {
933             code = impose_reuse_limits(&apassword, &tentry);
934             if (!code)
935                 code =
936                     set_password(tt, aname, ainstance, &apassword, akvno, 0);
937         }
938     } else if (ntohl(tentry.flags) & KAFADMIN) {
939         code = set_password(tt, aname, ainstance, &apassword, akvno, caller);
940     } else
941         code = KANOAUTH;
942     if (code)
943         goto abort;
944
945     code = ubik_EndTrans(tt);
946     KALOG(aname, ainstance, NULL, NULL, NULL, call->conn->peer->host,
947           LOG_CHPASSWD);
948     return code;
949
950   abort:
951     COUNT_ABO;
952     ubik_AbortTrans(tt);
953     return code;
954 }
955
956 static Date
957 CoerseLifetime(Date start, Date end)
958 {
959     unsigned char kerberosV4Life;
960     kerberosV4Life = time_to_life(start, end);
961     end = life_to_time(start, kerberosV4Life);
962     return end;
963 }
964
965 static afs_int32
966 GetEndTime(Date start,          /* start time of ticket */
967            Date reqEnd,         /* requested end time */
968            Date expiration,     /* authorizing ticket's expiration */
969            struct kaentry *caller,
970            struct kaentry *server,
971            Date *endP)          /* actual end time */
972 {
973     Date cExp, sExp;
974     Date cLife, sLife;
975     Date end;
976
977     if (ntohl(caller->flags) & KAFNOTGS)
978         return KABADUSER;       /* no new tickets for this user */
979     if (expiration && (ntohl(server->flags) & KAFNOSEAL))
980         return KABADSERVER;     /* can't be target of GetTicket req */
981     if (!expiration)
982         expiration = NEVERDATE;
983
984     cExp = ntohl(caller->user_expiration);
985     sExp = ntohl(server->user_expiration);
986     if (cExp < start)
987         return KAPWEXPIRED;
988     if (sExp < start)
989         return KABADSERVER;
990     cLife = start + ntohl(caller->max_ticket_lifetime);
991     sLife = start + ntohl(server->max_ticket_lifetime);
992     end =
993         umin(umin(reqEnd, expiration),
994              umin(umin(cLife, sLife), umin(cExp, sExp)));
995     end = CoerseLifetime(start, end);
996     *endP = end;
997     return 0;
998 }
999
1000 static afs_int32
1001 PrepareTicketAnswer(ka_BBS *oanswer, afs_int32 challenge, char *ticket,
1002                     afs_int32 ticketLen, struct ktc_encryptionKey *sessionKey,
1003                     Date start, Date end, struct kaentry *caller, 
1004                     struct kaentry *server, char *cell, char *label)
1005 {
1006     afs_int32 code;
1007     struct ka_ticketAnswer *answer;
1008     afs_int32 cksum;
1009
1010     code = KAANSWERTOOLONG;
1011     if (oanswer->MaxSeqLen <
1012         sizeof(struct ka_ticketAnswer) - 5 * MAXKTCNAMELEN - MAXKTCTICKETLEN +
1013         ticketLen)
1014         return code;
1015
1016     answer = (struct ka_ticketAnswer *)oanswer->SeqBody;
1017     answer->challenge = htonl(challenge);
1018     memcpy(&answer->sessionKey, sessionKey, sizeof(struct ktc_encryptionKey));
1019     answer->startTime = htonl(start);
1020     answer->endTime = htonl(end);
1021     answer->kvno = server->key_version;
1022     answer->ticketLen = htonl(ticketLen);
1023
1024     {
1025         char *ans = answer->name;       /* pointer to variable part */
1026         int rem;                /* space remaining */
1027         int len;                /* macro temp. */
1028
1029         rem = oanswer->MaxSeqLen - (ans - oanswer->SeqBody);
1030 #undef putstr
1031 #define putstr(str) len = strlen (str)+1;\
1032                     if (rem < len) return code;\
1033                     strcpy (ans, str);\
1034                     ans += len; rem -= len
1035         putstr(caller->userID.name);
1036         putstr(caller->userID.instance);
1037         putstr(cell);
1038         putstr(server->userID.name);
1039         putstr(server->userID.instance);
1040         if (rem < ticketLen + KA_LABELSIZE)
1041             return code;
1042         memcpy(ans, ticket, ticketLen);
1043         ans += ticketLen;
1044         if (label)
1045             memcpy(ans, label, KA_LABELSIZE);
1046         else
1047             memset(ans, 0, KA_LABELSIZE);
1048         ans += KA_LABELSIZE;
1049         oanswer->SeqLen = (ans - oanswer->SeqBody);
1050     }
1051     cksum = 0;
1052     answer->cksum = htonl(cksum);
1053     oanswer->SeqLen = round_up_to_ebs(oanswer->SeqLen);
1054     if (oanswer->SeqLen > oanswer->MaxSeqLen)
1055         return code;
1056     return 0;
1057 }
1058
1059 /* This is used to get a ticket granting ticket or an admininstration ticket.
1060    These two specific, built-in servers are special cases, which require the
1061    client's key as an additional security precaution.  The GetTicket operation
1062    is normally disabled for these two principals. */
1063
1064 static afs_int32
1065 Authenticate(int version, struct rx_call *call, char *aname, char *ainstance, 
1066              Date start, Date end, ka_CBS *arequest, ka_BBS *oanswer)
1067 {
1068     int code;
1069     struct ubik_trans *tt;
1070     afs_int32 to;               /* offset of block */
1071     kaentry tentry;
1072     struct kaentry server;      /* entry for desired server */
1073     struct ka_gettgtRequest request;    /* request after decryption */
1074     int tgt, adm;               /* type of request */
1075     char *sname;                /* principal of server */
1076     char *sinst;
1077     char ticket[MAXKTCTICKETLEN];       /* our copy of the ticket */
1078     int ticketLen;
1079     struct ktc_encryptionKey sessionKey;        /* we have to invent a session key */
1080     char *answer;               /* where answer is to be put */
1081     int answer_len;             /* length of answer packet */
1082     Date answer_time;           /* 1+ request time in network order */
1083     afs_int32 temp;             /* for htonl conversions */
1084     des_key_schedule user_schedule;     /* key schedule for user's key */
1085     afs_int32 tgskvno;          /* key version of service key */
1086     struct ktc_encryptionKey tgskey;    /* service key for encrypting ticket */
1087     Date now;
1088     afs_uint32 pwexpires;
1089
1090     COUNT_REQ(Authenticate);
1091     if (!name_instance_legal(aname, ainstance))
1092         return KABADNAME;
1093     if ((code = InitAuthServ(&tt, LOCKREAD, this_op)))
1094         return code;
1095     get_time(&now, 0, 0);
1096
1097     sname = sinst = NULL;
1098
1099     code = FindBlock(tt, aname, ainstance, &to, &tentry);
1100     if (code) {
1101         goto abort;
1102     }
1103     if (to == 0) {              /* no such user */
1104         code = KANOENT;
1105         goto abort;
1106     }
1107 #ifdef LOCKPW
1108     /* have to check for locked before verifying the password, otherwise all
1109      * KALOCKED means is "yup, you guessed the password all right, now wait a 
1110      * few minutes and we'll let you in"
1111      */
1112     if (kaux_islocked
1113         (to, (u_int) tentry.misc_auth_bytes[ATTEMPTS],
1114          (afs_uint32) tentry.misc_auth_bytes[LOCKTIME] << 9)) {
1115         code = KALOCKED;
1116         goto abort;
1117     }
1118 #endif /* LOCKPW */
1119
1120     save_principal(authPrincipal, aname, ainstance, 0);
1121
1122     /* decrypt request w/ user password */
1123     if ((code = des_key_sched(&tentry.key, user_schedule)))
1124         es_Report("In KAAuthenticate: key_sched returned %d\n", code);
1125     des_pcbc_encrypt(arequest->SeqBody, &request,
1126                      min(arequest->SeqLen, sizeof(request)), user_schedule,
1127                      &tentry.key, DECRYPT);
1128
1129     request.time = ntohl(request.time); /* reorder date */
1130     tgt = !strncmp(request.label, KA_GETTGT_REQ_LABEL, sizeof(request.label));
1131     adm = !strncmp(request.label, KA_GETADM_REQ_LABEL, sizeof(request.label));
1132     if (!(tgt || adm)) {
1133         kaux_inc(to, ((unsigned char)tentry.misc_auth_bytes[LOCKTIME]) << 9);
1134         code = KABADREQUEST;
1135         goto abort;
1136     } else
1137         kaux_write(to, 0, 0);   /* reset counters */
1138
1139 #ifdef EXPIREPW
1140     if (!tentry.misc_auth_bytes[EXPIRES]) {
1141         /* 0 in the database means never, but 0 on the network means today */
1142         /* 255 on the network means "long time, maybe never" */
1143         pwexpires = 255;
1144     } else {
1145         pwexpires = tentry.misc_auth_bytes[EXPIRES];
1146
1147         pwexpires =
1148             ntohl(tentry.change_password_time) + 24 * 60 * 60 * pwexpires;
1149         if (adm) {              /* provide a little slack for admin ticket */
1150             pwexpires += 30 * 24 * 60 * 60;     /*  30 days */
1151         }
1152         if (pwexpires < now) {
1153             code = KAPWEXPIRED;
1154             goto abort;
1155         } else {
1156             pwexpires = (pwexpires - now) / (24 * 60 * 60);
1157             if (pwexpires > 255)
1158                 pwexpires = 255;
1159         }
1160     }
1161 #endif /* EXPIREPW */
1162
1163     if (abs(request.time - now) > KTC_TIME_UNCERTAINTY) {
1164 #if 0
1165         if (oanswer->MaxSeqLen < sizeof(afs_int32))
1166             code = KAANSWERTOOLONG;
1167         else {                  /* return our time if possible */
1168             oanswer->SeqLen = sizeof(afs_int32);
1169             request.time = htonl(now);
1170             memcpy(oanswer->SeqBody, &request.time, sizeof(afs_int32));
1171         }
1172 #endif
1173         code = KACLOCKSKEW;
1174         goto abort;
1175     }
1176     sname = (tgt ? KA_TGS_NAME : KA_ADMIN_NAME);
1177     sinst = (tgt ? lrealm : KA_ADMIN_INST);
1178     code = FindBlock(tt, sname, sinst, &to, &server);
1179     if (code)
1180         goto abort;
1181     if (to == 0) {
1182         code = KANOENT;
1183         goto abort;
1184     }
1185
1186     tgskvno = ntohl(server.key_version);
1187     memcpy(&tgskey, &server.key, sizeof(tgskey));
1188
1189     code = des_random_key(&sessionKey);
1190     if (code) {
1191         code = KANOKEYS;
1192         goto abort;
1193     }
1194
1195     code = GetEndTime(start, end, 0 /*!GetTicket */ , &tentry, &server, &end);
1196     if (code)
1197         goto abort;
1198
1199     code =
1200         tkt_MakeTicket(ticket, &ticketLen, &tgskey, aname, ainstance, "",
1201                        start, end, &sessionKey,
1202                        rx_HostOf(rx_PeerOf(rx_ConnectionOf(call))), sname,
1203                        sinst);
1204     if (code)
1205         goto abort;
1206
1207     switch (version) {
1208     case 0:
1209         answer_len =
1210             ticketLen + sizeof(Date) + sizeof(struct ktc_encryptionKey) +
1211             2 * sizeof(afs_int32) + KA_LABELSIZE;
1212         answer_len = round_up_to_ebs(answer_len);
1213         if (answer_len > oanswer->MaxSeqLen) {
1214             code = KAANSWERTOOLONG;
1215             goto abort;
1216         }
1217         oanswer->SeqLen = answer_len;
1218         answer = oanswer->SeqBody;
1219         answer_time = htonl(request.time + 1);
1220         memcpy(answer, (char *)&answer_time, sizeof(Date));
1221         answer += sizeof(Date);
1222         memcpy(answer, (char *)&sessionKey, sizeof(struct ktc_encryptionKey));
1223         answer += sizeof(struct ktc_encryptionKey);
1224         temp = htonl(tgskvno);
1225         memcpy(answer, (char *)&temp, sizeof(afs_int32));
1226         answer += sizeof(afs_int32);
1227         temp = htonl(ticketLen);
1228         memcpy(answer, (char *)&temp, sizeof(afs_int32));
1229         answer += sizeof(afs_int32);
1230         memcpy(answer, ticket, ticketLen);
1231         answer += ticketLen;
1232         memcpy(answer, (tgt ? KA_GETTGT_ANS_LABEL : KA_GETADM_ANS_LABEL),
1233                KA_LABELSIZE);
1234         break;
1235     case 1:
1236     case 2:
1237         code =
1238             PrepareTicketAnswer(oanswer, request.time + 1, ticket, ticketLen,
1239                                 &sessionKey, start, end, &tentry, &server, "",
1240                                 (tgt ? KA_GETTGT_ANS_LABEL :
1241                                  KA_GETADM_ANS_LABEL));
1242         if (code)
1243             goto abort;
1244 #ifdef EXPIREPW
1245         if ((version == 2)
1246             && oanswer->SeqLen < oanswer->MaxSeqLen + sizeof(afs_int32)) {
1247             temp = pwexpires << 24;     /* move it into the high byte */
1248             pwexpires = htonl(temp);
1249
1250             memcpy((char *)oanswer->SeqBody + oanswer->SeqLen, &pwexpires,
1251                    sizeof(afs_int32));
1252             oanswer->SeqLen += sizeof(afs_int32);
1253             oanswer->SeqLen = round_up_to_ebs(oanswer->SeqLen);
1254             if (oanswer->SeqLen > oanswer->MaxSeqLen) {
1255                 code = KAANSWERTOOLONG;
1256                 goto abort;
1257             }
1258         }
1259 #endif /* EXPIREPW */
1260         break;
1261
1262     default:
1263         code = KAINTERNALERROR;
1264         goto abort;
1265     }
1266     des_pcbc_encrypt(oanswer->SeqBody, oanswer->SeqBody, oanswer->SeqLen,
1267                      user_schedule, &tentry.key, ENCRYPT);
1268     code = ubik_EndTrans(tt);
1269     KALOG(aname, ainstance, sname, sinst, NULL, call->conn->peer->host,
1270           LOG_AUTHENTICATE);
1271     return code;
1272
1273   abort:
1274     COUNT_ABO;
1275     ubik_AbortTrans(tt);
1276     KALOG(aname, ainstance, sname, sinst, NULL, call->conn->peer->host,
1277           LOG_AUTHFAILED);
1278     return code;
1279 }
1280
1281 afs_int32
1282 SKAA_Authenticate_old(struct rx_call *call, char *aname, char *ainstance,
1283                       Date start, Date end, ka_CBS *arequest, 
1284                       ka_BBS *oanswer)
1285 {
1286     int code;
1287
1288     IOMGR_Sleep(1);             /* discourage use of this mechanism */
1289     code =
1290         Authenticate(0, call, aname, ainstance, start, end, arequest,
1291                      oanswer);
1292     osi_auditU(call, AFS_KAA_AuthOEvent, code, AUD_STR, aname, AUD_STR,
1293                ainstance, AUD_END);
1294
1295     return code;
1296 }
1297
1298 afs_int32
1299 SKAA_Authenticate(struct rx_call *call, char *aname, char *ainstance, 
1300                   Date start, Date end, ka_CBS *arequest, ka_BBS *oanswer)
1301 {
1302     int code;
1303
1304     code =
1305         Authenticate(1, call, aname, ainstance, start, end, arequest,
1306                      oanswer);
1307     osi_auditU(call, AFS_KAA_AuthEvent, code, AUD_STR, aname, AUD_STR,
1308                ainstance, AUD_END);
1309
1310     return code;
1311 }
1312
1313 afs_int32
1314 SKAA_AuthenticateV2(struct rx_call *call, char *aname, char *ainstance, 
1315                     Date start, Date end, ka_CBS *arequest, ka_BBS *oanswer)
1316 {
1317     int code;
1318
1319     code =
1320         Authenticate(2, call, aname, ainstance, start, end, arequest,
1321                      oanswer);
1322     osi_auditU(call, AFS_KAA_AuthEvent, code, AUD_STR, aname, AUD_STR,
1323                ainstance, AUD_END);
1324
1325     return code;
1326 }
1327
1328 afs_int32
1329 SKAM_SetFields(struct rx_call *call,
1330                char *aname,
1331                char *ainstance,
1332                afs_int32 aflags,
1333                Date aexpiration,
1334                afs_int32 alifetime,
1335                afs_int32 amaxAssociates,
1336                afs_uint32 misc_auth_bytes,      /* 4 bytes, each 0 means unspecified */
1337                afs_int32 spare2)
1338 {
1339     afs_int32 code;
1340
1341     code =
1342         kamSetFields(call, aname, ainstance, aflags, aexpiration, alifetime,
1343                      amaxAssociates, misc_auth_bytes, spare2);
1344     osi_auditU(call, AFS_KAM_SetFldEvent, code, AUD_STR, aname, AUD_STR,
1345                ainstance, AUD_LONG, aflags, AUD_DATE, aexpiration, AUD_LONG,
1346                alifetime, AUD_LONG, amaxAssociates, AUD_END);
1347     return code;
1348 }
1349
1350 afs_int32
1351 kamSetFields(struct rx_call *call,
1352              char *aname,
1353              char *ainstance,
1354              afs_int32 aflags,
1355              Date aexpiration,
1356              afs_int32 alifetime,
1357              afs_int32 amaxAssociates,
1358              afs_uint32 misc_auth_bytes,        /* 4 bytes, each 0 means unspecified */
1359              afs_int32 spare2)
1360 {
1361     afs_int32 code;
1362     Date now;
1363     struct ubik_trans *tt;
1364     afs_int32 caller;
1365     afs_int32 tentry_offset;    /* offset of entry */
1366     struct kaentry tentry;
1367     unsigned char newvals[4];
1368
1369     COUNT_REQ(SetFields);
1370
1371     if (spare2)
1372         return KABADARGUMENT;   /* not supported yet... */
1373
1374     /* make sure we're supposed to do something */
1375     if (!(aflags || aexpiration || alifetime || (amaxAssociates >= 0)
1376           || misc_auth_bytes)
1377         || ((aflags & ~KAFNORMAL) & ~KAF_SETTABLE_FLAGS))
1378         return KABADARGUMENT;   /* arguments no good */
1379     if (!name_instance_legal(aname, ainstance))
1380         return KABADNAME;
1381     if ((code = InitAuthServ(&tt, LOCKWRITE, this_op)))
1382         return code;
1383     code = check_auth(call, tt, 1, &caller);
1384     if (code) {
1385         goto abort;
1386     }
1387
1388     code = FindBlock(tt, aname, ainstance, &tentry_offset, &tentry);
1389     if (code)
1390         goto abort;
1391     if (tentry_offset == 0) {   /* no such user */
1392         code = KANOENT;
1393         goto abort;
1394     }
1395     if ((ntohl(tentry.flags) & KAFNORMAL) == 0)
1396         return KAINTERNALERROR;
1397     if (aflags) {
1398         /* Keep track of the total number of admin accounts.  This way we can
1399          * update database without any admin privilege initially */
1400         if ((aflags & KAFADMIN) != (ntohl(tentry.flags) & KAFADMIN)) {
1401             /* if admin state is changing */
1402             int delta;
1403             if (ntohl(tentry.flags) & KAFADMIN)
1404                 delta = -1;
1405             else
1406                 delta = 1;
1407             if ((code = update_admin_count(tt, delta)))
1408                 goto abort;
1409         }
1410         tentry.flags =
1411             htonl((ntohl(tentry.flags) & ~KAF_SETTABLE_FLAGS) | aflags);
1412     }
1413     if ((code = get_time(&now, tt, 1)))
1414         goto abort;
1415     if (aexpiration) {
1416         tentry.user_expiration = htonl(aexpiration);
1417         if (!ntohl(tentry.change_password_time)) {
1418             tentry.change_password_time = htonl(now);
1419         }
1420     }
1421     if (alifetime)
1422         tentry.max_ticket_lifetime = htonl(alifetime);
1423
1424 #ifndef NOPWCONTROLS
1425     /* 
1426      * We've packed a bunch of bytes into a long for backward compatibility.
1427      * These include password expiration time, and some failed login limits
1428      * counters.  Now let's unpack them and stick them into the
1429      * kaentry struct.  All the bytes have values in the range
1430      * 1..255, else they were not specified in the interface, and are
1431      * set to zero. 
1432      * In the case of password expiration times, 1 means password never
1433      * expires (==>0), 2 means password only lives for one day (==>1),
1434      * and so on.
1435      */
1436     if (misc_auth_bytes) {
1437         unpack_long(misc_auth_bytes, newvals);
1438         if (newvals[EXPIRES]) {
1439             tentry.misc_auth_bytes[EXPIRES] = newvals[EXPIRES] - 1;
1440         }
1441
1442         if (newvals[REUSEFLAGS]) {
1443             if (newvals[REUSEFLAGS] & KA_REUSEPW)
1444                 memset(tentry.pwsums, 0, KA_NPWSUMS);
1445             else if ((newvals[REUSEFLAGS] & KA_NOREUSEPW)
1446                      && !tentry.pwsums[0])
1447                 tentry.pwsums[0] = 0xff;
1448         }
1449
1450         if (newvals[ATTEMPTS]) {
1451             tentry.misc_auth_bytes[ATTEMPTS] = newvals[ATTEMPTS] - 1;
1452         }
1453         if (newvals[LOCKTIME]) {
1454             tentry.misc_auth_bytes[LOCKTIME] = newvals[LOCKTIME] - 1;
1455         }
1456 /*
1457        tentry.misc_auth_bytes = htonl(tentry.misc_auth_bytes);
1458 */
1459     }
1460 #endif /* NOPWCONTROLS */
1461
1462     if (amaxAssociates >= 0) {
1463         if ((ntohl(tentry.flags) & KAFASSOC)
1464             || (ntohl(tentry.flags) & KAFSPECIAL))
1465             return KAASSOCUSER;
1466         if (((ntohl(tentry.flags) & KAFASSOCROOT) == 0) && (amaxAssociates > 0))        /* convert normal user to assoc root */
1467             tentry.flags = htonl(ntohl(tentry.flags) | KAFASSOCROOT);
1468         tentry.misc.assocRoot.maxAssociates = htonl(amaxAssociates);
1469     }
1470
1471     tentry.modification_time = htonl(now);
1472     tentry.modification_id = htonl(caller);
1473     code = kawrite(tt, tentry_offset, &tentry, sizeof(tentry));
1474     if (code)
1475         goto abort;
1476
1477     code = ubik_EndTrans(tt);
1478     KALOG(aname, ainstance, NULL, NULL, NULL, call->conn->peer->host,
1479           LOG_SETFIELDS);
1480     return code;
1481
1482   abort:
1483     COUNT_ABO;
1484     ubik_AbortTrans(tt);
1485     return code;
1486 }
1487
1488 /* delete a user */
1489
1490 afs_int32
1491 SKAM_DeleteUser(struct rx_call *call, char *aname, char *ainstance)
1492 {
1493     afs_int32 code;
1494
1495     code = kamDeleteUser(call, aname, ainstance);
1496     osi_auditU(call, AFS_KAM_DelUserEvent, code, AUD_STR, aname, AUD_STR,
1497                ainstance, AUD_END);
1498     return code;
1499 }
1500
1501 afs_int32
1502 kamDeleteUser(struct rx_call *call, char *aname, char *ainstance)
1503 {
1504     register int code;
1505     struct ubik_trans *tt;
1506     afs_int32 caller;
1507     afs_int32 to;
1508     struct kaentry tentry;
1509     int nfailures;
1510     afs_uint32 locktime;
1511
1512     COUNT_REQ(DeleteUser);
1513     if (!name_instance_legal(aname, ainstance))
1514         return KABADNAME;
1515     if ((code = InitAuthServ(&tt, LOCKWRITE, this_op)))
1516         return code;
1517     code = check_auth(call, tt, 1, &caller);
1518     if (code) {
1519       abort:
1520         COUNT_ABO;
1521         ubik_AbortTrans(tt);
1522         return code;
1523     }
1524
1525     code = FindBlock(tt, aname, ainstance, &to, &tentry);
1526     if (code)
1527         goto abort;
1528     if (to == 0) {              /* name not found */
1529         code = KANOENT;
1530         goto abort;
1531     }
1532
1533     kaux_read(to, &nfailures, &locktime);
1534     if (nfailures || locktime)
1535         kaux_write(to, 0, 0);   /* zero failure counters at this offset */
1536
1537     /* track all AuthServer identities */
1538     if (special_name(aname, ainstance))
1539         if ((code = ka_DelKey(tt, to, &tentry)))
1540             goto abort;
1541
1542     if (ntohl(tentry.flags) & KAFADMIN) /* keep admin count up-to-date */
1543         if ((code = update_admin_count(tt, -1)))
1544             goto abort;
1545
1546     if ((code = UnthreadBlock(tt, &tentry)) || (code = FreeBlock(tt, to)) || (code = get_time(0, tt, 1))        /* update randomness */
1547         )
1548         goto abort;
1549
1550     code = ubik_EndTrans(tt);
1551     KALOG(aname, ainstance, NULL, NULL, NULL, call->conn->peer->host,
1552           LOG_DELUSER);
1553     return code;
1554 }
1555
1556 /* we set a bit in here which indicates that the user's limit of
1557  * authentication failures has been exceeded.  If that bit is not set,
1558  * kas can take it on faith that the user ID is not locked.  If that
1559  * bit is set, kas has to check all the servers to find one who will
1560  * report that the ID is not locked, or else to find out when the ID
1561  * will be unlocked.
1562  */
1563 afs_int32
1564 SKAM_GetEntry(struct rx_call *call,
1565               char *aname,
1566               char *ainstance,
1567               afs_int32 aversion,       /* major version assumed by caller */
1568               kaentryinfo *aentry)      /* entry data copied here */
1569 {
1570     afs_int32 code;
1571
1572     code = kamGetEntry(call, aname, ainstance, aversion, aentry);
1573     osi_auditU(call, AFS_KAM_GetEntEvent, code, AUD_STR, aname, AUD_STR,
1574                ainstance, AUD_END);
1575     return code;
1576 }
1577
1578 afs_int32
1579 kamGetEntry(struct rx_call *call,
1580             char *aname,
1581             char *ainstance,
1582             afs_int32 aversion,         /* major version assumed by caller */
1583             kaentryinfo *aentry)        /* entry data copied here */
1584 {
1585     register afs_int32 code;
1586     struct ubik_trans *tt;
1587     afs_int32 callerIndex;
1588     struct kaentry caller;
1589     afs_int32 to;
1590     afs_uint32 temp;
1591     struct kaentry tentry;
1592     rxkad_level enc_level = rxkad_clear;
1593     int callerIsAdmin = 0;
1594
1595     COUNT_REQ(GetEntry);
1596     if (aversion != KAMAJORVERSION)
1597         return KAOLDINTERFACE;
1598     if (!name_instance_legal(aname, ainstance))
1599         return KABADNAME;
1600     if ((code = InitAuthServ(&tt, LOCKREAD, this_op)))
1601         return code;
1602     code = check_auth(call, tt, 0, &callerIndex);
1603     if (code) {
1604         goto abort;
1605     }
1606     if (noAuthenticationRequired) {
1607     } else if (!callerIndex) {
1608         code = KANOENT;
1609         goto abort;
1610     } else {
1611         if ((code = karead(tt, callerIndex, &caller, sizeof(caller)))) {
1612             code = KAIO;
1613             goto abort;
1614         }
1615         /* if the user is checking his own entry or ADMIN then go ahead. */
1616         callerIsAdmin = (ntohl(caller.flags) & KAFADMIN);
1617
1618         if (strcmp(caller.userID.name, aname) != 0 && !callerIsAdmin) {
1619             code = KANOAUTH;
1620             goto abort;
1621         }
1622     }
1623
1624     code = FindBlock(tt, aname, ainstance, &to, &tentry);
1625     if (code)
1626         goto abort;
1627     if (to == 0) {              /* entry not found */
1628         code = KANOENT;
1629         goto abort;
1630     }
1631
1632     get_time(0, 0, 0);          /* generate random update */
1633
1634     memset(aentry, 0, sizeof(*aentry));
1635     aentry->minor_version = KAMINORVERSION;
1636     aentry->flags = ntohl(tentry.flags);
1637     aentry->user_expiration = ntohl(tentry.user_expiration);
1638     aentry->modification_time = ntohl(tentry.modification_time);
1639     aentry->change_password_time = ntohl(tentry.change_password_time);
1640     aentry->max_ticket_lifetime = ntohl(tentry.max_ticket_lifetime);
1641     aentry->key_version = ntohl(tentry.key_version);
1642
1643     temp = (unsigned char)tentry.misc_auth_bytes[LOCKTIME];
1644     temp = temp << 9;
1645     if (kaux_islocked(to, (u_int) tentry.misc_auth_bytes[ATTEMPTS], temp))
1646         tentry.misc_auth_bytes[REUSEFLAGS] |= KA_ISLOCKED;      /* saves an RPC */
1647
1648     temp = pack_long(tentry.misc_auth_bytes);
1649     aentry->misc_auth_bytes = temp;
1650     /* 
1651      * only return user's key if security disabled or if admin and
1652      * we have an encrypted connection to the user
1653      */
1654     rxkad_GetServerInfo(call->conn, &enc_level, 0, 0, 0, 0, 0);
1655     if ((noAuthenticationRequired)
1656         || (callerIsAdmin && enc_level == rxkad_crypt))
1657         memcpy(&aentry->key, &tentry.key, sizeof(struct ktc_encryptionKey));
1658     else
1659         memset(&aentry->key, 0, sizeof(aentry->key));
1660     code = ka_KeyCheckSum((char *)&tentry.key, &aentry->keyCheckSum);
1661     if (!tentry.pwsums[0] && npwSums > 1 && !tentry.pwsums[1]) {
1662         aentry->reserved3 = 0x12340000;
1663     } else {
1664         aentry->reserved3 = 0x12340001;
1665     }
1666
1667     /* Now get entry of user who last modified this entry */
1668     if (ntohl(tentry.modification_id)) {
1669         temp = ntohl(tentry.modification_id);
1670         code = karead(tt, temp, &tentry, sizeof(tentry));
1671         if (code) {
1672             code = KAIO;
1673             goto abort;
1674         }
1675         aentry->modification_user = tentry.userID;
1676     } else {
1677         strcpy(aentry->modification_user.name, "<none>");
1678         strcpy(aentry->modification_user.instance, "\0");
1679     }
1680     code = ubik_EndTrans(tt);
1681     return code;
1682
1683   abort:
1684     COUNT_ABO;
1685     ubik_AbortTrans(tt);
1686     return code;
1687 }
1688
1689 afs_int32
1690 SKAM_ListEntry(struct rx_call *call,
1691                afs_int32 previous_index, /* last entry ret'd or 0 for first */
1692                afs_int32 *index,         /* index of this entry */
1693                afs_int32 *count,         /* total entries in database */
1694                kaident *name)            /* name & instance of this entry */
1695 {
1696     afs_int32 code;
1697
1698     code = kamListEntry(call, previous_index, index, count, name);
1699     osi_auditU(call, AFS_KAM_LstEntEvent, code, AUD_LONG, *index, AUD_END);
1700     return code;
1701 }
1702
1703
1704 afs_int32
1705 kamListEntry(struct rx_call *call,
1706              afs_int32 previous_index,  /* last entry ret'd or 0 for first */
1707              afs_int32 *index,          /* index of this entry */
1708              afs_int32 *count,          /* total entries in database */
1709              kaident *name)             /* name & instance of this entry */
1710 {
1711     register int code;
1712     struct ubik_trans *tt;
1713     afs_int32 caller;
1714     struct kaentry tentry;
1715
1716     COUNT_REQ(ListEntry);
1717     if ((code = InitAuthServ(&tt, LOCKREAD, this_op)))
1718         return code;
1719     code = check_auth(call, tt, 1, &caller);
1720     if (code) {
1721         goto abort;
1722     }
1723
1724     *index = NextBlock(tt, previous_index, &tentry, count);
1725     if (*count < 0) {
1726         code = KAIO;
1727         goto abort;
1728     }
1729
1730     if (*index) {               /* return name & inst of this entry */
1731         strncpy(name->name, tentry.userID.name, sizeof(name->name));
1732         strncpy(name->instance, tentry.userID.instance,
1733                 sizeof(name->instance));
1734     } else {
1735         strcpy(name->name, "\0");
1736         strcpy(name->instance, "\0");
1737     }
1738     code = ubik_EndTrans(tt);
1739     return code;
1740
1741   abort:
1742     COUNT_ABO;
1743     ubik_AbortTrans(tt);
1744     return code;
1745 }
1746
1747 static afs_int32
1748 GetTicket(int version,
1749           struct rx_call *call,
1750           afs_int32 kvno,
1751           char *authDomain,
1752           ka_CBS *aticket,
1753           char *sname,
1754           char *sinstance,
1755           ka_CBS *atimes,               /* encrypted start & end time */
1756           ka_BBS *oanswer)
1757 {
1758     afs_int32 code;
1759     int import, export;
1760     struct ubik_trans *tt;
1761     struct ktc_encryptionKey tgskey;
1762     des_key_schedule schedule;
1763     afs_int32 to;
1764     char name[MAXKTCNAMELEN];
1765     char instance[MAXKTCNAMELEN];
1766     char cell[MAXKTCNAMELEN];
1767     int celllen;
1768     struct kaentry caller;
1769     struct kaentry server;
1770     struct ktc_encryptionKey authSessionKey;
1771     struct ktc_encryptionKey sessionKey;
1772     int ticketLen;
1773     char ticket[MAXKTCTICKETLEN];
1774     afs_int32 host;
1775     Date start;
1776     Date expiration;
1777     Date now;
1778     Date end;
1779     struct ka_getTicketTimes times;
1780     struct ka_getTicketAnswer *answer;
1781
1782     COUNT_REQ(GetTicket);
1783     if (!name_instance_legal(sname, sinstance))
1784         return KABADNAME;
1785     if (atimes->SeqLen != sizeof(times))
1786         return KABADARGUMENT;
1787     if ((code = InitAuthServ(&tt, LOCKREAD, this_op)))
1788         return code;
1789
1790     export = import = 0;
1791     if ((strcmp(sname, KA_TGS_NAME) == 0) && (strcmp(sinstance, lrealm) != 0))
1792         export = 1;
1793     if ((strlen(authDomain) > 0) && (strcmp(authDomain, lrealm) != 0))
1794         import = 1;
1795
1796     if (strlen(authDomain) == 0)
1797         authDomain = lrealm;
1798     code = ka_LookupKvno(tt, KA_TGS_NAME, authDomain, kvno, &tgskey);
1799     if (code) {
1800         goto abort;
1801     }
1802     code =
1803         tkt_DecodeTicket(aticket->SeqBody, aticket->SeqLen, &tgskey, name,
1804                          instance, cell, &authSessionKey, &host, &start,
1805                          &expiration);
1806     if (code) {
1807         code = KANOAUTH;
1808         goto abort;
1809     }
1810     save_principal(tgsPrincipal, name, instance, cell);
1811
1812     if ((code = get_time(&now, 0, 0)))
1813         goto abort;
1814
1815     code = tkt_CheckTimes(start, expiration, now);
1816     if (code <= 0) {
1817         if (code == -1)
1818             code = RXKADEXPIRED;
1819         else
1820             code = KANOAUTH;
1821         goto abort;
1822     }
1823     code = des_key_sched(&authSessionKey, schedule);
1824     if (code) {
1825         code = KANOAUTH;
1826         goto abort;
1827     }
1828     celllen = strlen(cell);
1829     if (import && (celllen == 0)) {
1830         code = KABADTICKET;
1831         goto abort;
1832     }
1833     if (export && (celllen == 0))
1834         strcpy(cell, lrealm);
1835
1836     if (!krb4_cross && celllen && strcmp(lrealm, cell) != 0) {
1837         code = KABADUSER;
1838         goto abort;
1839     }
1840
1841     des_ecb_encrypt(atimes->SeqBody, &times, schedule, DECRYPT);
1842     times.start = ntohl(times.start);
1843     times.end = ntohl(times.end);
1844     code = tkt_CheckTimes(times.start, times.end, now);
1845     if (code < 0) {
1846         code = KABADREQUEST;
1847         goto abort;
1848     }
1849
1850     if (import) {
1851         strcpy(caller.userID.name, name);
1852         strcpy(caller.userID.instance, instance);
1853         caller.max_ticket_lifetime = htonl(MAXKTCTICKETLIFETIME);
1854         caller.flags = htonl(KAFNORMAL);
1855         caller.user_expiration = htonl(NEVERDATE);
1856     } else {
1857         code = FindBlock(tt, name, instance, &to, &caller);
1858         if (code)
1859             goto abort;
1860         if (to == 0) {
1861             ka_PrintUserID("GetTicket: User ", name, instance, " unknown.\n");
1862             code = KANOENT;
1863             goto abort;
1864         }
1865     }
1866
1867     /* get server's entry */
1868     code = FindBlock(tt, sname, sinstance, &to, &server);
1869     if (code)
1870         goto abort;
1871     if (to == 0) {              /* entry not found */
1872         ka_PrintUserID("GetTicket: Server ", sname, sinstance, " unknown.\n");
1873         code = KANOENT;
1874         goto abort;
1875     }
1876     save_principal(tgsServerPrincipal, sname, sinstance, 0);
1877
1878     code = des_random_key(&sessionKey);
1879     if (code) {
1880         code = KANOKEYS;
1881         goto abort;
1882     }
1883
1884     code =
1885         GetEndTime(times.start, times.end, expiration, &caller, &server,
1886                    &end);
1887     if (code)
1888         goto abort;
1889
1890     code =
1891         tkt_MakeTicket(ticket, &ticketLen, &server.key, caller.userID.name,
1892                        caller.userID.instance, cell, times.start, end,
1893                        &sessionKey,
1894                        rx_HostOf(rx_PeerOf(rx_ConnectionOf(call))),
1895                        server.userID.name, server.userID.instance);
1896     if (code)
1897         goto abort;
1898
1899     switch (version) {
1900     case 0:
1901         code = KAANSWERTOOLONG;
1902         if (oanswer->MaxSeqLen <
1903             sizeof(struct ka_getTicketAnswer) - 5 * MAXKTCNAMELEN -
1904             MAXKTCTICKETLEN + ticketLen)
1905             goto abort;
1906
1907         answer = (struct ka_getTicketAnswer *)oanswer->SeqBody;
1908         memcpy(&answer->sessionKey, &sessionKey,
1909                sizeof(struct ktc_encryptionKey));
1910         answer->startTime = htonl(times.start);
1911         answer->endTime = htonl(end);
1912         answer->kvno = server.key_version;
1913         answer->ticketLen = htonl(ticketLen);
1914
1915         {
1916             char *ans = answer->name;   /* ptr to variable part of answer */
1917             int rem, len;
1918
1919             /* space remaining */
1920             rem = oanswer->MaxSeqLen - (ans - oanswer->SeqBody);
1921 #undef putstr
1922 #define putstr(str) len = strlen (str)+1;\
1923             if (rem < len) goto abort;\
1924             strcpy (ans, str);\
1925             ans += len; rem -= len
1926
1927             putstr(name);
1928             putstr(instance);
1929             putstr(cell);
1930             putstr(sname);
1931             putstr(sinstance);
1932             if (rem < ticketLen)
1933                 goto abort;
1934             memcpy(ans, ticket, ticketLen);
1935             oanswer->SeqLen = (ans - oanswer->SeqBody) + ticketLen;
1936         }
1937         oanswer->SeqLen = round_up_to_ebs(oanswer->SeqLen);
1938         break;
1939     case 1:
1940         code =
1941             PrepareTicketAnswer(oanswer, /*challenge */ 0, ticket, ticketLen,
1942                                 &sessionKey, times.start, end, &caller,
1943                                 &server, cell, KA_GETTICKET_ANS_LABEL);
1944         if (code)
1945             goto abort;
1946         break;
1947     default:
1948         code = KAINTERNALERROR;
1949         goto abort;
1950     }
1951     des_pcbc_encrypt(oanswer->SeqBody, oanswer->SeqBody, oanswer->SeqLen,
1952                      schedule, &authSessionKey, ENCRYPT);
1953     code = ubik_EndTrans(tt);
1954     KALOG(name, instance, sname, sinstance, (import ? authDomain : NULL),
1955           call->conn->peer->host, LOG_GETTICKET);
1956     return code;
1957
1958   abort:
1959     COUNT_ABO;
1960     ubik_AbortTrans(tt);
1961     return code;
1962 }
1963
1964 afs_int32
1965 SKAT_GetTicket_old(struct rx_call *call,
1966                    afs_int32 kvno,
1967                    char *authDomain,
1968                    ka_CBS *aticket,
1969                    char *sname,
1970                    char *sinstance,
1971                    ka_CBS *atimes,              /* encrypted start & end time */
1972                    ka_BBS *oanswer)
1973 {
1974     int code;
1975
1976     sleep(1);                   /* strongly discourage this */
1977     code =
1978         GetTicket(0, call, kvno, authDomain, aticket, sname, sinstance,
1979                   atimes, oanswer);
1980
1981     osi_auditU(call, AFS_KAT_GetTicketOEvent, code, AUD_STR, sname, AUD_STR,
1982                sinstance, AUD_END);
1983     return code;
1984 }
1985
1986 afs_int32
1987 SKAT_GetTicket(struct rx_call *call,
1988                afs_int32 kvno,
1989                char *authDomain,
1990                ka_CBS *aticket,
1991                char *sname,
1992                char *sinstance,
1993                ka_CBS *atimes,          /* encrypted start & end time */
1994                ka_BBS *oanswer)
1995 {
1996     int code;
1997
1998     code =
1999         GetTicket(1, call, kvno, authDomain, aticket, sname, sinstance,
2000                   atimes, oanswer);
2001     osi_auditU(call, AFS_KAT_GetTicketEvent, code, AUD_STR, sname, AUD_STR,
2002                sinstance, AUD_END);
2003     return code;
2004 }
2005
2006 afs_int32
2007 SKAM_GetStats(struct rx_call *call, afs_int32 version, 
2008               afs_int32 *admin_accounts, kasstats *statics,
2009               kadstats *dynamics)
2010 {
2011     afs_int32 code;
2012
2013     code = kamGetStats(call, version, admin_accounts, statics, dynamics);
2014     osi_auditU(call, AFS_KAM_GetStatEvent, code, AUD_END);
2015     return code;
2016 }
2017
2018 afs_int32
2019 kamGetStats(struct rx_call *call, afs_int32 version, 
2020             afs_int32 *admin_accounts, kasstats *statics,
2021             kadstats *dynamics)
2022 {
2023     afs_int32 code;
2024     struct ubik_trans *tt;
2025     afs_int32 caller;
2026
2027     COUNT_REQ(GetStats);
2028     if (version != KAMAJORVERSION)
2029         return KAOLDINTERFACE;
2030     if ((code = InitAuthServ(&tt, LOCKREAD, this_op)))
2031         return code;
2032     code = check_auth(call, tt, 1, &caller);
2033     if (code) {
2034         COUNT_ABO;
2035         ubik_AbortTrans(tt);
2036         return code;
2037     }
2038
2039     *admin_accounts = ntohl(cheader.admin_accounts);
2040     /* memcpy((char *)statics, (char *)&cheader.stats, sizeof(kasstats)); */
2041     /* these are stored in network byte order and must be copied */
2042     statics->allocs = ntohl(cheader.stats.allocs);
2043     statics->frees = ntohl(cheader.stats.frees);
2044     statics->cpws = ntohl(cheader.stats.cpws);
2045 #if KADBVERSION != 5
2046     check that the statistics command copies all the fields
2047 #endif
2048       memcpy((char *)dynamics, (char *)&dynamic_statistics, sizeof(kadstats));
2049     statics->minor_version = KAMINORVERSION;
2050     dynamics->minor_version = KAMINORVERSION;
2051
2052     {
2053         int used = 0;
2054         int i;
2055
2056         for (i = 0; i < HASHSIZE; i++)
2057             if (cheader.nameHash[i])
2058                 used++;
2059         dynamics->hashTableUtilization =
2060             (used * 10000 + HASHSIZE / 2) / HASHSIZE;
2061     }
2062     {
2063 #if !defined(AFS_AIX_ENV) && !defined(AFS_HPUX_ENV) && !defined(AFS_NT40_ENV)
2064         struct rusage ru;
2065         /* Unfortunately, although aix_22 has a minimal compatibility
2066          * method of getting to some rusage fields (i.e. stime &
2067          * utime), the version that we have doesn't even have the
2068          * related include files needed for the aix vtimes() call; so
2069          * ignore this for aix till v3.1... */
2070         getrusage(RUSAGE_SELF, &ru);
2071 #if (KAMAJORVERSION>5)
2072         memcpy(&dynamics->utime, &ru.ru_utime, sizeof(struct katimeval));
2073         memcpy(&dynamics->stime, &ru.ru_stime, sizeof(struct katimeval));
2074         dynamics->dataSize = ru.ru_idrss;
2075         dynamics->stackSize = ru.ru_isrss;
2076         dynamics->pageFailts = ru.ru_majflt;
2077 #else
2078         dynamics->string_checks =
2079             (afs_int32) (1000.0 *
2080                          ((ru.ru_utime.tv_sec +
2081                            ru.ru_utime.tv_usec / 1000000.0) +
2082                           (ru.ru_stime.tv_sec +
2083                            ru.ru_stime.tv_usec / 1000000.0)));
2084 #endif
2085 #endif /* AFS_AIX_ENV && AFS_HPUX_ENV && AFS_NT40_ENV */
2086     }
2087
2088     code = ubik_EndTrans(tt);
2089     return code;
2090 }
2091
2092 afs_int32
2093 SKAM_GetPassword(struct rx_call *call, char *name, EncryptionKey *password)
2094 {
2095     afs_int32 code;
2096
2097     code = kamGetPassword(call, name, password);
2098     osi_auditU(call, AFS_KAM_GetPswdEvent, code, AUD_STR, name, AUD_END);
2099     return code;
2100 }
2101
2102 afs_int32
2103 kamGetPassword(struct rx_call *call, char *name, EncryptionKey *password)
2104 {
2105     int code = KANOAUTH;
2106     COUNT_REQ(GetPassword);
2107
2108 #ifdef GETPASSWORD
2109     {
2110         afs_int32 to;
2111         struct ubik_trans *tt;
2112         struct kaentry tentry;
2113
2114         if (!name_instance_legal(name, ""))
2115             return KABADNAME;
2116         /* only requests from this host work */
2117         if (rx_HostOf(rx_PeerOf(rx_ConnectionOf(call))) !=
2118             htonl(INADDR_LOOPBACK))
2119             return KANOAUTH;
2120         if (code = InitAuthServ(&tt, LOCKREAD, this_op))
2121             return code;
2122
2123         /* this isn't likely to be used because of string to key problems, so since
2124          * this is a temporary thing anyway, we'll use it here. */
2125         {
2126             extern char udpAuthPrincipal[256];
2127
2128             save_principal(udpAuthPrincipal, name, 0, 0);
2129         }
2130
2131         get_time(0, 0, 0);      /* update random value */
2132         code = FindBlock(tt, name, "", &to, &tentry);
2133         if (code)
2134             goto abort;
2135         if (to == 0) {
2136             code = KANOENT;
2137           abort:
2138             COUNT_ABO;
2139             ubik_AbortTrans(tt);
2140             return code;
2141         }
2142
2143         memcpy(password, &tentry.key, sizeof(*password));
2144         code = ubik_EndTrans(tt);
2145     }
2146 #endif
2147     return code;
2148 }
2149
2150 afs_int32
2151 SKAM_GetRandomKey(struct rx_call *call, EncryptionKey *key)
2152 {
2153     afs_int32 code;
2154
2155     code = kamGetRandomKey(call, key);
2156     osi_auditU(call, AFS_KAM_GetRndKeyEvent, code, AUD_END);
2157     return code;
2158 }
2159
2160 afs_int32
2161 kamGetRandomKey(struct rx_call *call, EncryptionKey *key)
2162 {
2163     int code;
2164
2165     COUNT_REQ(GetRandomKey);
2166     if ((code = AwaitInitialization()))
2167         return code;
2168     code = des_random_key(key);
2169     if (code)
2170         return KANOKEYS;
2171     return 0;
2172 }
2173
2174 afs_int32
2175 SKAM_Debug(struct rx_call *call,
2176            afs_int32 version,
2177            int checkDB,         /* start a transaction to examine DB */
2178            struct ka_debugInfo *info)
2179 {
2180     afs_int32 code;
2181
2182     code = kamDebug(call, version, checkDB, info);
2183     osi_auditU(call, AFS_KAM_DbgEvent, code, AUD_END);
2184     return code;
2185 }
2186
2187 afs_int32
2188 kamDebug(struct rx_call *call,
2189          afs_int32 version,
2190          int checkDB,           /* start a transaction to examine DB */
2191          struct ka_debugInfo *info)
2192 {
2193 /*  COUNT_REQ (Debug); */
2194     if (sizeof(struct kaentry) != sizeof(struct kaOldKeys))
2195         return KAINTERNALERROR;
2196     if (sizeof(struct ka_cpwRequest) % 8)
2197         return KAINTERNALERROR;
2198     if (version != KAMAJORVERSION)
2199         return KAOLDINTERFACE;
2200
2201     memset(info, 0, sizeof(*info));
2202
2203     info->minorVersion = KAMINORVERSION;
2204     info->host = dynamic_statistics.host;
2205     info->startTime = dynamic_statistics.start_time;
2206     info->
2207 #if (KAMAJORVERSION>5)
2208         now
2209 #else
2210         reserved1
2211 #endif
2212         = time(0);
2213     info->noAuth = noAuthenticationRequired;
2214
2215     info->dbVersion = ntohl(cheader.version);
2216     info->dbFreePtr = ntohl(cheader.freePtr);
2217     info->dbEofPtr = ntohl(cheader.eofPtr);
2218     info->dbKvnoPtr = ntohl(cheader.kvnoPtr);
2219     info->dbSpecialKeysVersion = ntohl(cheader.specialKeysVersion);
2220
2221     info->dbHeaderRead = cheaderReadTime;
2222     info->lastTrans = lastTrans;
2223     if (!lastOperation)
2224         lastOperation = "(Not Available)";
2225     strncpy(info->lastOperation, lastOperation, sizeof(info->lastOperation));
2226     strncpy(info->lastAuth, authPrincipal, sizeof(info->lastAuth));
2227     strncpy(info->lastTGS, tgsPrincipal, sizeof(info->lastTGS));
2228     strncpy(info->lastAdmin, adminPrincipal, sizeof(info->lastAdmin));
2229     strncpy(info->lastTGSServer, tgsServerPrincipal,
2230             sizeof(info->lastTGSServer));
2231     {
2232         extern char udpAuthPrincipal[256];
2233         extern char udptgsPrincipal[256];
2234         extern char udptgsServerPrincipal[256];
2235
2236         strncpy(info->lastUAuth, udpAuthPrincipal, sizeof(info->lastUAuth));
2237         strncpy(info->lastUTGS, udptgsPrincipal, sizeof(info->lastUTGS));
2238         strncpy(info->lastUTGSServer, udptgsServerPrincipal,
2239                 sizeof(info->lastUTGSServer));
2240     }
2241     info->nextAutoCPW = nextAutoCPWTime;
2242     info->updatesRemaining = autoCPWUpdates - totalUpdates;
2243     ka_debugKeyCache(info);
2244     return 0;
2245 }
2246
2247 /* these are auxiliary routines. They don't do any Ubik stuff.  They use 
2248  * a tacked-on-the-side data file.
2249  * prob'ly ought to check the noauth flag.
2250  */
2251 #define ABORTIF(A) {if((code = A)){goto abort;}}
2252 afs_int32
2253 SKAM_Unlock(struct rx_call *call,
2254             char *aname,
2255             char *ainstance,
2256             afs_int32 spare1, 
2257             afs_int32 spare2, 
2258             afs_int32 spare3, 
2259             afs_int32 spare4)
2260 {
2261     register int code;
2262     struct ubik_trans *tt;
2263     afs_int32 caller;
2264     afs_int32 to;
2265     struct kaentry tentry;
2266
2267     COUNT_REQ(Unlock);
2268     if (!name_instance_legal(aname, ainstance)) {
2269         code = KABADNAME;
2270         goto exit;
2271     }
2272     if ((code = InitAuthServ(&tt, LOCKREAD, this_op)))
2273         goto exit;
2274
2275     ABORTIF(check_auth(call, tt, 1, &caller));
2276     ABORTIF(FindBlock(tt, aname, ainstance, &to, &tentry));
2277     ABORTIF((to == 0 ? KANOENT : 0));
2278
2279     kaux_write(to, 0, 0);       /* zero failure counters at this offset */
2280
2281     code = ubik_EndTrans(tt);
2282     KALOG(aname, ainstance, NULL, NULL, NULL, call->conn->peer->host,
2283           LOG_UNLOCK);
2284     goto exit;
2285
2286   abort:
2287     COUNT_ABO;
2288     ubik_AbortTrans(tt);
2289
2290   exit:
2291     osi_auditU(call, UnlockEvent, code, AUD_STR, aname, AUD_STR, ainstance,
2292                AUD_END);
2293     return code;
2294 }
2295
2296 afs_int32
2297 SKAM_LockStatus(struct rx_call *call,
2298                 char *aname,
2299                 char *ainstance,
2300                 afs_int32 *lockeduntil,
2301                 afs_int32 spare1, 
2302                 afs_int32 spare2, 
2303                 afs_int32 spare3, 
2304                 afs_int32 spare4)
2305 {
2306     register int code;
2307     struct ubik_trans *tt;
2308     afs_int32 callerIndex;
2309     afs_int32 to;
2310     struct kaentry caller;
2311     struct kaentry tentry;
2312     afs_uint32 temp;
2313
2314     COUNT_REQ(LockStatus);
2315
2316     if (!name_instance_legal(aname, ainstance)) {
2317         code = KABADNAME;
2318         goto exit;
2319     }
2320     if ((code = InitAuthServ(&tt, LOCKREAD, this_op)))
2321         goto exit;
2322
2323     if ((code = check_auth(call, tt, 0, &callerIndex)))
2324         goto abort;
2325
2326     if (!noAuthenticationRequired && callerIndex) {
2327         if (karead(tt, callerIndex, &caller, sizeof(caller))) {
2328             code = KAIO;
2329             goto abort;
2330         }
2331         /* if the user is checking his own entry or ADMIN then go ahead. */
2332         if ((strcmp(caller.userID.name, aname) != 0)
2333             && !(ntohl(caller.flags) & KAFADMIN)) {
2334             code = KANOAUTH;
2335             goto abort;
2336         }
2337     }
2338
2339     if ((code = FindBlock(tt, aname, ainstance, &to, &tentry)))
2340         goto abort;
2341
2342     if (to == 0) {
2343         code = KANOENT;
2344         goto abort;
2345     }
2346
2347     temp = (unsigned char)tentry.misc_auth_bytes[LOCKTIME];
2348     temp = temp << 9;
2349     *lockeduntil =
2350         kaux_islocked(to, (u_int) tentry.misc_auth_bytes[ATTEMPTS], temp);
2351
2352     code = ubik_EndTrans(tt);
2353     goto exit;
2354
2355   abort:
2356     COUNT_ABO;
2357     ubik_AbortTrans(tt);
2358     osi_auditU(call, LockStatusEvent, code, AUD_STR, aname, AUD_STR,
2359                ainstance, AUD_END);
2360
2361   exit:
2362     return code;
2363 }