ticket-1241-20040414
[openafs.git] / src / auth / ktc.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 /* ticket caching code */
11
12 #include <afsconfig.h>
13 #if defined(UKERNEL)
14 #include "afs/param.h"
15 #else
16 #include <afs/param.h>
17 #endif
18
19 RCSID
20     ("$Header$");
21
22 #if defined(UKERNEL)
23 #include "afs/sysincludes.h"
24 #include "afsincludes.h"
25 #include "afs/stds.h"
26 #include "afs/pthread_glock.h"
27 #include "afs/vice.h"
28 #include "afs/auth.h"
29 #include "afs/venus.h"
30 #include "afs/pthread_glock.h"
31 #include "afs/dirpath.h"
32
33 #if !defined(min)
34 #define min(a,b) ((a)<(b)?(a):(b))
35 #endif /* !defined(min) */
36
37 #else /* defined(UKERNEL) */
38
39 #ifdef  AFS_SUN5_ENV
40 #include <unistd.h>
41 #endif
42 #include <stdio.h>
43 #include <afs/stds.h>
44 #include <afs/pthread_glock.h>
45 #include <sys/types.h>
46 #include <ctype.h>
47 #include <sys/stat.h>
48 #include <signal.h>
49 #include <errno.h>
50 #include <sys/ioctl.h>
51 #include <netinet/in.h>
52 #ifdef HAVE_STRING_H
53 #include <string.h>
54 #else
55 #ifdef HAVE_STRINGS_H
56 #include <strings.h>
57 #endif
58 #endif
59 #include <afs/vice.h>
60 #ifdef  AFS_AIX_ENV
61 #include <sys/lockf.h>
62 #endif
63 #ifdef HAVE_UNISTD_H
64 #include <unistd.h>
65 #endif
66 #include "auth.h"
67 #include <afs/venus.h>
68 #include <afs/afsutil.h>
69
70 #endif /* defined(UKERNEL) */
71
72 /* For malloc() */
73 #include <stdlib.h>
74
75
76 #ifdef  notdef
77 /* AFS_KERBEROS_ENV is now conditionally defined in the Makefile */
78 #define AFS_KERBEROS_ENV
79 #endif
80
81 #ifdef AFS_KERBEROS_ENV
82 #include <fcntl.h>
83 #include <sys/file.h>
84 #include "cellconfig.h"
85 static char lcell[MAXCELLCHARS];
86
87 #define TKT_ROOT "/tmp/tkt"
88
89 #define KSUCCESS 0
90 #define KFAILURE 255
91
92 /* Definitions for ticket file utilities */
93 #define R_TKT_FIL       0
94 #define W_TKT_FIL       1
95
96 /* Error codes returned by ticket file utilities */
97 #define         NO_TKT_FIL      76      /* No ticket file found */
98 #define         TKT_FIL_ACC     77      /* Couldn't access tkt file */
99 #define         TKT_FIL_LCK     78      /* Couldn't lock ticket file */
100 #define         TKT_FIL_FMT     79      /* Bad ticket file format */
101 #define         TKT_FIL_INI     80      /* afs_tf_init not called first */
102
103 /* Values returned by get_credentials */
104 #define         RET_TKFIL      21       /* Can't read ticket file */
105
106 #ifndef BUFSIZ
107 #define BUFSIZ 4096
108 #endif
109
110 #ifdef  AFS_HPUX_ENV
111 #include <unistd.h>
112 #endif
113 #if     defined(AFS_AIX_ENV) || defined(AFS_SUN5_ENV)
114 static struct flock fileWlock = { F_WRLCK, 0, 0, 0, 0, 0 };
115 static struct flock fileRlock = { F_RDLCK, 0, 0, 0, 0, 0 };
116 static struct flock fileUlock = { F_UNLCK, 0, 0, 0, 0, 0 };
117 #endif
118 #ifdef AFS_HPUX_ENV
119 static struct flock fileWlock = { F_WRLCK, 0, 0, 0, 0 };
120 static struct flock fileRlock = { F_RDLCK, 0, 0, 0, 0 };
121 static struct flock fileUlock = { F_UNLCK, 0, 0, 0, 0 };
122 #endif
123
124 #ifndef EOF
125 #define EOF (-1)
126 #endif
127
128 /* the following routines aren't static anymore on behalf of the kerberos IV
129  * compatibility library built in subtree krb.
130  */
131 int afs_tf_init(), afs_tf_get_pname(), afs_tf_get_pinst(), afs_tf_get_cred();
132 int afs_tf_save_cred(), afs_tf_close(), afs_tf_create();
133 int afs_tf_dest_tkt();
134 static void ktc_LocalCell();
135 char *ktc_tkt_string();
136 #endif /* AFS_KERBEROS_ENV */
137
138 #ifdef AFS_DUX40_ENV
139 #define PIOCTL afs_pioctl
140 #elif defined(UKERNEL)
141 #define PIOCTL(A,B,C,D) call_syscall(AFSCALL_PIOCTL,A,B,C,D)
142 #else
143 #define PIOCTL pioctl
144 #endif
145
146
147 #ifdef KERNEL_KTC_COMPAT
148
149 #ifndef KTC_SYSCALL
150 #define KTC_SYSCALL     32
151 #endif
152
153 /* Kernel call opcode definitions */
154 #define KTC_OPCODE_BASE         4300
155 #define KTC_NO_OP               (0+KTC_OPCODE_BASE)
156 #define KTC_SETTOKEN_OP         (1+KTC_OPCODE_BASE)
157 #define KTC_GETTOKEN_OP         (2+KTC_OPCODE_BASE)
158 #define KTC_LISTTOKENS_OP       (3+KTC_OPCODE_BASE)
159 #define KTC_FORGETTOKEN_OP      (4+KTC_OPCODE_BASE)
160 #define KTC_FORGETALLTOKENS_OP  (5+KTC_OPCODE_BASE)
161 #define KTC_STATUS_OP           (6+KTC_OPCODE_BASE)
162 #define KTC_GC_OP               (7+KTC_OPCODE_BASE)
163
164 #define KTC_INTERFACE_VERSION 3
165
166 /* We have to determine if the kernel supports the ktc system call.  To do so
167  * we attempt to execute its noop function.  If this is successful we use the
168  * kernel calls in the future otherwise we let the old code run. */
169
170 /* To safely check to see whether a system call exists we have to intercept the
171  * SIGSYS signal which is caused by executing a non-existant system call.  If
172  * it is ignored the syscall routine returns EINVAL.  The SIGSYS is reset to
173  * its old value after the return from syscall.  */
174
175 static int kernelKTC = 0;
176
177 #ifdef  AFS_DECOSF_ENV
178 /*
179  * SIGSYS semantics are broken on Dec AXP OSF/1 v1.2 systems.  You need
180  * to ignore SIGTRAP too.  It is claimed to be fixed under v1.3, but...
181  */
182
183 #define CHECK_KERNEL                                                    \
184     if (kernelKTC == 0) {                                               \
185         int code, ecode;                                                \
186         int (*old)();                                                   \
187         int (*old_t)();                                                 \
188         old = (int (*)())signal(SIGSYS, SIG_IGN);                       \
189         old_t = (int (*)())signal(SIGTRAP, SIG_IGN);                    \
190         code = syscall (KTC_SYSCALL, KTC_NO_OP, 0,0,0,0,0);             \
191         ecode = errno;                                                  \
192         signal(SIGSYS, old);                                            \
193         signal(SIGTRAP, old_t);                                         \
194         if (code == 0) kernelKTC = 1;                                   \
195         else kernelKTC = 2;                                             \
196 /* printf ("returned from KTC_NO_OP kernelKTC <= %d; code=%d, errno=%d\n", kernelKTC, code, errno); */\
197     }
198
199 #else /* AFS_DECOSF_ENV */
200
201 #define CHECK_KERNEL                                                    \
202     if (kernelKTC == 0) {                                               \
203         int code, ecode;                                                \
204         int (*old)();                                                   \
205         old = (int (*)())signal(SIGSYS, SIG_IGN);                       \
206         code = syscall (KTC_SYSCALL, KTC_NO_OP, 0,0,0,0,0);             \
207         ecode = errno;                                                  \
208         signal(SIGSYS, old);                                            \
209         if (code == 0) kernelKTC = 1;                                   \
210         else kernelKTC = 2;                                             \
211 /* printf ("returned from KTC_NO_OP kernelKTC <= %d; code=%d, errno=%d\n", kernelKTC, code, errno); */\
212     }
213 #endif /* AFS_DECOSF_ENV */
214
215 #define TRY_KERNEL(cmd,a1,a2,a3,a4)                                     \
216 {   CHECK_KERNEL;                                                       \
217     if (kernelKTC == 1)                                                 \
218         return syscall (KTC_SYSCALL, cmd,                               \
219                         KTC_INTERFACE_VERSION, a1,a2,a3,a4);            \
220 }
221
222 #else
223 #define TRY_KERNEL(cmd,a1,a2,a3,a4)
224 #endif /* KERNEL_KTC_COMPAT */
225
226 #if !defined(UKERNEL)
227 /* this is a structure used to communicate with the afs cache mgr, but is
228  * otherwise irrelevant */
229 struct ClearToken {
230     afs_int32 AuthHandle;
231     char HandShakeKey[8];
232     afs_int32 ViceId;
233     afs_int32 BeginTimestamp;
234     afs_int32 EndTimestamp;
235 };
236 #endif /* !defined(UKERNEL) */
237
238 #define MAXLOCALTOKENS 4
239
240 static struct {
241     int valid;
242     struct ktc_principal server;
243     struct ktc_principal client;
244     struct ktc_token token;
245 } local_tokens[MAXLOCALTOKENS] = { {
246 0}, {
247 0}, {
248 0}, {
249 0}};
250
251 /* new interface routines to the ticket cache.  Only handle afs service right
252  * now. */
253
254 static int
255 NewSetToken(aserver, atoken, aclient, flags)
256      struct ktc_principal *aserver;
257      struct ktc_principal *aclient;
258      struct ktc_token *atoken;
259      afs_int32 flags;
260 {
261     TRY_KERNEL(KTC_SETTOKEN_OP, aserver, aclient, atoken,
262                sizeof(struct ktc_token));
263     /* no kernel ticket cache */
264     return EINVAL;
265 }
266
267 #define MAXPIOCTLTOKENLEN \
268 (3*sizeof(afs_int32)+MAXKTCTICKETLEN+sizeof(struct ClearToken)+MAXKTCREALMLEN)
269
270 static int
271 OldSetToken(aserver, atoken, aclient, flags)
272      struct ktc_principal *aserver, *aclient;
273      struct ktc_token *atoken;
274      afs_int32 flags;
275 {
276     struct ViceIoctl iob;
277     char tbuffer[MAXPIOCTLTOKENLEN];
278     register char *tp;
279     struct ClearToken ct;
280     register afs_int32 code;
281     afs_int32 temp;
282
283     if (strcmp(aserver->name, "afs") != 0) {
284         int found = -1;
285         int i;
286         for (i = 0; i < MAXLOCALTOKENS; i++)
287             if (local_tokens[i].valid) {
288                 if ((strcmp(local_tokens[i].server.name, aserver->name) == 0)
289                     &&
290                     (strcmp
291                      (local_tokens[i].server.instance,
292                       aserver->instance) == 0)
293                     && (strcmp(local_tokens[i].server.cell, aserver->cell) ==
294                         0)) {
295                     found = i;  /* replace existing entry */
296                     break;
297                 } else          /* valid, but no match */
298                     ;
299             } else
300                 found = i;      /* remember this empty slot */
301         if (found == -1)
302             return KTC_NOENT;
303         memcpy(&local_tokens[found].token, atoken, sizeof(struct ktc_token));
304         local_tokens[found].server = *aserver;
305         local_tokens[found].client = *aclient;
306         local_tokens[found].valid = 1;
307         return 0;
308     }
309     tp = tbuffer;               /* start copying here */
310     if ((atoken->ticketLen < MINKTCTICKETLEN)
311         || (atoken->ticketLen > MAXKTCTICKETLEN))
312         return KTC_TOOBIG;
313     memcpy(tp, &atoken->ticketLen, sizeof(afs_int32));  /* copy in ticket length */
314     tp += sizeof(afs_int32);
315     memcpy(tp, atoken->ticket, atoken->ticketLen);      /* copy in ticket */
316     tp += atoken->ticketLen;
317     /* next, copy in the "clear token", describing who we are */
318     ct.AuthHandle = atoken->kvno;       /* hide auth handle here */
319     memcpy(ct.HandShakeKey, &atoken->sessionKey, 8);
320
321     ct.BeginTimestamp = atoken->startTime;
322     ct.EndTimestamp = atoken->endTime;
323     if (ct.BeginTimestamp == 0)
324         ct.BeginTimestamp = 1;
325
326     if ((strlen(aclient->name) > strlen("AFS ID "))
327         && (aclient->instance[0] == 0)) {
328         int sign = 1;
329         afs_int32 viceId = 0;
330         char *cp = aclient->name + strlen("AFS ID ");
331         if (*cp == '-') {
332             sign = -1;
333             cp++;
334         }
335         while (*cp) {
336             if (isdigit(*cp))
337                 viceId = viceId * 10 + (int)(*cp - '0');
338             else
339                 goto not_vice_id;
340             cp++;
341         }
342         ct.ViceId = viceId * sign;      /* OK to let any value here? */
343         if (((ct.EndTimestamp - ct.BeginTimestamp) & 1) == 0)
344             ct.BeginTimestamp++;        /* force lifetime to be odd */
345     } else {
346       not_vice_id:
347         ct.ViceId = getuid();   /* wrong, but works in primary cell */
348         if (((ct.EndTimestamp - ct.BeginTimestamp) & 1) == 1)
349             ct.BeginTimestamp++;        /* force lifetime to be even */
350     }
351
352 #ifdef UKERNEL
353     /*
354      * Information needed by the user space cache manager
355      */
356     u.u_expiration = ct.EndTimestamp;
357     u.u_viceid = ct.ViceId;
358 #endif
359
360     temp = sizeof(struct ClearToken);
361     memcpy(tp, &temp, sizeof(afs_int32));
362     tp += sizeof(afs_int32);
363     memcpy(tp, &ct, sizeof(struct ClearToken));
364     tp += sizeof(struct ClearToken);
365
366     /* next copy in primary flag */
367     temp = 0;
368
369     /*
370      * The following means that setpag will happen inside afs just before
371      * the authentication to prevent the setpag/klog race condition.
372      *
373      * The following means that setpag will affect the parent process as
374      * well as the current process.
375      */
376     if (flags & AFS_SETTOK_SETPAG)
377         temp |= 0x8000;
378
379     memcpy(tp, &temp, sizeof(afs_int32));
380     tp += sizeof(afs_int32);
381
382     /* finally copy in the cell name */
383     temp = strlen(aserver->cell);
384     if (temp >= MAXKTCREALMLEN)
385         return KTC_TOOBIG;
386     strcpy(tp, aserver->cell);
387     tp += temp + 1;
388
389     /* now setup for the pioctl */
390     iob.in = tbuffer;
391     iob.in_size = tp - tbuffer;
392     iob.out = tbuffer;
393     iob.out_size = sizeof(tbuffer);
394
395 #if defined(NO_AFS_CLIENT)
396     {
397         int fd;                 /* DEBUG */
398         char *tkfile;
399         if ((tkfile = getenv("TKTFILE"))
400             &&
401             ((fd =
402               open(tkfile, O_WRONLY | O_APPEND | O_TRUNC | O_CREAT,
403                    0644)) >= 0)) {
404             printf("Writing ticket to: %s\n", tkfile);
405             code = (write(fd, iob.in, iob.in_size) != iob.in_size);
406             close(fd);
407         } else
408             code = KTC_PIOCTLFAIL;
409     }
410 #else /* NO_AFS_CLIENT */
411     code = PIOCTL(0, VIOCSETTOK, &iob, 0);
412 #endif /* NO_AFS_CLIENT */
413     if (code)
414         return KTC_PIOCTLFAIL;
415     return 0;
416 }
417
418
419 ktc_SetToken(aserver, atoken, aclient, flags)
420      struct ktc_principal *aserver;
421      struct ktc_principal *aclient;
422      struct ktc_token *atoken;
423      afs_int32 flags;
424 {
425     int ncode, ocode;
426
427     LOCK_GLOBAL_MUTEX
428 #ifdef AFS_KERBEROS_ENV
429         if (!lcell[0])
430         ktc_LocalCell();
431
432     if (                        /*!strcmp(aclient->cell, lcell) && this would only store local creds */
433            (strcmp(aserver->name, "AuthServer")
434             || strcmp(aserver->instance, "Admin"))) {
435         if (strcmp(aserver->name, "krbtgt") == 0) {
436             static char lrealm[MAXKTCREALMLEN];
437
438             if (!lrealm[0])
439                 ucstring(lrealm, lcell, MAXKTCREALMLEN);
440             if (strcmp(aserver->instance, lrealm) == 0) {
441                 afs_tf_create(aclient->name, aclient->instance);
442             }
443         }
444
445         ncode = afs_tf_init(ktc_tkt_string(), W_TKT_FIL);
446         if (ncode == NO_TKT_FIL) {
447             (void)afs_tf_create(aclient->name, aclient->instance);
448             ncode = afs_tf_init(ktc_tkt_string(), W_TKT_FIL);
449         }
450
451         if (!ncode) {
452             afs_tf_save_cred(aserver, atoken, aclient);
453         }
454         afs_tf_close();
455 #ifdef NO_AFS_CLIENT
456         UNLOCK_GLOBAL_MUTEX return ncode;
457 #endif /* NO_AFS_CLIENT */
458     }
459 #endif
460
461 #ifndef NO_AFS_CLIENT
462     ncode = NewSetToken(aserver, atoken, aclient, flags);
463     if (ncode ||                /* new style failed */
464         (strcmp(aserver->name, "afs") == 0)) {  /* for afs tokens do both */
465         ocode = OldSetToken(aserver, atoken, aclient, flags);
466     } else
467         ocode = 0;
468     if (ncode && ocode) {
469         UNLOCK_GLOBAL_MUTEX if (ocode == -1)
470             ocode = errno;
471         else if (ocode == KTC_PIOCTLFAIL)
472             ocode = errno;
473         if (ocode == ESRCH)
474             return KTC_NOCELL;
475         if (ocode == EINVAL)
476             return KTC_NOPIOCTL;
477         if (ocode == EIO)
478             return KTC_NOCM;
479         return KTC_PIOCTLFAIL;
480     }
481 #endif /* NO_AFS_CLIENT */
482     UNLOCK_GLOBAL_MUTEX return 0;
483 }
484
485 /* get token, given server we need and token buffer.  aclient will eventually
486  * be set to our identity to the server.
487  */
488 ktc_GetToken(aserver, atoken, atokenLen, aclient)
489      struct ktc_principal *aserver, *aclient;
490      int atokenLen;
491      struct ktc_token *atoken;
492 {
493     struct ViceIoctl iob;
494     char tbuffer[MAXPIOCTLTOKENLEN];
495     register afs_int32 code;
496     int index;
497     char *stp, *cellp;          /* secret token ptr */
498     struct ClearToken ct;
499     register char *tp;
500     afs_int32 temp;
501     int maxLen;                 /* biggest ticket we can copy */
502     int tktLen;                 /* server ticket length */
503     char found = 0;
504
505     LOCK_GLOBAL_MUTEX
506 #ifndef NO_AFS_CLIENT
507         TRY_KERNEL(KTC_GETTOKEN_OP, aserver, aclient, atoken, atokenLen);
508 #endif /* NO_AFS_CLIENT */
509
510 #ifdef AFS_KERBEROS_ENV
511     if (!lcell[0])
512         ktc_LocalCell();
513 #endif
514 #ifndef NO_AFS_CLIENT
515     if (strcmp(aserver->name, "afs") != 0)
516 #endif /* NO_AFS_CLIENT */
517     {
518         int i;
519         /* try the local tokens */
520         for (i = 0; i < MAXLOCALTOKENS; i++)
521             if (local_tokens[i].valid
522                 && (strcmp(local_tokens[i].server.name, aserver->name) == 0)
523                 && (strcmp(local_tokens[i].server.instance, aserver->instance)
524                     == 0)
525                 && (strcmp(local_tokens[i].server.cell, aserver->cell) == 0)) {
526                 memcpy(atoken, &local_tokens[i].token,
527                        min(atokenLen, sizeof(struct ktc_token)));
528                 if (aclient)
529                     *aclient = local_tokens[i].client;
530                 UNLOCK_GLOBAL_MUTEX return 0;
531             }
532 #ifdef AFS_KERBEROS_ENV
533         if (!afs_tf_init(ktc_tkt_string(), R_TKT_FIL)) {
534             if (aclient) {
535                 if (!afs_tf_get_pname(aclient->name)
536                     && !afs_tf_get_pinst(aclient->instance))
537                     found = 1;
538             } else {
539                 char tmpstring[MAXHOSTCHARS];
540                 afs_tf_get_pname(&tmpstring);
541                 afs_tf_get_pinst(&tmpstring);
542                 found = 1;
543             }
544         }
545         if (found) {
546             struct ktc_principal cprincipal;
547             struct ktc_token ctoken;
548
549             while (!afs_tf_get_cred(&cprincipal, &ctoken)) {
550                 if (strcmp(cprincipal.name, aserver->name) == 0
551                     && strcmp(cprincipal.instance, aserver->instance) == 0
552                     && strcmp(cprincipal.cell, aserver->cell) == 0) {
553
554                     if (aclient)
555                         strcpy(aclient->cell, lcell);
556                     memcpy(atoken, &ctoken,
557                            min(atokenLen, sizeof(struct ktc_token)));
558
559                     afs_tf_close();
560                     UNLOCK_GLOBAL_MUTEX return 0;
561                 }
562             }
563         }
564         afs_tf_close();
565 #endif
566         UNLOCK_GLOBAL_MUTEX return KTC_NOENT;
567     }
568 #ifndef NO_AFS_CLIENT
569     for (index = 0; index < 200; index++) {     /* sanity check in case pioctl fails */
570         iob.in = (char *)&index;
571         iob.in_size = sizeof(afs_int32);
572         iob.out = tbuffer;
573         iob.out_size = sizeof(tbuffer);
574
575         code = PIOCTL(0, VIOCGETTOK, &iob, 0);
576
577         if (code) {
578             /* failed to retrieve specified token */
579             if (code < 0 && errno == EDOM) {
580                 UNLOCK_GLOBAL_MUTEX return KTC_NOENT;
581             }
582         } else {
583             /* token retrieved; parse buffer */
584             tp = tbuffer;
585
586             /* get ticket length */
587             memcpy(&temp, tp, sizeof(afs_int32));
588             tktLen = temp;
589             tp += sizeof(afs_int32);
590
591             /* remember where ticket is and skip over it */
592             stp = tp;
593             tp += tktLen;
594
595             /* get size of clear token and verify */
596             memcpy(&temp, tp, sizeof(afs_int32));
597             if (temp != sizeof(struct ClearToken)) {
598                 UNLOCK_GLOBAL_MUTEX return KTC_ERROR;
599             }
600             tp += sizeof(afs_int32);
601
602             /* copy clear token */
603             memcpy(&ct, tp, temp);
604             tp += temp;
605
606             /* skip over primary flag */
607             tp += sizeof(afs_int32);
608
609             /* remember where cell name is */
610             cellp = tp;
611
612             if ((strcmp(cellp, aserver->cell) == 0)
613 #ifdef  AFS_KERBEROS_ENV
614                 || (*aserver->cell == '\0' && strcmp(cellp, lcell) == 0)
615 #endif
616                 ) {
617                 /* got token for cell; check that it will fit */
618                 maxLen =
619                     atokenLen - sizeof(struct ktc_token) + MAXKTCTICKETLEN;
620                 if (maxLen < tktLen) {
621                     UNLOCK_GLOBAL_MUTEX return KTC_TOOBIG;
622                 }
623
624                 /* set return values */
625                 memcpy(atoken->ticket, stp, tktLen);
626                 atoken->startTime = ct.BeginTimestamp;
627                 atoken->endTime = ct.EndTimestamp;
628                 if (ct.AuthHandle == -1) {
629                     ct.AuthHandle = 999;
630                 }
631                 atoken->kvno = ct.AuthHandle;
632                 memcpy(&atoken->sessionKey, ct.HandShakeKey,
633                        sizeof(struct ktc_encryptionKey));
634                 atoken->ticketLen = tktLen;
635
636                 if (aclient) {
637                     strcpy(aclient->cell, cellp);
638                     aclient->instance[0] = 0;
639
640                     if ((atoken->kvno == 999) ||        /* old style bcrypt ticket */
641                         (ct.BeginTimestamp &&   /* new w/ prserver lookup */
642                          (((ct.EndTimestamp - ct.BeginTimestamp) & 1) == 1))) {
643                         sprintf(aclient->name, "AFS ID %d", ct.ViceId);
644                     } else {
645                         sprintf(aclient->name, "Unix UID %d", ct.ViceId);
646                     }
647                 }
648                 UNLOCK_GLOBAL_MUTEX return 0;
649             }
650         }
651     }
652 #endif /* NO_AFS_CLIENT */
653
654     UNLOCK_GLOBAL_MUTEX if ((code < 0) && (errno == EINVAL))
655         return KTC_NOPIOCTL;
656     return KTC_PIOCTLFAIL;      /* probable cause */
657 }
658
659 /*
660  * Forget tokens for this server and the calling user.
661  * NOT IMPLEMENTED YET!
662  */
663 #ifndef NO_AFS_CLIENT
664 ktc_ForgetToken(aserver)
665      struct ktc_principal *aserver;
666 {
667     int rc;
668
669     LOCK_GLOBAL_MUTEX TRY_KERNEL(KTC_FORGETTOKEN_OP, aserver, 0, 0, 0);
670
671     rc = ktc_ForgetAllTokens(); /* bogus, but better */
672     UNLOCK_GLOBAL_MUTEX return rc;
673 }
674 #endif /* NO_AFS_CLIENT */
675
676 /* ktc_ListTokens - list all tokens.  start aprevIndex at 0, it returns the
677  * next rock in (*aindex).  (*aserver) is set to the relevant ticket on
678  * success.  */
679
680 ktc_ListTokens(aprevIndex, aindex, aserver)
681      int aprevIndex, *aindex;
682      struct ktc_principal *aserver;
683 {
684     struct ViceIoctl iob;
685     char tbuffer[MAXPIOCTLTOKENLEN];
686     register afs_int32 code;
687     register char *tp;
688     afs_int32 temp, index;
689
690     memset(tbuffer, 0, sizeof(tbuffer));
691
692     LOCK_GLOBAL_MUTEX
693 #ifndef NO_AFS_CLIENT
694         TRY_KERNEL(KTC_LISTTOKENS_OP, aserver, aprevIndex, aindex, 0);
695 #endif /* NO_AFS_CLIENT */
696
697     index = aprevIndex;
698 #ifdef NO_AFS_CLIENT
699     if (index < 214)
700         index = 214;
701 #endif /* NO_AFS_CLIENT */
702 #ifdef AFS_KERBEROS_ENV
703     if (index >= 214) {
704         int i;
705         struct ktc_principal cprincipal;
706         struct ktc_token ctoken;
707
708         if (afs_tf_init(ktc_tkt_string(), R_TKT_FIL)
709             || afs_tf_get_pname(tbuffer) || afs_tf_get_pinst(tbuffer)) {
710             afs_tf_close();
711             UNLOCK_GLOBAL_MUTEX return KTC_NOENT;
712         }
713
714         for (i = 214; i < index; i++) {
715             if (afs_tf_get_cred(&cprincipal, &ctoken)) {
716                 afs_tf_close();
717                 UNLOCK_GLOBAL_MUTEX return KTC_NOENT;
718             }
719         }
720
721       again:
722         if (afs_tf_get_cred(&cprincipal, &ctoken)) {
723             afs_tf_close();
724             UNLOCK_GLOBAL_MUTEX return KTC_NOENT;
725         }
726         index++;
727
728 #ifndef NO_AFS_CLIENT
729         if (!strcmp(cprincipal.name, "afs") && cprincipal.instance[0] == 0) {
730             goto again;
731         }
732 #endif /* NO_AFS_CLIENT */
733
734         for (i = 0; i < MAXLOCALTOKENS; i++) {
735             if (!strcmp(cprincipal.name, local_tokens[i].server.name)
736                 && !strcmp(cprincipal.instance,
737                            local_tokens[i].server.instance)
738                 && !strcmp(cprincipal.cell, local_tokens[i].server.cell)) {
739                 goto again;
740             }
741         }
742
743         *aserver = cprincipal;
744         *aindex = index;
745         afs_tf_close();
746         UNLOCK_GLOBAL_MUTEX return 0;
747     }
748 #endif
749
750 #ifndef NO_AFS_CLIENT
751     if (index >= 123) {         /* special hack for returning TCS */
752         while (index - 123 < MAXLOCALTOKENS) {
753             if (local_tokens[index - 123].valid) {
754                 *aserver = local_tokens[index - 123].server;
755                 *aindex = index + 1;
756                 UNLOCK_GLOBAL_MUTEX return 0;
757             }
758             index++;
759         }
760         UNLOCK_GLOBAL_MUTEX
761 #ifdef AFS_KERBEROS_ENV
762             return ktc_ListTokens(214, aindex, aserver);
763 #else
764             return KTC_NOENT;
765 #endif
766     }
767
768     /* get tokens from the kernel */
769     while (index < 200) {       /* sanity check in case pioctl fails */
770         iob.in = (char *)&index;
771         iob.in_size = sizeof(afs_int32);
772         iob.out = tbuffer;
773         iob.out_size = sizeof(tbuffer);
774         code = PIOCTL(0, VIOCGETTOK, &iob, 0);
775         if (code < 0 && errno == EDOM) {
776             if (index < 123) {
777                 int rc;
778                 rc = ktc_ListTokens(123, aindex, aserver);
779                 UNLOCK_GLOBAL_MUTEX return rc;
780             } else {
781                 UNLOCK_GLOBAL_MUTEX return KTC_NOENT;
782             }
783         }
784         if (code == 0)
785             break;              /* got a ticket */
786         /* otherwise we should skip this ticket slot */
787         index++;
788     }
789     if (code < 0) {
790         UNLOCK_GLOBAL_MUTEX if (errno == EINVAL)
791             return KTC_NOPIOCTL;
792         return KTC_PIOCTLFAIL;
793     }
794
795     /* parse buffer */
796     tp = tbuffer;
797
798     /* next iterator determined by earlier loop */
799     *aindex = index + 1;
800
801     memcpy(&temp, tp, sizeof(afs_int32));       /* get size of secret token */
802     tp += sizeof(afs_int32);
803     tp += temp;                 /* skip ticket for now */
804     memcpy(&temp, tp, sizeof(afs_int32));       /* get size of clear token */
805     if (temp != sizeof(struct ClearToken)) {
806         UNLOCK_GLOBAL_MUTEX return KTC_ERROR;
807     }
808     tp += sizeof(afs_int32);    /* skip length */
809     tp += temp;                 /* skip clear token itself */
810     tp += sizeof(afs_int32);    /* skip primary flag */
811     /* tp now points to the cell name */
812     strcpy(aserver->cell, tp);
813     aserver->instance[0] = 0;
814     strcpy(aserver->name, "afs");
815 #endif /* NO_AFS_CLIENT */
816     UNLOCK_GLOBAL_MUTEX return 0;
817 }
818
819 /* discard all tokens from this user's cache */
820
821 static int
822 NewForgetAll()
823 {
824 #ifndef NO_AFS_CLIENT
825     TRY_KERNEL(KTC_FORGETALLTOKENS_OP, 0, 0, 0, 0);
826 #endif /* NO_AFS_CLIENT */
827     return EINVAL;
828 }
829
830 static int
831 OldForgetAll()
832 {
833     struct ViceIoctl iob;
834     register afs_int32 code;
835     int i;
836
837     for (i = 0; i < MAXLOCALTOKENS; i++)
838         local_tokens[i].valid = 0;
839
840     iob.in = 0;
841     iob.in_size = 0;
842     iob.out = 0;
843     iob.out_size = 0;
844 #ifndef NO_AFS_CLIENT
845     code = PIOCTL(0, VIOCUNPAG, &iob, 0);
846     if (code)
847         return KTC_PIOCTLFAIL;
848 #endif /* NO_AFS_CLIENT */
849     return 0;
850 }
851
852 int
853 ktc_ForgetAllTokens()
854 {
855     int ncode, ocode;
856
857     LOCK_GLOBAL_MUTEX
858 #ifdef AFS_KERBEROS_ENV
859     (void) afs_tf_dest_tkt();
860 #endif
861
862     ncode = NewForgetAll();
863     ocode = OldForgetAll();
864     if (ncode && ocode) {
865         if (ocode == -1)
866             ocode = errno;
867         else if (ocode == KTC_PIOCTLFAIL)
868             ocode = errno;
869         UNLOCK_GLOBAL_MUTEX if (ocode == EINVAL)
870             return KTC_NOPIOCTL;
871         return KTC_PIOCTLFAIL;
872     }
873     UNLOCK_GLOBAL_MUTEX return 0;
874 }
875
876 /* ktc_OldPioctl - returns a boolean true if the kernel supports only the old
877  * pioctl interface for delivering AFS tickets to the cache manager. */
878
879 ktc_OldPioctl()
880 {
881     int rc;
882     LOCK_GLOBAL_MUTEX
883 #ifdef  KERNEL_KTC_COMPAT
884         CHECK_KERNEL;
885     rc = (kernelKTC != 1);      /* old style interface */
886 #else
887         rc = 1;
888 #endif
889     UNLOCK_GLOBAL_MUTEX return rc;
890 }
891
892
893 #ifdef AFS_KERBEROS_ENV
894  /*
895   * Copyright 1987, 1988 by the Massachusetts Institute of Technology.
896   *
897   * For copying and distribution information, please see the file
898   * <mit-copyright.h>.
899   */
900
901 #if 0
902 #include <stdio.h>
903 #include <errno.h>
904 #include <sys/types.h>
905 #include <sys/stat.h>
906 #include <sys/file.h>
907 #include <krb.h>
908 #endif
909
910 #define TOO_BIG -1
911 #define TF_LCK_RETRY ((unsigned)2)      /* seconds to sleep before
912                                          * retry if ticket file is
913                                          * locked */
914
915 /*
916  * fd must be initialized to something that won't ever occur as a real
917  * file descriptor. Since open(2) returns only non-negative numbers as
918  * valid file descriptors, and afs_tf_init always stuffs the return value
919  * from open in here even if it is an error flag, we must
920  *      a. Initialize fd to a negative number, to indicate that it is
921  *         not initially valid.
922  *      b. When checking for a valid fd, assume that negative values
923  *         are invalid (ie. when deciding whether afs_tf_init has been
924  *         called.)
925  *      c. In tf_close, be sure it gets reinitialized to a negative
926  *         number. 
927  */
928 static fd = -1;
929 static curpos;                  /* Position in tfbfr */
930 static lastpos;                 /* End of tfbfr */
931 static char tfbfr[BUFSIZ];      /* Buffer for ticket data */
932
933 static tf_gets(), tf_read();
934
935 /*
936  * This file contains routines for manipulating the ticket cache file.
937  *
938  * The ticket file is in the following format:
939  *
940  *      principal's name        (null-terminated string)
941  *      principal's instance    (null-terminated string)
942  *      CREDENTIAL_1
943  *      CREDENTIAL_2
944  *      ...
945  *      CREDENTIAL_n
946  *      EOF
947  *
948  *      Where "CREDENTIAL_x" consists of the following fixed-length
949  *      fields from the CREDENTIALS structure (see "krb.h"):
950  *
951  *              char            service[MAXKTCNAMELEN]
952  *              char            instance[MAXKTCNAMELEN]
953  *              char            realm[REALM_SZ]
954  *              C_Block         session
955  *              int             lifetime
956  *              int             kvno
957  *              KTEXT_ST        ticket_st
958  *              afs_int32            issue_date
959  *
960  * Short description of routines:
961  *
962  * afs_tf_init() opens the ticket file and locks it.
963  *
964  * afs_tf_get_pname() returns the principal's name.
965  *
966  * afs_tf_get_pinst() returns the principal's instance (may be null).
967  *
968  * afs_tf_get_cred() returns the next CREDENTIALS record.
969  *
970  * afs_tf_save_cred() appends a new CREDENTIAL record to the ticket file.
971  *
972  * afs_tf_close() closes the ticket file and releases the lock.
973  *
974  * tf_gets() returns the next null-terminated string.  It's an internal
975  * routine used by afs_tf_get_pname(), afs_tf_get_pinst(), and 
976  * afs_tf_get_cred().
977  *
978  * tf_read() reads a given number of bytes.  It's an internal routine
979  * used by afs_tf_get_cred().
980  */
981
982 /*
983  * afs_tf_init() should be called before the other ticket file routines.
984  * It takes the name of the ticket file to use, "tf_name", and a
985  * read/write flag "rw" as arguments. 
986  *
987  * It tries to open the ticket file, checks the mode, and if everything
988  * is okay, locks the file.  If it's opened for reading, the lock is
989  * shared.  If it's opened for writing, the lock is exclusive. 
990  *
991  * Returns 0 if all went well, otherwise one of the following: 
992  *
993  * NO_TKT_FIL   - file wasn't there
994  * TKT_FIL_ACC  - file was in wrong mode, etc.
995  * TKT_FIL_LCK  - couldn't lock the file, even after a retry
996  */
997
998 afs_tf_init(tf_name, rw)
999      char *tf_name;
1000 {
1001     int wflag;
1002     int me;
1003     struct stat stat_buf;
1004
1005     switch (rw) {
1006     case R_TKT_FIL:
1007         wflag = 0;
1008         break;
1009     case W_TKT_FIL:
1010         wflag = 1;
1011         break;
1012     default:
1013         return TKT_FIL_ACC;
1014     }
1015     if (lstat(tf_name, &stat_buf) < 0)
1016         switch (errno) {
1017         case ENOENT:
1018             return NO_TKT_FIL;
1019         default:
1020             return TKT_FIL_ACC;
1021         }
1022     me = getuid();
1023     if ((stat_buf.st_uid != me && me != 0)
1024         || ((stat_buf.st_mode & S_IFMT) != S_IFREG))
1025         return TKT_FIL_ACC;
1026
1027     /*
1028      * If "wflag" is set, open the ticket file in append-writeonly mode
1029      * and lock the ticket file in exclusive mode.  If unable to lock
1030      * the file, sleep and try again.  If we fail again, return with the
1031      * proper error message. 
1032      */
1033
1034     curpos = sizeof(tfbfr);
1035
1036     if (wflag) {
1037         fd = open(tf_name, O_RDWR, 0600);
1038         if (fd < 0) {
1039             return TKT_FIL_ACC;
1040         }
1041 #if defined(AFS_AIX_ENV) || defined(AFS_HPUX_ENV) || defined(AFS_SUN5_ENV)
1042         if (fcntl(fd, F_SETLK, &fileWlock) == -1) {
1043             sleep(TF_LCK_RETRY);
1044             if (fcntl(fd, F_SETLK, &fileWlock) == -1) {
1045 #else
1046         if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
1047             sleep(TF_LCK_RETRY);
1048             if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
1049 #endif
1050                 (void)close(fd);
1051                 fd = -1;
1052                 return TKT_FIL_LCK;
1053             }
1054         }
1055         return 0;
1056     }
1057     /*
1058      * Otherwise "wflag" is not set and the ticket file should be opened
1059      * for read-only operations and locked for shared access. 
1060      */
1061
1062     fd = open(tf_name, O_RDONLY, 0600);
1063     if (fd < 0) {
1064         return TKT_FIL_ACC;
1065     }
1066 #if defined(AFS_AIX_ENV) || defined(AFS_HPUX_ENV) || defined(AFS_SUN5_ENV)
1067     if (fcntl(fd, F_SETLK, &fileRlock) == -1) {
1068         sleep(TF_LCK_RETRY);
1069         if (fcntl(fd, F_SETLK, &fileRlock) == -1) {
1070 #else
1071     if (flock(fd, LOCK_SH | LOCK_NB) < 0) {
1072         sleep(TF_LCK_RETRY);
1073         if (flock(fd, LOCK_SH | LOCK_NB) < 0) {
1074 #endif
1075             (void)close(fd);
1076             fd = -1;
1077             return TKT_FIL_LCK;
1078         }
1079     }
1080     return 0;
1081 }
1082
1083 /*
1084  * afs_tf_get_pname() reads the principal's name from the ticket file. It
1085  * should only be called after afs_tf_init() has been called.  The
1086  * principal's name is filled into the "p" parameter.  If all goes well,
1087  * 0 is returned.  If afs_tf_init() wasn't called, TKT_FIL_INI is
1088  * returned.  If the name was null, or EOF was encountered, or the name
1089  * was longer than MAXKTCNAMELEN, TKT_FIL_FMT is returned. 
1090  */
1091
1092 afs_tf_get_pname(p)
1093      char *p;
1094 {
1095     if (fd < 0) {
1096         return TKT_FIL_INI;
1097     }
1098     if (tf_gets(p, MAXKTCNAMELEN) < 2)  /* can't be just a null */
1099         return TKT_FIL_FMT;
1100     return 0;
1101 }
1102
1103 /*
1104  * afs_tf_get_pinst() reads the principal's instance from a ticket file.
1105  * It should only be called after afs_tf_init() and afs_tf_get_pname() have
1106  * been called.  The instance is filled into the "inst" parameter.  If all
1107  * goes well, 0 is returned.  If afs_tf_init() wasn't called,
1108  * TKT_FIL_INI is returned.  If EOF was encountered, or the instance
1109  * was longer than MAXKTCNAMELEN, TKT_FIL_FMT is returned.  Note that the
1110  * instance may be null. 
1111  */
1112
1113 afs_tf_get_pinst(inst)
1114      char *inst;
1115 {
1116     if (fd < 0) {
1117         return TKT_FIL_INI;
1118     }
1119     if (tf_gets(inst, MAXKTCNAMELEN) < 1)
1120         return TKT_FIL_FMT;
1121     return 0;
1122 }
1123
1124 /*
1125  * afs_tf_get_cred() reads a CREDENTIALS record from a ticket file and fills
1126  * in the given structure "c".  It should only be called after afs_tf_init(),
1127  * afs_tf_get_pname(), and afs_tf_get_pinst() have been called. If all goes 
1128  * well, 0 is returned.  Possible error codes are: 
1129  *
1130  * TKT_FIL_INI  - afs_tf_init wasn't called first
1131  * TKT_FIL_FMT  - bad format
1132  * EOF          - end of file encountered
1133  */
1134
1135 afs_tf_get_cred(principal, token)
1136      struct ktc_principal *principal;
1137      struct ktc_token *token;
1138 {
1139     int k_errno;
1140     int kvno, lifetime;
1141
1142     if (fd < 0) {
1143         return TKT_FIL_INI;
1144     }
1145     if ((k_errno = tf_gets(principal->name, MAXKTCNAMELEN)) < 2)
1146         switch (k_errno) {
1147         case TOO_BIG:
1148         case 1:         /* can't be just a null */
1149             return TKT_FIL_FMT;
1150         case 0:
1151             return EOF;
1152         }
1153     if ((k_errno = tf_gets(principal->instance, MAXKTCNAMELEN)) < 1)
1154         switch (k_errno) {
1155         case TOO_BIG:
1156             return TKT_FIL_FMT;
1157         case 0:
1158             return EOF;
1159         }
1160     if ((k_errno = tf_gets(principal->cell, MAXKTCREALMLEN)) < 2)
1161         switch (k_errno) {
1162         case TOO_BIG:
1163         case 1:         /* can't be just a null */
1164             return TKT_FIL_FMT;
1165         case 0:
1166             return EOF;
1167         }
1168     lcstring(principal->cell, principal->cell, MAXKTCREALMLEN);
1169     if (tf_read((char *)&(token->sessionKey), 8) < 1
1170         || tf_read((char *)&(lifetime), sizeof(lifetime)) < 1
1171         || tf_read((char *)&(kvno), sizeof(kvno)) < 1
1172         || tf_read((char *)&(token->ticketLen), sizeof(token->ticketLen))
1173         < 1 ||
1174         /* don't try to read a silly amount into ticket->dat */
1175         token->ticketLen > MAXKTCTICKETLEN
1176         || tf_read((char *)(token->ticket), token->ticketLen) < 1
1177         || tf_read((char *)&(token->startTime),
1178                    sizeof(token->startTime)) < 1) {
1179         return TKT_FIL_FMT;
1180     }
1181     token->endTime = life_to_time(token->startTime, lifetime);
1182     token->kvno = kvno;
1183     return 0;
1184 }
1185
1186 /*
1187  * tf_close() closes the ticket file and sets "fd" to -1. If "fd" is
1188  * not a valid file descriptor, it just returns.  It also clears the
1189  * buffer used to read tickets.
1190  *
1191  * The return value is not defined.
1192  */
1193
1194 afs_tf_close()
1195 {
1196     if (!(fd < 0)) {
1197 #if defined(AFS_AIX_ENV) || defined(AFS_HPUX_ENV) || defined(AFS_SUN5_ENV)
1198         (void)fcntl(fd, F_SETLK, &fileUlock);
1199 #else
1200         (void)flock(fd, LOCK_UN);
1201 #endif
1202         (void)close(fd);
1203         fd = -1;                /* see declaration of fd above */
1204     }
1205     memset(tfbfr, 0, sizeof(tfbfr));
1206 }
1207
1208 /*
1209  * tf_gets() is an internal routine.  It takes a string "s" and a count
1210  * "n", and reads from the file until either it has read "n" characters,
1211  * or until it reads a null byte. When finished, what has been read exists
1212  * in "s".
1213  *
1214  * Possible return values are:
1215  *
1216  * n            the number of bytes read (including null terminator)
1217  *              when all goes well
1218  *
1219  * 0            end of file or read error
1220  *
1221  * TOO_BIG      if "count" characters are read and no null is
1222  *              encountered. This is an indication that the ticket
1223  *              file is seriously ill.
1224  */
1225
1226 static
1227 tf_gets(s, n)
1228      register char *s;
1229 {
1230     register count;
1231
1232     if (fd < 0) {
1233         return TKT_FIL_INI;
1234     }
1235     for (count = n - 1; count > 0; --count) {
1236         if (curpos >= sizeof(tfbfr)) {
1237             lastpos = read(fd, tfbfr, sizeof(tfbfr));
1238             curpos = 0;
1239         }
1240         if (curpos == lastpos) {
1241             return 0;
1242         }
1243         *s = tfbfr[curpos++];
1244         if (*s++ == '\0')
1245             return (n - count);
1246     }
1247     return TOO_BIG;
1248 }
1249
1250 /*
1251  * tf_read() is an internal routine.  It takes a string "s" and a count
1252  * "n", and reads from the file until "n" bytes have been read.  When
1253  * finished, what has been read exists in "s".
1254  *
1255  * Possible return values are:
1256  *
1257  * n            the number of bytes read when all goes well
1258  *
1259  * 0            on end of file or read error
1260  */
1261
1262 static
1263 tf_read(s, n)
1264      register char *s;
1265      register n;
1266 {
1267     register count;
1268
1269     for (count = n; count > 0; --count) {
1270         if (curpos >= sizeof(tfbfr)) {
1271             lastpos = read(fd, tfbfr, sizeof(tfbfr));
1272             curpos = 0;
1273         }
1274         if (curpos == lastpos) {
1275             return 0;
1276         }
1277         *s++ = tfbfr[curpos++];
1278     }
1279     return n;
1280 }
1281
1282 char *tkt_string();
1283
1284 /*
1285  * afs_tf_save_cred() appends an incoming ticket to the end of the ticket
1286  * file.  You must call afs_tf_init() before calling afs_tf_save_cred().
1287  *
1288  * The "service", "instance", and "realm" arguments specify the
1289  * server's name; "aticket" contains the credential.
1290  *
1291  * Returns 0 if all goes well, TKT_FIL_INI if afs_tf_init() wasn't
1292  * called previously, and KFAILURE for anything else that went wrong.
1293  */
1294
1295 afs_tf_save_cred(aserver, atoken, aclient)
1296      struct ktc_principal *aserver;
1297      struct ktc_principal *aclient;
1298      struct ktc_token *atoken;  /* Token */
1299 {
1300     char realm[MAXKTCREALMLEN + 1];
1301     char junk[MAXKTCNAMELEN];
1302     struct ktc_principal principal;
1303     struct ktc_token token;
1304     int status;
1305     off_t start;
1306     int lifetime, kvno;
1307     int count;                  /* count for write */
1308
1309     if (fd < 0) {               /* fd is ticket file as set by afs_tf_init */
1310         return TKT_FIL_INI;
1311     }
1312
1313     ucstring(realm, aserver->cell, MAXKTCREALMLEN);
1314     realm[MAXKTCREALMLEN] = '\0';
1315
1316     /* Look for a duplicate ticket */
1317     (void)lseek(fd, (off_t) 0L, 0);
1318     curpos = sizeof(tfbfr);
1319
1320     if (afs_tf_get_pname(junk) || strcmp(junk, aclient->name)
1321         || afs_tf_get_pinst(junk) || strcmp(junk, aclient->instance))
1322         goto bad;
1323
1324     do {
1325         start = lseek(fd, (off_t) 0L, 1) - lastpos + curpos;
1326         status = afs_tf_get_cred(&principal, &token);
1327     } while (status == 0
1328              && (strcmp(aserver->name, principal.name) != 0
1329                  || strcmp(aserver->instance, principal.instance) != 0
1330                  || strcmp(aserver->cell, principal.cell) != 0));
1331
1332     /*
1333      * Two tickets for the same user authenticating to the same service
1334      * should be the same length, but we check here just to make sure.
1335      */
1336     if (status == 0 && token.ticketLen != atoken->ticketLen)
1337         return KFAILURE;
1338     if (status && status != EOF)
1339         return status;
1340
1341     /* Position over the credential we just matched (or the EOF) */
1342     lseek(fd, start, 0);
1343     curpos = lastpos = sizeof(tfbfr);
1344
1345     /* Write the ticket and associated data */
1346     /* Service */
1347     count = strlen(aserver->name) + 1;
1348     if (write(fd, aserver->name, count) != count)
1349         goto bad;
1350     /* Instance */
1351     count = strlen(aserver->instance) + 1;
1352     if (write(fd, aserver->instance, count) != count)
1353         goto bad;
1354     /* Realm */
1355     count = strlen(realm) + 1;
1356     if (write(fd, realm, count) != count)
1357         goto bad;
1358     /* Session key */
1359     if (write(fd, (char *)&atoken->sessionKey, 8) != 8)
1360         goto bad;
1361     /* Lifetime */
1362     lifetime = time_to_life(atoken->startTime, atoken->endTime);
1363     if (write(fd, (char *)&lifetime, sizeof(int)) != sizeof(int))
1364         goto bad;
1365     /* Key vno */
1366     kvno = atoken->kvno;
1367     if (write(fd, (char *)&kvno, sizeof(int)) != sizeof(int))
1368         goto bad;
1369     /* Tkt length */
1370     if (write(fd, (char *)&(atoken->ticketLen), sizeof(int)) != sizeof(int))
1371         goto bad;
1372     /* Ticket */
1373     count = atoken->ticketLen;
1374     if (write(fd, atoken->ticket, count) != count)
1375         goto bad;
1376     /* Issue date */
1377     if (write(fd, (char *)&atoken->startTime, sizeof(afs_int32))
1378         != sizeof(afs_int32))
1379         goto bad;
1380
1381     /* Actually, we should check each write for success */
1382     return (0);
1383   bad:
1384     return (KFAILURE);
1385 }
1386
1387 /*
1388  * Copyright 1985, 1986, 1987, 1988 by the Massachusetts Institute
1389  * of Technology.
1390  *
1391  * For copying and distribution information, please see the file
1392  * <mit-copyright.h>.
1393  */
1394
1395 char *getenv();
1396
1397 /*
1398  * This routine is used to generate the name of the file that holds
1399  * the user's cache of server tickets and associated session keys.
1400  *
1401  * If it is set, krb_ticket_string contains the ticket file name.
1402  * Otherwise, the filename is constructed as follows:
1403  *
1404  * If it is set, the environment variable "KRBTKFILE" will be used as
1405  * the ticket file name.  Otherwise TKT_ROOT (defined in "krb.h") and
1406  * the user's uid are concatenated to produce the ticket file name
1407  * (e.g., "/tmp/tkt123").  A pointer to the string containing the ticket
1408  * file name is returned.
1409  */
1410
1411 static char krb_ticket_string[4096] = "";
1412
1413 char *
1414 ktc_tkt_string()
1415 {
1416     char *env;
1417
1418     LOCK_GLOBAL_MUTEX if (!*krb_ticket_string) {
1419         if (env = getenv("KRBTKFILE")) {
1420             (void)strncpy(krb_ticket_string, env,
1421                           sizeof(krb_ticket_string) - 1);
1422             krb_ticket_string[sizeof(krb_ticket_string) - 1] = '\0';
1423         } else {
1424             /* 32 bits of signed integer will always fit in 11 characters
1425              * (including the sign), so no need to worry about overflow */
1426             (void)sprintf(krb_ticket_string, "%s%d", TKT_ROOT, getuid());
1427         }
1428     }
1429     UNLOCK_GLOBAL_MUTEX return krb_ticket_string;
1430 }
1431
1432 /*
1433  * This routine is used to set the name of the file that holds the user's
1434  * cache of server tickets and associated session keys.
1435  *
1436  * The value passed in is copied into local storage.
1437  *
1438  * NOTE:  This routine should be called during initialization, before other
1439  * Kerberos routines are called; otherwise tkt_string() above may be called
1440  * and return an undesired ticket file name until this routine is called.
1441  */
1442
1443 void
1444 ktc_set_tkt_string(val)
1445      char *val;
1446 {
1447
1448     LOCK_GLOBAL_MUTEX(void) strncpy(krb_ticket_string, val,
1449                                     sizeof(krb_ticket_string) - 1);
1450     krb_ticket_string[sizeof(krb_ticket_string) - 1] = '\0';
1451     UNLOCK_GLOBAL_MUTEX return;
1452 }
1453
1454 /*
1455  * tf_create() is used to initialize the ticket store.  It creates the
1456  * file to contain the tickets and writes the given user's name "pname"
1457  * and instance "pinst" in the file.  in_tkt() returns KSUCCESS on
1458  * success, or KFAILURE if something goes wrong.
1459  */
1460
1461 afs_tf_create(pname, pinst)
1462      char *pname;
1463      char *pinst;
1464 {
1465     int tktfile;
1466     int me, metoo;
1467     int count;
1468     char *file = ktc_tkt_string();
1469     int fd;
1470     register int i;
1471     char zerobuf[1024];
1472     struct stat sbuf;
1473
1474     me = getuid();
1475     metoo = geteuid();
1476
1477     if (lstat(file, &sbuf) == 0) {
1478         if ((sbuf.st_uid != me && me != 0)
1479             || ((sbuf.st_mode & S_IFMT) != S_IFREG) || sbuf.st_mode & 077) {
1480             return KFAILURE;
1481         }
1482         /* file already exists, and permissions appear ok, so nuke it */
1483         if ((fd = open(file, O_RDWR, 0)) < 0)
1484             goto out;           /* can't zero it, but we can still try truncating it */
1485
1486         memset(zerobuf, 0, sizeof(zerobuf));
1487
1488         for (i = 0; i < sbuf.st_size; i += sizeof(zerobuf))
1489             if (write(fd, zerobuf, sizeof(zerobuf)) != sizeof(zerobuf)) {
1490                 (void)fsync(fd);
1491                 (void)close(fd);
1492                 goto out;
1493             }
1494
1495         (void)fsync(fd);
1496         (void)close(fd);
1497     }
1498
1499   out:
1500     /* arrange so the file is owned by the ruid
1501      * (swap real & effective uid if necessary).
1502      * This isn't a security problem, since the ticket file, if it already
1503      * exists, has the right uid (== ruid) and mode. */
1504     if (me != metoo) {
1505         if (setreuid(metoo, me) < 0) {
1506             return (KFAILURE);
1507         }
1508     }
1509     tktfile = creat(file, 0600);
1510     if (me != metoo) {
1511         if (setreuid(me, metoo) < 0) {
1512             /* can't switch??? fail! */
1513             return (KFAILURE);
1514         }
1515     }
1516     if (tktfile < 0) {
1517         return (KFAILURE);
1518     }
1519     count = strlen(pname) + 1;
1520     if (write(tktfile, pname, count) != count) {
1521         (void)close(tktfile);
1522         return (KFAILURE);
1523     }
1524     count = strlen(pinst) + 1;
1525     if (write(tktfile, pinst, count) != count) {
1526         (void)close(tktfile);
1527         return (KFAILURE);
1528     }
1529     (void)close(tktfile);
1530     return (KSUCCESS);
1531 }
1532
1533 /*
1534  * dest_tkt() is used to destroy the ticket store upon logout.
1535  * If the ticket file does not exist, dest_tkt() returns RET_TKFIL.
1536  * Otherwise the function returns 0 on success, KFAILURE on
1537  * failure.
1538  */
1539
1540 afs_tf_dest_tkt()
1541 {
1542     char *file = ktc_tkt_string();
1543     int i, fd;
1544     struct stat statb;
1545     char buf[BUFSIZ];
1546
1547     errno = 0;
1548     if (lstat(file, &statb) < 0)
1549         goto out;
1550
1551     if (!(statb.st_mode & S_IFREG))
1552         goto out;
1553
1554     if ((fd = open(file, O_RDWR, 0)) < 0)
1555         goto out;
1556
1557     memset(buf, 0, BUFSIZ);
1558
1559     for (i = 0; i < statb.st_size; i += BUFSIZ)
1560         if (write(fd, buf, BUFSIZ) != BUFSIZ) {
1561             (void)fsync(fd);
1562             (void)close(fd);
1563             goto out;
1564         }
1565
1566     (void)fsync(fd);
1567     (void)close(fd);
1568
1569     (void)unlink(file);
1570
1571   out:
1572     if (errno == ENOENT)
1573         return RET_TKFIL;
1574     else if (errno != 0)
1575         return KFAILURE;
1576     return 0;
1577 }
1578
1579 static afs_uint32
1580 curpag()
1581 {
1582     gid_t groups[NGROUPS_MAX];
1583     afs_uint32 g0, g1;
1584     afs_uint32 h, l, ret;
1585
1586     if (getgroups(sizeof groups / sizeof groups[0], groups) < 2)
1587         return 0;
1588
1589     g0 = groups[0] & 0xffff;
1590     g1 = groups[1] & 0xffff;
1591     g0 -= 0x3f00;
1592     g1 -= 0x3f00;
1593     if (g0 < 0xc000 && g1 < 0xc000) {
1594         l = ((g0 & 0x3fff) << 14) | (g1 & 0x3fff);
1595         h = (g0 >> 14);
1596         h = (g1 >> 14) + h + h + h;
1597         ret = ((h << 28) | l);
1598         /* Additional testing */
1599         if (((ret >> 24) & 0xff) == 'A')
1600             return ret;
1601         else
1602             return -1;
1603     }
1604     return -1;
1605 }
1606
1607
1608 ktc_newpag()
1609 {
1610     extern char **environ;
1611
1612     afs_uint32 pag;
1613     struct stat sbuf;
1614     char fname[256], *prefix = "/ticket/";
1615     int numenv;
1616     char **newenv, **senv, **denv;
1617
1618     LOCK_GLOBAL_MUTEX if (stat("/ticket", &sbuf) == -1) {
1619         prefix = "/tmp/tkt";
1620     }
1621
1622     pag = curpag() & 0xffffffff;
1623     if (pag == -1) {
1624         sprintf(fname, "%s%d", prefix, getuid());
1625     } else {
1626         sprintf(fname, "%sp%ld", prefix, pag);
1627     }
1628     ktc_set_tkt_string(fname);
1629
1630     for (senv = environ, numenv = 0; *senv; senv++)
1631         numenv++;
1632     newenv = (char **)malloc((numenv + 2) * sizeof(char *));
1633
1634     for (senv = environ, denv = newenv; *senv; *senv++) {
1635         if (strncmp(*senv, "KRBTKFILE=", 10) != 0)
1636             *denv++ = *senv;
1637     }
1638
1639     *denv = (char *)malloc(10 + strlen(fname) + 1);
1640     strcpy(*denv, "KRBTKFILE=");
1641     strcat(*denv, fname);
1642     *++denv = 0;
1643     environ = newenv;
1644 UNLOCK_GLOBAL_MUTEX}
1645
1646 /*
1647  * BLETCH!  We have to invoke the entire afsconf package just to
1648  * find out what the local cell is.
1649  */
1650 static void
1651 ktc_LocalCell()
1652 {
1653     int code;
1654     struct afsconf_dir *conf;
1655
1656     if ((conf = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH))
1657         || (conf = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH))) {
1658         code = afsconf_GetLocalCell(conf, lcell, sizeof(lcell));
1659         afsconf_Close(conf);
1660     }
1661     if (!conf || code) {
1662         printf("** Can't determine local cell name!\n");
1663     }
1664 }
1665
1666 #endif