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