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