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