home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / kerberosIV / krb / tf_util.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-05-11  |  15.2 KB  |  577 lines

  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. }
  577.