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