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