1 /* $NetBSD: load.c,v 1.3 2019/12/15 22:50:46 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1997-2005 Kungliga Tekniska Hgskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <limits.h> 37 38 #include "kadmin_locl.h" 39 #include "kadmin-commands.h" 40 #include <kadm5/private.h> 41 42 struct entry { 43 char *principal; 44 char *key; 45 char *max_life; 46 char *max_renew; 47 char *created; 48 char *modified; 49 char *valid_start; 50 char *valid_end; 51 char *pw_end; 52 char *flags; 53 char *generation; 54 char *extensions; 55 }; 56 57 static char * 58 skip_next(char *p) 59 { 60 while(*p && !isspace((unsigned char)*p)) 61 p++; 62 *p++ = 0; 63 while(*p && isspace((unsigned char)*p)) 64 p++; 65 return p; 66 } 67 68 /* 69 * Parse the time in `s', returning: 70 * -1 if error parsing 71 * 0 if none present 72 * 1 if parsed ok 73 */ 74 75 static int 76 parse_time_string(time_t *t, const char *s) 77 { 78 int year, month, date, hour, minute, second; 79 struct tm tm; 80 81 if(strcmp(s, "-") == 0) 82 return 0; 83 if(sscanf(s, "%04d%02d%02d%02d%02d%02d", 84 &year, &month, &date, &hour, &minute, &second) != 6) 85 return -1; 86 tm.tm_year = year - 1900; 87 tm.tm_mon = month - 1; 88 tm.tm_mday = date; 89 tm.tm_hour = hour; 90 tm.tm_min = minute; 91 tm.tm_sec = second; 92 tm.tm_isdst = 0; 93 *t = timegm(&tm); 94 return 1; 95 } 96 97 /* 98 * parse time, allocating space in *t if it's there 99 */ 100 101 static int 102 parse_time_string_alloc (time_t **t, const char *s) 103 { 104 time_t tmp; 105 int ret; 106 107 *t = NULL; 108 ret = parse_time_string (&tmp, s); 109 if (ret == 1) { 110 *t = malloc (sizeof (**t)); 111 if (*t == NULL) 112 krb5_errx (context, 1, "malloc: out of memory"); 113 **t = tmp; 114 } 115 return ret; 116 } 117 118 /* 119 * see parse_time_string for calling convention 120 */ 121 122 static int 123 parse_integer(unsigned int *u, const char *s) 124 { 125 if(strcmp(s, "-") == 0) 126 return 0; 127 if (sscanf(s, "%u", u) != 1) 128 return -1; 129 return 1; 130 } 131 132 static int 133 parse_integer_alloc (unsigned int **u, const char *s) 134 { 135 unsigned int tmp; 136 int ret; 137 138 *u = NULL; 139 ret = parse_integer (&tmp, s); 140 if (ret == 1) { 141 *u = malloc (sizeof (**u)); 142 if (*u == NULL) 143 krb5_errx (context, 1, "malloc: out of memory"); 144 **u = tmp; 145 } 146 return ret; 147 } 148 149 /* 150 * Parse dumped keys in `str' and store them in `ent' 151 * return -1 if parsing failed 152 */ 153 154 static int 155 parse_keys(hdb_entry *ent, char *str) 156 { 157 krb5_error_code ret; 158 int tmp; 159 char *p; 160 size_t i; 161 162 p = strsep(&str, ":"); 163 if (sscanf(p, "%d", &tmp) != 1) 164 return 1; 165 ent->kvno = tmp; 166 p = strsep(&str, ":"); 167 while(p){ 168 Key *key; 169 key = realloc(ent->keys.val, 170 (ent->keys.len + 1) * sizeof(*ent->keys.val)); 171 if(key == NULL) 172 krb5_errx (context, 1, "realloc: out of memory"); 173 ent->keys.val = key; 174 key = ent->keys.val + ent->keys.len; 175 ent->keys.len++; 176 memset(key, 0, sizeof(*key)); 177 if(sscanf(p, "%d", &tmp) == 1) { 178 key->mkvno = malloc(sizeof(*key->mkvno)); 179 *key->mkvno = tmp; 180 } else 181 key->mkvno = NULL; 182 p = strsep(&str, ":"); 183 if (sscanf(p, "%d", &tmp) != 1) 184 return 1; 185 key->key.keytype = tmp; 186 p = strsep(&str, ":"); 187 ret = krb5_data_alloc(&key->key.keyvalue, (strlen(p) - 1) / 2 + 1); 188 if (ret) 189 krb5_err (context, 1, ret, "krb5_data_alloc"); 190 for(i = 0; i < strlen(p); i += 2) { 191 if(sscanf(p + i, "%02x", &tmp) != 1) 192 return 1; 193 ((u_char*)key->key.keyvalue.data)[i / 2] = tmp; 194 } 195 p = strsep(&str, ":"); 196 if(strcmp(p, "-") != 0){ 197 unsigned type; 198 size_t p_len; 199 200 if(sscanf(p, "%u/", &type) != 1) 201 return 1; 202 p = strchr(p, '/'); 203 if(p == NULL) 204 return 1; 205 p++; 206 p_len = strlen(p); 207 208 key->salt = calloc(1, sizeof(*key->salt)); 209 if (key->salt == NULL) 210 krb5_errx (context, 1, "malloc: out of memory"); 211 key->salt->type = type; 212 213 if (p_len) { 214 if(*p == '\"') { 215 ret = krb5_data_copy(&key->salt->salt, p + 1, p_len - 2); 216 if (ret) 217 krb5_err (context, 1, ret, "krb5_data_copy"); 218 } else { 219 ret = krb5_data_alloc(&key->salt->salt, 220 (p_len - 1) / 2 + 1); 221 if (ret) 222 krb5_err (context, 1, ret, "krb5_data_alloc"); 223 for(i = 0; i < p_len; i += 2){ 224 if (sscanf(p + i, "%02x", &tmp) != 1) 225 return 1; 226 ((u_char*)key->salt->salt.data)[i / 2] = tmp; 227 } 228 } 229 } else 230 krb5_data_zero (&key->salt->salt); 231 } 232 p = strsep(&str, ":"); 233 } 234 return 0; 235 } 236 237 /* 238 * see parse_time_string for calling convention 239 */ 240 241 static int 242 parse_event(Event *ev, char *s) 243 { 244 krb5_error_code ret; 245 char *p; 246 247 if(strcmp(s, "-") == 0) 248 return 0; 249 memset(ev, 0, sizeof(*ev)); 250 p = strsep(&s, ":"); 251 if(parse_time_string(&ev->time, p) != 1) 252 return -1; 253 p = strsep(&s, ":"); 254 ret = krb5_parse_name(context, p, &ev->principal); 255 if (ret) 256 return -1; 257 return 1; 258 } 259 260 static int 261 parse_event_alloc (Event **ev, char *s) 262 { 263 Event tmp; 264 int ret; 265 266 *ev = NULL; 267 ret = parse_event (&tmp, s); 268 if (ret == 1) { 269 *ev = malloc (sizeof (**ev)); 270 if (*ev == NULL) 271 krb5_errx (context, 1, "malloc: out of memory"); 272 **ev = tmp; 273 } 274 return ret; 275 } 276 277 static int 278 parse_hdbflags2int(HDBFlags *f, const char *s) 279 { 280 int ret; 281 unsigned int tmp; 282 283 ret = parse_integer (&tmp, s); 284 if (ret == 1) 285 *f = int2HDBFlags (tmp); 286 return ret; 287 } 288 289 static int 290 parse_generation(char *str, GENERATION **gen) 291 { 292 char *p; 293 int v; 294 295 if(strcmp(str, "-") == 0 || *str == '\0') { 296 *gen = NULL; 297 return 0; 298 } 299 *gen = calloc(1, sizeof(**gen)); 300 301 p = strsep(&str, ":"); 302 if(parse_time_string(&(*gen)->time, p) != 1) 303 return -1; 304 p = strsep(&str, ":"); 305 if(sscanf(p, "%d", &v) != 1) 306 return -1; 307 (*gen)->usec = v; 308 p = strsep(&str, ":"); 309 if(sscanf(p, "%d", &v) != 1) 310 return -1; 311 (*gen)->gen = v - 1; /* XXX gets bumped in _hdb_store */ 312 return 0; 313 } 314 315 /* On error modify strp to point to the problem element */ 316 static int 317 parse_extensions(char **strp, HDB_extensions **e) 318 { 319 char *str = *strp; 320 char *p; 321 int ret; 322 323 if(strcmp(str, "-") == 0 || *str == '\0') { 324 *e = NULL; 325 return 0; 326 } 327 *e = calloc(1, sizeof(**e)); 328 329 p = strsep(&str, ":"); 330 331 while (p) { 332 HDB_extension ext; 333 ssize_t len; 334 void *d; 335 336 len = strlen(p); 337 d = emalloc(len); 338 339 len = hex_decode(p, d, len); 340 if (len < 0) { 341 free(d); 342 *strp = p; 343 return -1; 344 } 345 346 ret = decode_HDB_extension(d, len, &ext, NULL); 347 free(d); 348 if (ret) { 349 *strp = p; 350 return -1; 351 } 352 d = realloc((*e)->val, ((*e)->len + 1) * sizeof((*e)->val[0])); 353 if (d == NULL) 354 abort(); 355 (*e)->val = d; 356 (*e)->val[(*e)->len] = ext; 357 (*e)->len++; 358 359 p = strsep(&str, ":"); 360 } 361 362 return 0; 363 } 364 365 /* XXX: Principal names with '\n' cannot be dumped or loaded */ 366 static int 367 my_fgetln(FILE *f, char **bufp, size_t *szp, size_t *lenp) 368 { 369 size_t len; 370 size_t sz = *szp; 371 char *buf = *bufp; 372 char *p, *n; 373 374 if (!buf) { 375 buf = malloc(sz ? sz : 8192); 376 if (!buf) 377 return ENOMEM; 378 if (!sz) 379 sz = 8192; 380 } 381 382 len = 0; 383 while ((p = fgets(&buf[len], sz-len, f)) != NULL) { 384 len += strlen(&buf[len]); 385 if (buf[len-1] == '\n') 386 break; 387 if (feof(f)) 388 break; 389 if (sz > SIZE_MAX/2 || 390 (n = realloc(buf, sz += 1 + (sz >> 1))) == NULL) { 391 free(buf); 392 *bufp = NULL; 393 *szp = 0; 394 *lenp = 0; 395 return ENOMEM; 396 } 397 buf = n; 398 } 399 *bufp = buf; 400 *szp = sz; 401 *lenp = len; 402 return 0; /* *len == 0 || no EOL -> EOF */ 403 } 404 405 /* 406 * Parse the dump file in `filename' and create the database (merging 407 * iff merge) 408 */ 409 410 static int 411 doit(const char *filename, int mergep) 412 { 413 krb5_error_code ret = 0; 414 krb5_error_code ret2 = 0; 415 FILE *f; 416 char *line = NULL; 417 size_t linesz = 0; 418 size_t linelen = 0; 419 char *p; 420 int lineno; 421 int flags = O_RDWR; 422 struct entry e; 423 hdb_entry_ex ent; 424 HDB *db = _kadm5_s_get_db(kadm_handle); 425 426 f = fopen(filename, "r"); 427 if (f == NULL) { 428 krb5_warn(context, errno, "fopen(%s)", filename); 429 return 1; 430 } 431 /* 432 * We don't have a version number in the dump, so we don't know which iprop 433 * log entries to keep, if any. We throw the log away. 434 * 435 * We could merge the ipropd-master/slave dump/load here as an option, in 436 * which case we would first load the dump. 437 * 438 * If we're merging, first recover unconfirmed records in the existing log. 439 */ 440 if (mergep) 441 ret = kadm5_log_init(kadm_handle); 442 if (ret == 0) 443 ret = kadm5_log_reinit(kadm_handle, 0); 444 if (ret) { 445 fclose (f); 446 krb5_warn(context, ret, "kadm5_log_reinit"); 447 return 1; 448 } 449 450 if (!mergep) 451 flags |= O_CREAT | O_TRUNC; 452 ret = db->hdb_open(context, db, flags, 0600); 453 if (ret){ 454 krb5_warn(context, ret, "hdb_open"); 455 fclose(f); 456 return 1; 457 } 458 (void) db->hdb_set_sync(context, db, 0); 459 for (lineno = 1; 460 (ret2 = my_fgetln(f, &line, &linesz, &linelen)) == 0 && linelen > 0; 461 ++lineno) { 462 p = line; 463 while (isspace((unsigned char)*p)) 464 p++; 465 466 e.principal = p; 467 for (p = line; *p; p++){ 468 if (*p == '\\') /* Support '\n' escapes??? */ 469 p++; 470 else if (isspace((unsigned char)*p)) { 471 *p = 0; 472 break; 473 } 474 } 475 p = skip_next(p); 476 477 e.key = p; 478 p = skip_next(p); 479 480 e.created = p; 481 p = skip_next(p); 482 483 e.modified = p; 484 p = skip_next(p); 485 486 e.valid_start = p; 487 p = skip_next(p); 488 489 e.valid_end = p; 490 p = skip_next(p); 491 492 e.pw_end = p; 493 p = skip_next(p); 494 495 e.max_life = p; 496 p = skip_next(p); 497 498 e.max_renew = p; 499 p = skip_next(p); 500 501 e.flags = p; 502 p = skip_next(p); 503 504 e.generation = p; 505 p = skip_next(p); 506 507 e.extensions = p; 508 skip_next(p); 509 510 memset(&ent, 0, sizeof(ent)); 511 ret2 = krb5_parse_name(context, e.principal, &ent.entry.principal); 512 if (ret2) { 513 const char *msg = krb5_get_error_message(context, ret); 514 fprintf(stderr, "%s:%d:%s (%s)\n", 515 filename, lineno, msg, e.principal); 516 krb5_free_error_message(context, msg); 517 ret = 1; 518 continue; 519 } 520 521 if (parse_keys(&ent.entry, e.key)) { 522 fprintf (stderr, "%s:%d:error parsing keys (%s)\n", 523 filename, lineno, e.key); 524 hdb_free_entry (context, &ent); 525 ret = 1; 526 continue; 527 } 528 529 if (parse_event(&ent.entry.created_by, e.created) == -1) { 530 fprintf (stderr, "%s:%d:error parsing created event (%s)\n", 531 filename, lineno, e.created); 532 hdb_free_entry (context, &ent); 533 ret = 1; 534 continue; 535 } 536 if (parse_event_alloc (&ent.entry.modified_by, e.modified) == -1) { 537 fprintf (stderr, "%s:%d:error parsing event (%s)\n", 538 filename, lineno, e.modified); 539 hdb_free_entry (context, &ent); 540 ret = 1; 541 continue; 542 } 543 if (parse_time_string_alloc (&ent.entry.valid_start, e.valid_start) == -1) { 544 fprintf (stderr, "%s:%d:error parsing time (%s)\n", 545 filename, lineno, e.valid_start); 546 hdb_free_entry (context, &ent); 547 ret = 1; 548 continue; 549 } 550 if (parse_time_string_alloc (&ent.entry.valid_end, e.valid_end) == -1) { 551 fprintf (stderr, "%s:%d:error parsing time (%s)\n", 552 filename, lineno, e.valid_end); 553 hdb_free_entry (context, &ent); 554 ret = 1; 555 continue; 556 } 557 if (parse_time_string_alloc (&ent.entry.pw_end, e.pw_end) == -1) { 558 fprintf (stderr, "%s:%d:error parsing time (%s)\n", 559 filename, lineno, e.pw_end); 560 hdb_free_entry (context, &ent); 561 ret = 1; 562 continue; 563 } 564 565 if (parse_integer_alloc (&ent.entry.max_life, e.max_life) == -1) { 566 fprintf (stderr, "%s:%d:error parsing lifetime (%s)\n", 567 filename, lineno, e.max_life); 568 hdb_free_entry (context, &ent); 569 ret = 1; 570 continue; 571 572 } 573 if (parse_integer_alloc (&ent.entry.max_renew, e.max_renew) == -1) { 574 fprintf (stderr, "%s:%d:error parsing lifetime (%s)\n", 575 filename, lineno, e.max_renew); 576 hdb_free_entry (context, &ent); 577 ret = 1; 578 continue; 579 } 580 581 if (parse_hdbflags2int (&ent.entry.flags, e.flags) != 1) { 582 fprintf (stderr, "%s:%d:error parsing flags (%s)\n", 583 filename, lineno, e.flags); 584 hdb_free_entry (context, &ent); 585 ret = 1; 586 continue; 587 } 588 589 if(parse_generation(e.generation, &ent.entry.generation) == -1) { 590 fprintf (stderr, "%s:%d:error parsing generation (%s)\n", 591 filename, lineno, e.generation); 592 hdb_free_entry (context, &ent); 593 ret = 1; 594 continue; 595 } 596 597 if (parse_extensions(&e.extensions, &ent.entry.extensions) == -1) { 598 fprintf (stderr, "%s:%d:error parsing extension (%s)\n", 599 filename, lineno, e.extensions); 600 hdb_free_entry (context, &ent); 601 ret = 1; 602 continue; 603 } 604 605 ret2 = db->hdb_store(context, db, HDB_F_REPLACE, &ent); 606 hdb_free_entry (context, &ent); 607 if (ret2) { 608 krb5_warn(context, ret2, "db_store"); 609 break; 610 } 611 } 612 free(line); 613 if (ret2) 614 ret = ret2; 615 ret2 = db->hdb_set_sync(context, db, 1); 616 if (ret2) 617 krb5_err(context, 1, ret, "failed to sync the HDB"); 618 (void) kadm5_log_end(kadm_handle); 619 ret2 = db->hdb_close(context, db); 620 if (ret2) 621 ret = ret2; 622 fclose(f); 623 return ret != 0; 624 } 625 626 627 extern int local_flag; 628 629 static int 630 loadit(int mergep, const char *name, int argc, char **argv) 631 { 632 if(!local_flag) { 633 krb5_warnx(context, "%s is only available in local (-l) mode", name); 634 return 0; 635 } 636 637 return doit(argv[0], mergep); 638 } 639 640 int 641 load(void *opt, int argc, char **argv) 642 { 643 return loadit(0, "load", argc, argv); 644 } 645 646 int 647 merge(void *opt, int argc, char **argv) 648 { 649 return loadit(1, "merge", argc, argv); 650 } 651