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