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