1 /* 2 * Copyright (c) 1997, 1998, 1999 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.23 1999/12/02 17:04:58 joda 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 (resp[0] == '\0') 107 return 0; 108 else if (str2attributes(resp, &tmp) == 0) { 109 *attr = tmp; 110 if (mask) 111 *mask |= bit; 112 return 0; 113 } else if(*resp == '?') { 114 print_flags_table (kdb_attrs, stderr); 115 } else { 116 fprintf (stderr, "Unable to parse '%s'\n", resp); 117 } 118 return -1; 119 } 120 121 /* 122 * allow the user to edit the attributes in `attr', prompting with `prompt' 123 */ 124 125 int 126 edit_attributes (const char *prompt, krb5_flags *attr, int *mask, int bit) 127 { 128 char buf[1024], resp[1024]; 129 130 if (mask && (*mask & bit)) 131 return 0; 132 133 attributes2str(*attr, buf, sizeof(buf)); 134 for (;;) { 135 get_response("Attributes", buf, resp, sizeof(resp)); 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 *time) 172 { 173 const char *p; 174 struct tm tm; 175 176 memset (&tm, 0, sizeof (tm)); 177 178 if(strcasecmp(str, "never") == 0) { 179 *time = 0; 180 return 0; 181 } 182 183 p = strptime (str, "%Y-%m-%d", &tm); 184 185 if (p == NULL) 186 return -1; 187 188 /* Do it on the end of the day */ 189 tm.tm_hour = 23; 190 tm.tm_min = 59; 191 tm.tm_sec = 59; 192 193 strptime (p, "%H:%M:%S", &tm); 194 195 *time = tm2time (tm, 0); 196 return 0; 197 } 198 199 /* 200 * try to parse the time in `resp' storing it in `value' 201 */ 202 203 int 204 parse_timet (const char *resp, krb5_timestamp *value, int *mask, int bit) 205 { 206 time_t tmp; 207 208 if (str2time_t(resp, &tmp) == 0) { 209 *value = tmp; 210 if(mask) 211 *mask |= bit; 212 return 0; 213 } else if(*resp == '?') { 214 printf ("Print date on format YYYY-mm-dd [hh:mm:ss]\n"); 215 } else { 216 fprintf (stderr, "Unable to parse time '%s'\n", resp); 217 } 218 return -1; 219 } 220 221 /* 222 * allow the user to edit the time in `value' 223 */ 224 225 int 226 edit_timet (const char *prompt, krb5_timestamp *value, int *mask, int bit) 227 { 228 char buf[1024], resp[1024]; 229 230 if (mask && (*mask & bit)) 231 return 0; 232 233 time_t2str (*value, buf, sizeof (buf), 0); 234 235 for (;;) { 236 get_response(prompt, buf, resp, sizeof(resp)); 237 if (parse_timet (resp, value, mask, bit) == 0) 238 break; 239 } 240 return 0; 241 } 242 243 /* 244 * deltat 245 * the special value 0 means ``unlimited'' 246 */ 247 248 /* 249 * convert the delta_t value in `t' into a printable form in `str, len' 250 */ 251 252 void 253 deltat2str(unsigned t, char *str, size_t len) 254 { 255 if(t) 256 unparse_time(t, str, len); 257 else 258 snprintf(str, len, "unlimited"); 259 } 260 261 /* 262 * parse the delta value in `str', storing result in `*delta' 263 * return 0 if ok, else -1 264 */ 265 266 int 267 str2deltat(const char *str, krb5_deltat *delta) 268 { 269 int res; 270 271 if(strcasecmp(str, "unlimited") == 0) { 272 *delta = 0; 273 return 0; 274 } 275 res = parse_time(str, "day"); 276 if (res < 0) 277 return res; 278 else { 279 *delta = res; 280 return 0; 281 } 282 } 283 284 /* 285 * try to parse the string in `resp' into a deltad in `value' 286 * `mask' will get the bit `bit' set if a value was given. 287 */ 288 289 int 290 parse_deltat (const char *resp, krb5_deltat *value, int *mask, int bit) 291 { 292 krb5_deltat tmp; 293 294 if (str2deltat(resp, &tmp) == 0) { 295 *value = tmp; 296 if (mask) 297 *mask |= bit; 298 return 0; 299 } else if(*resp == '?') { 300 print_time_table (stderr); 301 } else { 302 fprintf (stderr, "Unable to parse time '%s'\n", resp); 303 } 304 return -1; 305 } 306 307 /* 308 * allow the user to edit the deltat in `value' 309 */ 310 311 int 312 edit_deltat (const char *prompt, krb5_deltat *value, int *mask, int bit) 313 { 314 char buf[1024], resp[1024]; 315 316 if (mask && (*mask & bit)) 317 return 0; 318 319 deltat2str(*value, buf, sizeof(buf)); 320 for (;;) { 321 get_response(prompt, buf, resp, sizeof(resp)); 322 if (parse_deltat (resp, value, mask, bit) == 0) 323 break; 324 } 325 return 0; 326 } 327 328 /* 329 * allow the user to edit `ent' 330 */ 331 332 int 333 edit_entry(kadm5_principal_ent_t ent, int *mask, 334 kadm5_principal_ent_t default_ent, int default_mask) 335 { 336 if (default_ent && (default_mask & KADM5_MAX_LIFE)) 337 ent->max_life = default_ent->max_life; 338 edit_deltat ("Max ticket life", &ent->max_life, mask, 339 KADM5_MAX_LIFE); 340 341 if (default_ent && (default_mask & KADM5_MAX_RLIFE)) 342 ent->max_renewable_life = default_ent->max_renewable_life; 343 edit_deltat ("Max renewable life", &ent->max_renewable_life, mask, 344 KADM5_MAX_RLIFE); 345 346 if (default_ent && (default_mask & KADM5_PRINC_EXPIRE_TIME)) 347 ent->princ_expire_time = default_ent->princ_expire_time; 348 edit_timet ("Principal expiration time", &ent->princ_expire_time, mask, 349 KADM5_PRINC_EXPIRE_TIME); 350 351 if (default_ent && (default_mask & KADM5_PW_EXPIRATION)) 352 ent->pw_expiration = default_ent->pw_expiration; 353 edit_timet ("Password expiration time", &ent->pw_expiration, mask, 354 KADM5_PW_EXPIRATION); 355 356 if (default_ent && (default_mask & KADM5_ATTRIBUTES)) 357 ent->attributes = default_ent->attributes & ~KRB5_KDB_DISALLOW_ALL_TIX; 358 edit_attributes ("Attributes", &ent->attributes, mask, 359 KADM5_ATTRIBUTES); 360 return 0; 361 } 362 363 /* 364 * Parse the arguments, set the fields in `ent' and the `mask' for the 365 * entries having been set. 366 * Return 1 on failure and 0 on success. 367 */ 368 369 int 370 set_entry(krb5_context context, 371 kadm5_principal_ent_t ent, 372 int *mask, 373 const char *max_ticket_life, 374 const char *max_renewable_life, 375 const char *expiration, 376 const char *pw_expiration, 377 const char *attributes) 378 { 379 if (max_ticket_life != NULL) { 380 if (parse_deltat (max_ticket_life, &ent->max_life, 381 mask, KADM5_MAX_LIFE)) { 382 krb5_warnx (context, "unable to parse `%s'", max_ticket_life); 383 return 1; 384 } 385 } 386 if (max_renewable_life != NULL) { 387 if (parse_deltat (max_renewable_life, &ent->max_renewable_life, 388 mask, KADM5_MAX_RLIFE)) { 389 krb5_warnx (context, "unable to parse `%s'", max_renewable_life); 390 return 1; 391 } 392 } 393 394 if (expiration) { 395 if (parse_timet (expiration, &ent->princ_expire_time, 396 mask, KADM5_PRINC_EXPIRE_TIME)) { 397 krb5_warnx (context, "unable to parse `%s'", expiration); 398 return 1; 399 } 400 } 401 if (pw_expiration) { 402 if (parse_timet (pw_expiration, &ent->pw_expiration, 403 mask, KADM5_PW_EXPIRATION)) { 404 krb5_warnx (context, "unable to parse `%s'", pw_expiration); 405 return 1; 406 } 407 } 408 if (attributes != NULL) { 409 if (parse_attributes (attributes, &ent->attributes, 410 mask, KADM5_ATTRIBUTES)) { 411 krb5_warnx (context, "unable to parse `%s'", attributes); 412 return 1; 413 } 414 } 415 return 0; 416 } 417 418 /* 419 * Does `string' contain any globing characters? 420 */ 421 422 static int 423 is_expression(const char *string) 424 { 425 const char *p; 426 int quote = 0; 427 428 for(p = string; *p; p++) { 429 if(quote) { 430 quote = 0; 431 continue; 432 } 433 if(*p == '\\') 434 quote++; 435 else if(strchr("[]*?", *p) != NULL) 436 return 1; 437 } 438 return 0; 439 } 440 441 /* loop over all principals matching exp */ 442 int 443 foreach_principal(const char *exp, 444 int (*func)(krb5_principal, void*), 445 void *data) 446 { 447 char **princs; 448 int num_princs; 449 int i; 450 krb5_error_code ret; 451 krb5_principal princ_ent; 452 int is_expr; 453 454 /* if this isn't an expression, there is no point in wading 455 through the whole database looking for matches */ 456 is_expr = is_expression(exp); 457 if(is_expr) 458 ret = kadm5_get_principals(kadm_handle, exp, &princs, &num_princs); 459 if(!is_expr || ret == KADM5_AUTH_LIST) { 460 /* we might be able to perform the requested opreration even 461 if we're not allowed to list principals */ 462 num_princs = 1; 463 princs = malloc(sizeof(*princs)); 464 if(princs == NULL) 465 return ENOMEM; 466 princs[0] = strdup(exp); 467 if(princs[0] == NULL){ 468 free(princs); 469 return ENOMEM; 470 } 471 } else if(ret) { 472 krb5_warn(context, ret, "kadm5_get_principals"); 473 return ret; 474 } 475 for(i = 0; i < num_princs; i++) { 476 ret = krb5_parse_name(context, princs[i], &princ_ent); 477 if(ret){ 478 krb5_warn(context, ret, "krb5_parse_name(%s)", princs[i]); 479 continue; 480 } 481 ret = (*func)(princ_ent, data); 482 if(ret) { 483 char *tmp; 484 krb5_error_code ret2; 485 486 ret2 = krb5_unparse_name(context, princ_ent, &tmp); 487 if(ret2) { 488 krb5_warn(context, ret2, "krb5_unparse_name"); 489 krb5_warn(context, ret, "<unknown principal>"); 490 } else { 491 krb5_warn(context, ret, "%s", tmp); 492 free(tmp); 493 } 494 } 495 krb5_free_principal(context, princ_ent); 496 } 497 kadm5_free_name_list(kadm_handle, princs, &num_princs); 498 return 0; 499 } 500 501 /* 502 * prompt with `prompt' and default value `def', and store the reply 503 * in `buf, len' 504 */ 505 506 void 507 get_response(const char *prompt, const char *def, char *buf, size_t len) 508 { 509 char *p; 510 511 printf("%s [%s]:", prompt, def); 512 if(fgets(buf, len, stdin) == NULL) 513 *buf = '\0'; 514 p = strchr(buf, '\n'); 515 if(p) 516 *p = '\0'; 517 if(strcmp(buf, "") == 0) 518 strncpy(buf, def, len); 519 buf[len-1] = 0; 520 } 521