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 "kadmin_locl.h" 35 #include <parse_units.h> 36 37 RCSID("$Id: util.c,v 1.39 2003/04/14 11:55:27 lha Exp $"); 38 39 /* 40 * util.c - functions for parsing, unparsing, and editing different 41 * types of data used in kadmin. 42 */ 43 44 static int 45 get_response(const char *prompt, const char *def, char *buf, size_t len); 46 47 /* 48 * attributes 49 */ 50 51 struct units kdb_attrs[] = { 52 { "new-princ", KRB5_KDB_NEW_PRINC }, 53 { "support-desmd5", KRB5_KDB_SUPPORT_DESMD5 }, 54 { "pwchange-service", KRB5_KDB_PWCHANGE_SERVICE }, 55 { "disallow-svr", KRB5_KDB_DISALLOW_SVR }, 56 { "requires-pw-change", KRB5_KDB_REQUIRES_PWCHANGE }, 57 { "requires-hw-auth", KRB5_KDB_REQUIRES_HW_AUTH }, 58 { "requires-pre-auth", KRB5_KDB_REQUIRES_PRE_AUTH }, 59 { "disallow-all-tix", KRB5_KDB_DISALLOW_ALL_TIX }, 60 { "disallow-dup-skey", KRB5_KDB_DISALLOW_DUP_SKEY }, 61 { "disallow-proxiable", KRB5_KDB_DISALLOW_PROXIABLE }, 62 { "disallow-renewable", KRB5_KDB_DISALLOW_RENEWABLE }, 63 { "disallow-tgt-based", KRB5_KDB_DISALLOW_TGT_BASED }, 64 { "disallow-forwardable", KRB5_KDB_DISALLOW_FORWARDABLE }, 65 { "disallow-postdated", KRB5_KDB_DISALLOW_POSTDATED }, 66 { NULL } 67 }; 68 69 /* 70 * convert the attributes in `attributes' into a printable string 71 * in `str, len' 72 */ 73 74 void 75 attributes2str(krb5_flags attributes, char *str, size_t len) 76 { 77 unparse_flags (attributes, kdb_attrs, str, len); 78 } 79 80 /* 81 * convert the string in `str' into attributes in `flags' 82 * return 0 if parsed ok, else -1. 83 */ 84 85 int 86 str2attributes(const char *str, krb5_flags *flags) 87 { 88 int res; 89 90 res = parse_flags (str, kdb_attrs, *flags); 91 if (res < 0) 92 return res; 93 else { 94 *flags = res; 95 return 0; 96 } 97 } 98 99 /* 100 * try to parse the string `resp' into attributes in `attr', also 101 * setting the `bit' in `mask' if attributes are given and valid. 102 */ 103 104 int 105 parse_attributes (const char *resp, krb5_flags *attr, int *mask, int bit) 106 { 107 krb5_flags tmp = *attr; 108 109 if (str2attributes(resp, &tmp) == 0) { 110 *attr = tmp; 111 if (mask) 112 *mask |= bit; 113 return 0; 114 } else if(*resp == '?') { 115 print_flags_table (kdb_attrs, stderr); 116 } else { 117 fprintf (stderr, "Unable to parse '%s'\n", resp); 118 } 119 return -1; 120 } 121 122 /* 123 * allow the user to edit the attributes in `attr', prompting with `prompt' 124 */ 125 126 int 127 edit_attributes (const char *prompt, krb5_flags *attr, int *mask, int bit) 128 { 129 char buf[1024], resp[1024]; 130 131 if (mask && (*mask & bit)) 132 return 0; 133 134 attributes2str(*attr, buf, sizeof(buf)); 135 for (;;) { 136 if(get_response("Attributes", buf, resp, sizeof(resp)) != 0) 137 return 1; 138 if (resp[0] == '\0') 139 break; 140 if (parse_attributes (resp, attr, mask, bit) == 0) 141 break; 142 } 143 return 0; 144 } 145 146 /* 147 * time_t 148 * the special value 0 means ``never'' 149 */ 150 151 /* 152 * Convert the time `t' to a string representation in `str' (of max 153 * size `len'). If include_time also include time, otherwise just 154 * date. 155 */ 156 157 void 158 time_t2str(time_t t, char *str, size_t len, int include_time) 159 { 160 if(t) { 161 if(include_time) 162 strftime(str, len, "%Y-%m-%d %H:%M:%S UTC", gmtime(&t)); 163 else 164 strftime(str, len, "%Y-%m-%d", gmtime(&t)); 165 } else 166 snprintf(str, len, "never"); 167 } 168 169 /* 170 * Convert the time representation in `str' to a time in `time'. 171 * Return 0 if succesful, else -1. 172 */ 173 174 int 175 str2time_t (const char *str, time_t *t) 176 { 177 const char *p; 178 struct tm tm, tm2; 179 180 memset (&tm, 0, sizeof (tm)); 181 182 if(strcasecmp(str, "never") == 0) { 183 *t = 0; 184 return 0; 185 } 186 187 if(strcasecmp(str, "now") == 0) { 188 *t = time(NULL); 189 return 0; 190 } 191 192 p = strptime (str, "%Y-%m-%d", &tm); 193 194 if (p == NULL) 195 return -1; 196 197 /* Do it on the end of the day */ 198 tm2.tm_hour = 23; 199 tm2.tm_min = 59; 200 tm2.tm_sec = 59; 201 202 if(strptime (p, "%H:%M:%S", &tm2) != NULL) { 203 tm.tm_hour = tm2.tm_hour; 204 tm.tm_min = tm2.tm_min; 205 tm.tm_sec = tm2.tm_sec; 206 } 207 208 *t = tm2time (tm, 0); 209 return 0; 210 } 211 212 /* 213 * try to parse the time in `resp' storing it in `value' 214 */ 215 216 int 217 parse_timet (const char *resp, krb5_timestamp *value, int *mask, int bit) 218 { 219 time_t tmp; 220 221 if (str2time_t(resp, &tmp) == 0) { 222 *value = tmp; 223 if(mask) 224 *mask |= bit; 225 return 0; 226 } else if(*resp == '?') { 227 printf ("Print date on format YYYY-mm-dd [hh:mm:ss]\n"); 228 } else { 229 fprintf (stderr, "Unable to parse time '%s'\n", resp); 230 } 231 return -1; 232 } 233 234 /* 235 * allow the user to edit the time in `value' 236 */ 237 238 int 239 edit_timet (const char *prompt, krb5_timestamp *value, int *mask, int bit) 240 { 241 char buf[1024], resp[1024]; 242 243 if (mask && (*mask & bit)) 244 return 0; 245 246 time_t2str (*value, buf, sizeof (buf), 0); 247 248 for (;;) { 249 if(get_response(prompt, buf, resp, sizeof(resp)) != 0) 250 return 1; 251 if (parse_timet (resp, value, mask, bit) == 0) 252 break; 253 } 254 return 0; 255 } 256 257 /* 258 * deltat 259 * the special value 0 means ``unlimited'' 260 */ 261 262 /* 263 * convert the delta_t value in `t' into a printable form in `str, len' 264 */ 265 266 void 267 deltat2str(unsigned t, char *str, size_t len) 268 { 269 if(t == 0 || t == INT_MAX) 270 snprintf(str, len, "unlimited"); 271 else 272 unparse_time(t, str, len); 273 } 274 275 /* 276 * parse the delta value in `str', storing result in `*delta' 277 * return 0 if ok, else -1 278 */ 279 280 int 281 str2deltat(const char *str, krb5_deltat *delta) 282 { 283 int res; 284 285 if(strcasecmp(str, "unlimited") == 0) { 286 *delta = 0; 287 return 0; 288 } 289 res = parse_time(str, "day"); 290 if (res < 0) 291 return res; 292 else { 293 *delta = res; 294 return 0; 295 } 296 } 297 298 /* 299 * try to parse the string in `resp' into a deltad in `value' 300 * `mask' will get the bit `bit' set if a value was given. 301 */ 302 303 int 304 parse_deltat (const char *resp, krb5_deltat *value, int *mask, int bit) 305 { 306 krb5_deltat tmp; 307 308 if (str2deltat(resp, &tmp) == 0) { 309 *value = tmp; 310 if (mask) 311 *mask |= bit; 312 return 0; 313 } else if(*resp == '?') { 314 print_time_table (stderr); 315 } else { 316 fprintf (stderr, "Unable to parse time '%s'\n", resp); 317 } 318 return -1; 319 } 320 321 /* 322 * allow the user to edit the deltat in `value' 323 */ 324 325 int 326 edit_deltat (const char *prompt, krb5_deltat *value, int *mask, int bit) 327 { 328 char buf[1024], resp[1024]; 329 330 if (mask && (*mask & bit)) 331 return 0; 332 333 deltat2str(*value, buf, sizeof(buf)); 334 for (;;) { 335 if(get_response(prompt, buf, resp, sizeof(resp)) != 0) 336 return 1; 337 if (parse_deltat (resp, value, mask, bit) == 0) 338 break; 339 } 340 return 0; 341 } 342 343 /* 344 * allow the user to edit `ent' 345 */ 346 347 void 348 set_defaults(kadm5_principal_ent_t ent, int *mask, 349 kadm5_principal_ent_t default_ent, int default_mask) 350 { 351 if (default_ent 352 && (default_mask & KADM5_MAX_LIFE) 353 && !(*mask & KADM5_MAX_LIFE)) 354 ent->max_life = default_ent->max_life; 355 356 if (default_ent 357 && (default_mask & KADM5_MAX_RLIFE) 358 && !(*mask & KADM5_MAX_RLIFE)) 359 ent->max_renewable_life = default_ent->max_renewable_life; 360 361 if (default_ent 362 && (default_mask & KADM5_PRINC_EXPIRE_TIME) 363 && !(*mask & KADM5_PRINC_EXPIRE_TIME)) 364 ent->princ_expire_time = default_ent->princ_expire_time; 365 366 if (default_ent 367 && (default_mask & KADM5_PW_EXPIRATION) 368 && !(*mask & KADM5_PW_EXPIRATION)) 369 ent->pw_expiration = default_ent->pw_expiration; 370 371 if (default_ent 372 && (default_mask & KADM5_ATTRIBUTES) 373 && !(*mask & KADM5_ATTRIBUTES)) 374 ent->attributes = default_ent->attributes & ~KRB5_KDB_DISALLOW_ALL_TIX; 375 } 376 377 int 378 edit_entry(kadm5_principal_ent_t ent, int *mask, 379 kadm5_principal_ent_t default_ent, int default_mask) 380 { 381 382 set_defaults(ent, mask, default_ent, default_mask); 383 384 if(edit_deltat ("Max ticket life", &ent->max_life, mask, 385 KADM5_MAX_LIFE) != 0) 386 return 1; 387 388 if(edit_deltat ("Max renewable life", &ent->max_renewable_life, mask, 389 KADM5_MAX_RLIFE) != 0) 390 return 1; 391 392 if(edit_timet ("Principal expiration time", &ent->princ_expire_time, mask, 393 KADM5_PRINC_EXPIRE_TIME) != 0) 394 return 1; 395 396 if(edit_timet ("Password expiration time", &ent->pw_expiration, mask, 397 KADM5_PW_EXPIRATION) != 0) 398 return 1; 399 400 if(edit_attributes ("Attributes", &ent->attributes, mask, 401 KADM5_ATTRIBUTES) != 0) 402 return 1; 403 404 return 0; 405 } 406 407 /* 408 * Parse the arguments, set the fields in `ent' and the `mask' for the 409 * entries having been set. 410 * Return 1 on failure and 0 on success. 411 */ 412 413 int 414 set_entry(krb5_context context, 415 kadm5_principal_ent_t ent, 416 int *mask, 417 const char *max_ticket_life, 418 const char *max_renewable_life, 419 const char *expiration, 420 const char *pw_expiration, 421 const char *attributes) 422 { 423 if (max_ticket_life != NULL) { 424 if (parse_deltat (max_ticket_life, &ent->max_life, 425 mask, KADM5_MAX_LIFE)) { 426 krb5_warnx (context, "unable to parse `%s'", max_ticket_life); 427 return 1; 428 } 429 } 430 if (max_renewable_life != NULL) { 431 if (parse_deltat (max_renewable_life, &ent->max_renewable_life, 432 mask, KADM5_MAX_RLIFE)) { 433 krb5_warnx (context, "unable to parse `%s'", max_renewable_life); 434 return 1; 435 } 436 } 437 438 if (expiration) { 439 if (parse_timet (expiration, &ent->princ_expire_time, 440 mask, KADM5_PRINC_EXPIRE_TIME)) { 441 krb5_warnx (context, "unable to parse `%s'", expiration); 442 return 1; 443 } 444 } 445 if (pw_expiration) { 446 if (parse_timet (pw_expiration, &ent->pw_expiration, 447 mask, KADM5_PW_EXPIRATION)) { 448 krb5_warnx (context, "unable to parse `%s'", pw_expiration); 449 return 1; 450 } 451 } 452 if (attributes != NULL) { 453 if (parse_attributes (attributes, &ent->attributes, 454 mask, KADM5_ATTRIBUTES)) { 455 krb5_warnx (context, "unable to parse `%s'", attributes); 456 return 1; 457 } 458 } 459 return 0; 460 } 461 462 /* 463 * Does `string' contain any globing characters? 464 */ 465 466 static int 467 is_expression(const char *string) 468 { 469 const char *p; 470 int quote = 0; 471 472 for(p = string; *p; p++) { 473 if(quote) { 474 quote = 0; 475 continue; 476 } 477 if(*p == '\\') 478 quote++; 479 else if(strchr("[]*?", *p) != NULL) 480 return 1; 481 } 482 return 0; 483 } 484 485 /* loop over all principals matching exp */ 486 int 487 foreach_principal(const char *exp, 488 int (*func)(krb5_principal, void*), 489 const char *funcname, 490 void *data) 491 { 492 char **princs; 493 int num_princs; 494 int i; 495 krb5_error_code ret; 496 krb5_principal princ_ent; 497 int is_expr; 498 499 /* if this isn't an expression, there is no point in wading 500 through the whole database looking for matches */ 501 is_expr = is_expression(exp); 502 if(is_expr) 503 ret = kadm5_get_principals(kadm_handle, exp, &princs, &num_princs); 504 if(!is_expr || ret == KADM5_AUTH_LIST) { 505 /* we might be able to perform the requested opreration even 506 if we're not allowed to list principals */ 507 num_princs = 1; 508 princs = malloc(sizeof(*princs)); 509 if(princs == NULL) 510 return ENOMEM; 511 princs[0] = strdup(exp); 512 if(princs[0] == NULL){ 513 free(princs); 514 return ENOMEM; 515 } 516 } else if(ret) { 517 krb5_warn(context, ret, "kadm5_get_principals"); 518 return ret; 519 } 520 for(i = 0; i < num_princs; i++) { 521 ret = krb5_parse_name(context, princs[i], &princ_ent); 522 if(ret){ 523 krb5_warn(context, ret, "krb5_parse_name(%s)", princs[i]); 524 continue; 525 } 526 ret = (*func)(princ_ent, data); 527 if(ret) 528 krb5_warn(context, ret, "%s %s", funcname, princs[i]); 529 krb5_free_principal(context, princ_ent); 530 } 531 kadm5_free_name_list(kadm_handle, princs, &num_princs); 532 return 0; 533 } 534 535 /* 536 * prompt with `prompt' and default value `def', and store the reply 537 * in `buf, len' 538 */ 539 540 #include <setjmp.h> 541 542 static jmp_buf jmpbuf; 543 544 static void 545 interrupt(int sig) 546 { 547 longjmp(jmpbuf, 1); 548 } 549 550 static int 551 get_response(const char *prompt, const char *def, char *buf, size_t len) 552 { 553 char *p; 554 void (*osig)(int); 555 556 osig = signal(SIGINT, interrupt); 557 if(setjmp(jmpbuf)) { 558 signal(SIGINT, osig); 559 printf("\n"); 560 return 1; 561 } 562 563 printf("%s [%s]:", prompt, def); 564 if(fgets(buf, len, stdin) == NULL) { 565 int save_errno = errno; 566 if(ferror(stdin)) 567 krb5_err(context, 1, save_errno, "<stdin>"); 568 signal(SIGINT, osig); 569 return 1; 570 } 571 p = strchr(buf, '\n'); 572 if(p) 573 *p = '\0'; 574 if(strcmp(buf, "") == 0) 575 strlcpy(buf, def, len); 576 signal(SIGINT, osig); 577 return 0; 578 } 579 580 /* 581 * return [0, 16) or -1 582 */ 583 584 static int 585 hex2n (char c) 586 { 587 static char hexdigits[] = "0123456789abcdef"; 588 const char *p; 589 590 p = strchr (hexdigits, tolower((unsigned char)c)); 591 if (p == NULL) 592 return -1; 593 else 594 return p - hexdigits; 595 } 596 597 /* 598 * convert a key in a readable format into a keyblock. 599 * return 0 iff succesful, otherwise `err' should point to an error message 600 */ 601 602 int 603 parse_des_key (const char *key_string, krb5_key_data *key_data, 604 const char **err) 605 { 606 const char *p = key_string; 607 unsigned char bits[8]; 608 int i; 609 610 if (strlen (key_string) != 16) { 611 *err = "bad length, should be 16 for DES key"; 612 return 1; 613 } 614 for (i = 0; i < 8; ++i) { 615 int d1, d2; 616 617 d1 = hex2n(p[2 * i]); 618 d2 = hex2n(p[2 * i + 1]); 619 if (d1 < 0 || d2 < 0) { 620 *err = "non-hex character"; 621 return 1; 622 } 623 bits[i] = (d1 << 4) | d2; 624 } 625 for (i = 0; i < 3; ++i) { 626 key_data[i].key_data_ver = 2; 627 key_data[i].key_data_kvno = 0; 628 /* key */ 629 key_data[i].key_data_type[0] = ETYPE_DES_CBC_CRC; 630 key_data[i].key_data_length[0] = 8; 631 key_data[i].key_data_contents[0] = malloc(8); 632 memcpy (key_data[i].key_data_contents[0], bits, 8); 633 /* salt */ 634 key_data[i].key_data_type[1] = KRB5_PW_SALT; 635 key_data[i].key_data_length[1] = 0; 636 key_data[i].key_data_contents[1] = NULL; 637 } 638 key_data[0].key_data_type[0] = ETYPE_DES_CBC_MD5; 639 key_data[1].key_data_type[0] = ETYPE_DES_CBC_MD4; 640 return 0; 641 } 642