1 /* 2 * Copyright (c) 1997 - 2000 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.31 2000/12/05 09:15:10 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 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 return KRB5_CC_NOMEM; 75 f->filename = strdup(res); 76 if(f->filename == NULL){ 77 free(f); 78 return KRB5_CC_NOMEM; 79 } 80 f->version = 0; 81 (*id)->data.data = f; 82 (*id)->data.length = sizeof(*f); 83 return 0; 84 } 85 86 /* 87 * Try to scrub the contents of `filename' safely. 88 */ 89 90 static int 91 scrub_file (int fd) 92 { 93 off_t pos; 94 char buf[128]; 95 96 pos = lseek(fd, 0, SEEK_END); 97 if (pos < 0) 98 return errno; 99 if (lseek(fd, 0, SEEK_SET) < 0) 100 return errno; 101 memset(buf, 0, sizeof(buf)); 102 while(pos > 0) { 103 ssize_t tmp = write(fd, buf, min(sizeof(buf), pos)); 104 105 if (tmp < 0) 106 return errno; 107 pos -= tmp; 108 } 109 fsync (fd); 110 return 0; 111 } 112 113 /* 114 * Erase `filename' if it exists, trying to remove the contents if 115 * it's `safe'. We always try to remove the file, it it exists. It's 116 * only overwritten if it's a regular file (not a symlink and not a 117 * hardlink) 118 */ 119 120 static krb5_error_code 121 erase_file(const char *filename) 122 { 123 int fd; 124 struct stat sb1, sb2; 125 int ret; 126 127 ret = lstat (filename, &sb1); 128 if (ret < 0) 129 return errno; 130 131 fd = open(filename, O_RDWR | O_BINARY); 132 if(fd < 0) { 133 if(errno == ENOENT) 134 return 0; 135 else 136 return errno; 137 } 138 if (unlink(filename) < 0) { 139 close (fd); 140 return errno; 141 } 142 143 ret = fstat (fd, &sb2); 144 if (ret < 0) { 145 close (fd); 146 return errno; 147 } 148 149 /* check if someone was playing with symlinks */ 150 151 if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) { 152 close (fd); 153 return EPERM; 154 } 155 156 /* there are still hard links to this file */ 157 158 if (sb2.st_nlink != 0) { 159 close (fd); 160 return 0; 161 } 162 163 ret = scrub_file (fd); 164 close (fd); 165 return ret; 166 } 167 168 static krb5_error_code 169 fcc_gen_new(krb5_context context, krb5_ccache *id) 170 { 171 krb5_fcache *f; 172 int fd; 173 char *file; 174 f = malloc(sizeof(*f)); 175 if(f == NULL) 176 return KRB5_CC_NOMEM; 177 asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT); 178 if(file == NULL) { 179 free(f); 180 return KRB5_CC_NOMEM; 181 } 182 fd = mkstemp(file); 183 if(fd < 0) { 184 free(f); 185 free(file); 186 return errno; 187 } 188 close(fd); 189 f->filename = file; 190 f->version = 0; 191 (*id)->data.data = f; 192 (*id)->data.length = sizeof(*f); 193 return 0; 194 } 195 196 static void 197 storage_set_flags(krb5_context context, krb5_storage *sp, int vno) 198 { 199 int flags = 0; 200 switch(vno) { 201 case KRB5_FCC_FVNO_1: 202 flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS; 203 flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE; 204 flags |= KRB5_STORAGE_HOST_BYTEORDER; 205 break; 206 case KRB5_FCC_FVNO_2: 207 flags |= KRB5_STORAGE_HOST_BYTEORDER; 208 break; 209 case KRB5_FCC_FVNO_3: 210 flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE; 211 break; 212 case KRB5_FCC_FVNO_4: 213 break; 214 default: 215 krb5_abortx(context, 216 "storage_set_flags called with bad vno (%x)", vno); 217 } 218 krb5_storage_set_flags(sp, flags); 219 } 220 221 static krb5_error_code 222 fcc_initialize(krb5_context context, 223 krb5_ccache id, 224 krb5_principal primary_principal) 225 { 226 krb5_fcache *f = FCACHE(id); 227 int ret = 0; 228 int fd; 229 char *filename = f->filename; 230 231 unlink (filename); 232 233 fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600); 234 if(fd == -1) 235 return errno; 236 { 237 krb5_storage *sp; 238 sp = krb5_storage_from_fd(fd); 239 if(context->fcache_vno != 0) 240 f->version = context->fcache_vno; 241 else 242 f->version = KRB5_FCC_FVNO_4; 243 ret |= krb5_store_int8(sp, 5); 244 ret |= krb5_store_int8(sp, f->version); 245 storage_set_flags(context, sp, f->version); 246 if(f->version == KRB5_FCC_FVNO_4 && ret == 0) { 247 /* V4 stuff */ 248 if (context->kdc_sec_offset) { 249 ret |= krb5_store_int16 (sp, 12); /* length */ 250 ret |= krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */ 251 ret |= krb5_store_int16 (sp, 8); /* length of data */ 252 ret |= krb5_store_int32 (sp, context->kdc_sec_offset); 253 ret |= krb5_store_int32 (sp, context->kdc_usec_offset); 254 } else { 255 ret |= krb5_store_int16 (sp, 0); 256 } 257 } 258 ret |= krb5_store_principal(sp, primary_principal); 259 krb5_storage_free(sp); 260 } 261 if(close(fd) < 0) 262 if (ret == 0) 263 ret = errno; 264 265 return ret; 266 } 267 268 static krb5_error_code 269 fcc_close(krb5_context context, 270 krb5_ccache id) 271 { 272 free (FILENAME(id)); 273 krb5_data_free(&id->data); 274 return 0; 275 } 276 277 static krb5_error_code 278 fcc_destroy(krb5_context context, 279 krb5_ccache id) 280 { 281 char *f; 282 f = FILENAME(id); 283 284 erase_file(f); 285 286 return 0; 287 } 288 289 static krb5_error_code 290 fcc_store_cred(krb5_context context, 291 krb5_ccache id, 292 krb5_creds *creds) 293 { 294 int ret; 295 int fd; 296 char *f; 297 298 f = FILENAME(id); 299 300 fd = open(f, O_WRONLY | O_APPEND | O_BINARY); 301 if(fd < 0) 302 return errno; 303 { 304 krb5_storage *sp; 305 sp = krb5_storage_from_fd(fd); 306 storage_set_flags(context, sp, FCACHE(id)->version); 307 ret = krb5_store_creds(sp, creds); 308 krb5_storage_free(sp); 309 } 310 if (close(fd) < 0) 311 if (ret == 0) 312 ret = errno; 313 return ret; 314 } 315 316 static krb5_error_code 317 fcc_read_cred (krb5_context context, 318 krb5_fcache *fc, 319 krb5_storage *sp, 320 krb5_creds *creds) 321 { 322 krb5_error_code ret; 323 324 storage_set_flags(context, sp, fc->version); 325 326 ret = krb5_ret_creds(sp, creds); 327 return ret; 328 } 329 330 static krb5_error_code 331 init_fcc (krb5_context context, 332 krb5_fcache *fcache, 333 krb5_storage **ret_sp, 334 int *ret_fd) 335 { 336 int fd; 337 int8_t pvno, tag; 338 krb5_storage *sp; 339 krb5_error_code ret; 340 341 fd = open(fcache->filename, O_RDONLY | O_BINARY); 342 if(fd < 0) 343 return errno; 344 sp = krb5_storage_from_fd(fd); 345 ret = krb5_ret_int8(sp, &pvno); 346 if(ret == KRB5_CC_END) 347 return ENOENT; 348 if(ret) 349 return ret; 350 if(pvno != 5) { 351 krb5_storage_free(sp); 352 close(fd); 353 return KRB5_CCACHE_BADVNO; 354 } 355 krb5_ret_int8(sp, &tag); /* should not be host byte order */ 356 fcache->version = tag; 357 storage_set_flags(context, sp, fcache->version); 358 switch (tag) { 359 case KRB5_FCC_FVNO_4: { 360 int16_t length; 361 362 krb5_ret_int16 (sp, &length); 363 while(length > 0) { 364 int16_t tag, data_len; 365 int i; 366 int8_t dummy; 367 368 krb5_ret_int16 (sp, &tag); 369 krb5_ret_int16 (sp, &data_len); 370 switch (tag) { 371 case FCC_TAG_DELTATIME : 372 krb5_ret_int32 (sp, &context->kdc_sec_offset); 373 krb5_ret_int32 (sp, &context->kdc_usec_offset); 374 break; 375 default : 376 for (i = 0; i < data_len; ++i) 377 krb5_ret_int8 (sp, &dummy); 378 break; 379 } 380 length -= 4 + data_len; 381 } 382 break; 383 } 384 case KRB5_FCC_FVNO_3: 385 case KRB5_FCC_FVNO_2: 386 case KRB5_FCC_FVNO_1: 387 break; 388 default : 389 krb5_storage_free (sp); 390 close (fd); 391 return KRB5_CCACHE_BADVNO; 392 } 393 *ret_sp = sp; 394 *ret_fd = fd; 395 return 0; 396 } 397 398 static krb5_error_code 399 fcc_get_principal(krb5_context context, 400 krb5_ccache id, 401 krb5_principal *principal) 402 { 403 krb5_error_code ret; 404 krb5_fcache *f = FCACHE(id); 405 int fd; 406 krb5_storage *sp; 407 408 ret = init_fcc (context, f, &sp, &fd); 409 if (ret) 410 return ret; 411 ret = krb5_ret_principal(sp, principal); 412 krb5_storage_free(sp); 413 close(fd); 414 return ret; 415 } 416 417 static krb5_error_code 418 fcc_get_first (krb5_context context, 419 krb5_ccache id, 420 krb5_cc_cursor *cursor) 421 { 422 krb5_error_code ret; 423 krb5_principal principal; 424 krb5_fcache *f = FCACHE(id); 425 426 *cursor = malloc(sizeof(struct fcc_cursor)); 427 428 ret = init_fcc (context, f, &FCC_CURSOR(*cursor)->sp, 429 &FCC_CURSOR(*cursor)->fd); 430 if (ret) 431 return ret; 432 krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal); 433 krb5_free_principal (context, principal); 434 return 0; 435 } 436 437 static krb5_error_code 438 fcc_get_next (krb5_context context, 439 krb5_ccache id, 440 krb5_cc_cursor *cursor, 441 krb5_creds *creds) 442 { 443 return fcc_read_cred (context, FCACHE(id), FCC_CURSOR(*cursor)->sp, creds); 444 } 445 446 static krb5_error_code 447 fcc_end_get (krb5_context context, 448 krb5_ccache id, 449 krb5_cc_cursor *cursor) 450 { 451 krb5_storage_free(FCC_CURSOR(*cursor)->sp); 452 close (FCC_CURSOR(*cursor)->fd); 453 free(*cursor); 454 return 0; 455 } 456 457 static krb5_error_code 458 fcc_remove_cred(krb5_context context, 459 krb5_ccache id, 460 krb5_flags which, 461 krb5_creds *cred) 462 { 463 return 0; /* XXX */ 464 } 465 466 static krb5_error_code 467 fcc_set_flags(krb5_context context, 468 krb5_ccache id, 469 krb5_flags flags) 470 { 471 return 0; /* XXX */ 472 } 473 474 static krb5_error_code 475 fcc_get_version(krb5_context context, 476 krb5_ccache id) 477 { 478 return FCACHE(id)->version; 479 } 480 481 const krb5_cc_ops krb5_fcc_ops = { 482 "FILE", 483 fcc_get_name, 484 fcc_resolve, 485 fcc_gen_new, 486 fcc_initialize, 487 fcc_destroy, 488 fcc_close, 489 fcc_store_cred, 490 NULL, /* fcc_retrieve */ 491 fcc_get_principal, 492 fcc_get_first, 493 fcc_get_next, 494 fcc_end_get, 495 fcc_remove_cred, 496 fcc_set_flags, 497 fcc_get_version 498 }; 499