1 /* 2 * Copyright (c) 1997 - 2001 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include "krb5_locl.h" 35 36 RCSID("$Id: fcache.c,v 1.33 2001/05/14 06:14:46 assar Exp $"); 37 38 typedef struct krb5_fcache{ 39 char *filename; 40 int version; 41 }krb5_fcache; 42 43 struct fcc_cursor { 44 int fd; 45 krb5_storage *sp; 46 }; 47 48 #define KRB5_FCC_FVNO_1 1 49 #define KRB5_FCC_FVNO_2 2 50 #define KRB5_FCC_FVNO_3 3 51 #define KRB5_FCC_FVNO_4 4 52 53 #define FCC_TAG_DELTATIME 1 54 55 #define FCACHE(X) ((krb5_fcache*)(X)->data.data) 56 57 #define FILENAME(X) (FCACHE(X)->filename) 58 59 #define FCC_CURSOR(C) ((struct fcc_cursor*)(C)) 60 61 static char* 62 fcc_get_name(krb5_context context, 63 krb5_ccache id) 64 { 65 return FILENAME(id); 66 } 67 68 static krb5_error_code 69 fcc_resolve(krb5_context context, krb5_ccache *id, const char *res) 70 { 71 krb5_fcache *f; 72 f = malloc(sizeof(*f)); 73 if(f == NULL) { 74 krb5_set_error_string(context, "malloc: out of memory"); 75 return KRB5_CC_NOMEM; 76 } 77 f->filename = strdup(res); 78 if(f->filename == NULL){ 79 free(f); 80 krb5_set_error_string(context, "malloc: out of memory"); 81 return KRB5_CC_NOMEM; 82 } 83 f->version = 0; 84 (*id)->data.data = f; 85 (*id)->data.length = sizeof(*f); 86 return 0; 87 } 88 89 /* 90 * Try to scrub the contents of `filename' safely. 91 */ 92 93 static int 94 scrub_file (int fd) 95 { 96 off_t pos; 97 char buf[128]; 98 99 pos = lseek(fd, 0, SEEK_END); 100 if (pos < 0) 101 return errno; 102 if (lseek(fd, 0, SEEK_SET) < 0) 103 return errno; 104 memset(buf, 0, sizeof(buf)); 105 while(pos > 0) { 106 ssize_t tmp = write(fd, buf, min(sizeof(buf), pos)); 107 108 if (tmp < 0) 109 return errno; 110 pos -= tmp; 111 } 112 fsync (fd); 113 return 0; 114 } 115 116 /* 117 * Erase `filename' if it exists, trying to remove the contents if 118 * it's `safe'. We always try to remove the file, it it exists. It's 119 * only overwritten if it's a regular file (not a symlink and not a 120 * hardlink) 121 */ 122 123 static krb5_error_code 124 erase_file(const char *filename) 125 { 126 int fd; 127 struct stat sb1, sb2; 128 int ret; 129 130 ret = lstat (filename, &sb1); 131 if (ret < 0) 132 return errno; 133 134 fd = open(filename, O_RDWR | O_BINARY); 135 if(fd < 0) { 136 if(errno == ENOENT) 137 return 0; 138 else 139 return errno; 140 } 141 if (unlink(filename) < 0) { 142 close (fd); 143 return errno; 144 } 145 146 ret = fstat (fd, &sb2); 147 if (ret < 0) { 148 close (fd); 149 return errno; 150 } 151 152 /* check if someone was playing with symlinks */ 153 154 if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) { 155 close (fd); 156 return EPERM; 157 } 158 159 /* there are still hard links to this file */ 160 161 if (sb2.st_nlink != 0) { 162 close (fd); 163 return 0; 164 } 165 166 ret = scrub_file (fd); 167 close (fd); 168 return ret; 169 } 170 171 static krb5_error_code 172 fcc_gen_new(krb5_context context, krb5_ccache *id) 173 { 174 krb5_fcache *f; 175 int fd; 176 char *file; 177 178 f = malloc(sizeof(*f)); 179 if(f == NULL) { 180 krb5_set_error_string(context, "malloc: out of memory"); 181 return KRB5_CC_NOMEM; 182 } 183 asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT); 184 if(file == NULL) { 185 free(f); 186 krb5_set_error_string(context, "malloc: out of memory"); 187 return KRB5_CC_NOMEM; 188 } 189 fd = mkstemp(file); 190 if(fd < 0) { 191 free(f); 192 free(file); 193 krb5_set_error_string(context, "mkstemp %s", file); 194 return errno; 195 } 196 close(fd); 197 f->filename = file; 198 f->version = 0; 199 (*id)->data.data = f; 200 (*id)->data.length = sizeof(*f); 201 return 0; 202 } 203 204 static void 205 storage_set_flags(krb5_context context, krb5_storage *sp, int vno) 206 { 207 int flags = 0; 208 switch(vno) { 209 case KRB5_FCC_FVNO_1: 210 flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS; 211 flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE; 212 flags |= KRB5_STORAGE_HOST_BYTEORDER; 213 break; 214 case KRB5_FCC_FVNO_2: 215 flags |= KRB5_STORAGE_HOST_BYTEORDER; 216 break; 217 case KRB5_FCC_FVNO_3: 218 flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE; 219 break; 220 case KRB5_FCC_FVNO_4: 221 break; 222 default: 223 krb5_abortx(context, 224 "storage_set_flags called with bad vno (%x)", vno); 225 } 226 krb5_storage_set_flags(sp, flags); 227 } 228 229 static krb5_error_code 230 fcc_initialize(krb5_context context, 231 krb5_ccache id, 232 krb5_principal primary_principal) 233 { 234 krb5_fcache *f = FCACHE(id); 235 int ret = 0; 236 int fd; 237 char *filename = f->filename; 238 239 unlink (filename); 240 241 fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600); 242 if(fd == -1) { 243 ret = errno; 244 krb5_set_error_string(context, "open(%s): %s", filename, 245 strerror(ret)); 246 return ret; 247 } 248 { 249 krb5_storage *sp; 250 sp = krb5_storage_from_fd(fd); 251 if(context->fcache_vno != 0) 252 f->version = context->fcache_vno; 253 else 254 f->version = KRB5_FCC_FVNO_4; 255 ret |= krb5_store_int8(sp, 5); 256 ret |= krb5_store_int8(sp, f->version); 257 storage_set_flags(context, sp, f->version); 258 if(f->version == KRB5_FCC_FVNO_4 && ret == 0) { 259 /* V4 stuff */ 260 if (context->kdc_sec_offset) { 261 ret |= krb5_store_int16 (sp, 12); /* length */ 262 ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */ 263 ret |= krb5_store_int16 (sp, 8); /* length of data */ 264 ret |= krb5_store_int32 (sp, context->kdc_sec_offset); 265 ret |= krb5_store_int32 (sp, context->kdc_usec_offset); 266 } else { 267 ret |= krb5_store_int16 (sp, 0); 268 } 269 } 270 ret |= krb5_store_principal(sp, primary_principal); 271 krb5_storage_free(sp); 272 } 273 if(close(fd) < 0) 274 if (ret == 0) { 275 ret = errno; 276 krb5_set_error_string (context, "close %s: %s", filename, 277 strerror(ret)); 278 } 279 280 return ret; 281 } 282 283 static krb5_error_code 284 fcc_close(krb5_context context, 285 krb5_ccache id) 286 { 287 free (FILENAME(id)); 288 krb5_data_free(&id->data); 289 return 0; 290 } 291 292 static krb5_error_code 293 fcc_destroy(krb5_context context, 294 krb5_ccache id) 295 { 296 char *f; 297 f = FILENAME(id); 298 299 erase_file(f); 300 301 return 0; 302 } 303 304 static krb5_error_code 305 fcc_store_cred(krb5_context context, 306 krb5_ccache id, 307 krb5_creds *creds) 308 { 309 int ret; 310 int fd; 311 char *f; 312 313 f = FILENAME(id); 314 315 fd = open(f, O_WRONLY | O_APPEND | O_BINARY); 316 if(fd < 0) { 317 ret = errno; 318 krb5_set_error_string (context, "open(%s): %s", f, strerror(ret)); 319 return ret; 320 } 321 { 322 krb5_storage *sp; 323 sp = krb5_storage_from_fd(fd); 324 storage_set_flags(context, sp, FCACHE(id)->version); 325 ret = krb5_store_creds(sp, creds); 326 krb5_storage_free(sp); 327 } 328 if (close(fd) < 0) 329 if (ret == 0) { 330 ret = errno; 331 krb5_set_error_string (context, "close %s: %s", f, strerror(ret)); 332 } 333 return ret; 334 } 335 336 static krb5_error_code 337 fcc_read_cred (krb5_context context, 338 krb5_fcache *fc, 339 krb5_storage *sp, 340 krb5_creds *creds) 341 { 342 krb5_error_code ret; 343 344 storage_set_flags(context, sp, fc->version); 345 346 ret = krb5_ret_creds(sp, creds); 347 return ret; 348 } 349 350 static krb5_error_code 351 init_fcc (krb5_context context, 352 krb5_fcache *fcache, 353 krb5_storage **ret_sp, 354 int *ret_fd) 355 { 356 int fd; 357 int8_t pvno, tag; 358 krb5_storage *sp; 359 krb5_error_code ret; 360 361 fd = open(fcache->filename, O_RDONLY | O_BINARY); 362 if(fd < 0) { 363 ret = errno; 364 krb5_set_error_string(context, "open(%s): %s", fcache->filename, 365 strerror(ret)); 366 return ret; 367 } 368 sp = krb5_storage_from_fd(fd); 369 ret = krb5_ret_int8(sp, &pvno); 370 if(ret == KRB5_CC_END) { 371 372 return ENOENT; 373 } 374 if(ret) 375 return ret; 376 if(pvno != 5) { 377 krb5_storage_free(sp); 378 close(fd); 379 return KRB5_CCACHE_BADVNO; 380 } 381 krb5_ret_int8(sp, &tag); /* should not be host byte order */ 382 fcache->version = tag; 383 storage_set_flags(context, sp, fcache->version); 384 switch (tag) { 385 case KRB5_FCC_FVNO_4: { 386 int16_t length; 387 388 krb5_ret_int16 (sp, &length); 389 while(length > 0) { 390 int16_t tag, data_len; 391 int i; 392 int8_t dummy; 393 394 krb5_ret_int16 (sp, &tag); 395 krb5_ret_int16 (sp, &data_len); 396 switch (tag) { 397 case FCC_TAG_DELTATIME : 398 krb5_ret_int32 (sp, &context->kdc_sec_offset); 399 krb5_ret_int32 (sp, &context->kdc_usec_offset); 400 break; 401 default : 402 for (i = 0; i < data_len; ++i) 403 krb5_ret_int8 (sp, &dummy); 404 break; 405 } 406 length -= 4 + data_len; 407 } 408 break; 409 } 410 case KRB5_FCC_FVNO_3: 411 case KRB5_FCC_FVNO_2: 412 case KRB5_FCC_FVNO_1: 413 break; 414 default : 415 krb5_storage_free (sp); 416 close (fd); 417 return KRB5_CCACHE_BADVNO; 418 } 419 *ret_sp = sp; 420 *ret_fd = fd; 421 return 0; 422 } 423 424 static krb5_error_code 425 fcc_get_principal(krb5_context context, 426 krb5_ccache id, 427 krb5_principal *principal) 428 { 429 krb5_error_code ret; 430 krb5_fcache *f = FCACHE(id); 431 int fd; 432 krb5_storage *sp; 433 434 ret = init_fcc (context, f, &sp, &fd); 435 if (ret) 436 return ret; 437 ret = krb5_ret_principal(sp, principal); 438 krb5_storage_free(sp); 439 close(fd); 440 return ret; 441 } 442 443 static krb5_error_code 444 fcc_get_first (krb5_context context, 445 krb5_ccache id, 446 krb5_cc_cursor *cursor) 447 { 448 krb5_error_code ret; 449 krb5_principal principal; 450 krb5_fcache *f = FCACHE(id); 451 452 *cursor = malloc(sizeof(struct fcc_cursor)); 453 454 ret = init_fcc (context, f, &FCC_CURSOR(*cursor)->sp, 455 &FCC_CURSOR(*cursor)->fd); 456 if (ret) 457 return ret; 458 krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal); 459 krb5_free_principal (context, principal); 460 return 0; 461 } 462 463 static krb5_error_code 464 fcc_get_next (krb5_context context, 465 krb5_ccache id, 466 krb5_cc_cursor *cursor, 467 krb5_creds *creds) 468 { 469 return fcc_read_cred (context, FCACHE(id), FCC_CURSOR(*cursor)->sp, creds); 470 } 471 472 static krb5_error_code 473 fcc_end_get (krb5_context context, 474 krb5_ccache id, 475 krb5_cc_cursor *cursor) 476 { 477 krb5_storage_free(FCC_CURSOR(*cursor)->sp); 478 close (FCC_CURSOR(*cursor)->fd); 479 free(*cursor); 480 return 0; 481 } 482 483 static krb5_error_code 484 fcc_remove_cred(krb5_context context, 485 krb5_ccache id, 486 krb5_flags which, 487 krb5_creds *cred) 488 { 489 return 0; /* XXX */ 490 } 491 492 static krb5_error_code 493 fcc_set_flags(krb5_context context, 494 krb5_ccache id, 495 krb5_flags flags) 496 { 497 return 0; /* XXX */ 498 } 499 500 static krb5_error_code 501 fcc_get_version(krb5_context context, 502 krb5_ccache id) 503 { 504 return FCACHE(id)->version; 505 } 506 507 const krb5_cc_ops krb5_fcc_ops = { 508 "FILE", 509 fcc_get_name, 510 fcc_resolve, 511 fcc_gen_new, 512 fcc_initialize, 513 fcc_destroy, 514 fcc_close, 515 fcc_store_cred, 516 NULL, /* fcc_retrieve */ 517 fcc_get_principal, 518 fcc_get_first, 519 fcc_get_next, 520 fcc_end_get, 521 fcc_remove_cred, 522 fcc_set_flags, 523 fcc_get_version 524 }; 525