Annotation of 43BSDReno/kerberosIV/krb/tf_util.c, revision 1.1.1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.