1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * lib/krb5/ccache/cc_file.c 8 * 9 * Copyright 1990,1991,1992,1993,1994,2000,2004 Massachusetts Institute of Technology. 10 * All Rights Reserved. 11 * 12 * Original stdio support copyright 1995 by Cygnus Support. 13 * 14 * Export of this software from the United States of America may 15 * require a specific license from the United States Government. 16 * It is the responsibility of any person or organization contemplating 17 * export to obtain such a license before exporting. 18 * 19 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 20 * distribute this software and its documentation for any purpose and 21 * without fee is hereby granted, provided that the above copyright 22 * notice appear in all copies and that both that copyright notice and 23 * this permission notice appear in supporting documentation, and that 24 * the name of M.I.T. not be used in advertising or publicity pertaining 25 * to distribution of the software without specific, written prior 26 * permission. Furthermore if you modify this software you must label 27 * your software as modified software and not distribute it in such a 28 * fashion that it might be confused with the original M.I.T. software. 29 * M.I.T. makes no representations about the suitability of 30 * this software for any purpose. It is provided "as is" without express 31 * or implied warranty. 32 * 33 * 34 * implementation of file-based credentials cache 35 */ 36 37 #pragma ident "%Z%%M% %I% %E% SMI" 38 39 /* 40 If OPENCLOSE is defined, each of the functions opens and closes the 41 file whenever it needs to access it. Otherwise, the file is opened 42 once in initialize and closed once is close. 43 44 This library depends on UNIX-like file descriptors, and UNIX-like 45 behavior from the functions: open, close, read, write, lseek. 46 47 The quasi-BNF grammar for a credentials cache: 48 49 file ::= 50 principal list-of-credentials 51 52 credential ::= 53 client (principal) 54 server (principal) 55 keyblock (keyblock) 56 times (ticket_times) 57 is_skey (boolean) 58 ticket_flags (flags) 59 ticket (data) 60 second_ticket (data) 61 62 principal ::= 63 number of components (int32) 64 component 1 (data) 65 component 2 (data) 66 ... 67 68 data ::= 69 length (int32) 70 string of length bytes 71 72 etc. 73 */ 74 /* todo: 75 Make sure that each time a function returns KRB5_NOMEM, everything 76 allocated earlier in the function and stack tree is freed. 77 78 File locking 79 80 Use pread/pwrite if available, so multiple threads can read 81 simultaneously. (That may require reader/writer locks.) 82 83 fcc_nseq.c and fcc_read don't check return values a lot. 84 */ 85 #include "k5-int.h" 86 #include <syslog.h> /* SUNW */ 87 88 #define NEED_SOCKETS /* Only for ntohs, etc. */ 89 #define NEED_LOWLEVEL_IO 90 91 #include <stdio.h> 92 #include <errno.h> 93 94 #if HAVE_UNISTD_H 95 #include <unistd.h> 96 #endif 97 98 /* How long to block if flock fails with EAGAIN */ 99 #define LOCK_RETRIES 100 100 #define WAIT_LENGTH 20 /* in milliseconds */ 101 102 #ifdef HAVE_NETINET_IN_H 103 #if !defined(_WIN32) 104 #include <netinet/in.h> 105 #else 106 #include "port-sockets.h" 107 #endif 108 #else 109 # error find some way to use net-byte-order file version numbers. 110 #endif 111 112 static krb5_error_code KRB5_CALLCONV krb5_fcc_close 113 (krb5_context, krb5_ccache id); 114 115 static krb5_error_code KRB5_CALLCONV krb5_fcc_destroy 116 (krb5_context, krb5_ccache id); 117 118 static krb5_error_code KRB5_CALLCONV krb5_fcc_end_seq_get 119 (krb5_context, krb5_ccache id, krb5_cc_cursor *cursor); 120 121 static krb5_error_code KRB5_CALLCONV krb5_fcc_generate_new 122 (krb5_context, krb5_ccache *id); 123 124 static const char * KRB5_CALLCONV krb5_fcc_get_name 125 (krb5_context, krb5_ccache id); 126 127 static krb5_error_code KRB5_CALLCONV krb5_fcc_get_principal 128 (krb5_context, krb5_ccache id, krb5_principal *princ); 129 130 static krb5_error_code KRB5_CALLCONV krb5_fcc_initialize 131 (krb5_context, krb5_ccache id, krb5_principal princ); 132 133 static krb5_error_code KRB5_CALLCONV krb5_fcc_next_cred 134 (krb5_context, krb5_ccache id, krb5_cc_cursor *cursor, 135 krb5_creds *creds); 136 137 static krb5_error_code krb5_fcc_read 138 (krb5_context, krb5_ccache id, krb5_pointer buf, unsigned int len); 139 static krb5_error_code krb5_fcc_read_principal 140 (krb5_context, krb5_ccache id, krb5_principal *princ); 141 static krb5_error_code krb5_fcc_read_keyblock 142 (krb5_context, krb5_ccache id, krb5_keyblock *keyblock); 143 static krb5_error_code krb5_fcc_read_data 144 (krb5_context, krb5_ccache id, krb5_data *data); 145 static krb5_error_code krb5_fcc_read_int32 146 (krb5_context, krb5_ccache id, krb5_int32 *i); 147 static krb5_error_code krb5_fcc_read_ui_2 148 (krb5_context, krb5_ccache id, krb5_ui_2 *i); 149 static krb5_error_code krb5_fcc_read_octet 150 (krb5_context, krb5_ccache id, krb5_octet *i); 151 static krb5_error_code krb5_fcc_read_times 152 (krb5_context, krb5_ccache id, krb5_ticket_times *t); 153 static krb5_error_code krb5_fcc_read_addrs 154 (krb5_context, krb5_ccache, krb5_address ***); 155 static krb5_error_code krb5_fcc_read_addr 156 (krb5_context, krb5_ccache, krb5_address *); 157 static krb5_error_code krb5_fcc_read_authdata 158 (krb5_context, krb5_ccache, krb5_authdata ***); 159 static krb5_error_code krb5_fcc_read_authdatum 160 (krb5_context, krb5_ccache, krb5_authdata *); 161 162 static krb5_error_code KRB5_CALLCONV krb5_fcc_resolve 163 (krb5_context, krb5_ccache *id, const char *residual); 164 165 static krb5_error_code KRB5_CALLCONV krb5_fcc_retrieve 166 (krb5_context, krb5_ccache id, krb5_flags whichfields, 167 krb5_creds *mcreds, krb5_creds *creds); 168 169 static krb5_error_code KRB5_CALLCONV krb5_fcc_start_seq_get 170 (krb5_context, krb5_ccache id, krb5_cc_cursor *cursor); 171 172 static krb5_error_code KRB5_CALLCONV krb5_fcc_store 173 (krb5_context, krb5_ccache id, krb5_creds *creds); 174 175 static krb5_error_code krb5_fcc_skip_header 176 (krb5_context, krb5_ccache); 177 static krb5_error_code krb5_fcc_skip_principal 178 (krb5_context, krb5_ccache id); 179 180 static krb5_error_code KRB5_CALLCONV krb5_fcc_set_flags 181 (krb5_context, krb5_ccache id, krb5_flags flags); 182 183 extern const krb5_cc_ops krb5_cc_file_ops; 184 185 krb5_error_code krb5_change_cache (void); 186 187 static krb5_error_code krb5_fcc_write 188 (krb5_context, krb5_ccache id, krb5_pointer buf, unsigned int len); 189 static krb5_error_code krb5_fcc_store_principal 190 (krb5_context, krb5_ccache id, krb5_principal princ); 191 static krb5_error_code krb5_fcc_store_keyblock 192 (krb5_context, krb5_ccache id, krb5_keyblock *keyblock); 193 static krb5_error_code krb5_fcc_store_data 194 (krb5_context, krb5_ccache id, krb5_data *data); 195 static krb5_error_code krb5_fcc_store_int32 196 (krb5_context, krb5_ccache id, krb5_int32 i); 197 static krb5_error_code krb5_fcc_store_ui_4 198 (krb5_context, krb5_ccache id, krb5_ui_4 i); 199 static krb5_error_code krb5_fcc_store_ui_2 200 (krb5_context, krb5_ccache id, krb5_int32 i); 201 static krb5_error_code krb5_fcc_store_octet 202 (krb5_context, krb5_ccache id, krb5_int32 i); 203 static krb5_error_code krb5_fcc_store_times 204 (krb5_context, krb5_ccache id, krb5_ticket_times *t); 205 static krb5_error_code krb5_fcc_store_addrs 206 (krb5_context, krb5_ccache, krb5_address **); 207 static krb5_error_code krb5_fcc_store_addr 208 (krb5_context, krb5_ccache, krb5_address *); 209 static krb5_error_code krb5_fcc_store_authdata 210 (krb5_context, krb5_ccache, krb5_authdata **); 211 static krb5_error_code krb5_fcc_store_authdatum 212 (krb5_context, krb5_ccache, krb5_authdata *); 213 214 static krb5_error_code krb5_fcc_interpret 215 (krb5_context, int); 216 217 struct _krb5_fcc_data; 218 static krb5_error_code krb5_fcc_close_file 219 (krb5_context, struct _krb5_fcc_data *data); 220 static krb5_error_code krb5_fcc_open_file 221 (krb5_context, krb5_ccache, int); 222 223 224 #define KRB5_OK 0 225 226 #define KRB5_FCC_MAXLEN 100 227 228 /* 229 * FCC version 2 contains type information for principals. FCC 230 * version 1 does not. 231 * 232 * FCC version 3 contains keyblock encryption type information, and is 233 * architecture independent. Previous versions are not. 234 * 235 * The code will accept version 1, 2, and 3 ccaches, and depending 236 * what KRB5_FCC_DEFAULT_FVNO is set to, it will create version 1, 2, 237 * or 3 FCC caches. 238 * 239 * The default credentials cache should be type 3 for now (see 240 * init_ctx.c). 241 */ 242 243 #define KRB5_FCC_FVNO_1 0x0501 /* krb v5, fcc v1 */ 244 #define KRB5_FCC_FVNO_2 0x0502 /* krb v5, fcc v2 */ 245 #define KRB5_FCC_FVNO_3 0x0503 /* krb v5, fcc v3 */ 246 #define KRB5_FCC_FVNO_4 0x0504 /* krb v5, fcc v4 */ 247 248 #define FCC_OPEN_AND_ERASE 1 249 #define FCC_OPEN_RDWR 2 250 #define FCC_OPEN_RDONLY 3 251 #define FCC_OPEN_AND_ERASE_NOUNLINK 255 /* SUNW */ 252 253 /* Credential file header tags. 254 * The header tags are constructed as: 255 * krb5_ui_2 tag 256 * krb5_ui_2 len 257 * krb5_octet data[len] 258 * This format allows for older versions of the fcc processing code to skip 259 * past unrecognized tag formats. 260 */ 261 #define FCC_TAG_DELTATIME 1 262 263 #ifndef TKT_ROOT 264 #ifdef MSDOS_FILESYSTEM 265 #define TKT_ROOT "\\tkt" 266 #else 267 #define TKT_ROOT "/tmp/tkt" 268 #endif 269 #endif 270 271 /* macros to make checking flags easier */ 272 #define OPENCLOSE(id) (((krb5_fcc_data *)id->data)->flags & KRB5_TC_OPENCLOSE) 273 274 typedef struct _krb5_fcc_data { 275 char *filename; 276 /* Lock this one before reading or modifying the data stored here 277 that can be changed. (Filename is fixed after 278 initialization.) */ 279 k5_mutex_t lock; 280 int file; 281 krb5_flags flags; 282 int mode; /* needed for locking code */ 283 int version; /* version number of the file */ 284 285 /* Buffer data on reading, for performance. 286 We used to have a stdio option, but we get more precise control 287 by using the POSIX I/O functions. */ 288 #define FCC_BUFSIZ 1024 289 int valid_bytes; 290 int cur_offset; 291 char buf[FCC_BUFSIZ]; 292 } krb5_fcc_data; 293 294 static inline void invalidate_cache(krb5_fcc_data *data) 295 { 296 data->valid_bytes = 0; 297 } 298 299 static off_t fcc_lseek(krb5_fcc_data *data, off_t offset, int whence) 300 { 301 /* If we read some extra data in advance, and then want to know or 302 use our "current" position, we need to back up a little. */ 303 if (whence == SEEK_CUR && data->valid_bytes) { 304 assert(data->valid_bytes > 0); 305 assert(data->cur_offset > 0); 306 assert(data->cur_offset <= data->valid_bytes); 307 offset -= (data->valid_bytes - data->cur_offset); 308 } 309 invalidate_cache(data); 310 return lseek(data->file, offset, whence); 311 } 312 313 struct fcc_set { 314 struct fcc_set *next; 315 krb5_fcc_data *data; 316 unsigned int refcount; 317 }; 318 319 k5_mutex_t krb5int_cc_file_mutex = K5_MUTEX_PARTIAL_INITIALIZER; 320 static struct fcc_set *fccs = NULL; 321 322 /* An off_t can be arbitrarily complex */ 323 typedef struct _krb5_fcc_cursor { 324 off_t pos; 325 } krb5_fcc_cursor; 326 327 #define MAYBE_OPEN(CONTEXT, ID, MODE) \ 328 { \ 329 k5_assert_locked(&((krb5_fcc_data *)(ID)->data)->lock); \ 330 if (OPENCLOSE (ID)) { \ 331 krb5_error_code maybe_open_ret; \ 332 maybe_open_ret = krb5_fcc_open_file (CONTEXT,ID,MODE); \ 333 if (maybe_open_ret) { \ 334 k5_mutex_unlock(&((krb5_fcc_data *)(ID)->data)->lock); \ 335 return maybe_open_ret; \ 336 } \ 337 } \ 338 } 339 340 #define MAYBE_CLOSE(CONTEXT, ID, RET) \ 341 { \ 342 if (OPENCLOSE (ID)) { \ 343 krb5_error_code maybe_close_ret; \ 344 maybe_close_ret = krb5_fcc_close_file (CONTEXT, \ 345 (krb5_fcc_data *)(ID)->data); \ 346 if (!(RET)) RET = maybe_close_ret; } } 347 348 #define MAYBE_CLOSE_IGNORE(CONTEXT, ID) \ 349 { \ 350 if (OPENCLOSE (ID)) { \ 351 (void) krb5_fcc_close_file (CONTEXT,(krb5_fcc_data *)(ID)->data); } } 352 353 #define CHECK(ret) if (ret != KRB5_OK) goto errout; 354 355 #define NO_FILE -1 356 357 /* 358 * Effects: 359 * Reads len bytes from the cache id, storing them in buf. 360 * 361 * Requires: 362 * Must be called with mutex locked. 363 * 364 * Errors: 365 * KRB5_CC_END - there were not len bytes available 366 * system errors (read) 367 */ 368 static krb5_error_code 369 krb5_fcc_read(krb5_context context, krb5_ccache id, krb5_pointer buf, unsigned int len) 370 { 371 #if 0 372 int ret; 373 374 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 375 376 ret = read(((krb5_fcc_data *) id->data)->file, (char *) buf, len); 377 if (ret == -1) 378 return krb5_fcc_interpret(context, errno); 379 if (ret != len) 380 return KRB5_CC_END; 381 else 382 return KRB5_OK; 383 #else 384 krb5_fcc_data *data = (krb5_fcc_data *) id->data; 385 386 k5_assert_locked(&data->lock); 387 388 while (len > 0) { 389 int nread, e; 390 size_t ncopied; 391 392 assert (data->valid_bytes >= 0); 393 if (data->valid_bytes > 0) 394 assert(data->cur_offset <= data->valid_bytes); 395 if (data->valid_bytes == 0 396 || data->cur_offset == data->valid_bytes) { 397 /* Fill buffer from current file position. */ 398 nread = read(data->file, data->buf, sizeof(data->buf)); 399 e = errno; 400 if (nread < 0) 401 return krb5_fcc_interpret(context, e); 402 if (nread == 0) 403 /* EOF */ 404 return KRB5_CC_END; 405 data->valid_bytes = nread; 406 data->cur_offset = 0; 407 } 408 assert(data->cur_offset < data->valid_bytes); 409 ncopied = len; 410 assert(ncopied == len); 411 if (data->valid_bytes - data->cur_offset < ncopied) 412 ncopied = data->valid_bytes - data->cur_offset; 413 memcpy(buf, data->buf + data->cur_offset, ncopied); 414 data->cur_offset += ncopied; 415 assert(data->cur_offset > 0); 416 assert(data->cur_offset <= data->valid_bytes); 417 len -= ncopied; 418 assert(len >= 0); 419 /* Don't do arithmetic on void pointers. */ 420 buf = (char*)buf + ncopied; 421 } 422 return 0; 423 #endif 424 } 425 426 /* 427 * FOR ALL OF THE FOLLOWING FUNCTIONS: 428 * 429 * Requires: 430 * id is open and set to read at the appropriate place in the file 431 * 432 * mutex is locked 433 * 434 * Effects: 435 * Fills in the second argument with data of the appropriate type from 436 * the file. In some cases, the functions have to allocate space for 437 * variable length fields; therefore, krb5_destroy_<type> must be 438 * called for each filled in structure. 439 * 440 * Errors: 441 * system errors (read errors) 442 * KRB5_CC_NOMEM 443 */ 444 445 #define ALLOC(NUM,TYPE) \ 446 (((NUM) <= (((size_t)0-1)/ sizeof(TYPE))) \ 447 ? (TYPE *) calloc((NUM), sizeof(TYPE)) \ 448 : (errno = ENOMEM,(TYPE *) 0)) 449 450 static krb5_error_code 451 krb5_fcc_read_principal(krb5_context context, krb5_ccache id, krb5_principal *princ) 452 { 453 krb5_fcc_data *data = (krb5_fcc_data *)id->data; 454 krb5_error_code kret; 455 register krb5_principal tmpprinc; 456 krb5_int32 length, type; 457 int i; 458 459 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 460 461 if (data->version == KRB5_FCC_FVNO_1) { 462 type = KRB5_NT_UNKNOWN; 463 } else { 464 /* Read principal type */ 465 kret = krb5_fcc_read_int32(context, id, &type); 466 if (kret != KRB5_OK) 467 return kret; 468 } 469 470 /* Read the number of components */ 471 kret = krb5_fcc_read_int32(context, id, &length); 472 if (kret != KRB5_OK) 473 return kret; 474 475 /* 476 * DCE includes the principal's realm in the count; the new format 477 * does not. 478 */ 479 if (data->version == KRB5_FCC_FVNO_1) 480 length--; 481 if (length < 0) 482 return KRB5_CC_NOMEM; 483 484 tmpprinc = (krb5_principal) malloc(sizeof(krb5_principal_data)); 485 if (tmpprinc == NULL) 486 return KRB5_CC_NOMEM; 487 if (length) { 488 size_t msize = length; 489 if (msize != length) { 490 free(tmpprinc); 491 return KRB5_CC_NOMEM; 492 } 493 tmpprinc->data = ALLOC (msize, krb5_data); 494 if (tmpprinc->data == 0) { 495 free((char *)tmpprinc); 496 return KRB5_CC_NOMEM; 497 } 498 } else 499 tmpprinc->data = 0; 500 tmpprinc->magic = KV5M_PRINCIPAL; 501 tmpprinc->length = length; 502 tmpprinc->type = type; 503 504 kret = krb5_fcc_read_data(context, id, krb5_princ_realm(context, tmpprinc)); 505 506 i = 0; 507 CHECK(kret); 508 509 for (i=0; i < length; i++) { 510 kret = krb5_fcc_read_data(context, id, krb5_princ_component(context, tmpprinc, i)); 511 CHECK(kret); 512 } 513 *princ = tmpprinc; 514 return KRB5_OK; 515 516 errout: 517 while(--i >= 0) 518 free(krb5_princ_component(context, tmpprinc, i)->data); 519 free((char *)tmpprinc->data); 520 free((char *)tmpprinc); 521 return kret; 522 } 523 524 static krb5_error_code 525 krb5_fcc_read_addrs(krb5_context context, krb5_ccache id, krb5_address ***addrs) 526 { 527 krb5_error_code kret; 528 krb5_int32 length; 529 size_t msize; 530 int i; 531 532 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 533 534 *addrs = 0; 535 536 /* Read the number of components */ 537 kret = krb5_fcc_read_int32(context, id, &length); 538 CHECK(kret); 539 540 /* Make *addrs able to hold length pointers to krb5_address structs 541 * Add one extra for a null-terminated list 542 */ 543 msize = length; 544 msize += 1; 545 if (msize == 0 || msize - 1 != length || length < 0) 546 return KRB5_CC_NOMEM; 547 *addrs = ALLOC (msize, krb5_address *); 548 if (*addrs == NULL) 549 return KRB5_CC_NOMEM; 550 551 for (i=0; i < length; i++) { 552 (*addrs)[i] = (krb5_address *) malloc(sizeof(krb5_address)); 553 if ((*addrs)[i] == NULL) { 554 krb5_free_addresses(context, *addrs); 555 return KRB5_CC_NOMEM; 556 } 557 kret = krb5_fcc_read_addr(context, id, (*addrs)[i]); 558 CHECK(kret); 559 } 560 561 return KRB5_OK; 562 errout: 563 if (*addrs) 564 krb5_free_addresses(context, *addrs); 565 return kret; 566 } 567 568 static krb5_error_code 569 krb5_fcc_read_keyblock(krb5_context context, krb5_ccache id, krb5_keyblock *keyblock) 570 { 571 krb5_fcc_data *data = (krb5_fcc_data *)id->data; 572 krb5_error_code kret; 573 krb5_ui_2 ui2; 574 krb5_int32 int32; 575 576 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 577 578 keyblock->magic = KV5M_KEYBLOCK; 579 keyblock->contents = 0; 580 581 kret = krb5_fcc_read_ui_2(context, id, &ui2); 582 keyblock->enctype = ui2; 583 CHECK(kret); 584 if (data->version == KRB5_FCC_FVNO_3) { 585 /* This works because the old etype is the same as the new enctype. */ 586 kret = krb5_fcc_read_ui_2(context, id, &ui2); 587 /* keyblock->enctype = ui2; */ 588 CHECK(kret); 589 } 590 591 kret = krb5_fcc_read_int32(context, id, &int32); 592 CHECK(kret); 593 if (int32 < 0) 594 return KRB5_CC_NOMEM; 595 keyblock->length = int32; 596 /* Overflow check. */ 597 if (keyblock->length != int32) 598 return KRB5_CC_NOMEM; 599 if ( keyblock->length == 0 ) 600 return KRB5_OK; 601 keyblock->contents = calloc(keyblock->length, sizeof(krb5_octet)); 602 if (keyblock->contents == NULL) 603 return KRB5_CC_NOMEM; 604 605 kret = krb5_fcc_read(context, id, keyblock->contents, keyblock->length); 606 if (kret) 607 goto errout; 608 609 return KRB5_OK; 610 errout: 611 if (keyblock->contents) 612 krb5_xfree(keyblock->contents); 613 return kret; 614 } 615 616 static krb5_error_code 617 krb5_fcc_read_data(krb5_context context, krb5_ccache id, krb5_data *data) 618 { 619 krb5_error_code kret; 620 krb5_int32 len; 621 622 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 623 624 data->magic = KV5M_DATA; 625 data->data = 0; 626 627 kret = krb5_fcc_read_int32(context, id, &len); 628 CHECK(kret); 629 if (len < 0) 630 return KRB5_CC_NOMEM; 631 data->length = len; 632 if (data->length != len || data->length + 1 == 0) 633 return KRB5_CC_NOMEM; 634 635 if (data->length == 0) { 636 data->data = 0; 637 return KRB5_OK; 638 } 639 640 data->data = (char *) malloc(data->length+1); 641 if (data->data == NULL) 642 return KRB5_CC_NOMEM; 643 644 kret = krb5_fcc_read(context, id, data->data, (unsigned) data->length); 645 CHECK(kret); 646 647 data->data[data->length] = 0; /* Null terminate, just in case.... */ 648 return KRB5_OK; 649 errout: 650 if (data->data) 651 krb5_xfree(data->data); 652 return kret; 653 } 654 655 static krb5_error_code 656 krb5_fcc_read_addr(krb5_context context, krb5_ccache id, krb5_address *addr) 657 { 658 krb5_error_code kret; 659 krb5_ui_2 ui2; 660 krb5_int32 int32; 661 662 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 663 664 addr->magic = KV5M_ADDRESS; 665 addr->contents = 0; 666 667 kret = krb5_fcc_read_ui_2(context, id, &ui2); 668 CHECK(kret); 669 addr->addrtype = ui2; 670 671 kret = krb5_fcc_read_int32(context, id, &int32); 672 CHECK(kret); 673 if ((int32 & VALID_INT_BITS) != int32) /* Overflow int??? */ 674 return KRB5_CC_NOMEM; 675 addr->length = int32; 676 /* Length field is "unsigned int", which may be smaller than 32 677 bits. */ 678 if (addr->length != int32) 679 return KRB5_CC_NOMEM; /* XXX */ 680 681 if (addr->length == 0) 682 return KRB5_OK; 683 684 addr->contents = (krb5_octet *) malloc(addr->length); 685 if (addr->contents == NULL) 686 return KRB5_CC_NOMEM; 687 688 kret = krb5_fcc_read(context, id, addr->contents, addr->length); 689 CHECK(kret); 690 691 return KRB5_OK; 692 errout: 693 if (addr->contents) 694 krb5_xfree(addr->contents); 695 return kret; 696 } 697 698 static krb5_error_code 699 krb5_fcc_read_int32(krb5_context context, krb5_ccache id, krb5_int32 *i) 700 { 701 krb5_fcc_data *data = (krb5_fcc_data *)id->data; 702 krb5_error_code retval; 703 unsigned char buf[4]; 704 krb5_int32 val; 705 706 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 707 708 if ((data->version == KRB5_FCC_FVNO_1) || 709 (data->version == KRB5_FCC_FVNO_2)) 710 return krb5_fcc_read(context, id, (krb5_pointer) i, sizeof(krb5_int32)); 711 else { 712 retval = krb5_fcc_read(context, id, buf, 4); 713 if (retval) 714 return retval; 715 val = buf[0]; 716 val = (val << 8) | buf[1]; 717 val = (val << 8) | buf[2]; 718 val = (val << 8) | buf[3]; 719 *i = val; 720 return 0; 721 } 722 } 723 724 static krb5_error_code 725 krb5_fcc_read_ui_2(krb5_context context, krb5_ccache id, krb5_ui_2 *i) 726 { 727 krb5_fcc_data *data = (krb5_fcc_data *)id->data; 728 krb5_error_code retval; 729 unsigned char buf[2]; 730 731 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 732 733 if ((data->version == KRB5_FCC_FVNO_1) || 734 (data->version == KRB5_FCC_FVNO_2)) 735 return krb5_fcc_read(context, id, (krb5_pointer) i, sizeof(krb5_ui_2)); 736 else { 737 retval = krb5_fcc_read(context, id, buf, 2); 738 if (retval) 739 return retval; 740 *i = (buf[0] << 8) + buf[1]; 741 return 0; 742 } 743 } 744 745 static krb5_error_code 746 krb5_fcc_read_octet(krb5_context context, krb5_ccache id, krb5_octet *i) 747 { 748 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 749 return krb5_fcc_read(context, id, (krb5_pointer) i, 1); 750 } 751 752 753 static krb5_error_code 754 krb5_fcc_read_times(krb5_context context, krb5_ccache id, krb5_ticket_times *t) 755 { 756 krb5_fcc_data *data = (krb5_fcc_data *)id->data; 757 krb5_error_code retval; 758 krb5_int32 i; 759 760 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 761 762 if ((data->version == KRB5_FCC_FVNO_1) || 763 (data->version == KRB5_FCC_FVNO_2)) 764 return krb5_fcc_read(context, id, (krb5_pointer) t, sizeof(krb5_ticket_times)); 765 else { 766 retval = krb5_fcc_read_int32(context, id, &i); 767 CHECK(retval); 768 t->authtime = i; 769 770 retval = krb5_fcc_read_int32(context, id, &i); 771 CHECK(retval); 772 t->starttime = i; 773 774 retval = krb5_fcc_read_int32(context, id, &i); 775 CHECK(retval); 776 t->endtime = i; 777 778 retval = krb5_fcc_read_int32(context, id, &i); 779 CHECK(retval); 780 t->renew_till = i; 781 } 782 return 0; 783 errout: 784 return retval; 785 } 786 787 static krb5_error_code 788 krb5_fcc_read_authdata(krb5_context context, krb5_ccache id, krb5_authdata ***a) 789 { 790 krb5_error_code kret; 791 krb5_int32 length; 792 size_t msize; 793 int i; 794 795 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 796 797 *a = 0; 798 799 /* Read the number of components */ 800 kret = krb5_fcc_read_int32(context, id, &length); 801 CHECK(kret); 802 803 if (length == 0) 804 return KRB5_OK; 805 806 /* Make *a able to hold length pointers to krb5_authdata structs 807 * Add one extra for a null-terminated list 808 */ 809 msize = length; 810 msize += 1; 811 if (msize == 0 || msize - 1 != length || length < 0) 812 return KRB5_CC_NOMEM; 813 *a = ALLOC (msize, krb5_authdata *); 814 if (*a == NULL) 815 return KRB5_CC_NOMEM; 816 817 for (i=0; i < length; i++) { 818 (*a)[i] = (krb5_authdata *) malloc(sizeof(krb5_authdata)); 819 if ((*a)[i] == NULL) { 820 krb5_free_authdata(context, *a); 821 return KRB5_CC_NOMEM; 822 } 823 kret = krb5_fcc_read_authdatum(context, id, (*a)[i]); 824 CHECK(kret); 825 } 826 827 return KRB5_OK; 828 errout: 829 if (*a) 830 krb5_free_authdata(context, *a); 831 return kret; 832 } 833 834 static krb5_error_code 835 krb5_fcc_read_authdatum(krb5_context context, krb5_ccache id, krb5_authdata *a) 836 { 837 krb5_error_code kret; 838 krb5_int32 int32; 839 krb5_ui_2 ui2; 840 841 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 842 843 a->magic = KV5M_AUTHDATA; 844 a->contents = NULL; 845 846 kret = krb5_fcc_read_ui_2(context, id, &ui2); 847 CHECK(kret); 848 a->ad_type = (krb5_authdatatype)ui2; 849 kret = krb5_fcc_read_int32(context, id, &int32); 850 CHECK(kret); 851 if ((int32 & VALID_INT_BITS) != int32) /* Overflow int??? */ 852 return KRB5_CC_NOMEM; 853 a->length = int32; 854 /* Value could have gotten truncated if int is smaller than 32 855 bits. */ 856 if (a->length != int32) 857 return KRB5_CC_NOMEM; /* XXX */ 858 859 if (a->length == 0 ) 860 return KRB5_OK; 861 862 a->contents = (krb5_octet *) malloc(a->length); 863 if (a->contents == NULL) 864 return KRB5_CC_NOMEM; 865 866 kret = krb5_fcc_read(context, id, a->contents, a->length); 867 CHECK(kret); 868 869 return KRB5_OK; 870 errout: 871 if (a->contents) 872 krb5_xfree(a->contents); 873 return kret; 874 875 } 876 #undef CHECK 877 878 #define CHECK(ret) if (ret != KRB5_OK) return ret; 879 880 /* 881 * Requires: 882 * id is open 883 * 884 * Effects: 885 * Writes len bytes from buf into the file cred cache id. 886 * 887 * Errors: 888 * system errors 889 */ 890 static krb5_error_code 891 krb5_fcc_write(krb5_context context, krb5_ccache id, krb5_pointer buf, unsigned int len) 892 { 893 int ret; 894 895 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 896 invalidate_cache((krb5_fcc_data *) id->data); 897 898 ret = write(((krb5_fcc_data *)id->data)->file, (char *) buf, len); 899 if (ret < 0) 900 return krb5_fcc_interpret(context, errno); 901 if (ret != len) 902 return KRB5_CC_WRITE; 903 return KRB5_OK; 904 } 905 906 /* 907 * FOR ALL OF THE FOLLOWING FUNCTIONS: 908 * 909 * Requires: 910 * ((krb5_fcc_data *) id->data)->file is open and at the right position. 911 * 912 * mutex is locked 913 * 914 * Effects: 915 * Stores an encoded version of the second argument in the 916 * cache file. 917 * 918 * Errors: 919 * system errors 920 */ 921 922 static krb5_error_code 923 krb5_fcc_store_principal(krb5_context context, krb5_ccache id, krb5_principal princ) 924 { 925 krb5_fcc_data *data = (krb5_fcc_data *)id->data; 926 krb5_error_code ret; 927 krb5_int32 i, length, tmp, type; 928 929 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 930 931 type = krb5_princ_type(context, princ); 932 tmp = length = krb5_princ_size(context, princ); 933 934 if (data->version == KRB5_FCC_FVNO_1) { 935 /* 936 * DCE-compatible format means that the length count 937 * includes the realm. (It also doesn't include the 938 * principal type information.) 939 */ 940 tmp++; 941 } else { 942 ret = krb5_fcc_store_int32(context, id, type); 943 CHECK(ret); 944 } 945 946 ret = krb5_fcc_store_int32(context, id, tmp); 947 CHECK(ret); 948 949 ret = krb5_fcc_store_data(context, id, krb5_princ_realm(context, princ)); 950 CHECK(ret); 951 952 for (i=0; i < length; i++) { 953 ret = krb5_fcc_store_data(context, id, krb5_princ_component(context, princ, i)); 954 CHECK(ret); 955 } 956 957 return KRB5_OK; 958 } 959 960 static krb5_error_code 961 krb5_fcc_store_addrs(krb5_context context, krb5_ccache id, krb5_address **addrs) 962 { 963 krb5_error_code ret; 964 krb5_address **temp; 965 krb5_int32 i, length = 0; 966 967 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 968 969 /* Count the number of components */ 970 if (addrs) { 971 temp = addrs; 972 while (*temp++) 973 length += 1; 974 } 975 976 ret = krb5_fcc_store_int32(context, id, length); 977 CHECK(ret); 978 for (i=0; i < length; i++) { 979 ret = krb5_fcc_store_addr(context, id, addrs[i]); 980 CHECK(ret); 981 } 982 983 return KRB5_OK; 984 } 985 986 static krb5_error_code 987 krb5_fcc_store_keyblock(krb5_context context, krb5_ccache id, krb5_keyblock *keyblock) 988 { 989 krb5_fcc_data *data = (krb5_fcc_data *)id->data; 990 krb5_error_code ret; 991 992 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 993 994 ret = krb5_fcc_store_ui_2(context, id, keyblock->enctype); 995 CHECK(ret); 996 if (data->version == KRB5_FCC_FVNO_3) { 997 ret = krb5_fcc_store_ui_2(context, id, keyblock->enctype); 998 CHECK(ret); 999 } 1000 ret = krb5_fcc_store_ui_4(context, id, keyblock->length); 1001 CHECK(ret); 1002 return krb5_fcc_write(context, id, (char *) keyblock->contents, keyblock->length); 1003 } 1004 1005 static krb5_error_code 1006 krb5_fcc_store_addr(krb5_context context, krb5_ccache id, krb5_address *addr) 1007 { 1008 krb5_error_code ret; 1009 1010 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 1011 1012 ret = krb5_fcc_store_ui_2(context, id, addr->addrtype); 1013 CHECK(ret); 1014 ret = krb5_fcc_store_ui_4(context, id, addr->length); 1015 CHECK(ret); 1016 return krb5_fcc_write(context, id, (char *) addr->contents, addr->length); 1017 } 1018 1019 1020 static krb5_error_code 1021 krb5_fcc_store_data(krb5_context context, krb5_ccache id, krb5_data *data) 1022 { 1023 krb5_error_code ret; 1024 1025 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 1026 1027 ret = krb5_fcc_store_ui_4(context, id, data->length); 1028 CHECK(ret); 1029 return krb5_fcc_write(context, id, data->data, data->length); 1030 } 1031 1032 static krb5_error_code 1033 krb5_fcc_store_int32(krb5_context context, krb5_ccache id, krb5_int32 i) 1034 { 1035 krb5_fcc_data *data = (krb5_fcc_data *)id->data; 1036 unsigned char buf[4]; 1037 1038 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 1039 1040 if ((data->version == KRB5_FCC_FVNO_1) || 1041 (data->version == KRB5_FCC_FVNO_2)) 1042 return krb5_fcc_write(context, id, (char *) &i, sizeof(krb5_int32)); 1043 else { 1044 buf[3] = (unsigned char) (i & 0xFF); 1045 i >>= 8; 1046 buf[2] = (unsigned char) (i & 0xFF); 1047 i >>= 8; 1048 buf[1] = (unsigned char) (i & 0xFF); 1049 i >>= 8; 1050 buf[0] = (unsigned char) (i & 0xFF); 1051 return krb5_fcc_write(context, id, buf, 4); 1052 } 1053 } 1054 1055 static krb5_error_code 1056 krb5_fcc_store_ui_4(krb5_context context, krb5_ccache id, krb5_ui_4 i) 1057 { 1058 krb5_fcc_data *data = (krb5_fcc_data *)id->data; 1059 unsigned char buf[4]; 1060 1061 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 1062 1063 if ((data->version == KRB5_FCC_FVNO_1) || 1064 (data->version == KRB5_FCC_FVNO_2)) 1065 return krb5_fcc_write(context, id, (char *) &i, sizeof(krb5_int32)); 1066 else { 1067 buf[3] = (unsigned char) (i & 0xFF); 1068 i >>= 8; 1069 buf[2] = (unsigned char) (i & 0xFF); 1070 i >>= 8; 1071 buf[1] = (unsigned char) (i & 0xFF); 1072 i >>= 8; 1073 buf[0] = (unsigned char) (i & 0xFF); 1074 return krb5_fcc_write(context, id, buf, 4); 1075 } 1076 } 1077 1078 static krb5_error_code 1079 krb5_fcc_store_ui_2(krb5_context context, krb5_ccache id, krb5_int32 i) 1080 { 1081 krb5_fcc_data *data = (krb5_fcc_data *)id->data; 1082 krb5_ui_2 ibuf; 1083 unsigned char buf[2]; 1084 1085 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 1086 1087 if ((data->version == KRB5_FCC_FVNO_1) || 1088 (data->version == KRB5_FCC_FVNO_2)) { 1089 ibuf = (krb5_ui_2) i; 1090 return krb5_fcc_write(context, id, (char *) &ibuf, sizeof(krb5_ui_2)); 1091 } else { 1092 buf[1] = (unsigned char) (i & 0xFF); 1093 i >>= 8; 1094 buf[0] = (unsigned char) (i & 0xFF); 1095 return krb5_fcc_write(context, id, buf, 2); 1096 } 1097 } 1098 1099 static krb5_error_code 1100 krb5_fcc_store_octet(krb5_context context, krb5_ccache id, krb5_int32 i) 1101 { 1102 krb5_octet ibuf; 1103 1104 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 1105 1106 ibuf = (krb5_octet) i; 1107 return krb5_fcc_write(context, id, (char *) &ibuf, 1); 1108 } 1109 1110 static krb5_error_code 1111 krb5_fcc_store_times(krb5_context context, krb5_ccache id, krb5_ticket_times *t) 1112 { 1113 krb5_fcc_data *data = (krb5_fcc_data *)id->data; 1114 krb5_error_code retval; 1115 1116 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 1117 1118 if ((data->version == KRB5_FCC_FVNO_1) || 1119 (data->version == KRB5_FCC_FVNO_2)) 1120 return krb5_fcc_write(context, id, (char *) t, sizeof(krb5_ticket_times)); 1121 else { 1122 retval = krb5_fcc_store_int32(context, id, t->authtime); 1123 CHECK(retval); 1124 retval = krb5_fcc_store_int32(context, id, t->starttime); 1125 CHECK(retval); 1126 retval = krb5_fcc_store_int32(context, id, t->endtime); 1127 CHECK(retval); 1128 retval = krb5_fcc_store_int32(context, id, t->renew_till); 1129 CHECK(retval); 1130 return 0; 1131 } 1132 } 1133 1134 static krb5_error_code 1135 krb5_fcc_store_authdata(krb5_context context, krb5_ccache id, krb5_authdata **a) 1136 { 1137 krb5_error_code ret; 1138 krb5_authdata **temp; 1139 krb5_int32 i, length=0; 1140 1141 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 1142 1143 if (a != NULL) { 1144 for (temp=a; *temp; temp++) 1145 length++; 1146 } 1147 1148 ret = krb5_fcc_store_int32(context, id, length); 1149 CHECK(ret); 1150 for (i=0; i<length; i++) { 1151 ret = krb5_fcc_store_authdatum (context, id, a[i]); 1152 CHECK(ret); 1153 } 1154 return KRB5_OK; 1155 } 1156 1157 static krb5_error_code 1158 krb5_fcc_store_authdatum (krb5_context context, krb5_ccache id, krb5_authdata *a) 1159 { 1160 krb5_error_code ret; 1161 1162 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 1163 1164 ret = krb5_fcc_store_ui_2(context, id, a->ad_type); 1165 CHECK(ret); 1166 ret = krb5_fcc_store_ui_4(context, id, a->length); 1167 CHECK(ret); 1168 return krb5_fcc_write(context, id, (krb5_pointer) a->contents, a->length); 1169 } 1170 #undef CHECK 1171 1172 static krb5_error_code 1173 krb5_fcc_close_file (krb5_context context, krb5_fcc_data *data) 1174 { 1175 int ret; 1176 krb5_error_code retval; 1177 1178 k5_assert_locked(&data->lock); 1179 1180 if (data->file == NO_FILE) 1181 return KRB5_FCC_INTERNAL; 1182 1183 retval = krb5_unlock_file(context, data->file); 1184 ret = close (data->file); 1185 data->file = NO_FILE; 1186 if (retval) 1187 return retval; 1188 1189 return ret ? krb5_fcc_interpret (context, errno) : 0; 1190 } 1191 1192 #if defined(ANSI_STDIO) || defined(_WIN32) 1193 #define BINARY_MODE "b" 1194 #else 1195 #define BINARY_MODE "" 1196 #endif 1197 1198 #ifndef HAVE_SETVBUF 1199 #undef setvbuf 1200 #define setvbuf(FILE,BUF,MODE,SIZE) \ 1201 ((SIZE) < BUFSIZE ? (abort(),0) : setbuf(FILE, BUF)) 1202 #endif 1203 1204 1205 1206 static krb5_error_code 1207 krb5_fcc_open_nounlink(char *filename, int open_flag, int *ret_fd, int *new) 1208 { 1209 struct stat lres; 1210 struct stat fres; 1211 int error; 1212 uid_t uid, euid; 1213 int fd; 1214 int newfile = 0; 1215 1216 *ret_fd = -1; 1217 /* 1218 * SUNW 1219 * If we are opening in NOUNLINK mode, we have to check that the 1220 * existing file, if any, is not a symlink. If it is, we try to 1221 * delete and re-create it. 1222 */ 1223 error = lstat(filename, &lres); 1224 if (error == -1 && errno != ENOENT) { 1225 syslog(LOG_ERR, "lstat failed for %s [%m]", filename); 1226 return (-1); 1227 } 1228 1229 if (error == 0 && !S_ISREG(lres.st_mode)) { 1230 syslog(LOG_WARNING, "%s is not a plain file!", filename); 1231 syslog(LOG_WARNING, "trying to unlink %s", filename); 1232 if (unlink(filename) != 0) { 1233 syslog(LOG_ERR, "could not unlink %s [%m]", filename); 1234 return (-1); 1235 } 1236 } 1237 1238 fd = THREEPARAMOPEN(filename, open_flag | O_NONBLOCK, 0600); 1239 if (fd == -1) { 1240 if (errno == ENOENT) { 1241 fd = THREEPARAMOPEN(filename, 1242 open_flag | O_EXCL | O_CREAT, 0600); 1243 if (fd != -1) { 1244 newfile = 1; 1245 } else { 1246 /* If the file got created after the open we must retry */ 1247 if (errno == EEXIST) 1248 return (0); 1249 } 1250 } else if (errno == EACCES) { 1251 /* 1252 * We failed since the file existed with wrong permissions. 1253 * Let's try to unlink it and if that succeeds retry. 1254 */ 1255 syslog(LOG_WARNING, "Insufficient permissions on %s", 1256 filename); 1257 syslog(LOG_WARNING, "trying to unlink %s", filename); 1258 if (unlink(filename) != 0) { 1259 syslog(LOG_ERR, "could not unlink %s [%m]", filename); 1260 return (-1); 1261 } 1262 return (0); 1263 } 1264 } 1265 /* If we still don't have a valid fd, we stop trying */ 1266 if (fd == -1) 1267 return (-1); 1268 1269 /* 1270 * SUNW 1271 * If the file was not created now with a O_CREAT | O_EXCL open, 1272 * we have opened an existing file. We should check if the file 1273 * owner is us, if not, unlink and retry. If unlink fails we log 1274 * the error and return. 1275 */ 1276 if (!newfile) { 1277 if (fstat(fd, &fres) == -1) { 1278 syslog(LOG_ERR, "lstat failed for %s [%m]", filename); 1279 close(fd); 1280 return (-1); 1281 } 1282 /* Check if this is the same file we lstat'd earlier */ 1283 if (lres.st_dev != fres.st_dev || lres.st_ino != fres.st_ino) { 1284 syslog(LOG_ERR, "%s changed between stat and open!", filename); 1285 close(fd); 1286 return (-1); 1287 } 1288 1289 uid = getuid(); 1290 euid = geteuid(); 1291 /* 1292 * Some apps (gssd, via a priv version of getuid()) 1293 * "set" the real uid only, others 1294 * (telnetd/login/pam_krb5, etc) set effective uid only. 1295 */ 1296 if (fres.st_uid != uid && fres.st_uid != euid) { 1297 close(fd); 1298 syslog(LOG_WARNING, 1299 "%s owned by %d instead of %d (euid=%d, uid=%d)", 1300 filename, fres.st_uid, euid, euid, uid); 1301 syslog(LOG_WARNING, "trying to unlink %s", filename); 1302 if (unlink(filename) != 0) { 1303 syslog(LOG_ERR, "could not unlink %s [%m]", filename); 1304 return (-1); 1305 } 1306 return (0); 1307 } 1308 } 1309 1310 *new = newfile; 1311 *ret_fd = fd; 1312 return (0); 1313 } 1314 1315 1316 static krb5_error_code 1317 krb5_fcc_open_file (krb5_context context, krb5_ccache id, int mode) 1318 { 1319 krb5_os_context os_ctx = (krb5_os_context)context->os_context; 1320 krb5_fcc_data *data = (krb5_fcc_data *)id->data; 1321 krb5_ui_2 fcc_fvno; 1322 krb5_ui_2 fcc_flen; 1323 krb5_ui_2 fcc_tag; 1324 krb5_ui_2 fcc_taglen; 1325 int f, open_flag; 1326 int lock_flag; 1327 krb5_error_code retval = 0; 1328 int retries; 1329 int newfile = 0; 1330 1331 k5_assert_locked(&data->lock); 1332 invalidate_cache(data); 1333 1334 if (data->file != NO_FILE) { 1335 /* Don't know what state it's in; shut down and start anew. */ 1336 (void) krb5_unlock_file(context, data->file); 1337 (void) close (data->file); 1338 data->file = NO_FILE; 1339 } 1340 1341 switch(mode) { 1342 case FCC_OPEN_AND_ERASE_NOUNLINK: 1343 open_flag = O_RDWR; 1344 break; 1345 case FCC_OPEN_AND_ERASE: 1346 unlink(data->filename); 1347 open_flag = O_CREAT|O_EXCL|O_TRUNC|O_RDWR; 1348 break; 1349 case FCC_OPEN_RDWR: 1350 open_flag = O_RDWR; 1351 break; 1352 case FCC_OPEN_RDONLY: 1353 default: 1354 open_flag = O_RDONLY; 1355 break; 1356 } 1357 1358 fcc_retry: 1359 /* 1360 * SUNW 1361 * If we are opening in NOUNLINK mode, check whether we are opening a 1362 * symlink or a file owned by some other user and take preventive action. 1363 */ 1364 newfile = 0; 1365 if (mode == FCC_OPEN_AND_ERASE_NOUNLINK) { 1366 retval = krb5_fcc_open_nounlink(data->filename, open_flag, 1367 &f, &newfile); 1368 if (retval == 0 && f == -1) 1369 goto fcc_retry; 1370 } else { 1371 f = THREEPARAMOPEN (data->filename, open_flag | O_BINARY, 0600); 1372 } 1373 if (f == NO_FILE) 1374 return krb5_fcc_interpret (context, errno); 1375 1376 data->mode = mode; 1377 1378 if (data->mode == FCC_OPEN_RDONLY) 1379 lock_flag = KRB5_LOCKMODE_SHARED; 1380 else 1381 lock_flag = KRB5_LOCKMODE_EXCLUSIVE; 1382 1383 if ((retval = krb5_lock_file(context, f, lock_flag))) { 1384 (void) close(f); 1385 if (retval == EAGAIN && retries++ < LOCK_RETRIES) { 1386 /* SUNW wait some time before retrying */ 1387 if (poll(NULL, 0, WAIT_LENGTH) == 0) 1388 goto fcc_retry; 1389 } 1390 syslog(LOG_ERR, "Failed to lock %s [%m]", data->filename); 1391 return retval; 1392 } 1393 1394 if (mode == FCC_OPEN_AND_ERASE || mode == FCC_OPEN_AND_ERASE_NOUNLINK) { 1395 int cnt; 1396 1397 /* 1398 * SUNW 1399 * If this file was not created, we have to flush existing data. 1400 * This will happen only if we are doing an ERASE_NOUNLINK open. 1401 */ 1402 if (newfile == 0 && (ftruncate(f, 0) == -1)) { 1403 syslog(LOG_ERR, "ftruncate failed for %s [%m]", data->filename); 1404 close(f); 1405 return (krb5_fcc_interpret(context, errno)); 1406 } 1407 1408 /* write the version number */ 1409 fcc_fvno = htons(context->fcc_default_format); 1410 data->version = context->fcc_default_format; 1411 if ((cnt = write(f, (char *)&fcc_fvno, sizeof(fcc_fvno))) != 1412 sizeof(fcc_fvno)) { 1413 retval = ((cnt == -1) ? krb5_fcc_interpret(context, errno) : 1414 KRB5_CC_IO); 1415 goto done; 1416 } 1417 data->file = f; 1418 1419 if (data->version == KRB5_FCC_FVNO_4) { 1420 /* V4 of the credentials cache format allows for header tags */ 1421 fcc_flen = 0; 1422 1423 if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) 1424 fcc_flen += (2*sizeof(krb5_ui_2) + 2*sizeof(krb5_int32)); 1425 1426 /* Write header length */ 1427 retval = krb5_fcc_store_ui_2(context, id, (krb5_int32)fcc_flen); 1428 if (retval) goto done; 1429 1430 if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) { 1431 /* Write time offset tag */ 1432 fcc_tag = FCC_TAG_DELTATIME; 1433 fcc_taglen = 2*sizeof(krb5_int32); 1434 1435 retval = krb5_fcc_store_ui_2(context,id,(krb5_int32)fcc_tag); 1436 if (retval) goto done; 1437 retval = krb5_fcc_store_ui_2(context,id,(krb5_int32)fcc_taglen); 1438 if (retval) goto done; 1439 retval = krb5_fcc_store_int32(context,id,os_ctx->time_offset); 1440 if (retval) goto done; 1441 retval = krb5_fcc_store_int32(context,id,os_ctx->usec_offset); 1442 if (retval) goto done; 1443 } 1444 } 1445 invalidate_cache(data); 1446 goto done; 1447 } 1448 1449 /* verify a valid version number is there */ 1450 invalidate_cache(data); 1451 if (read(f, (char *)&fcc_fvno, sizeof(fcc_fvno)) != sizeof(fcc_fvno)) { 1452 retval = KRB5_CC_FORMAT; 1453 goto done; 1454 } 1455 data->version = ntohs(fcc_fvno); 1456 if ((data->version != KRB5_FCC_FVNO_4) && 1457 (data->version != KRB5_FCC_FVNO_3) && 1458 (data->version != KRB5_FCC_FVNO_2) && 1459 (data->version != KRB5_FCC_FVNO_1)) { 1460 retval = KRB5_CCACHE_BADVNO; 1461 goto done; 1462 } 1463 1464 data->file = f; 1465 1466 if (data->version == KRB5_FCC_FVNO_4) { 1467 char buf[1024]; 1468 1469 if (krb5_fcc_read_ui_2(context, id, &fcc_flen) || 1470 (fcc_flen > sizeof(buf))) 1471 { 1472 retval = KRB5_CC_FORMAT; 1473 goto done; 1474 } 1475 1476 while (fcc_flen) { 1477 if ((fcc_flen < (2 * sizeof(krb5_ui_2))) || 1478 krb5_fcc_read_ui_2(context, id, &fcc_tag) || 1479 krb5_fcc_read_ui_2(context, id, &fcc_taglen) || 1480 (fcc_taglen > (fcc_flen - 2*sizeof(krb5_ui_2)))) 1481 { 1482 retval = KRB5_CC_FORMAT; 1483 goto done; 1484 } 1485 1486 switch (fcc_tag) { 1487 case FCC_TAG_DELTATIME: 1488 if (fcc_taglen != 2*sizeof(krb5_int32)) { 1489 retval = KRB5_CC_FORMAT; 1490 goto done; 1491 } 1492 if (!(context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) || 1493 (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)) 1494 { 1495 if (krb5_fcc_read(context, id, buf, fcc_taglen)) { 1496 retval = KRB5_CC_FORMAT; 1497 goto done; 1498 } 1499 break; 1500 } 1501 if (krb5_fcc_read_int32(context, id, &os_ctx->time_offset) || 1502 krb5_fcc_read_int32(context, id, &os_ctx->usec_offset)) 1503 { 1504 retval = KRB5_CC_FORMAT; 1505 goto done; 1506 } 1507 os_ctx->os_flags = 1508 ((os_ctx->os_flags & ~KRB5_OS_TOFFSET_TIME) | 1509 KRB5_OS_TOFFSET_VALID); 1510 break; 1511 default: 1512 if (fcc_taglen && krb5_fcc_read(context,id,buf,fcc_taglen)) { 1513 retval = KRB5_CC_FORMAT; 1514 goto done; 1515 } 1516 break; 1517 } 1518 fcc_flen -= (2*sizeof(krb5_ui_2) + fcc_taglen); 1519 } 1520 } 1521 1522 done: 1523 if (retval) { 1524 data->file = -1; 1525 (void) krb5_unlock_file(context, f); 1526 (void) close(f); 1527 } 1528 return retval; 1529 } 1530 1531 static krb5_error_code 1532 krb5_fcc_skip_header(krb5_context context, krb5_ccache id) 1533 { 1534 krb5_fcc_data *data = (krb5_fcc_data *)id->data; 1535 krb5_error_code kret; 1536 krb5_ui_2 fcc_flen; 1537 1538 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 1539 1540 fcc_lseek(data, (off_t) sizeof(krb5_ui_2), SEEK_SET); 1541 if (data->version == KRB5_FCC_FVNO_4) { 1542 kret = krb5_fcc_read_ui_2(context, id, &fcc_flen); 1543 if (kret) return kret; 1544 if(fcc_lseek(data, (off_t) fcc_flen, SEEK_CUR) < 0) 1545 return errno; 1546 } 1547 return KRB5_OK; 1548 } 1549 1550 static krb5_error_code 1551 krb5_fcc_skip_principal(krb5_context context, krb5_ccache id) 1552 { 1553 krb5_error_code kret; 1554 krb5_principal princ; 1555 1556 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 1557 1558 kret = krb5_fcc_read_principal(context, id, &princ); 1559 if (kret != KRB5_OK) 1560 return kret; 1561 1562 krb5_free_principal(context, princ); 1563 return KRB5_OK; 1564 } 1565 1566 1567 /* 1568 * Modifies: 1569 * id 1570 * 1571 * Effects: 1572 * Creates/refreshes the file cred cache id. If the cache exists, its 1573 * contents are destroyed. 1574 * 1575 * Errors: 1576 * system errors 1577 * permission errors 1578 */ 1579 static krb5_error_code KRB5_CALLCONV 1580 krb5_fcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ) 1581 { 1582 krb5_error_code kret = 0; 1583 int reti = 0; 1584 1585 kret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock); 1586 if (kret) 1587 return kret; 1588 1589 MAYBE_OPEN(context, id, FCC_OPEN_AND_ERASE_NOUNLINK); /* SUNW */ 1590 1591 /* 1592 * SUN14resync 1593 * This is not needed and can cause problems with ktkt_warnd(1M) 1594 * because it does tricks with getuid and if we enable this fchmod 1595 * we get EPERM [file_owner] failures on fchmod. 1596 */ 1597 #if 0 1598 #if defined(HAVE_FCHMOD) || defined(HAVE_CHMOD) 1599 { 1600 #ifdef HAVE_FCHMOD 1601 reti = fchmod(((krb5_fcc_data *) id->data)->file, S_IREAD | S_IWRITE); 1602 #else 1603 reti = chmod(((krb5_fcc_data *) id->data)->filename, S_IREAD | S_IWRITE); 1604 #endif 1605 #endif 1606 if (reti == -1) { 1607 kret = krb5_fcc_interpret(context, errno); 1608 MAYBE_CLOSE(context, id, kret); 1609 k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock); 1610 return kret; 1611 } 1612 } 1613 #endif 1614 kret = krb5_fcc_store_principal(context, id, princ); 1615 1616 MAYBE_CLOSE(context, id, kret); 1617 k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock); 1618 krb5_change_cache (); 1619 return kret; 1620 } 1621 1622 /* 1623 * Drop the ref count; if it hits zero, remove the entry from the 1624 * fcc_set list and free it. 1625 */ 1626 static krb5_error_code dereference(krb5_context context, krb5_fcc_data *data) 1627 { 1628 krb5_error_code kerr; 1629 struct fcc_set **fccsp; 1630 1631 kerr = k5_mutex_lock(&krb5int_cc_file_mutex); 1632 if (kerr) 1633 return kerr; 1634 for (fccsp = &fccs; *fccsp != NULL; fccsp = &(*fccsp)->next) 1635 if ((*fccsp)->data == data) 1636 break; 1637 assert(*fccsp != NULL); 1638 assert((*fccsp)->data == data); 1639 (*fccsp)->refcount--; 1640 if ((*fccsp)->refcount == 0) { 1641 struct fcc_set *temp; 1642 data = (*fccsp)->data; 1643 temp = *fccsp; 1644 *fccsp = (*fccsp)->next; 1645 free(temp); 1646 k5_mutex_unlock(&krb5int_cc_file_mutex); 1647 k5_mutex_assert_unlocked(&data->lock); 1648 free(data->filename); 1649 zap(data->buf, sizeof(data->buf)); 1650 if (data->file >= 0) { 1651 k5_mutex_lock(&data->lock); 1652 krb5_fcc_close_file(context, data); 1653 k5_mutex_unlock(&data->lock); 1654 } 1655 k5_mutex_destroy(&data->lock); 1656 free(data); 1657 } else 1658 k5_mutex_unlock(&krb5int_cc_file_mutex); 1659 return 0; 1660 } 1661 1662 /* 1663 * Modifies: 1664 * id 1665 * 1666 * Effects: 1667 * Closes the file cache, invalidates the id, and frees any resources 1668 * associated with the cache. 1669 */ 1670 static krb5_error_code KRB5_CALLCONV 1671 krb5_fcc_close(krb5_context context, krb5_ccache id) 1672 { 1673 dereference(context, (krb5_fcc_data *) id->data); 1674 krb5_xfree(id); 1675 return KRB5_OK; 1676 } 1677 1678 /* 1679 * Effects: 1680 * Destroys the contents of id. 1681 * 1682 * Errors: 1683 * system errors 1684 */ 1685 static krb5_error_code KRB5_CALLCONV 1686 krb5_fcc_destroy(krb5_context context, krb5_ccache id) 1687 { 1688 krb5_error_code kret = 0; 1689 krb5_fcc_data *data = (krb5_fcc_data *) id->data; 1690 register int ret; 1691 1692 struct stat buf; 1693 unsigned long i, size; 1694 unsigned int wlen; 1695 char zeros[BUFSIZ]; 1696 1697 kret = k5_mutex_lock(&data->lock); 1698 if (kret) 1699 return kret; 1700 1701 if (OPENCLOSE(id)) { 1702 invalidate_cache(data); 1703 ret = THREEPARAMOPEN(data->filename, 1704 O_RDWR | O_BINARY, 0); 1705 if (ret < 0) { 1706 kret = krb5_fcc_interpret(context, errno); 1707 goto cleanup; 1708 } 1709 data->file = ret; 1710 } 1711 else 1712 fcc_lseek(data, (off_t) 0, SEEK_SET); 1713 1714 #ifdef MSDOS_FILESYSTEM 1715 /* "disgusting bit of UNIX trivia" - that's how the writers of NFS describe 1716 ** the ability of UNIX to still write to a file which has been unlinked. 1717 ** Naturally, the PC can't do this. As a result, we have to delete the file 1718 ** after we wipe it clean but that throws off all the error handling code. 1719 ** So we have do the work ourselves. 1720 */ 1721 ret = fstat(data->file, &buf); 1722 if (ret == -1) { 1723 kret = krb5_fcc_interpret(context, errno); 1724 size = 0; /* Nothing to wipe clean */ 1725 } else 1726 size = (unsigned long) buf.st_size; 1727 1728 memset(zeros, 0, BUFSIZ); 1729 while (size > 0) { 1730 wlen = (int) ((size > BUFSIZ) ? BUFSIZ : size); /* How much to write */ 1731 i = write(data->file, zeros, wlen); 1732 if (i < 0) { 1733 kret = krb5_fcc_interpret(context, errno); 1734 /* Don't jump to cleanup--we still want to delete the file. */ 1735 break; 1736 } 1737 size -= i; /* We've read this much */ 1738 } 1739 1740 if (OPENCLOSE(id)) { 1741 (void) close(((krb5_fcc_data *)id->data)->file); 1742 data->file = -1; 1743 } 1744 1745 ret = unlink(data->filename); 1746 if (ret < 0) { 1747 kret = krb5_fcc_interpret(context, errno); 1748 goto cleanup; 1749 } 1750 1751 #else /* MSDOS_FILESYSTEM */ 1752 1753 ret = unlink(data->filename); 1754 if (ret < 0) { 1755 kret = krb5_fcc_interpret(context, errno); 1756 if (OPENCLOSE(id)) { 1757 (void) close(((krb5_fcc_data *)id->data)->file); 1758 data->file = -1; 1759 kret = ret; 1760 } 1761 goto cleanup; 1762 } 1763 1764 ret = fstat(data->file, &buf); 1765 if (ret < 0) { 1766 kret = krb5_fcc_interpret(context, errno); 1767 if (OPENCLOSE(id)) { 1768 (void) close(((krb5_fcc_data *)id->data)->file); 1769 data->file = -1; 1770 } 1771 goto cleanup; 1772 } 1773 1774 /* XXX This may not be legal XXX */ 1775 size = (unsigned long) buf.st_size; 1776 memset(zeros, 0, BUFSIZ); 1777 for (i=0; i < size / BUFSIZ; i++) 1778 if (write(data->file, zeros, BUFSIZ) < 0) { 1779 kret = krb5_fcc_interpret(context, errno); 1780 if (OPENCLOSE(id)) { 1781 (void) close(((krb5_fcc_data *)id->data)->file); 1782 data->file = -1; 1783 } 1784 goto cleanup; 1785 } 1786 1787 wlen = (unsigned int) (size % BUFSIZ); 1788 if (write(data->file, zeros, wlen) < 0) { 1789 kret = krb5_fcc_interpret(context, errno); 1790 if (OPENCLOSE(id)) { 1791 (void) close(((krb5_fcc_data *)id->data)->file); 1792 data->file = -1; 1793 } 1794 goto cleanup; 1795 } 1796 1797 ret = close(data->file); 1798 data->file = -1; 1799 1800 if (ret) 1801 kret = krb5_fcc_interpret(context, errno); 1802 1803 #endif /* MSDOS_FILESYSTEM */ 1804 1805 cleanup: 1806 k5_mutex_unlock(&data->lock); 1807 dereference(context, data); 1808 krb5_xfree(id); 1809 1810 krb5_change_cache (); 1811 return kret; 1812 } 1813 1814 extern const krb5_cc_ops krb5_fcc_ops; 1815 1816 /* 1817 * Requires: 1818 * residual is a legal path name, and a null-terminated string 1819 * 1820 * Modifies: 1821 * id 1822 * 1823 * Effects: 1824 * creates a file-based cred cache that will reside in the file 1825 * residual. The cache is not opened, but the filename is reserved. 1826 * 1827 * Returns: 1828 * A filled in krb5_ccache structure "id". 1829 * 1830 * Errors: 1831 * KRB5_CC_NOMEM - there was insufficient memory to allocate the 1832 * krb5_ccache. id is undefined. 1833 * permission errors 1834 */ 1835 static krb5_error_code KRB5_CALLCONV 1836 krb5_fcc_resolve (krb5_context context, krb5_ccache *id, const char *residual) 1837 { 1838 krb5_ccache lid; 1839 krb5_error_code kret; 1840 krb5_fcc_data *data; 1841 struct fcc_set *setptr; 1842 1843 kret = k5_mutex_lock(&krb5int_cc_file_mutex); 1844 if (kret) 1845 return kret; 1846 for (setptr = fccs; setptr; setptr = setptr->next) { 1847 if (!strcmp(setptr->data->filename, residual)) 1848 break; 1849 } 1850 if (setptr) { 1851 data = setptr->data; 1852 assert(setptr->refcount != 0); 1853 setptr->refcount++; 1854 assert(setptr->refcount != 0); 1855 kret = k5_mutex_lock(&data->lock); 1856 if (kret) { 1857 k5_mutex_unlock(&krb5int_cc_file_mutex); 1858 return kret; 1859 } 1860 k5_mutex_unlock(&krb5int_cc_file_mutex); 1861 } else { 1862 data = malloc(sizeof(krb5_fcc_data)); 1863 if (data == NULL) { 1864 k5_mutex_unlock(&krb5int_cc_file_mutex); 1865 return KRB5_CC_NOMEM; 1866 } 1867 data->filename = strdup(residual); 1868 if (data->filename == NULL) { 1869 k5_mutex_unlock(&krb5int_cc_file_mutex); 1870 free(data); 1871 return KRB5_CC_NOMEM; 1872 } 1873 kret = k5_mutex_init(&data->lock); 1874 if (kret) { 1875 k5_mutex_unlock(&krb5int_cc_file_mutex); 1876 free(data->filename); 1877 free(data); 1878 return kret; 1879 } 1880 kret = k5_mutex_lock(&data->lock); 1881 if (kret) { 1882 k5_mutex_unlock(&krb5int_cc_file_mutex); 1883 k5_mutex_destroy(&data->lock); 1884 free(data->filename); 1885 free(data); 1886 return kret; 1887 } 1888 /* data->version,mode filled in for real later */ 1889 data->version = data->mode = 0; 1890 data->flags = KRB5_TC_OPENCLOSE; 1891 data->file = -1; 1892 data->valid_bytes = 0; 1893 setptr = malloc(sizeof(struct fcc_set)); 1894 if (setptr == NULL) { 1895 k5_mutex_unlock(&krb5int_cc_file_mutex); 1896 k5_mutex_destroy(&data->lock); 1897 free(data->filename); 1898 free(data); 1899 return KRB5_CC_NOMEM; 1900 } 1901 setptr->refcount = 1; 1902 setptr->data = data; 1903 setptr->next = fccs; 1904 fccs = setptr; 1905 k5_mutex_unlock(&krb5int_cc_file_mutex); 1906 } 1907 1908 k5_mutex_assert_locked(&data->lock); 1909 k5_mutex_unlock(&data->lock); 1910 lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache)); 1911 if (lid == NULL) { 1912 dereference(context, data); 1913 return KRB5_CC_NOMEM; 1914 } 1915 1916 lid->ops = &krb5_fcc_ops; 1917 lid->data = data; 1918 lid->magic = KV5M_CCACHE; 1919 1920 /* other routines will get errors on open, and callers must expect them, 1921 if cache is non-existent/unusable */ 1922 *id = lid; 1923 return KRB5_OK; 1924 } 1925 1926 /* 1927 * Effects: 1928 * Prepares for a sequential search of the credentials cache. 1929 * Returns and krb5_cc_cursor to be used with krb5_fcc_next_cred and 1930 * krb5_fcc_end_seq_get. 1931 * 1932 * If the cache is modified between the time of this call and the time 1933 * of the final krb5_fcc_end_seq_get, the results are undefined. 1934 * 1935 * Errors: 1936 * KRB5_CC_NOMEM 1937 * system errors 1938 */ 1939 static krb5_error_code KRB5_CALLCONV 1940 krb5_fcc_start_seq_get(krb5_context context, krb5_ccache id, 1941 krb5_cc_cursor *cursor) 1942 { 1943 krb5_fcc_cursor *fcursor; 1944 krb5_error_code kret = KRB5_OK; 1945 krb5_fcc_data *data = (krb5_fcc_data *)id->data; 1946 1947 kret = k5_mutex_lock(&data->lock); 1948 if (kret) 1949 return kret; 1950 1951 fcursor = (krb5_fcc_cursor *) malloc(sizeof(krb5_fcc_cursor)); 1952 if (fcursor == NULL) { 1953 k5_mutex_unlock(&data->lock); 1954 return KRB5_CC_NOMEM; 1955 } 1956 if (OPENCLOSE(id)) { 1957 kret = krb5_fcc_open_file(context, id, FCC_OPEN_RDONLY); 1958 if (kret) { 1959 krb5_xfree(fcursor); 1960 k5_mutex_unlock(&data->lock); 1961 return kret; 1962 } 1963 } 1964 1965 /* Make sure we start reading right after the primary principal */ 1966 kret = krb5_fcc_skip_header(context, id); 1967 if (kret) { 1968 /* SUNW14resync - fix mem leak */ 1969 krb5_xfree(fcursor); 1970 goto done; 1971 } 1972 kret = krb5_fcc_skip_principal(context, id); 1973 if (kret) { 1974 /* SUNW14resync - fix mem leak */ 1975 krb5_xfree(fcursor); 1976 goto done; 1977 } 1978 1979 fcursor->pos = fcc_lseek(data, (off_t) 0, SEEK_CUR); 1980 *cursor = (krb5_cc_cursor) fcursor; 1981 1982 done: 1983 MAYBE_CLOSE(context, id, kret); 1984 k5_mutex_unlock(&data->lock); 1985 return kret; 1986 } 1987 1988 1989 /* 1990 * Requires: 1991 * cursor is a krb5_cc_cursor originally obtained from 1992 * krb5_fcc_start_seq_get. 1993 * 1994 * Modifes: 1995 * cursor, creds 1996 * 1997 * Effects: 1998 * Fills in creds with the "next" credentals structure from the cache 1999 * id. The actual order the creds are returned in is arbitrary. 2000 * Space is allocated for the variable length fields in the 2001 * credentials structure, so the object returned must be passed to 2002 * krb5_destroy_credential. 2003 * 2004 * The cursor is updated for the next call to krb5_fcc_next_cred. 2005 * 2006 * Errors: 2007 * system errors 2008 */ 2009 static krb5_error_code KRB5_CALLCONV 2010 krb5_fcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor, 2011 krb5_creds *creds) 2012 { 2013 #define TCHECK(ret) if (ret != KRB5_OK) goto lose; 2014 krb5_error_code kret; 2015 krb5_fcc_cursor *fcursor; 2016 krb5_int32 int32; 2017 krb5_octet octet; 2018 krb5_fcc_data *d = (krb5_fcc_data *) id->data; 2019 2020 kret = k5_mutex_lock(&d->lock); 2021 if (kret) 2022 return kret; 2023 2024 memset((char *)creds, 0, sizeof(*creds)); 2025 MAYBE_OPEN(context, id, FCC_OPEN_RDONLY); 2026 fcursor = (krb5_fcc_cursor *) *cursor; 2027 2028 kret = (fcc_lseek(d, fcursor->pos, SEEK_SET) == (off_t) -1); 2029 if (kret) { 2030 kret = krb5_fcc_interpret(context, errno); 2031 MAYBE_CLOSE(context, id, kret); 2032 k5_mutex_unlock(&d->lock); 2033 return kret; 2034 } 2035 2036 kret = krb5_fcc_read_principal(context, id, &creds->client); 2037 TCHECK(kret); 2038 kret = krb5_fcc_read_principal(context, id, &creds->server); 2039 TCHECK(kret); 2040 kret = krb5_fcc_read_keyblock(context, id, &creds->keyblock); 2041 TCHECK(kret); 2042 kret = krb5_fcc_read_times(context, id, &creds->times); 2043 TCHECK(kret); 2044 kret = krb5_fcc_read_octet(context, id, &octet); 2045 TCHECK(kret); 2046 creds->is_skey = octet; 2047 kret = krb5_fcc_read_int32(context, id, &int32); 2048 TCHECK(kret); 2049 creds->ticket_flags = int32; 2050 kret = krb5_fcc_read_addrs(context, id, &creds->addresses); 2051 TCHECK(kret); 2052 kret = krb5_fcc_read_authdata(context, id, &creds->authdata); 2053 TCHECK(kret); 2054 kret = krb5_fcc_read_data(context, id, &creds->ticket); 2055 TCHECK(kret); 2056 kret = krb5_fcc_read_data(context, id, &creds->second_ticket); 2057 TCHECK(kret); 2058 2059 fcursor->pos = fcc_lseek(d, (off_t) 0, SEEK_CUR); 2060 cursor = (krb5_cc_cursor *) fcursor; 2061 2062 lose: 2063 MAYBE_CLOSE (context, id, kret); 2064 k5_mutex_unlock(&d->lock); 2065 if (kret != KRB5_OK) 2066 krb5_free_cred_contents(context, creds); 2067 return kret; 2068 } 2069 2070 /* 2071 * Requires: 2072 * cursor is a krb5_cc_cursor originally obtained from 2073 * krb5_fcc_start_seq_get. 2074 * 2075 * Modifies: 2076 * id, cursor 2077 * 2078 * Effects: 2079 * Finishes sequential processing of the file credentials ccache id, 2080 * and invalidates the cursor (it must never be used after this call). 2081 */ 2082 /* ARGSUSED */ 2083 static krb5_error_code KRB5_CALLCONV 2084 krb5_fcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor) 2085 { 2086 /* We don't do anything with the file cache itself, so 2087 no need to lock anything. */ 2088 2089 /* don't close; it may be left open by the caller, 2090 and if not, fcc_start_seq_get and/or fcc_next_cred will do the 2091 MAYBE_CLOSE. 2092 MAYBE_CLOSE(context, id, kret); */ 2093 krb5_xfree((krb5_fcc_cursor *) *cursor); 2094 return 0; 2095 } 2096 2097 2098 /* 2099 * Effects: 2100 * Creates a new file cred cache whose name is guaranteed to be 2101 * unique. The name begins with the string TKT_ROOT (from fcc.h). 2102 * The cache is not opened, but the new filename is reserved. 2103 * 2104 * Returns: 2105 * The filled in krb5_ccache id. 2106 * 2107 * Errors: 2108 * KRB5_CC_NOMEM - there was insufficient memory to allocate the 2109 * krb5_ccache. id is undefined. 2110 * system errors (from open) 2111 */ 2112 static krb5_error_code KRB5_CALLCONV 2113 krb5_fcc_generate_new (krb5_context context, krb5_ccache *id) 2114 { 2115 krb5_ccache lid; 2116 int ret; 2117 krb5_error_code retcode = 0; 2118 char scratch[sizeof(TKT_ROOT)+6+1]; /* +6 for the scratch part, +1 for 2119 NUL */ 2120 krb5_fcc_data *data; 2121 2122 /* Allocate memory */ 2123 lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache)); 2124 if (lid == NULL) 2125 return KRB5_CC_NOMEM; 2126 2127 lid->ops = &krb5_fcc_ops; 2128 2129 (void) strcpy(scratch, TKT_ROOT); 2130 (void) strcat(scratch, "XXXXXX"); 2131 #ifdef HAVE_MKSTEMP 2132 ret = mkstemp(scratch); 2133 if (ret == -1) { 2134 return krb5_fcc_interpret(context, errno); 2135 } else close(ret); 2136 #else /*HAVE_MKSTEMP*/ 2137 mktemp(scratch); 2138 #endif 2139 2140 lid->data = (krb5_pointer) malloc(sizeof(krb5_fcc_data)); 2141 if (lid->data == NULL) { 2142 krb5_xfree(lid); 2143 return KRB5_CC_NOMEM; 2144 } 2145 2146 ((krb5_fcc_data *) lid->data)->filename = (char *) 2147 malloc(strlen(scratch) + 1); 2148 if (((krb5_fcc_data *) lid->data)->filename == NULL) { 2149 krb5_xfree(((krb5_fcc_data *) lid->data)); 2150 krb5_xfree(lid); 2151 return KRB5_CC_NOMEM; 2152 } 2153 2154 /* 2155 * The file is initially closed at the end of this call... 2156 */ 2157 ((krb5_fcc_data *) lid->data)->flags = 0; 2158 ((krb5_fcc_data *) lid->data)->file = -1; 2159 ((krb5_fcc_data *) lid->data)->valid_bytes = 0; 2160 data = (krb5_fcc_data *) lid->data; 2161 2162 retcode = k5_mutex_init(&data->lock); 2163 if (retcode) 2164 goto err_out; 2165 2166 /* Set up the filename */ 2167 strcpy(((krb5_fcc_data *) lid->data)->filename, scratch); 2168 2169 /* Make sure the file name is reserved */ 2170 ret = THREEPARAMOPEN(((krb5_fcc_data *) lid->data)->filename, 2171 O_CREAT | O_EXCL | O_WRONLY | O_BINARY, 0); 2172 if (ret == -1) { 2173 retcode = krb5_fcc_interpret(context, errno); 2174 goto err_out; 2175 } else { 2176 krb5_int16 fcc_fvno = htons(context->fcc_default_format); 2177 krb5_int16 fcc_flen = 0; 2178 int errsave, cnt; 2179 2180 /* Ignore user's umask, set mode = 0600 */ 2181 #ifndef HAVE_FCHMOD 2182 #ifdef HAVE_CHMOD 2183 chmod(((krb5_fcc_data *) lid->data)->filename, S_IRUSR | S_IWUSR); 2184 #endif 2185 #else 2186 fchmod(ret, S_IRUSR | S_IWUSR); 2187 #endif 2188 if ((cnt = write(ret, (char *)&fcc_fvno, sizeof(fcc_fvno))) 2189 != sizeof(fcc_fvno)) { 2190 errsave = errno; 2191 (void) close(ret); 2192 (void) unlink(((krb5_fcc_data *) lid->data)->filename); 2193 retcode = (cnt == -1) ? krb5_fcc_interpret(context, errsave) : KRB5_CC_IO; 2194 goto err_out; 2195 } 2196 /* For version 4 we save a length for the rest of the header */ 2197 if (context->fcc_default_format == KRB5_FCC_FVNO_4) { 2198 if ((cnt = write(ret, (char *)&fcc_flen, sizeof(fcc_flen))) 2199 != sizeof(fcc_flen)) { 2200 errsave = errno; 2201 (void) close(ret); 2202 (void) unlink(((krb5_fcc_data *) lid->data)->filename); 2203 retcode = (cnt == -1) ? krb5_fcc_interpret(context, errsave) : KRB5_CC_IO; 2204 goto err_out; 2205 } 2206 } 2207 if (close(ret) == -1) { 2208 errsave = errno; 2209 (void) unlink(((krb5_fcc_data *) lid->data)->filename); 2210 retcode = krb5_fcc_interpret(context, errsave); 2211 goto err_out; 2212 } 2213 *id = lid; 2214 /* default to open/close on every trn - otherwise destroy 2215 will get as to state confused */ 2216 ((krb5_fcc_data *) lid->data)->flags = KRB5_TC_OPENCLOSE; 2217 krb5_change_cache (); 2218 return KRB5_OK; 2219 } 2220 2221 err_out: 2222 krb5_xfree(((krb5_fcc_data *) lid->data)->filename); 2223 krb5_xfree(((krb5_fcc_data *) lid->data)); 2224 krb5_xfree(lid); 2225 return retcode; 2226 } 2227 2228 /* 2229 * Requires: 2230 * id is a file credential cache 2231 * 2232 * Returns: 2233 * The name of the file cred cache id. 2234 */ 2235 static const char * KRB5_CALLCONV 2236 krb5_fcc_get_name (krb5_context context, krb5_ccache id) 2237 { 2238 return (char *) ((krb5_fcc_data *) id->data)->filename; 2239 } 2240 2241 /* 2242 * Modifies: 2243 * id, princ 2244 * 2245 * Effects: 2246 * Retrieves the primary principal from id, as set with 2247 * krb5_fcc_initialize. The principal is returned is allocated 2248 * storage that must be freed by the caller via krb5_free_principal. 2249 * 2250 * Errors: 2251 * system errors 2252 * KRB5_CC_NOMEM 2253 */ 2254 static krb5_error_code KRB5_CALLCONV 2255 krb5_fcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *princ) 2256 { 2257 krb5_error_code kret = KRB5_OK; 2258 2259 kret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock); 2260 if (kret) 2261 return kret; 2262 2263 MAYBE_OPEN(context, id, FCC_OPEN_RDONLY); 2264 2265 /* make sure we're beyond the header */ 2266 kret = krb5_fcc_skip_header(context, id); 2267 if (kret) goto done; 2268 kret = krb5_fcc_read_principal(context, id, princ); 2269 2270 done: 2271 MAYBE_CLOSE(context, id, kret); 2272 k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock); 2273 return kret; 2274 } 2275 2276 2277 static krb5_error_code KRB5_CALLCONV 2278 krb5_fcc_retrieve(krb5_context context, krb5_ccache id, krb5_flags whichfields, krb5_creds *mcreds, krb5_creds *creds) 2279 { 2280 return krb5_cc_retrieve_cred_default (context, id, whichfields, 2281 mcreds, creds); 2282 } 2283 2284 2285 /* 2286 * Modifies: 2287 * the file cache 2288 * 2289 * Effects: 2290 * stores creds in the file cred cache 2291 * 2292 * Errors: 2293 * system errors 2294 * storage failure errors 2295 */ 2296 static krb5_error_code KRB5_CALLCONV 2297 krb5_fcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds) 2298 { 2299 #define TCHECK(ret) if (ret != KRB5_OK) goto lose; 2300 krb5_error_code ret; 2301 2302 ret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock); 2303 if (ret) 2304 return ret; 2305 2306 /* Make sure we are writing to the end of the file */ 2307 MAYBE_OPEN(context, id, FCC_OPEN_RDWR); 2308 2309 /* Make sure we are writing to the end of the file */ 2310 ret = fcc_lseek((krb5_fcc_data *) id->data, (off_t) 0, SEEK_END); 2311 if (ret < 0) { 2312 MAYBE_CLOSE_IGNORE(context, id); 2313 k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock); 2314 return krb5_fcc_interpret(context, errno); 2315 } 2316 2317 ret = krb5_fcc_store_principal(context, id, creds->client); 2318 TCHECK(ret); 2319 ret = krb5_fcc_store_principal(context, id, creds->server); 2320 TCHECK(ret); 2321 ret = krb5_fcc_store_keyblock(context, id, &creds->keyblock); 2322 TCHECK(ret); 2323 ret = krb5_fcc_store_times(context, id, &creds->times); 2324 TCHECK(ret); 2325 ret = krb5_fcc_store_octet(context, id, (krb5_int32) creds->is_skey); 2326 TCHECK(ret); 2327 ret = krb5_fcc_store_int32(context, id, creds->ticket_flags); 2328 TCHECK(ret); 2329 ret = krb5_fcc_store_addrs(context, id, creds->addresses); 2330 TCHECK(ret); 2331 ret = krb5_fcc_store_authdata(context, id, creds->authdata); 2332 TCHECK(ret); 2333 ret = krb5_fcc_store_data(context, id, &creds->ticket); 2334 TCHECK(ret); 2335 ret = krb5_fcc_store_data(context, id, &creds->second_ticket); 2336 TCHECK(ret); 2337 2338 lose: 2339 MAYBE_CLOSE(context, id, ret); 2340 k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock); 2341 krb5_change_cache (); 2342 return ret; 2343 #undef TCHECK 2344 } 2345 2346 /* 2347 * Non-functional stub implementation for krb5_fcc_remove 2348 * 2349 * Errors: 2350 * KRB5_CC_NOSUPP - not implemented 2351 */ 2352 static krb5_error_code KRB5_CALLCONV 2353 krb5_fcc_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags, 2354 krb5_creds *creds) 2355 { 2356 return KRB5_CC_NOSUPP; 2357 } 2358 2359 /* 2360 * Requires: 2361 * id is a cred cache returned by krb5_fcc_resolve or 2362 * krb5_fcc_generate_new, but has not been opened by krb5_fcc_initialize. 2363 * 2364 * Modifies: 2365 * id 2366 * 2367 * Effects: 2368 * Sets the operational flags of id to flags. 2369 */ 2370 static krb5_error_code KRB5_CALLCONV 2371 krb5_fcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags) 2372 { 2373 krb5_error_code ret = KRB5_OK; 2374 2375 ret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock); 2376 if (ret) 2377 return ret; 2378 2379 /* XXX This should check for illegal combinations, if any.. */ 2380 if (flags & KRB5_TC_OPENCLOSE) { 2381 /* asking to turn on OPENCLOSE mode */ 2382 if (!OPENCLOSE(id) 2383 /* XXX Is this test necessary? */ 2384 && ((krb5_fcc_data *) id->data)->file != NO_FILE) 2385 (void) krb5_fcc_close_file (context, ((krb5_fcc_data *) id->data)); 2386 } else { 2387 /* asking to turn off OPENCLOSE mode, meaning it must be 2388 left open. We open if it's not yet open */ 2389 MAYBE_OPEN(context, id, FCC_OPEN_RDONLY); 2390 } 2391 2392 ((krb5_fcc_data *) id->data)->flags = flags; 2393 k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock); 2394 return ret; 2395 } 2396 2397 2398 static krb5_error_code 2399 krb5_fcc_interpret(krb5_context context, int errnum) 2400 { 2401 register krb5_error_code retval; 2402 switch (errnum) { 2403 case ENOENT: 2404 retval = KRB5_FCC_NOFILE; 2405 break; 2406 case EPERM: 2407 case EACCES: 2408 #ifdef EISDIR 2409 case EISDIR: /* Mac doesn't have EISDIR */ 2410 #endif 2411 case ENOTDIR: 2412 #ifdef ELOOP 2413 case ELOOP: /* Bad symlink is like no file. */ 2414 #endif 2415 #ifdef ETXTBSY 2416 case ETXTBSY: 2417 #endif 2418 case EBUSY: 2419 case EROFS: 2420 retval = KRB5_FCC_PERM; 2421 break; 2422 case EINVAL: 2423 case EEXIST: /* XXX */ 2424 case EFAULT: 2425 case EBADF: 2426 #ifdef ENAMETOOLONG 2427 case ENAMETOOLONG: 2428 #endif 2429 #ifdef EWOULDBLOCK 2430 case EWOULDBLOCK: 2431 #endif 2432 retval = KRB5_FCC_INTERNAL; 2433 break; 2434 #ifdef EDQUOT 2435 case EDQUOT: 2436 #endif 2437 case ENOSPC: 2438 case EIO: 2439 case ENFILE: 2440 case EMFILE: 2441 case ENXIO: 2442 default: 2443 retval = KRB5_CC_IO; /* XXX */ 2444 } 2445 return retval; 2446 } 2447 2448 const krb5_cc_ops krb5_fcc_ops = { 2449 0, 2450 "FILE", 2451 krb5_fcc_get_name, 2452 krb5_fcc_resolve, 2453 krb5_fcc_generate_new, 2454 krb5_fcc_initialize, 2455 krb5_fcc_destroy, 2456 krb5_fcc_close, 2457 krb5_fcc_store, 2458 krb5_fcc_retrieve, 2459 krb5_fcc_get_principal, 2460 krb5_fcc_start_seq_get, 2461 krb5_fcc_next_cred, 2462 krb5_fcc_end_seq_get, 2463 krb5_fcc_remove_cred, 2464 krb5_fcc_set_flags, 2465 }; 2466 2467 #if defined(_WIN32) 2468 /* 2469 * krb5_change_cache should be called after the cache changes. 2470 * A notification message is is posted out to all top level 2471 * windows so that they may recheck the cache based on the 2472 * changes made. We register a unique message type with which 2473 * we'll communicate to all other processes. 2474 */ 2475 2476 krb5_error_code 2477 krb5_change_cache (void) { 2478 2479 PostMessage(HWND_BROADCAST, krb5_get_notification_message(), 0, 0); 2480 2481 return 0; 2482 } 2483 2484 unsigned int KRB5_CALLCONV 2485 krb5_get_notification_message (void) { 2486 static unsigned int message = 0; 2487 2488 if (message == 0) 2489 message = RegisterWindowMessage(WM_KERBEROS5_CHANGED); 2490 2491 return message; 2492 } 2493 #else /* _WIN32 */ 2494 2495 krb5_error_code 2496 krb5_change_cache (void) 2497 { 2498 return 0; 2499 } 2500 unsigned int 2501 krb5_get_notification_message (void) 2502 { 2503 return 0; 2504 } 2505 2506 #endif /* _WIN32 */ 2507 2508 const krb5_cc_ops krb5_cc_file_ops = { 2509 0, 2510 "FILE", 2511 krb5_fcc_get_name, 2512 krb5_fcc_resolve, 2513 krb5_fcc_generate_new, 2514 krb5_fcc_initialize, 2515 krb5_fcc_destroy, 2516 krb5_fcc_close, 2517 krb5_fcc_store, 2518 krb5_fcc_retrieve, 2519 krb5_fcc_get_principal, 2520 krb5_fcc_start_seq_get, 2521 krb5_fcc_next_cred, 2522 krb5_fcc_end_seq_get, 2523 krb5_fcc_remove_cred, 2524 krb5_fcc_set_flags, 2525 }; 2526