1 /* 2 * Copyright (c) 1997 - 2002 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.34 2002/04/18 14:01:29 joda 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 const 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 krb5_storage_set_eof_code(sp, KRB5_CC_END); 252 if(context->fcache_vno != 0) 253 f->version = context->fcache_vno; 254 else 255 f->version = KRB5_FCC_FVNO_4; 256 ret |= krb5_store_int8(sp, 5); 257 ret |= krb5_store_int8(sp, f->version); 258 storage_set_flags(context, sp, f->version); 259 if(f->version == KRB5_FCC_FVNO_4 && ret == 0) { 260 /* V4 stuff */ 261 if (context->kdc_sec_offset) { 262 ret |= krb5_store_int16 (sp, 12); /* length */ 263 ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */ 264 ret |= krb5_store_int16 (sp, 8); /* length of data */ 265 ret |= krb5_store_int32 (sp, context->kdc_sec_offset); 266 ret |= krb5_store_int32 (sp, context->kdc_usec_offset); 267 } else { 268 ret |= krb5_store_int16 (sp, 0); 269 } 270 } 271 ret |= krb5_store_principal(sp, primary_principal); 272 krb5_storage_free(sp); 273 } 274 if(close(fd) < 0) 275 if (ret == 0) { 276 ret = errno; 277 krb5_set_error_string (context, "close %s: %s", filename, 278 strerror(ret)); 279 } 280 281 return ret; 282 } 283 284 static krb5_error_code 285 fcc_close(krb5_context context, 286 krb5_ccache id) 287 { 288 free (FILENAME(id)); 289 krb5_data_free(&id->data); 290 return 0; 291 } 292 293 static krb5_error_code 294 fcc_destroy(krb5_context context, 295 krb5_ccache id) 296 { 297 char *f; 298 f = FILENAME(id); 299 300 erase_file(f); 301 302 return 0; 303 } 304 305 static krb5_error_code 306 fcc_store_cred(krb5_context context, 307 krb5_ccache id, 308 krb5_creds *creds) 309 { 310 int ret; 311 int fd; 312 char *f; 313 314 f = FILENAME(id); 315 316 fd = open(f, O_WRONLY | O_APPEND | O_BINARY); 317 if(fd < 0) { 318 ret = errno; 319 krb5_set_error_string (context, "open(%s): %s", f, strerror(ret)); 320 return ret; 321 } 322 { 323 krb5_storage *sp; 324 sp = krb5_storage_from_fd(fd); 325 krb5_storage_set_eof_code(sp, KRB5_CC_END); 326 storage_set_flags(context, sp, FCACHE(id)->version); 327 ret = krb5_store_creds(sp, creds); 328 krb5_storage_free(sp); 329 } 330 if (close(fd) < 0) 331 if (ret == 0) { 332 ret = errno; 333 krb5_set_error_string (context, "close %s: %s", f, strerror(ret)); 334 } 335 return ret; 336 } 337 338 static krb5_error_code 339 fcc_read_cred (krb5_context context, 340 krb5_fcache *fc, 341 krb5_storage *sp, 342 krb5_creds *creds) 343 { 344 krb5_error_code ret; 345 346 storage_set_flags(context, sp, fc->version); 347 348 ret = krb5_ret_creds(sp, creds); 349 return ret; 350 } 351 352 static krb5_error_code 353 init_fcc (krb5_context context, 354 krb5_fcache *fcache, 355 krb5_storage **ret_sp, 356 int *ret_fd) 357 { 358 int fd; 359 int8_t pvno, tag; 360 krb5_storage *sp; 361 krb5_error_code ret; 362 363 fd = open(fcache->filename, O_RDONLY | O_BINARY); 364 if(fd < 0) { 365 ret = errno; 366 krb5_set_error_string(context, "open(%s): %s", fcache->filename, 367 strerror(ret)); 368 return ret; 369 } 370 sp = krb5_storage_from_fd(fd); 371 krb5_storage_set_eof_code(sp, KRB5_CC_END); 372 ret = krb5_ret_int8(sp, &pvno); 373 if(ret == KRB5_CC_END) 374 return ENOENT; 375 if(ret) 376 return ret; 377 if(pvno != 5) { 378 krb5_storage_free(sp); 379 close(fd); 380 return KRB5_CCACHE_BADVNO; 381 } 382 krb5_ret_int8(sp, &tag); /* should not be host byte order */ 383 fcache->version = tag; 384 storage_set_flags(context, sp, fcache->version); 385 switch (tag) { 386 case KRB5_FCC_FVNO_4: { 387 int16_t length; 388 389 krb5_ret_int16 (sp, &length); 390 while(length > 0) { 391 int16_t tag, data_len; 392 int i; 393 int8_t dummy; 394 395 krb5_ret_int16 (sp, &tag); 396 krb5_ret_int16 (sp, &data_len); 397 switch (tag) { 398 case FCC_TAG_DELTATIME : 399 krb5_ret_int32 (sp, &context->kdc_sec_offset); 400 krb5_ret_int32 (sp, &context->kdc_usec_offset); 401 break; 402 default : 403 for (i = 0; i < data_len; ++i) 404 krb5_ret_int8 (sp, &dummy); 405 break; 406 } 407 length -= 4 + data_len; 408 } 409 break; 410 } 411 case KRB5_FCC_FVNO_3: 412 case KRB5_FCC_FVNO_2: 413 case KRB5_FCC_FVNO_1: 414 break; 415 default : 416 krb5_storage_free (sp); 417 close (fd); 418 return KRB5_CCACHE_BADVNO; 419 } 420 *ret_sp = sp; 421 *ret_fd = fd; 422 return 0; 423 } 424 425 static krb5_error_code 426 fcc_get_principal(krb5_context context, 427 krb5_ccache id, 428 krb5_principal *principal) 429 { 430 krb5_error_code ret; 431 krb5_fcache *f = FCACHE(id); 432 int fd; 433 krb5_storage *sp; 434 435 ret = init_fcc (context, f, &sp, &fd); 436 if (ret) 437 return ret; 438 ret = krb5_ret_principal(sp, principal); 439 krb5_storage_free(sp); 440 close(fd); 441 return ret; 442 } 443 444 static krb5_error_code 445 fcc_get_first (krb5_context context, 446 krb5_ccache id, 447 krb5_cc_cursor *cursor) 448 { 449 krb5_error_code ret; 450 krb5_principal principal; 451 krb5_fcache *f = FCACHE(id); 452 453 *cursor = malloc(sizeof(struct fcc_cursor)); 454 455 ret = init_fcc (context, f, &FCC_CURSOR(*cursor)->sp, 456 &FCC_CURSOR(*cursor)->fd); 457 if (ret) 458 return ret; 459 krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal); 460 krb5_free_principal (context, principal); 461 return 0; 462 } 463 464 static krb5_error_code 465 fcc_get_next (krb5_context context, 466 krb5_ccache id, 467 krb5_cc_cursor *cursor, 468 krb5_creds *creds) 469 { 470 return fcc_read_cred (context, FCACHE(id), FCC_CURSOR(*cursor)->sp, creds); 471 } 472 473 static krb5_error_code 474 fcc_end_get (krb5_context context, 475 krb5_ccache id, 476 krb5_cc_cursor *cursor) 477 { 478 krb5_storage_free(FCC_CURSOR(*cursor)->sp); 479 close (FCC_CURSOR(*cursor)->fd); 480 free(*cursor); 481 return 0; 482 } 483 484 static krb5_error_code 485 fcc_remove_cred(krb5_context context, 486 krb5_ccache id, 487 krb5_flags which, 488 krb5_creds *cred) 489 { 490 return 0; /* XXX */ 491 } 492 493 static krb5_error_code 494 fcc_set_flags(krb5_context context, 495 krb5_ccache id, 496 krb5_flags flags) 497 { 498 return 0; /* XXX */ 499 } 500 501 static krb5_error_code 502 fcc_get_version(krb5_context context, 503 krb5_ccache id) 504 { 505 return FCACHE(id)->version; 506 } 507 508 const krb5_cc_ops krb5_fcc_ops = { 509 "FILE", 510 fcc_get_name, 511 fcc_resolve, 512 fcc_gen_new, 513 fcc_initialize, 514 fcc_destroy, 515 fcc_close, 516 fcc_store_cred, 517 NULL, /* fcc_retrieve */ 518 fcc_get_principal, 519 fcc_get_first, 520 fcc_get_next, 521 fcc_end_get, 522 fcc_remove_cred, 523 fcc_set_flags, 524 fcc_get_version 525 }; 526