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