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