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