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