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