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