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