* $Source: /afs/athena.mit.edu/astaff/project/kerberos/src/lib/krb/RCS/tf_util.c,v $
* Copyright 1987, 1988 by the Massachusetts Institute of Technology.
* For copying and distribution information, please see the file
static char rcsid_tf_util_c
[] =
"$Id: tf_util.c,v 4.8 90/01/02 13:34:27 jtkohl Exp Locker: jtkohl $";
#include <mit-copyright.h>
#define TF_LCK_RETRY ((unsigned)2) /* seconds to sleep before
* retry if ticket file is
static char *tmp_shm_addr
= 0;
static char krb_dummy_skey
[8] = {0,0,0,0,0,0,0,0};
* fd must be initialized to something that won't ever occur as a real
* file descriptor. Since open(2) returns only non-negative numbers as
* valid file descriptors, and tf_init always stuffs the return value
* from open in here even if it is an error flag, we must
* a. Initialize fd to a negative number, to indicate that it is
* b. When checking for a valid fd, assume that negative values
* are invalid (ie. when deciding whether tf_init has been
* c. In tf_close, be sure it gets reinitialized to a negative
static curpos
; /* Position in tfbfr */
static lastpos
; /* End of tfbfr */
static char tfbfr
[BUFSIZ
]; /* Buffer for ticket data */
static tf_gets(), tf_read();
* This file contains routines for manipulating the ticket cache file.
* The ticket file is in the following format:
* principal's name (null-terminated string)
* principal's instance (null-terminated string)
* Where "CREDENTIAL_x" consists of the following fixed-length
* fields from the CREDENTIALS structure (see "krb.h"):
* Short description of routines:
* tf_init() opens the ticket file and locks it.
* tf_get_pname() returns the principal's name.
* tf_get_pinst() returns the principal's instance (may be null).
* tf_get_cred() returns the next CREDENTIALS record.
* tf_save_cred() appends a new CREDENTIAL record to the ticket file.
* tf_close() closes the ticket file and releases the lock.
* tf_gets() returns the next null-terminated string. It's an internal
* routine used by tf_get_pname(), tf_get_pinst(), and tf_get_cred().
* tf_read() reads a given number of bytes. It's an internal routine
* tf_init() should be called before the other ticket file routines.
* It takes the name of the ticket file to use, "tf_name", and a
* read/write flag "rw" as arguments.
* It tries to open the ticket file, checks the mode, and if everything
* is okay, locks the file. If it's opened for reading, the lock is
* shared. If it's opened for writing, the lock is exclusive.
* Returns KSUCCESS if all went well, otherwise one of the following:
* NO_TKT_FIL - file wasn't there
* TKT_FIL_ACC - file was in wrong mode, etc.
* TKT_FIL_LCK - couldn't lock the file, even after a retry
char shmidname
[MAXPATHLEN
];
if (krb_debug
) fprintf(stderr
, "tf_init: illegal parameter\n");
if (lstat(tf_name
, &stat_buf
) < 0)
if ((stat_buf
.st_uid
!= me
&& me
!= 0) ||
((stat_buf
.st_mode
& S_IFMT
) != S_IFREG
))
(void) strcpy(shmidname
, tf_name
);
(void) strcat(shmidname
, ".shm");
if (stat(shmidname
,&stat_buf
) < 0)
if ((stat_buf
.st_uid
!= me
&& me
!= 0) ||
((stat_buf
.st_mode
& S_IFMT
) != S_IFREG
))
* If "wflag" is set, open the ticket file in append-writeonly mode
* and lock the ticket file in exclusive mode. If unable to lock
* the file, sleep and try again. If we fail again, return with the
sfp
= fopen(shmidname
, "r"); /* only need read/write on the
int val
; /* useful for debugging fscanf */
/* We provide our own buffer here since some STDIO libraries
barf on unbuffered input with fscanf() */
if ((val
= fscanf(sfp
,"%d",&shmid
)) != 1) {
* global krb_shm_addr is initialized to 0. Ultrix bombs when you try and
* attach the same segment twice so we need this check.
if ((krb_shm_addr
= shmat(shmid
,0,0)) == -1){
"cannot attach shared memory for segment %d\n",
krb_shm_addr
= 0; /* reset so we catch further errors */
tmp_shm_addr
= krb_shm_addr
;
fd
= open(tf_name
, O_RDWR
, 0600);
if (flock(fd
, LOCK_EX
| LOCK_NB
) < 0) {
if (flock(fd
, LOCK_EX
| LOCK_NB
) < 0) {
* Otherwise "wflag" is not set and the ticket file should be opened
* for read-only operations and locked for shared access.
fd
= open(tf_name
, O_RDONLY
, 0600);
if (flock(fd
, LOCK_SH
| LOCK_NB
) < 0) {
if (flock(fd
, LOCK_SH
| LOCK_NB
) < 0) {
* tf_get_pname() reads the principal's name from the ticket file. It
* should only be called after tf_init() has been called. The
* principal's name is filled into the "p" parameter. If all goes well,
* KSUCCESS is returned. If tf_init() wasn't called, TKT_FIL_INI is
* returned. If the name was null, or EOF was encountered, or the name
* was longer than ANAME_SZ, TKT_FIL_FMT is returned.
fprintf(stderr
, "tf_get_pname called before tf_init.\n");
if (tf_gets(p
, ANAME_SZ
) < 2) /* can't be just a null */
* tf_get_pinst() reads the principal's instance from a ticket file.
* It should only be called after tf_init() and tf_get_pname() have been
* called. The instance is filled into the "inst" parameter. If all
* goes well, KSUCCESS is returned. If tf_init() wasn't called,
* TKT_FIL_INI is returned. If EOF was encountered, or the instance
* was longer than ANAME_SZ, TKT_FIL_FMT is returned. Note that the
fprintf(stderr
, "tf_get_pinst called before tf_init.\n");
if (tf_gets(inst
, INST_SZ
) < 1)
* tf_get_cred() reads a CREDENTIALS record from a ticket file and fills
* in the given structure "c". It should only be called after tf_init(),
* tf_get_pname(), and tf_get_pinst() have been called. If all goes well,
* KSUCCESS is returned. Possible error codes are:
* TKT_FIL_INI - tf_init wasn't called first
* TKT_FIL_FMT - bad format
* EOF - end of file encountered
KTEXT ticket
= &c
->ticket_st
; /* pointer to ticket */
fprintf(stderr
, "tf_get_cred called before tf_init.\n");
if ((k_errno
= tf_gets(c
->service
, SNAME_SZ
)) < 2)
case 1: /* can't be just a null */
if ((k_errno
= tf_gets(c
->instance
, INST_SZ
)) < 1)
if ((k_errno
= tf_gets(c
->realm
, REALM_SZ
)) < 2)
case 1: /* can't be just a null */
tf_read((char *) (c
->session
), KEY_SZ
) < 1 ||
tf_read((char *) &(c
->lifetime
), sizeof(c
->lifetime
)) < 1 ||
tf_read((char *) &(c
->kvno
), sizeof(c
->kvno
)) < 1 ||
tf_read((char *) &(ticket
->length
), sizeof(ticket
->length
))
/* don't try to read a silly amount into ticket->dat */
ticket
->length
> MAX_KTXT_LEN
||
tf_read((char *) (ticket
->dat
), ticket
->length
) < 1 ||
tf_read((char *) &(c
->issue_date
), sizeof(c
->issue_date
)) < 1
bcopy(tmp_shm_addr
,c
->session
,KEY_SZ
);
* tf_close() closes the ticket file and sets "fd" to -1. If "fd" is
* not a valid file descriptor, it just returns. It also clears the
* buffer used to read tickets.
* The return value is not defined.
if (shmdt(krb_shm_addr
)) {
/* what kind of error? */
fprintf(stderr
, "shmdt 0x%x: errno %d",krb_shm_addr
, errno
);
(void) flock(fd
, LOCK_UN
);
fd
= -1; /* see declaration of fd above */
bzero(tfbfr
, sizeof(tfbfr
));
* tf_gets() is an internal routine. It takes a string "s" and a count
* "n", and reads from the file until either it has read "n" characters,
* or until it reads a null byte. When finished, what has been read exists
* in "s". If it encounters EOF or an error, it closes the ticket file.
* Possible return values are:
* n the number of bytes read (including null terminator)
* 0 end of file or read error
* TOO_BIG if "count" characters are read and no null is
* encountered. This is an indication that the ticket
fprintf(stderr
, "tf_gets called before tf_init.\n");
for (count
= n
- 1; count
> 0; --count
) {
if (curpos
>= sizeof(tfbfr
)) {
lastpos
= read(fd
, tfbfr
, sizeof(tfbfr
));
* tf_read() is an internal routine. It takes a string "s" and a count
* "n", and reads from the file until "n" bytes have been read. When
* finished, what has been read exists in "s". If it encounters EOF or
* an error, it closes the ticket file.
* Possible return values are:
* n the number of bytes read when all goes well
* 0 on end of file or read error
for (count
= n
; count
> 0; --count
) {
if (curpos
>= sizeof(tfbfr
)) {
lastpos
= read(fd
, tfbfr
, sizeof(tfbfr
));
* tf_save_cred() appends an incoming ticket to the end of the ticket
* file. You must call tf_init() before calling tf_save_cred().
* The "service", "instance", and "realm" arguments specify the
* server's name; "session" contains the session key to be used with
* the ticket; "kvno" is the server key version number in which the
* ticket is encrypted, "ticket" contains the actual ticket, and
* "issue_date" is the time the ticket was requested (local host's time).
* Returns KSUCCESS if all goes well, TKT_FIL_INI if tf_init() wasn't
* called previously, and KFAILURE for anything else that went wrong.
tf_save_cred(service
, instance
, realm
, session
, lifetime
, kvno
,
char *service
; /* Service name */
char *instance
; /* Instance */
char *realm
; /* Auth domain */
C_Block session
; /* Session key */
int lifetime
; /* Lifetime */
int kvno
; /* Key version number */
KTEXT ticket
; /* The ticket itself */
long issue_date
; /* The issue time */
int count
; /* count for write */
if (fd
< 0) { /* fd is ticket file as set by tf_init */
fprintf(stderr
, "tf_save_cred called before tf_init.\n");
/* Find the end of the ticket file */
/* scan to end of existing keys: pick first 'empty' slot.
we assume that no real keys will be completely zero (it's a weak
skey_check
= (int *) krb_shm_addr
;
while (*skey_check
&& *(skey_check
+1))
tmp_shm_addr
= (char *)skey_check
;
/* Write the ticket and associated data */
count
= strlen(service
) + 1;
if (write(fd
, service
, count
) != count
)
count
= strlen(instance
) + 1;
if (write(fd
, instance
, count
) != count
)
count
= strlen(realm
) + 1;
if (write(fd
, realm
, count
) != count
)
bcopy(session
,tmp_shm_addr
,8);
if (write(fd
,krb_dummy_skey
,8) != 8)
if (write(fd
, (char *) session
, 8) != 8)
if (write(fd
, (char *) &lifetime
, sizeof(int)) != sizeof(int))
if (write(fd
, (char *) &kvno
, sizeof(int)) != sizeof(int))
if (write(fd
, (char *) &(ticket
->length
), sizeof(int)) !=
if (write(fd
, (char *) (ticket
->dat
), count
) != count
)
if (write(fd
, (char *) &issue_date
, sizeof(long))
/* Actually, we should check each write for success */