|
|
1.1 root 1: /*
2: * $Source: /afs/athena.mit.edu/astaff/project/kerberos/src/lib/krb/RCS/tf_util.c,v $
3: * $Author: jon $
4: *
5: * Copyright 1987, 1988 by the Massachusetts Institute of Technology.
6: *
7: * For copying and distribution information, please see the file
8: * <mit-copyright.h>.
9: */
10:
11: #ifndef lint
12: static char rcsid_tf_util_c[] =
13: "$Id: tf_util.c,v 4.8 90/01/02 13:34:27 jtkohl Exp Locker: jtkohl $";
14: #endif /* lint */
15:
16: #include <mit-copyright.h>
17:
18: #include <stdio.h>
19: #include <errno.h>
20: #include <sys/types.h>
21: #include <sys/stat.h>
22: #include <sys/file.h>
23: #include <des.h>
24: #include <krb.h>
25:
26: #ifdef TKT_SHMEM
27: #include <sys/param.h>
28: #include <sys/ipc.h>
29: #include <sys/shm.h>
30: #endif /* TKT_SHMEM */
31:
32: #define TOO_BIG -1
33: #define TF_LCK_RETRY ((unsigned)2) /* seconds to sleep before
34: * retry if ticket file is
35: * locked */
36: extern errno;
37: extern int krb_debug;
38:
39: #ifdef TKT_SHMEM
40: char *krb_shm_addr = 0;
41: static char *tmp_shm_addr = 0;
42: static char krb_dummy_skey[8] = {0,0,0,0,0,0,0,0};
43:
44: char *shmat();
45: #endif /* TKT_SHMEM */
46:
47: /*
48: * fd must be initialized to something that won't ever occur as a real
49: * file descriptor. Since open(2) returns only non-negative numbers as
50: * valid file descriptors, and tf_init always stuffs the return value
51: * from open in here even if it is an error flag, we must
52: * a. Initialize fd to a negative number, to indicate that it is
53: * not initially valid.
54: * b. When checking for a valid fd, assume that negative values
55: * are invalid (ie. when deciding whether tf_init has been
56: * called.)
57: * c. In tf_close, be sure it gets reinitialized to a negative
58: * number.
59: */
60: static fd = -1;
61: static curpos; /* Position in tfbfr */
62: static lastpos; /* End of tfbfr */
63: static char tfbfr[BUFSIZ]; /* Buffer for ticket data */
64:
65: static tf_gets(), tf_read();
66:
67: /*
68: * This file contains routines for manipulating the ticket cache file.
69: *
70: * The ticket file is in the following format:
71: *
72: * principal's name (null-terminated string)
73: * principal's instance (null-terminated string)
74: * CREDENTIAL_1
75: * CREDENTIAL_2
76: * ...
77: * CREDENTIAL_n
78: * EOF
79: *
80: * Where "CREDENTIAL_x" consists of the following fixed-length
81: * fields from the CREDENTIALS structure (see "krb.h"):
82: *
83: * char service[ANAME_SZ]
84: * char instance[INST_SZ]
85: * char realm[REALM_SZ]
86: * C_Block session
87: * int lifetime
88: * int kvno
89: * KTEXT_ST ticket_st
90: * long issue_date
91: *
92: * Short description of routines:
93: *
94: * tf_init() opens the ticket file and locks it.
95: *
96: * tf_get_pname() returns the principal's name.
97: *
98: * tf_get_pinst() returns the principal's instance (may be null).
99: *
100: * tf_get_cred() returns the next CREDENTIALS record.
101: *
102: * tf_save_cred() appends a new CREDENTIAL record to the ticket file.
103: *
104: * tf_close() closes the ticket file and releases the lock.
105: *
106: * tf_gets() returns the next null-terminated string. It's an internal
107: * routine used by tf_get_pname(), tf_get_pinst(), and tf_get_cred().
108: *
109: * tf_read() reads a given number of bytes. It's an internal routine
110: * used by tf_get_cred().
111: */
112:
113: /*
114: * tf_init() should be called before the other ticket file routines.
115: * It takes the name of the ticket file to use, "tf_name", and a
116: * read/write flag "rw" as arguments.
117: *
118: * It tries to open the ticket file, checks the mode, and if everything
119: * is okay, locks the file. If it's opened for reading, the lock is
120: * shared. If it's opened for writing, the lock is exclusive.
121: *
122: * Returns KSUCCESS if all went well, otherwise one of the following:
123: *
124: * NO_TKT_FIL - file wasn't there
125: * TKT_FIL_ACC - file was in wrong mode, etc.
126: * TKT_FIL_LCK - couldn't lock the file, even after a retry
127: */
128:
129: tf_init(tf_name, rw)
130: char *tf_name;
131: {
132: int wflag;
133: uid_t me, getuid();
134: struct stat stat_buf;
135: #ifdef TKT_SHMEM
136: char shmidname[MAXPATHLEN];
137: FILE *sfp;
138: int shmid;
139: #endif
140:
141: switch (rw) {
142: case R_TKT_FIL:
143: wflag = 0;
144: break;
145: case W_TKT_FIL:
146: wflag = 1;
147: break;
148: default:
149: if (krb_debug) fprintf(stderr, "tf_init: illegal parameter\n");
150: return TKT_FIL_ACC;
151: }
152: if (lstat(tf_name, &stat_buf) < 0)
153: switch (errno) {
154: case ENOENT:
155: return NO_TKT_FIL;
156: default:
157: return TKT_FIL_ACC;
158: }
159: me = getuid();
160: if ((stat_buf.st_uid != me && me != 0) ||
161: ((stat_buf.st_mode & S_IFMT) != S_IFREG))
162: return TKT_FIL_ACC;
163: #ifdef TKT_SHMEM
164: (void) strcpy(shmidname, tf_name);
165: (void) strcat(shmidname, ".shm");
166: if (stat(shmidname,&stat_buf) < 0)
167: return(TKT_FIL_ACC);
168: if ((stat_buf.st_uid != me && me != 0) ||
169: ((stat_buf.st_mode & S_IFMT) != S_IFREG))
170: return TKT_FIL_ACC;
171: #endif /* TKT_SHMEM */
172:
173: /*
174: * If "wflag" is set, open the ticket file in append-writeonly mode
175: * and lock the ticket file in exclusive mode. If unable to lock
176: * the file, sleep and try again. If we fail again, return with the
177: * proper error message.
178: */
179:
180: curpos = sizeof(tfbfr);
181:
182: #ifdef TKT_SHMEM
183: sfp = fopen(shmidname, "r"); /* only need read/write on the
184: actual tickets */
185: if (sfp == 0)
186: return TKT_FIL_ACC;
187: shmid = -1;
188: {
189: char buf[BUFSIZ];
190: int val; /* useful for debugging fscanf */
191: /* We provide our own buffer here since some STDIO libraries
192: barf on unbuffered input with fscanf() */
193:
194: setbuf(sfp, buf);
195: if ((val = fscanf(sfp,"%d",&shmid)) != 1) {
196: (void) fclose(sfp);
197: return TKT_FIL_ACC;
198: }
199: if (shmid < 0) {
200: (void) fclose(sfp);
201: return TKT_FIL_ACC;
202: }
203: (void) fclose(sfp);
204: }
205: /*
206: * global krb_shm_addr is initialized to 0. Ultrix bombs when you try and
207: * attach the same segment twice so we need this check.
208: */
209: if (!krb_shm_addr) {
210: if ((krb_shm_addr = shmat(shmid,0,0)) == -1){
211: if (krb_debug)
212: fprintf(stderr,
213: "cannot attach shared memory for segment %d\n",
214: shmid);
215: krb_shm_addr = 0; /* reset so we catch further errors */
216: return TKT_FIL_ACC;
217: }
218: }
219: tmp_shm_addr = krb_shm_addr;
220: #endif /* TKT_SHMEM */
221:
222: if (wflag) {
223: fd = open(tf_name, O_RDWR, 0600);
224: if (fd < 0) {
225: return TKT_FIL_ACC;
226: }
227: if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
228: sleep(TF_LCK_RETRY);
229: if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
230: (void) close(fd);
231: fd = -1;
232: return TKT_FIL_LCK;
233: }
234: }
235: return KSUCCESS;
236: }
237: /*
238: * Otherwise "wflag" is not set and the ticket file should be opened
239: * for read-only operations and locked for shared access.
240: */
241:
242: fd = open(tf_name, O_RDONLY, 0600);
243: if (fd < 0) {
244: return TKT_FIL_ACC;
245: }
246: if (flock(fd, LOCK_SH | LOCK_NB) < 0) {
247: sleep(TF_LCK_RETRY);
248: if (flock(fd, LOCK_SH | LOCK_NB) < 0) {
249: (void) close(fd);
250: fd = -1;
251: return TKT_FIL_LCK;
252: }
253: }
254: return KSUCCESS;
255: }
256:
257: /*
258: * tf_get_pname() reads the principal's name from the ticket file. It
259: * should only be called after tf_init() has been called. The
260: * principal's name is filled into the "p" parameter. If all goes well,
261: * KSUCCESS is returned. If tf_init() wasn't called, TKT_FIL_INI is
262: * returned. If the name was null, or EOF was encountered, or the name
263: * was longer than ANAME_SZ, TKT_FIL_FMT is returned.
264: */
265:
266: tf_get_pname(p)
267: char *p;
268: {
269: if (fd < 0) {
270: if (krb_debug)
271: fprintf(stderr, "tf_get_pname called before tf_init.\n");
272: return TKT_FIL_INI;
273: }
274: if (tf_gets(p, ANAME_SZ) < 2) /* can't be just a null */
275: return TKT_FIL_FMT;
276: return KSUCCESS;
277: }
278:
279: /*
280: * tf_get_pinst() reads the principal's instance from a ticket file.
281: * It should only be called after tf_init() and tf_get_pname() have been
282: * called. The instance is filled into the "inst" parameter. If all
283: * goes well, KSUCCESS is returned. If tf_init() wasn't called,
284: * TKT_FIL_INI is returned. If EOF was encountered, or the instance
285: * was longer than ANAME_SZ, TKT_FIL_FMT is returned. Note that the
286: * instance may be null.
287: */
288:
289: tf_get_pinst(inst)
290: char *inst;
291: {
292: if (fd < 0) {
293: if (krb_debug)
294: fprintf(stderr, "tf_get_pinst called before tf_init.\n");
295: return TKT_FIL_INI;
296: }
297: if (tf_gets(inst, INST_SZ) < 1)
298: return TKT_FIL_FMT;
299: return KSUCCESS;
300: }
301:
302: /*
303: * tf_get_cred() reads a CREDENTIALS record from a ticket file and fills
304: * in the given structure "c". It should only be called after tf_init(),
305: * tf_get_pname(), and tf_get_pinst() have been called. If all goes well,
306: * KSUCCESS is returned. Possible error codes are:
307: *
308: * TKT_FIL_INI - tf_init wasn't called first
309: * TKT_FIL_FMT - bad format
310: * EOF - end of file encountered
311: */
312:
313: tf_get_cred(c)
314: CREDENTIALS *c;
315: {
316: KTEXT ticket = &c->ticket_st; /* pointer to ticket */
317: int k_errno;
318:
319: if (fd < 0) {
320: if (krb_debug)
321: fprintf(stderr, "tf_get_cred called before tf_init.\n");
322: return TKT_FIL_INI;
323: }
324: if ((k_errno = tf_gets(c->service, SNAME_SZ)) < 2)
325: switch (k_errno) {
326: case TOO_BIG:
327: case 1: /* can't be just a null */
328: tf_close();
329: return TKT_FIL_FMT;
330: case 0:
331: return EOF;
332: }
333: if ((k_errno = tf_gets(c->instance, INST_SZ)) < 1)
334: switch (k_errno) {
335: case TOO_BIG:
336: return TKT_FIL_FMT;
337: case 0:
338: return EOF;
339: }
340: if ((k_errno = tf_gets(c->realm, REALM_SZ)) < 2)
341: switch (k_errno) {
342: case TOO_BIG:
343: case 1: /* can't be just a null */
344: tf_close();
345: return TKT_FIL_FMT;
346: case 0:
347: return EOF;
348: }
349: if (
350: tf_read((char *) (c->session), KEY_SZ) < 1 ||
351: tf_read((char *) &(c->lifetime), sizeof(c->lifetime)) < 1 ||
352: tf_read((char *) &(c->kvno), sizeof(c->kvno)) < 1 ||
353: tf_read((char *) &(ticket->length), sizeof(ticket->length))
354: < 1 ||
355: /* don't try to read a silly amount into ticket->dat */
356: ticket->length > MAX_KTXT_LEN ||
357: tf_read((char *) (ticket->dat), ticket->length) < 1 ||
358: tf_read((char *) &(c->issue_date), sizeof(c->issue_date)) < 1
359: ) {
360: tf_close();
361: return TKT_FIL_FMT;
362: }
363: #ifdef TKT_SHMEM
364: bcopy(tmp_shm_addr,c->session,KEY_SZ);
365: tmp_shm_addr += KEY_SZ;
366: #endif /* TKT_SHMEM */
367: return KSUCCESS;
368: }
369:
370: /*
371: * tf_close() closes the ticket file and sets "fd" to -1. If "fd" is
372: * not a valid file descriptor, it just returns. It also clears the
373: * buffer used to read tickets.
374: *
375: * The return value is not defined.
376: */
377:
378: tf_close()
379: {
380: if (!(fd < 0)) {
381: #ifdef TKT_SHMEM
382: if (shmdt(krb_shm_addr)) {
383: /* what kind of error? */
384: if (krb_debug)
385: fprintf(stderr, "shmdt 0x%x: errno %d",krb_shm_addr, errno);
386: } else {
387: krb_shm_addr = 0;
388: }
389: #endif TKT_SHMEM
390: (void) flock(fd, LOCK_UN);
391: (void) close(fd);
392: fd = -1; /* see declaration of fd above */
393: }
394: bzero(tfbfr, sizeof(tfbfr));
395: }
396:
397: /*
398: * tf_gets() is an internal routine. It takes a string "s" and a count
399: * "n", and reads from the file until either it has read "n" characters,
400: * or until it reads a null byte. When finished, what has been read exists
401: * in "s". If it encounters EOF or an error, it closes the ticket file.
402: *
403: * Possible return values are:
404: *
405: * n the number of bytes read (including null terminator)
406: * when all goes well
407: *
408: * 0 end of file or read error
409: *
410: * TOO_BIG if "count" characters are read and no null is
411: * encountered. This is an indication that the ticket
412: * file is seriously ill.
413: */
414:
415: static
416: tf_gets(s, n)
417: register char *s;
418: {
419: register count;
420:
421: if (fd < 0) {
422: if (krb_debug)
423: fprintf(stderr, "tf_gets called before tf_init.\n");
424: return TKT_FIL_INI;
425: }
426: for (count = n - 1; count > 0; --count) {
427: if (curpos >= sizeof(tfbfr)) {
428: lastpos = read(fd, tfbfr, sizeof(tfbfr));
429: curpos = 0;
430: }
431: if (curpos == lastpos) {
432: tf_close();
433: return 0;
434: }
435: *s = tfbfr[curpos++];
436: if (*s++ == '\0')
437: return (n - count);
438: }
439: tf_close();
440: return TOO_BIG;
441: }
442:
443: /*
444: * tf_read() is an internal routine. It takes a string "s" and a count
445: * "n", and reads from the file until "n" bytes have been read. When
446: * finished, what has been read exists in "s". If it encounters EOF or
447: * an error, it closes the ticket file.
448: *
449: * Possible return values are:
450: *
451: * n the number of bytes read when all goes well
452: *
453: * 0 on end of file or read error
454: */
455:
456: static
457: tf_read(s, n)
458: register char *s;
459: register n;
460: {
461: register count;
462:
463: for (count = n; count > 0; --count) {
464: if (curpos >= sizeof(tfbfr)) {
465: lastpos = read(fd, tfbfr, sizeof(tfbfr));
466: curpos = 0;
467: }
468: if (curpos == lastpos) {
469: tf_close();
470: return 0;
471: }
472: *s++ = tfbfr[curpos++];
473: }
474: return n;
475: }
476:
477: char *tkt_string();
478:
479: /*
480: * tf_save_cred() appends an incoming ticket to the end of the ticket
481: * file. You must call tf_init() before calling tf_save_cred().
482: *
483: * The "service", "instance", and "realm" arguments specify the
484: * server's name; "session" contains the session key to be used with
485: * the ticket; "kvno" is the server key version number in which the
486: * ticket is encrypted, "ticket" contains the actual ticket, and
487: * "issue_date" is the time the ticket was requested (local host's time).
488: *
489: * Returns KSUCCESS if all goes well, TKT_FIL_INI if tf_init() wasn't
490: * called previously, and KFAILURE for anything else that went wrong.
491: */
492:
493: tf_save_cred(service, instance, realm, session, lifetime, kvno,
494: ticket, issue_date)
495: char *service; /* Service name */
496: char *instance; /* Instance */
497: char *realm; /* Auth domain */
498: C_Block session; /* Session key */
499: int lifetime; /* Lifetime */
500: int kvno; /* Key version number */
501: KTEXT ticket; /* The ticket itself */
502: long issue_date; /* The issue time */
503: {
504:
505: off_t lseek();
506: int count; /* count for write */
507: #ifdef TKT_SHMEM
508: int *skey_check;
509: #endif /* TKT_SHMEM */
510:
511: if (fd < 0) { /* fd is ticket file as set by tf_init */
512: if (krb_debug)
513: fprintf(stderr, "tf_save_cred called before tf_init.\n");
514: return TKT_FIL_INI;
515: }
516: /* Find the end of the ticket file */
517: (void) lseek(fd, 0L, 2);
518: #ifdef TKT_SHMEM
519: /* scan to end of existing keys: pick first 'empty' slot.
520: we assume that no real keys will be completely zero (it's a weak
521: key under DES) */
522:
523: skey_check = (int *) krb_shm_addr;
524:
525: while (*skey_check && *(skey_check+1))
526: skey_check += 2;
527: tmp_shm_addr = (char *)skey_check;
528: #endif /* TKT_SHMEM */
529:
530: /* Write the ticket and associated data */
531: /* Service */
532: count = strlen(service) + 1;
533: if (write(fd, service, count) != count)
534: goto bad;
535: /* Instance */
536: count = strlen(instance) + 1;
537: if (write(fd, instance, count) != count)
538: goto bad;
539: /* Realm */
540: count = strlen(realm) + 1;
541: if (write(fd, realm, count) != count)
542: goto bad;
543: /* Session key */
544: #ifdef TKT_SHMEM
545: bcopy(session,tmp_shm_addr,8);
546: tmp_shm_addr+=8;
547: if (write(fd,krb_dummy_skey,8) != 8)
548: goto bad;
549: #else /* ! TKT_SHMEM */
550: if (write(fd, (char *) session, 8) != 8)
551: goto bad;
552: #endif /* TKT_SHMEM */
553: /* Lifetime */
554: if (write(fd, (char *) &lifetime, sizeof(int)) != sizeof(int))
555: goto bad;
556: /* Key vno */
557: if (write(fd, (char *) &kvno, sizeof(int)) != sizeof(int))
558: goto bad;
559: /* Tkt length */
560: if (write(fd, (char *) &(ticket->length), sizeof(int)) !=
561: sizeof(int))
562: goto bad;
563: /* Ticket */
564: count = ticket->length;
565: if (write(fd, (char *) (ticket->dat), count) != count)
566: goto bad;
567: /* Issue date */
568: if (write(fd, (char *) &issue_date, sizeof(long))
569: != sizeof(long))
570: goto bad;
571:
572: /* Actually, we should check each write for success */
573: return (KSUCCESS);
574: bad:
575: return (KFAILURE);
576: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.