1 /* 2 * Copyright 2009 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 | O_NOFOLLOW, 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 | O_NOFOLLOW, 1403 0600); 1404 } 1405 if (f == NO_FILE) 1406 return krb5_fcc_interpret (context, errno); 1407 1408 data->mode = mode; 1409 1410 if (data->mode == FCC_OPEN_RDONLY) 1411 lock_flag = KRB5_LOCKMODE_SHARED; 1412 else 1413 lock_flag = KRB5_LOCKMODE_EXCLUSIVE; 1414 if ((retval = krb5_lock_file(context, f, lock_flag))) { 1415 (void) close(f); 1416 if (retval == EAGAIN && retries++ < LOCK_RETRIES) { 1417 /* Solaris Kerberos wait some time before retrying */ 1418 if (poll(NULL, 0, WAIT_LENGTH) == 0) 1419 goto fcc_retry; 1420 } 1421 syslog(LOG_ERR, "Failed to lock %s [%m]", data->filename); 1422 return retval; 1423 } 1424 1425 if (mode == FCC_OPEN_AND_ERASE || mode == FCC_OPEN_AND_ERASE_NOUNLINK) { 1426 int cnt; 1427 1428 /* 1429 * Solaris Kerberos 1430 * If this file was not created, we have to flush existing data. 1431 * This will happen only if we are doing an ERASE_NOUNLINK open. 1432 */ 1433 if (newfile == 0 && (ftruncate(f, 0) == -1)) { 1434 syslog(LOG_ERR, "ftruncate failed for %s [%m]", data->filename); 1435 close(f); 1436 return (krb5_fcc_interpret(context, errno)); 1437 } 1438 1439 /* write the version number */ 1440 fcc_fvno = htons(context->fcc_default_format); 1441 data->version = context->fcc_default_format; 1442 if ((cnt = write(f, (char *)&fcc_fvno, sizeof(fcc_fvno))) != 1443 sizeof(fcc_fvno)) { 1444 retval = ((cnt == -1) ? krb5_fcc_interpret(context, errno) : 1445 KRB5_CC_IO); 1446 goto done; 1447 } 1448 data->file = f; 1449 1450 if (data->version == KRB5_FCC_FVNO_4) { 1451 /* V4 of the credentials cache format allows for header tags */ 1452 fcc_flen = 0; 1453 1454 if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) 1455 fcc_flen += (2*sizeof(krb5_ui_2) + 2*sizeof(krb5_int32)); 1456 1457 /* Write header length */ 1458 retval = krb5_fcc_store_ui_2(context, id, (krb5_int32)fcc_flen); 1459 if (retval) goto done; 1460 1461 if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) { 1462 /* Write time offset tag */ 1463 fcc_tag = FCC_TAG_DELTATIME; 1464 fcc_taglen = 2*sizeof(krb5_int32); 1465 1466 retval = krb5_fcc_store_ui_2(context,id,(krb5_int32)fcc_tag); 1467 if (retval) goto done; 1468 retval = krb5_fcc_store_ui_2(context,id,(krb5_int32)fcc_taglen); 1469 if (retval) goto done; 1470 retval = krb5_fcc_store_int32(context,id,os_ctx->time_offset); 1471 if (retval) goto done; 1472 retval = krb5_fcc_store_int32(context,id,os_ctx->usec_offset); 1473 if (retval) goto done; 1474 } 1475 } 1476 invalidate_cache(data); 1477 goto done; 1478 } 1479 1480 /* verify a valid version number is there */ 1481 invalidate_cache(data); 1482 if (read(f, (char *)&fcc_fvno, sizeof(fcc_fvno)) != sizeof(fcc_fvno)) { 1483 retval = KRB5_CC_FORMAT; 1484 goto done; 1485 } 1486 data->version = ntohs(fcc_fvno); 1487 if ((data->version != KRB5_FCC_FVNO_4) && 1488 (data->version != KRB5_FCC_FVNO_3) && 1489 (data->version != KRB5_FCC_FVNO_2) && 1490 (data->version != KRB5_FCC_FVNO_1)) { 1491 retval = KRB5_CCACHE_BADVNO; 1492 goto done; 1493 } 1494 1495 data->file = f; 1496 1497 if (data->version == KRB5_FCC_FVNO_4) { 1498 char buf[1024]; 1499 1500 if (krb5_fcc_read_ui_2(context, id, &fcc_flen) || 1501 (fcc_flen > sizeof(buf))) 1502 { 1503 retval = KRB5_CC_FORMAT; 1504 goto done; 1505 } 1506 1507 while (fcc_flen) { 1508 if ((fcc_flen < (2 * sizeof(krb5_ui_2))) || 1509 krb5_fcc_read_ui_2(context, id, &fcc_tag) || 1510 krb5_fcc_read_ui_2(context, id, &fcc_taglen) || 1511 (fcc_taglen > (fcc_flen - 2*sizeof(krb5_ui_2)))) 1512 { 1513 retval = KRB5_CC_FORMAT; 1514 goto done; 1515 } 1516 1517 switch (fcc_tag) { 1518 case FCC_TAG_DELTATIME: 1519 if (fcc_taglen != 2*sizeof(krb5_int32)) { 1520 retval = KRB5_CC_FORMAT; 1521 goto done; 1522 } 1523 if (!(context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) || 1524 (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)) 1525 { 1526 if (krb5_fcc_read(context, id, buf, fcc_taglen)) { 1527 retval = KRB5_CC_FORMAT; 1528 goto done; 1529 } 1530 break; 1531 } 1532 if (krb5_fcc_read_int32(context, id, &os_ctx->time_offset) || 1533 krb5_fcc_read_int32(context, id, &os_ctx->usec_offset)) 1534 { 1535 retval = KRB5_CC_FORMAT; 1536 goto done; 1537 } 1538 os_ctx->os_flags = 1539 ((os_ctx->os_flags & ~KRB5_OS_TOFFSET_TIME) | 1540 KRB5_OS_TOFFSET_VALID); 1541 break; 1542 default: 1543 if (fcc_taglen && krb5_fcc_read(context,id,buf,fcc_taglen)) { 1544 retval = KRB5_CC_FORMAT; 1545 goto done; 1546 } 1547 break; 1548 } 1549 fcc_flen -= (2*sizeof(krb5_ui_2) + fcc_taglen); 1550 } 1551 } 1552 1553 done: 1554 if (retval) { 1555 data->file = -1; 1556 (void) krb5_unlock_file(context, f); 1557 (void) close(f); 1558 } 1559 return retval; 1560 } 1561 1562 static krb5_error_code 1563 krb5_fcc_skip_header(krb5_context context, krb5_ccache id) 1564 { 1565 krb5_fcc_data *data = (krb5_fcc_data *)id->data; 1566 krb5_error_code kret; 1567 krb5_ui_2 fcc_flen; 1568 1569 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 1570 1571 fcc_lseek(data, (off_t) sizeof(krb5_ui_2), SEEK_SET); 1572 if (data->version == KRB5_FCC_FVNO_4) { 1573 kret = krb5_fcc_read_ui_2(context, id, &fcc_flen); 1574 if (kret) return kret; 1575 if(fcc_lseek(data, (off_t) fcc_flen, SEEK_CUR) < 0) 1576 return errno; 1577 } 1578 return KRB5_OK; 1579 } 1580 1581 static krb5_error_code 1582 krb5_fcc_skip_principal(krb5_context context, krb5_ccache id) 1583 { 1584 krb5_error_code kret; 1585 krb5_principal princ; 1586 1587 k5_assert_locked(&((krb5_fcc_data *) id->data)->lock); 1588 1589 kret = krb5_fcc_read_principal(context, id, &princ); 1590 if (kret != KRB5_OK) 1591 return kret; 1592 1593 krb5_free_principal(context, princ); 1594 return KRB5_OK; 1595 } 1596 1597 1598 /* 1599 * Modifies: 1600 * id 1601 * 1602 * Effects: 1603 * Creates/refreshes the file cred cache id. If the cache exists, its 1604 * contents are destroyed. 1605 * 1606 * Errors: 1607 * system errors 1608 * permission errors 1609 */ 1610 static krb5_error_code KRB5_CALLCONV 1611 krb5_fcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ) 1612 { 1613 krb5_error_code kret = 0; 1614 int reti = 0; 1615 1616 kret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock); 1617 if (kret) 1618 return kret; 1619 1620 MAYBE_OPEN(context, id, FCC_OPEN_AND_ERASE_NOUNLINK); /* Solaris Kerberos */ 1621 1622 /* 1623 * SUN14resync 1624 * This is not needed and can cause problems with ktkt_warnd(1M) 1625 * because it does tricks with getuid and if we enable this fchmod 1626 * we get EPERM [file_owner] failures on fchmod. 1627 */ 1628 #if 0 1629 #if defined(HAVE_FCHMOD) || defined(HAVE_CHMOD) 1630 { 1631 #ifdef HAVE_FCHMOD 1632 reti = fchmod(((krb5_fcc_data *) id->data)->file, S_IREAD | S_IWRITE); 1633 #else 1634 reti = chmod(((krb5_fcc_data *) id->data)->filename, S_IREAD | S_IWRITE); 1635 #endif 1636 #endif 1637 if (reti == -1) { 1638 kret = krb5_fcc_interpret(context, errno); 1639 MAYBE_CLOSE(context, id, kret); 1640 k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock); 1641 return kret; 1642 } 1643 } 1644 #endif 1645 kret = krb5_fcc_store_principal(context, id, princ); 1646 1647 MAYBE_CLOSE(context, id, kret); 1648 k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock); 1649 krb5_change_cache (); 1650 return kret; 1651 } 1652 1653 /* 1654 * Drop the ref count; if it hits zero, remove the entry from the 1655 * fcc_set list and free it. 1656 */ 1657 static krb5_error_code dereference(krb5_context context, krb5_fcc_data *data) 1658 { 1659 krb5_error_code kerr; 1660 struct fcc_set **fccsp; 1661 1662 kerr = k5_mutex_lock(&krb5int_cc_file_mutex); 1663 if (kerr) 1664 return kerr; 1665 for (fccsp = &fccs; *fccsp != NULL; fccsp = &(*fccsp)->next) 1666 if ((*fccsp)->data == data) 1667 break; 1668 assert(*fccsp != NULL); 1669 assert((*fccsp)->data == data); 1670 (*fccsp)->refcount--; 1671 if ((*fccsp)->refcount == 0) { 1672 struct fcc_set *temp; 1673 data = (*fccsp)->data; 1674 temp = *fccsp; 1675 *fccsp = (*fccsp)->next; 1676 free(temp); 1677 k5_mutex_unlock(&krb5int_cc_file_mutex); 1678 k5_mutex_assert_unlocked(&data->lock); 1679 free(data->filename); 1680 zap(data->buf, sizeof(data->buf)); 1681 if (data->file >= 0) { 1682 k5_mutex_lock(&data->lock); 1683 krb5_fcc_close_file(context, data); 1684 k5_mutex_unlock(&data->lock); 1685 } 1686 k5_mutex_destroy(&data->lock); 1687 free(data); 1688 } else 1689 k5_mutex_unlock(&krb5int_cc_file_mutex); 1690 return 0; 1691 } 1692 1693 /* 1694 * Modifies: 1695 * id 1696 * 1697 * Effects: 1698 * Closes the file cache, invalidates the id, and frees any resources 1699 * associated with the cache. 1700 */ 1701 static krb5_error_code KRB5_CALLCONV 1702 krb5_fcc_close(krb5_context context, krb5_ccache id) 1703 { 1704 dereference(context, (krb5_fcc_data *) id->data); 1705 krb5_xfree(id); 1706 return KRB5_OK; 1707 } 1708 1709 /* 1710 * Effects: 1711 * Destroys the contents of id. 1712 * 1713 * Errors: 1714 * system errors 1715 */ 1716 static krb5_error_code KRB5_CALLCONV 1717 krb5_fcc_destroy(krb5_context context, krb5_ccache id) 1718 { 1719 krb5_error_code kret = 0; 1720 krb5_fcc_data *data = (krb5_fcc_data *) id->data; 1721 register int ret; 1722 1723 struct stat buf; 1724 unsigned long i, size; 1725 unsigned int wlen; 1726 char zeros[BUFSIZ]; 1727 1728 kret = k5_mutex_lock(&data->lock); 1729 if (kret) 1730 return kret; 1731 1732 if (OPENCLOSE(id)) { 1733 invalidate_cache(data); 1734 ret = THREEPARAMOPEN(data->filename, 1735 O_RDWR | O_BINARY, 0); 1736 if (ret < 0) { 1737 kret = krb5_fcc_interpret(context, errno); 1738 goto cleanup; 1739 } 1740 data->file = ret; 1741 } 1742 else 1743 fcc_lseek(data, (off_t) 0, SEEK_SET); 1744 1745 #ifdef MSDOS_FILESYSTEM 1746 /* "disgusting bit of UNIX trivia" - that's how the writers of NFS describe 1747 ** the ability of UNIX to still write to a file which has been unlinked. 1748 ** Naturally, the PC can't do this. As a result, we have to delete the file 1749 ** after we wipe it clean but that throws off all the error handling code. 1750 ** So we have do the work ourselves. 1751 */ 1752 ret = fstat(data->file, &buf); 1753 if (ret == -1) { 1754 kret = krb5_fcc_interpret(context, errno); 1755 size = 0; /* Nothing to wipe clean */ 1756 } else 1757 size = (unsigned long) buf.st_size; 1758 1759 memset(zeros, 0, BUFSIZ); 1760 while (size > 0) { 1761 wlen = (int) ((size > BUFSIZ) ? BUFSIZ : size); /* How much to write */ 1762 i = write(data->file, zeros, wlen); 1763 if (i < 0) { 1764 kret = krb5_fcc_interpret(context, errno); 1765 /* Don't jump to cleanup--we still want to delete the file. */ 1766 break; 1767 } 1768 size -= i; /* We've read this much */ 1769 } 1770 1771 if (OPENCLOSE(id)) { 1772 (void) close(((krb5_fcc_data *)id->data)->file); 1773 data->file = -1; 1774 } 1775 1776 ret = unlink(data->filename); 1777 if (ret < 0) { 1778 kret = krb5_fcc_interpret(context, errno); 1779 goto cleanup; 1780 } 1781 1782 #else /* MSDOS_FILESYSTEM */ 1783 1784 ret = unlink(data->filename); 1785 if (ret < 0) { 1786 kret = krb5_fcc_interpret(context, errno); 1787 if (OPENCLOSE(id)) { 1788 (void) close(((krb5_fcc_data *)id->data)->file); 1789 data->file = -1; 1790 kret = ret; 1791 } 1792 goto cleanup; 1793 } 1794 1795 ret = fstat(data->file, &buf); 1796 if (ret < 0) { 1797 kret = krb5_fcc_interpret(context, errno); 1798 if (OPENCLOSE(id)) { 1799 (void) close(((krb5_fcc_data *)id->data)->file); 1800 data->file = -1; 1801 } 1802 goto cleanup; 1803 } 1804 1805 /* XXX This may not be legal XXX */ 1806 size = (unsigned long) buf.st_size; 1807 memset(zeros, 0, BUFSIZ); 1808 for (i=0; i < size / BUFSIZ; i++) 1809 if (write(data->file, zeros, BUFSIZ) < 0) { 1810 kret = krb5_fcc_interpret(context, errno); 1811 if (OPENCLOSE(id)) { 1812 (void) close(((krb5_fcc_data *)id->data)->file); 1813 data->file = -1; 1814 } 1815 goto cleanup; 1816 } 1817 1818 wlen = (unsigned int) (size % BUFSIZ); 1819 if (write(data->file, zeros, wlen) < 0) { 1820 kret = krb5_fcc_interpret(context, errno); 1821 if (OPENCLOSE(id)) { 1822 (void) close(((krb5_fcc_data *)id->data)->file); 1823 data->file = -1; 1824 } 1825 goto cleanup; 1826 } 1827 1828 ret = close(data->file); 1829 data->file = -1; 1830 1831 if (ret) 1832 kret = krb5_fcc_interpret(context, errno); 1833 1834 #endif /* MSDOS_FILESYSTEM */ 1835 1836 cleanup: 1837 k5_mutex_unlock(&data->lock); 1838 dereference(context, data); 1839 krb5_xfree(id); 1840 1841 krb5_change_cache (); 1842 return kret; 1843 } 1844 1845 extern const krb5_cc_ops krb5_fcc_ops; 1846 1847 /* 1848 * Requires: 1849 * residual is a legal path name, and a null-terminated string 1850 * 1851 * Modifies: 1852 * id 1853 * 1854 * Effects: 1855 * creates a file-based cred cache that will reside in the file 1856 * residual. The cache is not opened, but the filename is reserved. 1857 * 1858 * Returns: 1859 * A filled in krb5_ccache structure "id". 1860 * 1861 * Errors: 1862 * KRB5_CC_NOMEM - there was insufficient memory to allocate the 1863 * krb5_ccache. id is undefined. 1864 * permission errors 1865 */ 1866 static krb5_error_code KRB5_CALLCONV 1867 krb5_fcc_resolve (krb5_context context, krb5_ccache *id, const char *residual) 1868 { 1869 krb5_ccache lid; 1870 krb5_error_code kret; 1871 krb5_fcc_data *data; 1872 struct fcc_set *setptr; 1873 1874 kret = k5_mutex_lock(&krb5int_cc_file_mutex); 1875 if (kret) 1876 return kret; 1877 for (setptr = fccs; setptr; setptr = setptr->next) { 1878 if (!strcmp(setptr->data->filename, residual)) 1879 break; 1880 } 1881 if (setptr) { 1882 data = setptr->data; 1883 assert(setptr->refcount != 0); 1884 setptr->refcount++; 1885 assert(setptr->refcount != 0); 1886 kret = k5_mutex_lock(&data->lock); 1887 if (kret) { 1888 k5_mutex_unlock(&krb5int_cc_file_mutex); 1889 return kret; 1890 } 1891 k5_mutex_unlock(&krb5int_cc_file_mutex); 1892 } else { 1893 data = malloc(sizeof(krb5_fcc_data)); 1894 if (data == NULL) { 1895 k5_mutex_unlock(&krb5int_cc_file_mutex); 1896 return KRB5_CC_NOMEM; 1897 } 1898 data->filename = strdup(residual); 1899 if (data->filename == NULL) { 1900 k5_mutex_unlock(&krb5int_cc_file_mutex); 1901 free(data); 1902 return KRB5_CC_NOMEM; 1903 } 1904 kret = k5_mutex_init(&data->lock); 1905 if (kret) { 1906 k5_mutex_unlock(&krb5int_cc_file_mutex); 1907 free(data->filename); 1908 free(data); 1909 return kret; 1910 } 1911 kret = k5_mutex_lock(&data->lock); 1912 if (kret) { 1913 k5_mutex_unlock(&krb5int_cc_file_mutex); 1914 k5_mutex_destroy(&data->lock); 1915 free(data->filename); 1916 free(data); 1917 return kret; 1918 } 1919 /* data->version,mode filled in for real later */ 1920 data->version = data->mode = 0; 1921 data->flags = KRB5_TC_OPENCLOSE; 1922 data->file = -1; 1923 data->valid_bytes = 0; 1924 setptr = malloc(sizeof(struct fcc_set)); 1925 if (setptr == NULL) { 1926 k5_mutex_unlock(&krb5int_cc_file_mutex); 1927 k5_mutex_destroy(&data->lock); 1928 free(data->filename); 1929 free(data); 1930 return KRB5_CC_NOMEM; 1931 } 1932 setptr->refcount = 1; 1933 setptr->data = data; 1934 setptr->next = fccs; 1935 fccs = setptr; 1936 k5_mutex_unlock(&krb5int_cc_file_mutex); 1937 } 1938 1939 k5_mutex_assert_locked(&data->lock); 1940 k5_mutex_unlock(&data->lock); 1941 lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache)); 1942 if (lid == NULL) { 1943 dereference(context, data); 1944 return KRB5_CC_NOMEM; 1945 } 1946 1947 lid->ops = &krb5_fcc_ops; 1948 lid->data = data; 1949 lid->magic = KV5M_CCACHE; 1950 1951 /* other routines will get errors on open, and callers must expect them, 1952 if cache is non-existent/unusable */ 1953 *id = lid; 1954 return KRB5_OK; 1955 } 1956 1957 /* 1958 * Effects: 1959 * Prepares for a sequential search of the credentials cache. 1960 * Returns and krb5_cc_cursor to be used with krb5_fcc_next_cred and 1961 * krb5_fcc_end_seq_get. 1962 * 1963 * If the cache is modified between the time of this call and the time 1964 * of the final krb5_fcc_end_seq_get, the results are undefined. 1965 * 1966 * Errors: 1967 * KRB5_CC_NOMEM 1968 * system errors 1969 */ 1970 static krb5_error_code KRB5_CALLCONV 1971 krb5_fcc_start_seq_get(krb5_context context, krb5_ccache id, 1972 krb5_cc_cursor *cursor) 1973 { 1974 krb5_fcc_cursor *fcursor; 1975 krb5_error_code kret = KRB5_OK; 1976 krb5_fcc_data *data = (krb5_fcc_data *)id->data; 1977 1978 kret = k5_mutex_lock(&data->lock); 1979 if (kret) 1980 return kret; 1981 1982 fcursor = (krb5_fcc_cursor *) malloc(sizeof(krb5_fcc_cursor)); 1983 if (fcursor == NULL) { 1984 k5_mutex_unlock(&data->lock); 1985 return KRB5_CC_NOMEM; 1986 } 1987 if (OPENCLOSE(id)) { 1988 kret = krb5_fcc_open_file(context, id, FCC_OPEN_RDONLY); 1989 if (kret) { 1990 krb5_xfree(fcursor); 1991 k5_mutex_unlock(&data->lock); 1992 return kret; 1993 } 1994 } 1995 1996 /* Make sure we start reading right after the primary principal */ 1997 kret = krb5_fcc_skip_header(context, id); 1998 if (kret) { 1999 /* Solaris Kerberos - fix mem leak */ 2000 krb5_xfree(fcursor); 2001 goto done; 2002 } 2003 kret = krb5_fcc_skip_principal(context, id); 2004 if (kret) { 2005 /* Solaris Kerberos - fix mem leak */ 2006 krb5_xfree(fcursor); 2007 goto done; 2008 } 2009 2010 fcursor->pos = fcc_lseek(data, (off_t) 0, SEEK_CUR); 2011 *cursor = (krb5_cc_cursor) fcursor; 2012 2013 done: 2014 MAYBE_CLOSE(context, id, kret); 2015 k5_mutex_unlock(&data->lock); 2016 return kret; 2017 } 2018 2019 2020 /* 2021 * Requires: 2022 * cursor is a krb5_cc_cursor originally obtained from 2023 * krb5_fcc_start_seq_get. 2024 * 2025 * Modifes: 2026 * cursor, creds 2027 * 2028 * Effects: 2029 * Fills in creds with the "next" credentals structure from the cache 2030 * id. The actual order the creds are returned in is arbitrary. 2031 * Space is allocated for the variable length fields in the 2032 * credentials structure, so the object returned must be passed to 2033 * krb5_destroy_credential. 2034 * 2035 * The cursor is updated for the next call to krb5_fcc_next_cred. 2036 * 2037 * Errors: 2038 * system errors 2039 */ 2040 static krb5_error_code KRB5_CALLCONV 2041 krb5_fcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor, 2042 krb5_creds *creds) 2043 { 2044 #define TCHECK(ret) if (ret != KRB5_OK) goto lose; 2045 krb5_error_code kret; 2046 krb5_fcc_cursor *fcursor; 2047 krb5_int32 int32; 2048 krb5_octet octet; 2049 krb5_fcc_data *d = (krb5_fcc_data *) id->data; 2050 2051 kret = k5_mutex_lock(&d->lock); 2052 if (kret) 2053 return kret; 2054 2055 memset((char *)creds, 0, sizeof(*creds)); 2056 MAYBE_OPEN(context, id, FCC_OPEN_RDONLY); 2057 fcursor = (krb5_fcc_cursor *) *cursor; 2058 2059 kret = (fcc_lseek(d, fcursor->pos, SEEK_SET) == (off_t) -1); 2060 if (kret) { 2061 kret = krb5_fcc_interpret(context, errno); 2062 MAYBE_CLOSE(context, id, kret); 2063 k5_mutex_unlock(&d->lock); 2064 return kret; 2065 } 2066 2067 kret = krb5_fcc_read_principal(context, id, &creds->client); 2068 TCHECK(kret); 2069 kret = krb5_fcc_read_principal(context, id, &creds->server); 2070 TCHECK(kret); 2071 kret = krb5_fcc_read_keyblock(context, id, &creds->keyblock); 2072 TCHECK(kret); 2073 kret = krb5_fcc_read_times(context, id, &creds->times); 2074 TCHECK(kret); 2075 kret = krb5_fcc_read_octet(context, id, &octet); 2076 TCHECK(kret); 2077 creds->is_skey = octet; 2078 kret = krb5_fcc_read_int32(context, id, &int32); 2079 TCHECK(kret); 2080 creds->ticket_flags = int32; 2081 kret = krb5_fcc_read_addrs(context, id, &creds->addresses); 2082 TCHECK(kret); 2083 kret = krb5_fcc_read_authdata(context, id, &creds->authdata); 2084 TCHECK(kret); 2085 kret = krb5_fcc_read_data(context, id, &creds->ticket); 2086 TCHECK(kret); 2087 kret = krb5_fcc_read_data(context, id, &creds->second_ticket); 2088 TCHECK(kret); 2089 2090 fcursor->pos = fcc_lseek(d, (off_t) 0, SEEK_CUR); 2091 cursor = (krb5_cc_cursor *) fcursor; 2092 2093 lose: 2094 MAYBE_CLOSE (context, id, kret); 2095 k5_mutex_unlock(&d->lock); 2096 if (kret != KRB5_OK) 2097 krb5_free_cred_contents(context, creds); 2098 return kret; 2099 } 2100 2101 /* 2102 * Requires: 2103 * cursor is a krb5_cc_cursor originally obtained from 2104 * krb5_fcc_start_seq_get. 2105 * 2106 * Modifies: 2107 * id, cursor 2108 * 2109 * Effects: 2110 * Finishes sequential processing of the file credentials ccache id, 2111 * and invalidates the cursor (it must never be used after this call). 2112 */ 2113 /* ARGSUSED */ 2114 static krb5_error_code KRB5_CALLCONV 2115 krb5_fcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor) 2116 { 2117 /* We don't do anything with the file cache itself, so 2118 no need to lock anything. */ 2119 2120 /* don't close; it may be left open by the caller, 2121 and if not, fcc_start_seq_get and/or fcc_next_cred will do the 2122 MAYBE_CLOSE. 2123 MAYBE_CLOSE(context, id, kret); */ 2124 krb5_xfree((krb5_fcc_cursor *) *cursor); 2125 return 0; 2126 } 2127 2128 2129 /* 2130 * Effects: 2131 * Creates a new file cred cache whose name is guaranteed to be 2132 * unique. The name begins with the string TKT_ROOT (from fcc.h). 2133 * The cache is not opened, but the new filename is reserved. 2134 * 2135 * Returns: 2136 * The filled in krb5_ccache id. 2137 * 2138 * Errors: 2139 * KRB5_CC_NOMEM - there was insufficient memory to allocate the 2140 * krb5_ccache. id is undefined. 2141 * system errors (from open) 2142 */ 2143 static krb5_error_code KRB5_CALLCONV 2144 krb5_fcc_generate_new (krb5_context context, krb5_ccache *id) 2145 { 2146 krb5_ccache lid; 2147 int ret; 2148 krb5_error_code kret = 0; 2149 char scratch[sizeof(TKT_ROOT)+6+1]; /* +6 for the scratch part, +1 for 2150 NUL */ 2151 krb5_fcc_data *data; 2152 krb5_int16 fcc_fvno = htons(context->fcc_default_format); 2153 krb5_int16 fcc_flen = 0; 2154 int errsave, cnt; 2155 struct fcc_set *setptr; 2156 2157 /* Set master lock */ 2158 kret = k5_mutex_lock(&krb5int_cc_file_mutex); 2159 if (kret) 2160 return kret; 2161 2162 (void) strcpy(scratch, TKT_ROOT); 2163 (void) strcat(scratch, "XXXXXX"); 2164 #ifdef HAVE_MKSTEMP 2165 ret = mkstemp(scratch); 2166 if (ret == -1) { 2167 k5_mutex_unlock(&krb5int_cc_file_mutex); 2168 return krb5_fcc_interpret(context, errno); 2169 } 2170 #else /*HAVE_MKSTEMP*/ 2171 mktemp(scratch); 2172 /* Make sure the file name is reserved */ 2173 ret = THREEPARAMOPEN(scratch, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, 0); 2174 if (ret == -1) { 2175 return krb5_fcc_interpret(context, errno); 2176 } 2177 #endif 2178 2179 /* Allocate memory */ 2180 data = (krb5_pointer) malloc(sizeof(krb5_fcc_data)); 2181 if (data == NULL) { 2182 close(ret); 2183 unlink(scratch); 2184 k5_mutex_unlock(&krb5int_cc_file_mutex); 2185 return KRB5_CC_NOMEM; 2186 } 2187 2188 data->filename = strdup(scratch); 2189 if (data->filename == NULL) { 2190 k5_mutex_unlock(&krb5int_cc_file_mutex); 2191 free(data); 2192 close(ret); 2193 unlink(scratch); 2194 k5_mutex_unlock(&krb5int_cc_file_mutex); 2195 return KRB5_CC_NOMEM; 2196 } 2197 2198 kret = k5_mutex_init(&data->lock); 2199 if (kret) { 2200 k5_mutex_unlock(&krb5int_cc_file_mutex); 2201 free(data->filename); 2202 free(data); 2203 close(ret); 2204 unlink(scratch); 2205 return kret; 2206 } 2207 kret = k5_mutex_lock(&data->lock); 2208 if (kret) { 2209 k5_mutex_unlock(&krb5int_cc_file_mutex); 2210 k5_mutex_destroy(&data->lock); 2211 free(data->filename); 2212 free(data); 2213 close(ret); 2214 unlink(scratch); 2215 return kret; 2216 } 2217 2218 /* 2219 * The file is initially closed at the end of this call... 2220 */ 2221 data->flags = 0; 2222 data->file = -1; 2223 data->valid_bytes = 0; 2224 /* data->version,mode filled in for real later */ 2225 data->version = data->mode = 0; 2226 2227 2228 /* Ignore user's umask, set mode = 0600 */ 2229 #ifndef HAVE_FCHMOD 2230 #ifdef HAVE_CHMOD 2231 chmod(data->filename, S_IRUSR | S_IWUSR); 2232 #endif 2233 #else 2234 fchmod(ret, S_IRUSR | S_IWUSR); 2235 #endif 2236 if ((cnt = write(ret, (char *)&fcc_fvno, sizeof(fcc_fvno))) 2237 != sizeof(fcc_fvno)) { 2238 errsave = errno; 2239 (void) close(ret); 2240 (void) unlink(data->filename); 2241 kret = (cnt == -1) ? krb5_fcc_interpret(context, errsave) : KRB5_CC_IO; 2242 goto err_out; 2243 } 2244 /* For version 4 we save a length for the rest of the header */ 2245 if (context->fcc_default_format == KRB5_FCC_FVNO_4) { 2246 if ((cnt = write(ret, (char *)&fcc_flen, sizeof(fcc_flen))) 2247 != sizeof(fcc_flen)) { 2248 errsave = errno; 2249 (void) close(ret); 2250 (void) unlink(data->filename); 2251 kret = (cnt == -1) ? krb5_fcc_interpret(context, errsave) : KRB5_CC_IO; 2252 goto err_out; 2253 } 2254 } 2255 if (close(ret) == -1) { 2256 errsave = errno; 2257 (void) unlink(data->filename); 2258 kret = krb5_fcc_interpret(context, errsave); 2259 goto err_out; 2260 } 2261 2262 2263 setptr = malloc(sizeof(struct fcc_set)); 2264 if (setptr == NULL) { 2265 k5_mutex_unlock(&krb5int_cc_file_mutex); 2266 k5_mutex_destroy(&data->lock); 2267 free(data->filename); 2268 free(data); 2269 (void) close(ret); 2270 (void) unlink(scratch); 2271 return KRB5_CC_NOMEM; 2272 } 2273 setptr->refcount = 1; 2274 setptr->data = data; 2275 setptr->next = fccs; 2276 fccs = setptr; 2277 k5_mutex_unlock(&krb5int_cc_file_mutex); 2278 2279 k5_mutex_assert_locked(&data->lock); 2280 k5_mutex_unlock(&data->lock); 2281 lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache)); 2282 if (lid == NULL) { 2283 dereference(context, data); 2284 return KRB5_CC_NOMEM; 2285 } 2286 2287 lid->ops = &krb5_fcc_ops; 2288 lid->data = data; 2289 lid->magic = KV5M_CCACHE; 2290 2291 /* default to open/close on every trn - otherwise destroy 2292 will get as to state confused */ 2293 ((krb5_fcc_data *) lid->data)->flags = KRB5_TC_OPENCLOSE; 2294 2295 *id = lid; 2296 2297 2298 krb5_change_cache (); 2299 return KRB5_OK; 2300 2301 err_out: 2302 k5_mutex_unlock(&krb5int_cc_file_mutex); 2303 k5_mutex_destroy(&data->lock); 2304 free(data->filename); 2305 free(data); 2306 return kret; 2307 } 2308 2309 /* 2310 * Requires: 2311 * id is a file credential cache 2312 * 2313 * Returns: 2314 * The name of the file cred cache id. 2315 */ 2316 static const char * KRB5_CALLCONV 2317 krb5_fcc_get_name (krb5_context context, krb5_ccache id) 2318 { 2319 return (char *) ((krb5_fcc_data *) id->data)->filename; 2320 } 2321 2322 /* 2323 * Modifies: 2324 * id, princ 2325 * 2326 * Effects: 2327 * Retrieves the primary principal from id, as set with 2328 * krb5_fcc_initialize. The principal is returned is allocated 2329 * storage that must be freed by the caller via krb5_free_principal. 2330 * 2331 * Errors: 2332 * system errors 2333 * KRB5_CC_NOMEM 2334 */ 2335 static krb5_error_code KRB5_CALLCONV 2336 krb5_fcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *princ) 2337 { 2338 krb5_error_code kret = KRB5_OK; 2339 2340 kret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock); 2341 if (kret) 2342 return kret; 2343 2344 MAYBE_OPEN(context, id, FCC_OPEN_RDONLY); 2345 2346 /* make sure we're beyond the header */ 2347 kret = krb5_fcc_skip_header(context, id); 2348 if (kret) goto done; 2349 kret = krb5_fcc_read_principal(context, id, princ); 2350 2351 done: 2352 MAYBE_CLOSE(context, id, kret); 2353 k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock); 2354 return kret; 2355 } 2356 2357 2358 static krb5_error_code KRB5_CALLCONV 2359 krb5_fcc_retrieve(krb5_context context, krb5_ccache id, krb5_flags whichfields, krb5_creds *mcreds, krb5_creds *creds) 2360 { 2361 return krb5_cc_retrieve_cred_default (context, id, whichfields, 2362 mcreds, creds); 2363 } 2364 2365 2366 /* 2367 * Modifies: 2368 * the file cache 2369 * 2370 * Effects: 2371 * stores creds in the file cred cache 2372 * 2373 * Errors: 2374 * system errors 2375 * storage failure errors 2376 */ 2377 static krb5_error_code KRB5_CALLCONV 2378 krb5_fcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds) 2379 { 2380 #define TCHECK(ret) if (ret != KRB5_OK) goto lose; 2381 krb5_error_code ret; 2382 2383 ret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock); 2384 if (ret) 2385 return ret; 2386 2387 /* Make sure we are writing to the end of the file */ 2388 MAYBE_OPEN(context, id, FCC_OPEN_RDWR); 2389 2390 /* Make sure we are writing to the end of the file */ 2391 ret = fcc_lseek((krb5_fcc_data *) id->data, (off_t) 0, SEEK_END); 2392 if (ret < 0) { 2393 MAYBE_CLOSE_IGNORE(context, id); 2394 k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock); 2395 return krb5_fcc_interpret(context, errno); 2396 } 2397 2398 ret = krb5_fcc_store_principal(context, id, creds->client); 2399 TCHECK(ret); 2400 ret = krb5_fcc_store_principal(context, id, creds->server); 2401 TCHECK(ret); 2402 ret = krb5_fcc_store_keyblock(context, id, &creds->keyblock); 2403 TCHECK(ret); 2404 ret = krb5_fcc_store_times(context, id, &creds->times); 2405 TCHECK(ret); 2406 ret = krb5_fcc_store_octet(context, id, (krb5_int32) creds->is_skey); 2407 TCHECK(ret); 2408 ret = krb5_fcc_store_int32(context, id, creds->ticket_flags); 2409 TCHECK(ret); 2410 ret = krb5_fcc_store_addrs(context, id, creds->addresses); 2411 TCHECK(ret); 2412 ret = krb5_fcc_store_authdata(context, id, creds->authdata); 2413 TCHECK(ret); 2414 ret = krb5_fcc_store_data(context, id, &creds->ticket); 2415 TCHECK(ret); 2416 ret = krb5_fcc_store_data(context, id, &creds->second_ticket); 2417 TCHECK(ret); 2418 2419 lose: 2420 MAYBE_CLOSE(context, id, ret); 2421 k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock); 2422 krb5_change_cache (); 2423 return ret; 2424 #undef TCHECK 2425 } 2426 2427 /* 2428 * Non-functional stub implementation for krb5_fcc_remove 2429 * 2430 * Errors: 2431 * KRB5_CC_NOSUPP - not implemented 2432 */ 2433 static krb5_error_code KRB5_CALLCONV 2434 krb5_fcc_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags, 2435 krb5_creds *creds) 2436 { 2437 return KRB5_CC_NOSUPP; 2438 } 2439 2440 /* 2441 * Requires: 2442 * id is a cred cache returned by krb5_fcc_resolve or 2443 * krb5_fcc_generate_new, but has not been opened by krb5_fcc_initialize. 2444 * 2445 * Modifies: 2446 * id 2447 * 2448 * Effects: 2449 * Sets the operational flags of id to flags. 2450 */ 2451 static krb5_error_code KRB5_CALLCONV 2452 krb5_fcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags) 2453 { 2454 krb5_error_code ret = KRB5_OK; 2455 2456 ret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock); 2457 if (ret) 2458 return ret; 2459 2460 /* XXX This should check for illegal combinations, if any.. */ 2461 if (flags & KRB5_TC_OPENCLOSE) { 2462 /* asking to turn on OPENCLOSE mode */ 2463 if (!OPENCLOSE(id) 2464 /* XXX Is this test necessary? */ 2465 && ((krb5_fcc_data *) id->data)->file != NO_FILE) 2466 (void) krb5_fcc_close_file (context, ((krb5_fcc_data *) id->data)); 2467 } else { 2468 /* asking to turn off OPENCLOSE mode, meaning it must be 2469 left open. We open if it's not yet open */ 2470 MAYBE_OPEN(context, id, FCC_OPEN_RDONLY); 2471 } 2472 2473 ((krb5_fcc_data *) id->data)->flags = flags; 2474 k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock); 2475 return ret; 2476 } 2477 2478 /* 2479 * Requires: 2480 * id is a cred cache returned by krb5_fcc_resolve or 2481 * krb5_fcc_generate_new, but has not been opened by krb5_fcc_initialize. 2482 * 2483 * Modifies: 2484 * id (mutex only; temporary) 2485 * 2486 * Effects: 2487 * Returns the operational flags of id. 2488 */ 2489 static krb5_error_code KRB5_CALLCONV 2490 krb5_fcc_get_flags(krb5_context context, krb5_ccache id, krb5_flags *flags) 2491 { 2492 krb5_error_code ret = KRB5_OK; 2493 2494 ret = k5_mutex_lock(&((krb5_fcc_data *) id->data)->lock); 2495 if (ret) 2496 return ret; 2497 *flags = ((krb5_fcc_data *) id->data)->flags; 2498 k5_mutex_unlock(&((krb5_fcc_data *) id->data)->lock); 2499 return ret; 2500 } 2501 2502 2503 static krb5_error_code 2504 krb5_fcc_interpret(krb5_context context, int errnum) 2505 { 2506 register krb5_error_code retval; 2507 switch (errnum) { 2508 case ENOENT: 2509 retval = KRB5_FCC_NOFILE; 2510 break; 2511 case EPERM: 2512 case EACCES: 2513 #ifdef EISDIR 2514 case EISDIR: /* Mac doesn't have EISDIR */ 2515 #endif 2516 case ENOTDIR: 2517 #ifdef ELOOP 2518 case ELOOP: /* Bad symlink is like no file. */ 2519 #endif 2520 #ifdef ETXTBSY 2521 case ETXTBSY: 2522 #endif 2523 case EBUSY: 2524 case EROFS: 2525 retval = KRB5_FCC_PERM; 2526 break; 2527 case EINVAL: 2528 case EEXIST: /* XXX */ 2529 case EFAULT: 2530 case EBADF: 2531 #ifdef ENAMETOOLONG 2532 case ENAMETOOLONG: 2533 #endif 2534 #ifdef EWOULDBLOCK 2535 case EWOULDBLOCK: 2536 #endif 2537 retval = KRB5_FCC_INTERNAL; 2538 break; 2539 #ifdef EDQUOT 2540 case EDQUOT: 2541 #endif 2542 case ENOSPC: 2543 case EIO: 2544 case ENFILE: 2545 case EMFILE: 2546 case ENXIO: 2547 default: 2548 retval = KRB5_CC_IO; /* XXX */ 2549 krb5_set_error_message(context, retval, 2550 "Credentials cache I/O operation failed (%s)", 2551 strerror(errnum)); 2552 } 2553 return retval; 2554 } 2555 2556 const krb5_cc_ops krb5_fcc_ops = { 2557 0, 2558 "FILE", 2559 krb5_fcc_get_name, 2560 krb5_fcc_resolve, 2561 krb5_fcc_generate_new, 2562 krb5_fcc_initialize, 2563 krb5_fcc_destroy, 2564 krb5_fcc_close, 2565 krb5_fcc_store, 2566 krb5_fcc_retrieve, 2567 krb5_fcc_get_principal, 2568 krb5_fcc_start_seq_get, 2569 krb5_fcc_next_cred, 2570 krb5_fcc_end_seq_get, 2571 krb5_fcc_remove_cred, 2572 krb5_fcc_set_flags, 2573 krb5_fcc_get_flags, 2574 }; 2575 2576 #if defined(_WIN32) 2577 /* 2578 * krb5_change_cache should be called after the cache changes. 2579 * A notification message is is posted out to all top level 2580 * windows so that they may recheck the cache based on the 2581 * changes made. We register a unique message type with which 2582 * we'll communicate to all other processes. 2583 */ 2584 2585 krb5_error_code 2586 krb5_change_cache (void) { 2587 2588 PostMessage(HWND_BROADCAST, krb5_get_notification_message(), 0, 0); 2589 2590 return 0; 2591 } 2592 2593 unsigned int KRB5_CALLCONV 2594 krb5_get_notification_message (void) { 2595 static unsigned int message = 0; 2596 2597 if (message == 0) 2598 message = RegisterWindowMessage(WM_KERBEROS5_CHANGED); 2599 2600 return message; 2601 } 2602 #else /* _WIN32 */ 2603 2604 krb5_error_code 2605 krb5_change_cache (void) 2606 { 2607 return 0; 2608 } 2609 unsigned int 2610 krb5_get_notification_message (void) 2611 { 2612 return 0; 2613 } 2614 2615 #endif /* _WIN32 */ 2616 2617 const krb5_cc_ops krb5_cc_file_ops = { 2618 0, 2619 "FILE", 2620 krb5_fcc_get_name, 2621 krb5_fcc_resolve, 2622 krb5_fcc_generate_new, 2623 krb5_fcc_initialize, 2624 krb5_fcc_destroy, 2625 krb5_fcc_close, 2626 krb5_fcc_store, 2627 krb5_fcc_retrieve, 2628 krb5_fcc_get_principal, 2629 krb5_fcc_start_seq_get, 2630 krb5_fcc_next_cred, 2631 krb5_fcc_end_seq_get, 2632 krb5_fcc_remove_cred, 2633 krb5_fcc_set_flags, 2634 krb5_fcc_get_flags, 2635 NULL, 2636 NULL, 2637 NULL, 2638 NULL, 2639 NULL, 2640 NULL, 2641 }; 2642