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.32 2001/05/14 06:17:20 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 const char *funcname, 465 void *data) 466 { 467 char **princs; 468 int num_princs; 469 int i; 470 krb5_error_code ret; 471 krb5_principal princ_ent; 472 int is_expr; 473 474 /* if this isn't an expression, there is no point in wading 475 through the whole database looking for matches */ 476 is_expr = is_expression(exp); 477 if(is_expr) 478 ret = kadm5_get_principals(kadm_handle, exp, &princs, &num_princs); 479 if(!is_expr || ret == KADM5_AUTH_LIST) { 480 /* we might be able to perform the requested opreration even 481 if we're not allowed to list principals */ 482 num_princs = 1; 483 princs = malloc(sizeof(*princs)); 484 if(princs == NULL) 485 return ENOMEM; 486 princs[0] = strdup(exp); 487 if(princs[0] == NULL){ 488 free(princs); 489 return ENOMEM; 490 } 491 } else if(ret) { 492 krb5_warn(context, ret, "kadm5_get_principals"); 493 return ret; 494 } 495 for(i = 0; i < num_princs; i++) { 496 ret = krb5_parse_name(context, princs[i], &princ_ent); 497 if(ret){ 498 krb5_warn(context, ret, "krb5_parse_name(%s)", princs[i]); 499 continue; 500 } 501 ret = (*func)(princ_ent, data); 502 if(ret) 503 krb5_warn(context, ret, "%s %s", funcname, princs[i]); 504 krb5_free_principal(context, princ_ent); 505 } 506 kadm5_free_name_list(kadm_handle, princs, &num_princs); 507 return 0; 508 } 509 510 /* 511 * prompt with `prompt' and default value `def', and store the reply 512 * in `buf, len' 513 */ 514 515 void 516 get_response(const char *prompt, const char *def, char *buf, size_t len) 517 { 518 char *p; 519 520 printf("%s [%s]:", prompt, def); 521 if(fgets(buf, len, stdin) == NULL) 522 *buf = '\0'; 523 p = strchr(buf, '\n'); 524 if(p) 525 *p = '\0'; 526 if(strcmp(buf, "") == 0) 527 strncpy(buf, def, len); 528 buf[len-1] = 0; 529 } 530 531 /* 532 * return [0, 16) or -1 533 */ 534 535 static int 536 hex2n (char c) 537 { 538 static char hexdigits[] = "0123456789abcdef"; 539 const char *p; 540 541 p = strchr (hexdigits, tolower((int)c)); 542 if (p == NULL) 543 return -1; 544 else 545 return p - hexdigits; 546 } 547 548 /* 549 * convert a key in a readable format into a keyblock. 550 * return 0 iff succesful, otherwise `err' should point to an error message 551 */ 552 553 int 554 parse_des_key (const char *key_string, krb5_key_data *key_data, 555 const char **err) 556 { 557 const char *p = key_string; 558 unsigned char bits[8]; 559 int i; 560 561 if (strlen (key_string) != 16) { 562 *err = "bad length, should be 16 for DES key"; 563 return 1; 564 } 565 for (i = 0; i < 8; ++i) { 566 int d1, d2; 567 568 d1 = hex2n(p[2 * i]); 569 d2 = hex2n(p[2 * i + 1]); 570 if (d1 < 0 || d2 < 0) { 571 *err = "non-hex character"; 572 return 1; 573 } 574 bits[i] = (d1 << 4) | d2; 575 } 576 for (i = 0; i < 3; ++i) { 577 key_data[i].key_data_ver = 2; 578 key_data[i].key_data_kvno = 0; 579 /* key */ 580 key_data[i].key_data_type[0] = ETYPE_DES_CBC_CRC; 581 key_data[i].key_data_length[0] = 8; 582 key_data[i].key_data_contents[0] = malloc(8); 583 memcpy (key_data[i].key_data_contents[0], bits, 8); 584 /* salt */ 585 key_data[i].key_data_type[1] = KRB5_PW_SALT; 586 key_data[i].key_data_length[1] = 0; 587 key_data[i].key_data_contents[1] = NULL; 588 } 589 key_data[0].key_data_type[0] = ETYPE_DES_CBC_MD5; 590 key_data[1].key_data_type[0] = ETYPE_DES_CBC_MD4; 591 return 0; 592 } 593