2 Copyright (C) 1989 by the Massachusetts Institute of Technology
4 Export of this software from the United States of America is assumed
5 to require a specific license from the United States Government.
6 It is the responsibility of any person or organization contemplating
7 export to obtain such a license before exporting.
9 WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
10 distribute this software and its documentation for any purpose and
11 without fee is hereby granted, provided that the above copyright
12 notice appear in all copies and that both that copyright notice and
13 this permission notice appear in supporting documentation, and that
14 the name of M.I.T. not be used in advertising or publicity pertaining
15 to distribution of the software without specific, written prior
16 permission. M.I.T. makes no representations about the suitability of
17 this software for any purpose. It is provided "as is" without express
24 RCSID("$Id: tf_util.c,v 1.39.2.2 2000/06/23 04:03:58 assar Exp $");
28 #define TF_LCK_RETRY ((unsigned)2) /* seconds to sleep before
29 * retry if ticket file is
31 #define TF_LCK_RETRY_COUNT (50) /* number of retries */
37 #define MAGIC_TICKET_NAME "magic"
38 #define MAGIC_TICKET_TIME_DIFF_INST "time-diff"
39 #define MAGIC_TICKET_ADDR_INST "our-address"
42 * fd must be initialized to something that won't ever occur as a real
43 * file descriptor. Since open(2) returns only non-negative numbers as
44 * valid file descriptors, and tf_init always stuffs the return value
45 * from open in here even if it is an error flag, we must
46 * a. Initialize fd to a negative number, to indicate that it is
47 * not initially valid.
48 * b. When checking for a valid fd, assume that negative values
49 * are invalid (ie. when deciding whether tf_init has been
51 * c. In tf_close, be sure it gets reinitialized to a negative
55 static int curpos; /* Position in tfbfr */
56 static int lastpos; /* End of tfbfr */
57 static char tfbfr[BUFSIZ]; /* Buffer for ticket data */
59 static int tf_gets(char *s, int n);
60 static int tf_read(void *s, int n);
63 * This file contains routines for manipulating the ticket cache file.
65 * The ticket file is in the following format:
67 * principal's name (null-terminated string)
68 * principal's instance (null-terminated string)
75 * Where "CREDENTIAL_x" consists of the following fixed-length
76 * fields from the CREDENTIALS structure (see "krb.h"):
78 * char service[ANAME_SZ]
79 * char instance[INST_SZ]
80 * char realm[REALM_SZ]
85 * u_int32_t issue_date
87 * Short description of routines:
89 * tf_init() opens the ticket file and locks it.
91 * tf_get_pname() returns the principal's name.
93 * tf_put_pname() writes the principal's name to the ticket file.
95 * tf_get_pinst() returns the principal's instance (may be null).
97 * tf_put_pinst() writes the instance.
99 * tf_get_cred() returns the next CREDENTIALS record.
101 * tf_save_cred() appends a new CREDENTIAL record to the ticket file.
103 * tf_close() closes the ticket file and releases the lock.
105 * tf_gets() returns the next null-terminated string. It's an internal
106 * routine used by tf_get_pname(), tf_get_pinst(), and tf_get_cred().
108 * tf_read() reads a given number of bytes. It's an internal routine
109 * used by tf_get_cred().
113 * tf_init() should be called before the other ticket file routines.
114 * It takes the name of the ticket file to use, "tf_name", and a
115 * read/write flag "rw" as arguments.
117 * It tries to open the ticket file, checks the mode, and if everything
118 * is okay, locks the file. If it's opened for reading, the lock is
119 * shared. If it's opened for writing, the lock is exclusive.
121 * Returns KSUCCESS if all went well, otherwise one of the following:
123 * NO_TKT_FIL - file wasn't there
124 * TKT_FIL_ACC - file was in wrong mode, etc.
125 * TKT_FIL_LCK - couldn't lock the file, even after a retry
130 #define flock(F, M) 0
134 tf_init(char *tf_name, int rw)
136 /* Unix implementation */
138 struct stat stat_buf;
150 krb_warning("tf_init: illegal parameter\n");
153 if (lstat(tf_name, &stat_buf) < 0)
160 if (!S_ISREG(stat_buf.st_mode))
163 /* The code tries to guess when the calling program is running
164 * set-uid and prevent unauthorized access.
166 * All library functions now assume that the right set of userids
167 * are set upon entry, therefore it's not strictly necessary to
168 * perform these test for programs adhering to these assumptions.
170 * This doesn't work on cygwin because getuid() returns a different
171 * uid than the owner of files that are created.
176 if (stat_buf.st_uid != me && me != 0)
182 * If "wflag" is set, open the ticket file in append-writeonly mode
183 * and lock the ticket file in exclusive mode. If unable to lock
184 * the file, sleep and try again. If we fail again, return with the
185 * proper error message.
188 curpos = sizeof(tfbfr);
192 fd = open(tf_name, O_RDWR | O_BINARY, 0600);
196 for (i_retry = 0; i_retry < TF_LCK_RETRY_COUNT; i_retry++) {
197 if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
199 krb_warning("tf_init: retry %d of write lock of `%s'.\n",
201 sleep (TF_LCK_RETRY);
203 return KSUCCESS; /* all done */
211 * Otherwise "wflag" is not set and the ticket file should be opened
212 * for read-only operations and locked for shared access.
215 fd = open(tf_name, O_RDONLY | O_BINARY, 0600);
220 for (i_retry = 0; i_retry < TF_LCK_RETRY_COUNT; i_retry++) {
221 if (flock(fd, LOCK_SH | LOCK_NB) < 0) {
223 krb_warning("tf_init: retry %d of read lock of `%s'.\n",
225 sleep (TF_LCK_RETRY);
227 return KSUCCESS; /* all done */
237 * tf_create() should be called when creating a new ticket file.
238 * The only argument is the name of the ticket file.
239 * After calling this, it should be possible to use other tf_* functions.
241 * New algoritm for creating ticket file:
242 * 1. try to erase contents of existing file.
243 * 2. try to remove old file.
244 * 3. try to open with O_CREAT and O_EXCL
245 * 4. if this fails, someone has created a file in between 1 and 2 and
246 * we should fail. Otherwise, all is wonderful.
250 tf_create(char *tf_name)
252 if (unlink (tf_name) && errno != ENOENT)
255 fd = open(tf_name, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
258 if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
260 if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
270 * tf_get_pname() reads the principal's name from the ticket file. It
271 * should only be called after tf_init() has been called. The
272 * principal's name is filled into the "p" parameter. If all goes well,
273 * KSUCCESS is returned. If tf_init() wasn't called, TKT_FIL_INI is
274 * returned. If the name was null, or EOF was encountered, or the name
275 * was longer than ANAME_SZ, TKT_FIL_FMT is returned.
279 tf_get_pname(char *p)
283 krb_warning("tf_get_pname called before tf_init.\n");
286 if (tf_gets(p, ANAME_SZ) < 2) /* can't be just a null */
289 krb_warning ("tf_get_pname: pname < 2.\n");
296 * tf_put_pname() sets the principal's name in the ticket file. Call
301 tf_put_pname(const char *p)
307 krb_warning("tf_put_pname called before tf_create.\n");
311 if (write(fd,p,count) != count)
317 * tf_get_pinst() reads the principal's instance from a ticket file.
318 * It should only be called after tf_init() and tf_get_pname() have been
319 * called. The instance is filled into the "inst" parameter. If all
320 * goes well, KSUCCESS is returned. If tf_init() wasn't called,
321 * TKT_FIL_INI is returned. If EOF was encountered, or the instance
322 * was longer than ANAME_SZ, TKT_FIL_FMT is returned. Note that the
323 * instance may be null.
327 tf_get_pinst(char *inst)
331 krb_warning("tf_get_pinst called before tf_init.\n");
334 if (tf_gets(inst, INST_SZ) < 1)
337 krb_warning("tf_get_pinst: inst_sz < 1.\n");
344 * tf_put_pinst writes the principal's instance to the ticket file.
345 * Call after tf_create.
349 tf_put_pinst(const char *inst)
355 krb_warning("tf_put_pinst called before tf_create.\n");
358 count = strlen(inst)+1;
359 if (write(fd,inst,count) != count)
365 * tf_get_cred() reads a CREDENTIALS record from a ticket file and fills
366 * in the given structure "c". It should only be called after tf_init(),
367 * tf_get_pname(), and tf_get_pinst() have been called. If all goes well,
368 * KSUCCESS is returned. Possible error codes are:
370 * TKT_FIL_INI - tf_init wasn't called first
371 * TKT_FIL_FMT - bad format
372 * EOF - end of file encountered
376 real_tf_get_cred(CREDENTIALS *c)
378 KTEXT ticket = &c->ticket_st; /* pointer to ticket */
383 krb_warning ("tf_get_cred called before tf_init.\n");
386 if ((k_errno = tf_gets(c->service, SNAME_SZ)) < 2)
390 krb_warning("tf_get_cred: too big service cred.\n");
391 case 1: /* can't be just a null */
394 krb_warning("tf_get_cred: null service cred.\n");
399 if ((k_errno = tf_gets(c->instance, INST_SZ)) < 1)
403 krb_warning ("tf_get_cred: too big instance cred.\n");
408 if ((k_errno = tf_gets(c->realm, REALM_SZ)) < 2)
412 krb_warning ("tf_get_cred: too big realm cred.\n");
413 case 1: /* can't be just a null */
416 krb_warning ("tf_get_cred: null realm cred.\n");
422 tf_read((c->session), DES_KEY_SZ) < 1 ||
423 tf_read(&(c->lifetime), sizeof(c->lifetime)) < 1 ||
424 tf_read(&(c->kvno), sizeof(c->kvno)) < 1 ||
425 tf_read(&(ticket->length), sizeof(ticket->length))
427 /* don't try to read a silly amount into ticket->dat */
428 ticket->length > MAX_KTXT_LEN ||
429 tf_read((ticket->dat), ticket->length) < 1 ||
430 tf_read(&(c->issue_date), sizeof(c->issue_date)) < 1
434 krb_warning ("tf_get_cred: failed tf_read.\n");
441 tf_get_cred(CREDENTIALS *c)
449 ret = real_tf_get_cred (c);
453 if(strcmp(c->service, MAGIC_TICKET_NAME) == 0) {
454 if(strcmp(c->instance, MAGIC_TICKET_TIME_DIFF_INST) == 0) {
455 /* we found the magic `time diff' ticket; update the kdc time
456 differential, and then get the next ticket */
459 krb_get_int(c->ticket_st.dat, &d, 4, 0);
460 krb_set_kdc_time_diff(d);
462 } else if (strcmp(c->instance, MAGIC_TICKET_ADDR_INST) == 0) {
471 tf_get_cred_addr(char *realm, size_t realm_sz, struct in_addr *addr)
480 ret = real_tf_get_cred (&cred);
484 if(strcmp(cred.service, MAGIC_TICKET_NAME) == 0) {
485 if(strcmp(cred.instance, MAGIC_TICKET_TIME_DIFF_INST) == 0) {
486 /* we found the magic `time diff' ticket; update the kdc time
487 differential, and then get the next ticket */
490 krb_get_int(cred.ticket_st.dat, &d, 4, 0);
491 krb_set_kdc_time_diff(d);
492 } else if (strcmp(cred.instance, MAGIC_TICKET_ADDR_INST) == 0) {
493 strlcpy(realm, cred.realm, realm_sz);
494 memcpy (addr, cred.ticket_st.dat, sizeof(*addr));
503 * tf_close() closes the ticket file and sets "fd" to -1. If "fd" is
504 * not a valid file descriptor, it just returns. It also clears the
505 * buffer used to read tickets.
507 * The return value is not defined.
516 fd = -1; /* see declaration of fd above */
518 memset(tfbfr, 0, sizeof(tfbfr));
522 * tf_gets() is an internal routine. It takes a string "s" and a count
523 * "n", and reads from the file until either it has read "n" characters,
524 * or until it reads a null byte. When finished, what has been read exists
525 * in "s". If it encounters EOF or an error, it closes the ticket file.
527 * Possible return values are:
529 * n the number of bytes read (including null terminator)
532 * 0 end of file or read error
534 * TOO_BIG if "count" characters are read and no null is
535 * encountered. This is an indication that the ticket
536 * file is seriously ill.
540 tf_gets(char *s, int n)
546 krb_warning ("tf_gets called before tf_init.\n");
549 for (count = n - 1; count > 0; --count) {
550 if (curpos >= sizeof(tfbfr)) {
551 lastpos = read(fd, tfbfr, sizeof(tfbfr));
554 if (curpos == lastpos) {
558 *s = tfbfr[curpos++];
567 * tf_read() is an internal routine. It takes a string "s" and a count
568 * "n", and reads from the file until "n" bytes have been read. When
569 * finished, what has been read exists in "s". If it encounters EOF or
570 * an error, it closes the ticket file.
572 * Possible return values are:
574 * n the number of bytes read when all goes well
576 * 0 on end of file or read error
580 tf_read(void *v, int n)
585 for (count = n; count > 0; --count) {
586 if (curpos >= sizeof(tfbfr)) {
587 lastpos = read(fd, tfbfr, sizeof(tfbfr));
590 if (curpos == lastpos) {
594 *s++ = tfbfr[curpos++];
600 * tf_save_cred() appends an incoming ticket to the end of the ticket
601 * file. You must call tf_init() before calling tf_save_cred().
603 * The "service", "instance", and "realm" arguments specify the
604 * server's name; "session" contains the session key to be used with
605 * the ticket; "kvno" is the server key version number in which the
606 * ticket is encrypted, "ticket" contains the actual ticket, and
607 * "issue_date" is the time the ticket was requested (local host's time).
609 * Returns KSUCCESS if all goes well, TKT_FIL_INI if tf_init() wasn't
610 * called previously, and KFAILURE for anything else that went wrong.
614 tf_save_cred(char *service, /* Service name */
615 char *instance, /* Instance */
616 char *realm, /* Auth domain */
617 unsigned char *session, /* Session key */
618 int lifetime, /* Lifetime */
619 int kvno, /* Key version number */
620 KTEXT ticket, /* The ticket itself */
621 u_int32_t issue_date) /* The issue time */
623 int count; /* count for write */
625 if (fd < 0) { /* fd is ticket file as set by tf_init */
627 krb_warning ("tf_save_cred called before tf_init.\n");
630 /* Find the end of the ticket file */
631 lseek(fd, 0L, SEEK_END);
633 /* Write the ticket and associated data */
635 count = strlen(service) + 1;
636 if (write(fd, service, count) != count)
639 count = strlen(instance) + 1;
640 if (write(fd, instance, count) != count)
643 count = strlen(realm) + 1;
644 if (write(fd, realm, count) != count)
647 if (write(fd, session, 8) != 8)
650 if (write(fd, &lifetime, sizeof(int)) != sizeof(int))
653 if (write(fd, &kvno, sizeof(int)) != sizeof(int))
656 if (write(fd, &(ticket->length), sizeof(int)) !=
660 count = ticket->length;
661 if (write(fd, ticket->dat, count) != count)
664 if (write(fd, &issue_date, sizeof(issue_date)) != sizeof(issue_date))
673 tf_setup(CREDENTIALS *cred, const char *pname, const char *pinst)
676 ret = tf_create(tkt_string());
680 if (tf_put_pname(pname) != KSUCCESS ||
681 tf_put_pinst(pinst) != KSUCCESS) {
686 if(krb_get_kdc_time_diff() != 0) {
687 /* Add an extra magic ticket containing the time differential
688 to the kdc. The first ticket defines which realm we belong
689 to, but since this ticket gets the same realm as the tgt,
690 this shouldn't be a problem */
691 des_cblock s = { 0, 0, 0, 0, 0, 0, 0, 0 };
693 int d = krb_get_kdc_time_diff();
694 krb_put_int(d, t.dat, sizeof(t.dat), 4);
696 tf_save_cred(MAGIC_TICKET_NAME, MAGIC_TICKET_TIME_DIFF_INST,
698 cred->lifetime, 0, &t, cred->issue_date);
700 ret = tf_save_cred(cred->service, cred->instance, cred->realm,
701 cred->session, cred->lifetime, cred->kvno,
702 &cred->ticket_st, cred->issue_date);
708 in_tkt(char *pname, char *pinst)
712 ret = tf_create (tkt_string());
716 if (tf_put_pname(pname) != KSUCCESS ||
717 tf_put_pinst(pinst) != KSUCCESS) {
727 * If there's a magic ticket with an address for realm `realm' in
728 * ticket file, return it in `addr'.
729 * realm == NULL means any realm.
733 tf_get_addr (const char *realm, struct in_addr *addr)
739 ret = tf_init (tkt_string (), R_TKT_FIL);
743 ret = tf_get_pname (princ.name);
746 ret = tf_get_pinst (princ.name);
749 while ((ret = real_tf_get_cred (&cred)) == KSUCCESS) {
750 if (strcmp (cred.service, MAGIC_TICKET_NAME) == 0
751 && strcmp (cred.instance, MAGIC_TICKET_ADDR_INST) == 0
753 || strcmp (cred.realm, realm) == 0)) {
754 memcpy (addr, cred.ticket_st.dat, sizeof(*addr));
766 * Store `realm, addr' as a magic ticket.
770 tf_store_addr (const char *realm, struct in_addr *addr)
775 des_cblock s = { 0, 0, 0, 0, 0, 0, 0, 0 };
778 ret = tf_init (tkt_string (), W_TKT_FIL);
782 t.length = sizeof(*addr);
783 memcpy (t.dat, addr, sizeof(*addr));
785 ret = tf_save_cred (MAGIC_TICKET_NAME, MAGIC_TICKET_ADDR_INST,
786 (char *)realm, s, 0, /* lifetime */