1 /* $NetBSD: print.c,v 1.2 2017/01/28 21:31:48 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1999-2005 Kungliga Tekniska Högskolan 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 KTH nor the names of its contributors may be 20 * used to endorse or promote products derived from this software without 21 * specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY 24 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE 27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 30 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 32 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 34 35 #include "hdb_locl.h" 36 #include <krb5/hex.h> 37 #include <ctype.h> 38 39 /* 40 This is the present contents of a dump line. This might change at 41 any time. Fields are separated by white space. 42 43 principal 44 keyblock 45 kvno 46 keys... 47 mkvno 48 enctype 49 keyvalue 50 salt (- means use normal salt) 51 creation date and principal 52 modification date and principal 53 principal valid from date (not used) 54 principal valid end date (not used) 55 principal key expires (not used) 56 max ticket life 57 max renewable life 58 flags 59 generation number 60 */ 61 62 /* 63 * These utility functions return the number of bytes written or -1, and 64 * they set an error in the context. 65 */ 66 static ssize_t 67 append_string(krb5_context context, krb5_storage *sp, const char *fmt, ...) 68 { 69 ssize_t sz; 70 char *s; 71 int rc; 72 va_list ap; 73 va_start(ap, fmt); 74 rc = vasprintf(&s, fmt, ap); 75 va_end(ap); 76 if(rc < 0) { 77 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 78 return -1; 79 } 80 sz = krb5_storage_write(sp, s, strlen(s)); 81 free(s); 82 return sz; 83 } 84 85 static krb5_error_code 86 append_hex(krb5_context context, krb5_storage *sp, 87 int always_encode, int lower, krb5_data *data) 88 { 89 ssize_t sz; 90 int printable = 1; 91 size_t i; 92 char *p; 93 94 p = data->data; 95 if (!always_encode) { 96 for (i = 0; i < data->length; i++) { 97 if (!isalnum((unsigned char)p[i]) && p[i] != '.'){ 98 printable = 0; 99 break; 100 } 101 } 102 } 103 if (printable && !always_encode) 104 return append_string(context, sp, "\"%.*s\"", 105 data->length, data->data); 106 sz = hex_encode(data->data, data->length, &p); 107 if (sz == -1) return sz; 108 if (lower) 109 strlwr(p); 110 sz = append_string(context, sp, "%s", p); 111 free(p); 112 return sz; 113 } 114 115 static char * 116 time2str(time_t t) 117 { 118 static char buf[128]; 119 strftime(buf, sizeof(buf), "%Y%m%d%H%M%S", gmtime(&t)); 120 return buf; 121 } 122 123 static ssize_t 124 append_event(krb5_context context, krb5_storage *sp, Event *ev) 125 { 126 krb5_error_code ret; 127 ssize_t sz; 128 char *pr = NULL; 129 if(ev == NULL) 130 return append_string(context, sp, "- "); 131 if (ev->principal != NULL) { 132 ret = krb5_unparse_name(context, ev->principal, &pr); 133 if (ret) return -1; /* krb5_unparse_name() sets error info */ 134 } 135 sz = append_string(context, sp, "%s:%s ", time2str(ev->time), 136 pr ? pr : "UNKNOWN"); 137 free(pr); 138 return sz; 139 } 140 141 #define KRB5_KDB_SALTTYPE_NORMAL 0 142 #define KRB5_KDB_SALTTYPE_V4 1 143 #define KRB5_KDB_SALTTYPE_NOREALM 2 144 #define KRB5_KDB_SALTTYPE_ONLYREALM 3 145 #define KRB5_KDB_SALTTYPE_SPECIAL 4 146 #define KRB5_KDB_SALTTYPE_AFS3 5 147 148 static ssize_t 149 append_mit_key(krb5_context context, krb5_storage *sp, 150 krb5_const_principal princ, 151 unsigned int kvno, Key *key) 152 { 153 krb5_error_code ret; 154 krb5_salt k5salt; 155 ssize_t sz; 156 size_t key_versions = key->salt ? 2 : 1; 157 size_t decrypted_key_length; 158 char buf[2]; 159 krb5_data keylenbytes; 160 unsigned int salttype; 161 162 sz = append_string(context, sp, "\t%u\t%u\t%d\t%d\t", key_versions, kvno, 163 key->key.keytype, key->key.keyvalue.length + 2); 164 if (sz == -1) return sz; 165 ret = krb5_enctype_keysize(context, key->key.keytype, &decrypted_key_length); 166 if (ret) return -1; /* XXX we lose the error code */ 167 buf[0] = decrypted_key_length & 0xff; 168 buf[1] = (decrypted_key_length & 0xff00) >> 8; 169 keylenbytes.data = buf; 170 keylenbytes.length = sizeof (buf); 171 sz = append_hex(context, sp, 1, 1, &keylenbytes); 172 if (sz == -1) return sz; 173 sz = append_hex(context, sp, 1, 1, &key->key.keyvalue); 174 if (!key->salt) 175 return sz; 176 177 /* Map salt to MIT KDB style */ 178 switch (key->salt->type) { 179 case KRB5_PADATA_PW_SALT: 180 181 /* 182 * Compute normal salt and then see whether it matches the stored one 183 */ 184 ret = krb5_get_pw_salt(context, princ, &k5salt); 185 if (ret) return -1; 186 if (k5salt.saltvalue.length == key->salt->salt.length && 187 memcmp(k5salt.saltvalue.data, key->salt->salt.data, 188 k5salt.saltvalue.length) == 0) 189 salttype = KRB5_KDB_SALTTYPE_NORMAL; /* matches */ 190 else if (key->salt->salt.length == strlen(princ->realm) && 191 memcmp(key->salt->salt.data, princ->realm, 192 key->salt->salt.length) == 0) 193 salttype = KRB5_KDB_SALTTYPE_ONLYREALM; /* matches realm */ 194 else if (key->salt->salt.length == 195 k5salt.saltvalue.length - strlen(princ->realm) && 196 memcmp((char *)k5salt.saltvalue.data + strlen(princ->realm), 197 key->salt->salt.data, key->salt->salt.length) == 0) 198 salttype = KRB5_KDB_SALTTYPE_NOREALM; /* matches w/o realm */ 199 else 200 salttype = KRB5_KDB_SALTTYPE_NORMAL; /* hope for best */ 201 202 break; 203 204 case KRB5_PADATA_AFS3_SALT: 205 salttype = KRB5_KDB_SALTTYPE_AFS3; 206 break; 207 208 default: 209 return -1; 210 } 211 212 sz = append_string(context, sp, "\t%u\t%u\t", salttype, 213 key->salt->salt.length); 214 if (sz == -1) return sz; 215 return append_hex(context, sp, 1, 1, &key->salt->salt); 216 } 217 218 static krb5_error_code 219 entry2string_int (krb5_context context, krb5_storage *sp, hdb_entry *ent) 220 { 221 char *p; 222 size_t i; 223 krb5_error_code ret; 224 225 /* --- principal */ 226 ret = krb5_unparse_name(context, ent->principal, &p); 227 if(ret) 228 return ret; 229 append_string(context, sp, "%s ", p); 230 free(p); 231 /* --- kvno */ 232 append_string(context, sp, "%d", ent->kvno); 233 /* --- keys */ 234 for(i = 0; i < ent->keys.len; i++){ 235 /* --- mkvno, keytype */ 236 if(ent->keys.val[i].mkvno) 237 append_string(context, sp, ":%d:%d:", 238 *ent->keys.val[i].mkvno, 239 ent->keys.val[i].key.keytype); 240 else 241 append_string(context, sp, "::%d:", 242 ent->keys.val[i].key.keytype); 243 /* --- keydata */ 244 append_hex(context, sp, 0, 0, &ent->keys.val[i].key.keyvalue); 245 append_string(context, sp, ":"); 246 /* --- salt */ 247 if(ent->keys.val[i].salt){ 248 append_string(context, sp, "%u/", ent->keys.val[i].salt->type); 249 append_hex(context, sp, 0, 0, &ent->keys.val[i].salt->salt); 250 }else 251 append_string(context, sp, "-"); 252 } 253 append_string(context, sp, " "); 254 /* --- created by */ 255 append_event(context, sp, &ent->created_by); 256 /* --- modified by */ 257 append_event(context, sp, ent->modified_by); 258 259 /* --- valid start */ 260 if(ent->valid_start) 261 append_string(context, sp, "%s ", time2str(*ent->valid_start)); 262 else 263 append_string(context, sp, "- "); 264 265 /* --- valid end */ 266 if(ent->valid_end) 267 append_string(context, sp, "%s ", time2str(*ent->valid_end)); 268 else 269 append_string(context, sp, "- "); 270 271 /* --- password ends */ 272 if(ent->pw_end) 273 append_string(context, sp, "%s ", time2str(*ent->pw_end)); 274 else 275 append_string(context, sp, "- "); 276 277 /* --- max life */ 278 if(ent->max_life) 279 append_string(context, sp, "%d ", *ent->max_life); 280 else 281 append_string(context, sp, "- "); 282 283 /* --- max renewable life */ 284 if(ent->max_renew) 285 append_string(context, sp, "%d ", *ent->max_renew); 286 else 287 append_string(context, sp, "- "); 288 289 /* --- flags */ 290 append_string(context, sp, "%d ", HDBFlags2int(ent->flags)); 291 292 /* --- generation number */ 293 if(ent->generation) { 294 append_string(context, sp, "%s:%d:%d ", time2str(ent->generation->time), 295 ent->generation->usec, 296 ent->generation->gen); 297 } else 298 append_string(context, sp, "- "); 299 300 /* --- extensions */ 301 if(ent->extensions && ent->extensions->len > 0) { 302 for(i = 0; i < ent->extensions->len; i++) { 303 void *d; 304 size_t size, sz = 0; 305 306 ASN1_MALLOC_ENCODE(HDB_extension, d, size, 307 &ent->extensions->val[i], &sz, ret); 308 if (ret) { 309 krb5_clear_error_message(context); 310 return ret; 311 } 312 if(size != sz) 313 krb5_abortx(context, "internal asn.1 encoder error"); 314 315 if (hex_encode(d, size, &p) < 0) { 316 free(d); 317 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 318 return ENOMEM; 319 } 320 321 free(d); 322 append_string(context, sp, "%s%s", p, 323 ent->extensions->len - 1 != i ? ":" : ""); 324 free(p); 325 } 326 } else 327 append_string(context, sp, "-"); 328 329 return 0; 330 } 331 332 #define KRB5_KDB_DISALLOW_POSTDATED 0x00000001 333 #define KRB5_KDB_DISALLOW_FORWARDABLE 0x00000002 334 #define KRB5_KDB_DISALLOW_TGT_BASED 0x00000004 335 #define KRB5_KDB_DISALLOW_RENEWABLE 0x00000008 336 #define KRB5_KDB_DISALLOW_PROXIABLE 0x00000010 337 #define KRB5_KDB_DISALLOW_DUP_SKEY 0x00000020 338 #define KRB5_KDB_DISALLOW_ALL_TIX 0x00000040 339 #define KRB5_KDB_REQUIRES_PRE_AUTH 0x00000080 340 #define KRB5_KDB_REQUIRES_HW_AUTH 0x00000100 341 #define KRB5_KDB_REQUIRES_PWCHANGE 0x00000200 342 #define KRB5_KDB_DISALLOW_SVR 0x00001000 343 #define KRB5_KDB_PWCHANGE_SERVICE 0x00002000 344 #define KRB5_KDB_SUPPORT_DESMD5 0x00004000 345 #define KRB5_KDB_NEW_PRINC 0x00008000 346 347 static int 348 flags_to_attr(HDBFlags flags) 349 { 350 int a = 0; 351 352 if (!flags.postdate) 353 a |= KRB5_KDB_DISALLOW_POSTDATED; 354 if (!flags.forwardable) 355 a |= KRB5_KDB_DISALLOW_FORWARDABLE; 356 if (flags.initial) 357 a |= KRB5_KDB_DISALLOW_TGT_BASED; 358 if (!flags.renewable) 359 a |= KRB5_KDB_DISALLOW_RENEWABLE; 360 if (!flags.proxiable) 361 a |= KRB5_KDB_DISALLOW_PROXIABLE; 362 if (flags.invalid) 363 a |= KRB5_KDB_DISALLOW_ALL_TIX; 364 if (flags.require_preauth) 365 a |= KRB5_KDB_REQUIRES_PRE_AUTH; 366 if (flags.require_hwauth) 367 a |= KRB5_KDB_REQUIRES_HW_AUTH; 368 if (!flags.server) 369 a |= KRB5_KDB_DISALLOW_SVR; 370 if (flags.change_pw) 371 a |= KRB5_KDB_PWCHANGE_SERVICE; 372 return a; 373 } 374 375 krb5_error_code 376 entry2mit_string_int(krb5_context context, krb5_storage *sp, hdb_entry *ent) 377 { 378 krb5_error_code ret; 379 ssize_t sz; 380 size_t i, k; 381 size_t num_tl_data = 0; 382 size_t num_key_data = 0; 383 char *p; 384 HDB_Ext_KeySet *hist_keys = NULL; 385 HDB_extension *extp; 386 time_t last_pw_chg = 0; 387 time_t exp = 0; 388 time_t pwexp = 0; 389 unsigned int max_life = 0; 390 unsigned int max_renew = 0; 391 392 if (ent->modified_by) 393 num_tl_data++; 394 395 ret = hdb_entry_get_pw_change_time(ent, &last_pw_chg); 396 if (ret) return ret; 397 if (last_pw_chg) 398 num_tl_data++; 399 400 extp = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys); 401 if (extp) 402 hist_keys = &extp->data.u.hist_keys; 403 404 for (i = 0; i < ent->keys.len;i++) { 405 if (ent->keys.val[i].key.keytype == ETYPE_DES_CBC_MD4 || 406 ent->keys.val[i].key.keytype == ETYPE_DES_CBC_MD5) 407 continue; 408 num_key_data++; 409 } 410 if (hist_keys) { 411 for (i = 0; i < hist_keys->len; i++) { 412 /* 413 * MIT uses the highest kvno as the current kvno instead of 414 * tracking kvno separately, so we can't dump keysets with kvno 415 * higher than the entry's kvno. 416 */ 417 if (hist_keys->val[i].kvno >= ent->kvno) 418 continue; 419 for (k = 0; k < hist_keys->val[i].keys.len; k++) { 420 if (ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD4 || 421 ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD5) 422 continue; 423 num_key_data++; 424 } 425 } 426 } 427 428 ret = krb5_unparse_name(context, ent->principal, &p); 429 if (ret) return ret; 430 sz = append_string(context, sp, "princ\t38\t%u\t%u\t%u\t0\t%s\t%d", 431 strlen(p), num_tl_data, num_key_data, p, 432 flags_to_attr(ent->flags)); 433 free(p); 434 if (sz == -1) return ENOMEM; 435 436 if (ent->max_life) 437 max_life = *ent->max_life; 438 if (ent->max_renew) 439 max_renew = *ent->max_renew; 440 if (ent->valid_end) 441 exp = *ent->valid_end; 442 if (ent->pw_end) 443 pwexp = *ent->pw_end; 444 445 sz = append_string(context, sp, "\t%u\t%u\t%u\t%u\t0\t0\t0", 446 max_life, max_renew, exp, pwexp); 447 if (sz == -1) return ENOMEM; 448 449 /* Dump TL data we know: last pw chg and modified_by */ 450 #define mit_KRB5_TL_LAST_PWD_CHANGE 1 451 #define mit_KRB5_TL_MOD_PRINC 2 452 if (last_pw_chg) { 453 krb5_data d; 454 time_t val; 455 unsigned char *ptr; 456 457 ptr = (unsigned char *)&last_pw_chg; 458 val = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); 459 d.data = &val; 460 d.length = sizeof (last_pw_chg); 461 sz = append_string(context, sp, "\t%u\t%u\t", 462 mit_KRB5_TL_LAST_PWD_CHANGE, d.length); 463 if (sz == -1) return ENOMEM; 464 sz = append_hex(context, sp, 1, 1, &d); 465 if (sz == -1) return ENOMEM; 466 } 467 if (ent->modified_by) { 468 krb5_data d; 469 unsigned int val; 470 size_t plen; 471 unsigned char *ptr; 472 char *modby_p; 473 474 ptr = (unsigned char *)&ent->modified_by->time; 475 val = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); 476 d.data = &val; 477 d.length = sizeof (ent->modified_by->time); 478 ret = krb5_unparse_name(context, ent->modified_by->principal, &modby_p); 479 if (ret) return ret; 480 plen = strlen(modby_p); 481 sz = append_string(context, sp, "\t%u\t%u\t", 482 mit_KRB5_TL_MOD_PRINC, 483 d.length + plen + 1 /* NULL counted */); 484 if (sz == -1) return ENOMEM; 485 sz = append_hex(context, sp, 1, 1, &d); 486 if (sz == -1) { 487 free(modby_p); 488 return ENOMEM; 489 } 490 d.data = modby_p; 491 d.length = plen + 1; 492 sz = append_hex(context, sp, 1, 1, &d); 493 free(modby_p); 494 if (sz == -1) return ENOMEM; 495 } 496 /* 497 * Dump keys (remembering to not include any with kvno higher than 498 * the entry's because MIT doesn't track entry kvno separately from 499 * the entry's keys -- max kvno is it) 500 */ 501 for (i = 0; i < ent->keys.len; i++) { 502 if (ent->keys.val[i].key.keytype == ETYPE_DES_CBC_MD4 || 503 ent->keys.val[i].key.keytype == ETYPE_DES_CBC_MD5) 504 continue; 505 sz = append_mit_key(context, sp, ent->principal, ent->kvno, 506 &ent->keys.val[i]); 507 if (sz == -1) return ENOMEM; 508 } 509 for (i = 0; hist_keys && i < ent->kvno; i++) { 510 size_t m; 511 512 /* dump historical keys */ 513 for (k = 0; k < hist_keys->len; k++) { 514 if (hist_keys->val[k].kvno != ent->kvno - i) 515 continue; 516 for (m = 0; m < hist_keys->val[k].keys.len; m++) { 517 if (ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD4 || 518 ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD5) 519 continue; 520 sz = append_mit_key(context, sp, ent->principal, 521 hist_keys->val[k].kvno, 522 &hist_keys->val[k].keys.val[m]); 523 if (sz == -1) return ENOMEM; 524 } 525 } 526 } 527 sz = append_string(context, sp, "\t-1;"); /* "extra data" */ 528 if (sz == -1) return ENOMEM; 529 return 0; 530 } 531 532 krb5_error_code 533 hdb_entry2string(krb5_context context, hdb_entry *ent, char **str) 534 { 535 krb5_error_code ret; 536 krb5_data data; 537 krb5_storage *sp; 538 539 sp = krb5_storage_emem(); 540 if (sp == NULL) { 541 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 542 return ENOMEM; 543 } 544 545 ret = entry2string_int(context, sp, ent); 546 if (ret) { 547 krb5_storage_free(sp); 548 return ret; 549 } 550 551 krb5_storage_write(sp, "\0", 1); 552 krb5_storage_to_data(sp, &data); 553 krb5_storage_free(sp); 554 *str = data.data; 555 return 0; 556 } 557 558 /* print a hdb_entry to (FILE*)data; suitable for hdb_foreach */ 559 560 krb5_error_code 561 hdb_print_entry(krb5_context context, HDB *db, hdb_entry_ex *entry, 562 void *data) 563 { 564 struct hdb_print_entry_arg *parg = data; 565 krb5_error_code ret; 566 krb5_storage *sp; 567 568 fflush(parg->out); 569 sp = krb5_storage_from_fd(fileno(parg->out)); 570 if (sp == NULL) { 571 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 572 return ENOMEM; 573 } 574 575 switch (parg->fmt) { 576 case HDB_DUMP_HEIMDAL: 577 ret = entry2string_int(context, sp, &entry->entry); 578 break; 579 case HDB_DUMP_MIT: 580 ret = entry2mit_string_int(context, sp, &entry->entry); 581 break; 582 default: 583 heim_abort("Only two dump formats supported: Heimdal and MIT"); 584 } 585 if (ret) { 586 krb5_storage_free(sp); 587 return ret; 588 } 589 590 krb5_storage_write(sp, "\n", 1); 591 krb5_storage_free(sp); 592 return 0; 593 } 594