1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. 4 * 5 * $Id$ 6 * $Source$ 7 */ 8 9 /* 10 * Copyright (C) 1998 by the FundsXpress, INC. 11 * 12 * All rights reserved. 13 * 14 * Export of this software from the United States of America may require 15 * a specific license from the United States Government. It is the 16 * responsibility of any person or organization contemplating export to 17 * obtain such a license before exporting. 18 * 19 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 20 * distribute this software and its documentation for any purpose and 21 * without fee is hereby granted, provided that the above copyright 22 * notice appear in all copies and that both that copyright notice and 23 * this permission notice appear in supporting documentation, and that 24 * the name of FundsXpress. not be used in advertising or publicity pertaining 25 * to distribution of the software without specific, written prior 26 * permission. FundsXpress makes no representations about the suitability of 27 * this software for any purpose. It is provided "as is" without express 28 * or implied warranty. 29 * 30 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 31 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 32 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 33 */ 34 35 #include "k5-int.h" 36 #include <kadm5/admin.h> 37 #include <adm_proto.h> 38 #include "kadmin.h" 39 40 static void add_principal(void *lhandle, char *keytab_str, krb5_keytab keytab, 41 krb5_boolean keepold, 42 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, 43 char *princ_str); 44 static void remove_principal(char *keytab_str, krb5_keytab keytab, 45 char *princ_str, char *kvno_str); 46 static char *etype_string(krb5_enctype enctype); 47 48 static int quiet; 49 50 static int norandkey; 51 52 static void 53 add_usage(void) 54 { 55 fprintf(stderr, _("Usage: ktadd [-k[eytab] keytab] [-q] [-e keysaltlist] " 56 "[-norandkey] [principal | -glob princ-exp] [...]\n")); 57 } 58 59 static void 60 rem_usage(void) 61 { 62 fprintf(stderr, _("Usage: ktremove [-k[eytab] keytab] [-q] principal " 63 "[kvno|\"all\"|\"old\"]\n")); 64 } 65 66 static int 67 process_keytab(krb5_context my_context, char **keytab_str, 68 krb5_keytab *keytab) 69 { 70 int code; 71 char *name = *keytab_str; 72 73 if (name == NULL) { 74 name = malloc(BUFSIZ); 75 if (!name) { 76 com_err(whoami, ENOMEM, _("while creating keytab name")); 77 return 1; 78 } 79 code = krb5_kt_default(my_context, keytab); 80 if (code != 0) { 81 com_err(whoami, code, _("while opening default keytab")); 82 free(name); 83 return 1; 84 } 85 code = krb5_kt_get_name(my_context, *keytab, name, BUFSIZ); 86 if (code != 0) { 87 com_err(whoami, code, _("while getting keytab name")); 88 free(name); 89 return 1; 90 } 91 } else { 92 if (strchr(name, ':') != NULL) 93 name = strdup(name); 94 else if (asprintf(&name, "WRFILE:%s", name) < 0) 95 name = NULL; 96 if (name == NULL) { 97 com_err(whoami, ENOMEM, _("while creating keytab name")); 98 return 1; 99 } 100 101 code = krb5_kt_resolve(my_context, name, keytab); 102 if (code != 0) { 103 com_err(whoami, code, _("while resolving keytab %s"), name); 104 free(name); 105 return 1; 106 } 107 } 108 109 *keytab_str = name; 110 return 0; 111 } 112 113 void 114 kadmin_keytab_add(int argc, char **argv, int sci_idx, void *info_ptr) 115 { 116 krb5_keytab keytab = 0; 117 char *keytab_str = NULL, **princs; 118 int code, num, i; 119 krb5_error_code retval; 120 int n_ks_tuple = 0; 121 krb5_boolean keepold = FALSE; 122 krb5_key_salt_tuple *ks_tuple = NULL; 123 124 argc--; argv++; 125 quiet = 0; 126 norandkey = 0; 127 while (argc) { 128 if (strncmp(*argv, "-k", 2) == 0) { 129 argc--; argv++; 130 if (!argc || keytab_str) { 131 add_usage(); 132 return; 133 } 134 keytab_str = *argv; 135 } else if (strcmp(*argv, "-q") == 0) { 136 quiet++; 137 } else if (strcmp(*argv, "-norandkey") == 0) { 138 norandkey++; 139 } else if (strcmp(*argv, "-e") == 0) { 140 argc--; 141 if (argc < 1) { 142 add_usage(); 143 return; 144 } 145 retval = krb5_string_to_keysalts(*++argv, NULL, NULL, 0, 146 &ks_tuple, &n_ks_tuple); 147 if (retval) { 148 com_err("ktadd", retval, _("while parsing keysalts %s"), 149 *argv); 150 151 return; 152 } 153 } else 154 break; 155 argc--; argv++; 156 } 157 158 if (argc == 0) { 159 add_usage(); 160 return; 161 } 162 163 if (norandkey && ks_tuple) { 164 fprintf(stderr, 165 _("cannot specify keysaltlist when not changing key\n")); 166 return; 167 } 168 169 if (process_keytab(context, &keytab_str, &keytab)) 170 return; 171 172 while (*argv) { 173 if (strcmp(*argv, "-glob") == 0) { 174 if (*++argv == NULL) { 175 add_usage(); 176 break; 177 } 178 179 code = kadm5_get_principals(handle, *argv, &princs, &num); 180 if (code) { 181 com_err(whoami, code, _("while expanding expression \"%s\"."), 182 *argv); 183 argv++; 184 continue; 185 } 186 187 for (i = 0; i < num; i++) 188 add_principal(handle, keytab_str, keytab, keepold, 189 n_ks_tuple, ks_tuple, princs[i]); 190 kadm5_free_name_list(handle, princs, num); 191 } else { 192 add_principal(handle, keytab_str, keytab, keepold, 193 n_ks_tuple, ks_tuple, *argv); 194 argv++; 195 } 196 } 197 198 code = krb5_kt_close(context, keytab); 199 if (code != 0) 200 com_err(whoami, code, _("while closing keytab")); 201 202 free(keytab_str); 203 } 204 205 void 206 kadmin_keytab_remove(int argc, char **argv, int sci_idx, void *info_ptr) 207 { 208 krb5_keytab keytab = 0; 209 char *keytab_str = NULL; 210 int code; 211 212 argc--; argv++; 213 quiet = 0; 214 while (argc) { 215 if (strncmp(*argv, "-k", 2) == 0) { 216 argc--; argv++; 217 if (!argc || keytab_str) { 218 rem_usage(); 219 return; 220 } 221 keytab_str = *argv; 222 } else if (strcmp(*argv, "-q") == 0) { 223 quiet++; 224 } else 225 break; 226 argc--; argv++; 227 } 228 229 if (argc != 1 && argc != 2) { 230 rem_usage(); 231 return; 232 } 233 if (process_keytab(context, &keytab_str, &keytab)) 234 return; 235 236 remove_principal(keytab_str, keytab, argv[0], argv[1]); 237 238 code = krb5_kt_close(context, keytab); 239 if (code != 0) 240 com_err(whoami, code, _("while closing keytab")); 241 242 free(keytab_str); 243 } 244 245 /* Generate new random keys for princ, and convert them into a kadm5_key_data 246 * array (with no salt information). */ 247 static krb5_error_code 248 fetch_new_keys(void *lhandle, krb5_principal princ, krb5_boolean keepold, 249 int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, 250 kadm5_key_data **key_data_out, int *nkeys_out) 251 { 252 krb5_error_code code; 253 kadm5_key_data *key_data; 254 kadm5_principal_ent_rec princ_rec; 255 krb5_keyblock *keys = NULL; 256 int i, nkeys = 0; 257 258 *key_data_out = NULL; 259 *nkeys_out = 0; 260 memset(&princ_rec, 0, sizeof(princ_rec)); 261 262 /* Generate new random keys. */ 263 code = randkey_princ(lhandle, princ, keepold, n_ks_tuple, ks_tuple, 264 &keys, &nkeys); 265 if (code) 266 goto cleanup; 267 268 /* Get the principal entry to find the kvno of the new keys. (This is not 269 * atomic, but randkey doesn't report the new kvno.) */ 270 code = kadm5_get_principal(lhandle, princ, &princ_rec, 271 KADM5_PRINCIPAL_NORMAL_MASK); 272 if (code) 273 goto cleanup; 274 275 key_data = k5calloc(nkeys, sizeof(*key_data), &code); 276 if (key_data == NULL) 277 goto cleanup; 278 279 /* Transfer the keyblocks and free the container array. */ 280 for (i = 0; i < nkeys; i++) { 281 key_data[i].key = keys[i]; 282 key_data[i].kvno = princ_rec.kvno; 283 } 284 *key_data_out = key_data; 285 *nkeys_out = nkeys; 286 free(keys); 287 keys = NULL; 288 nkeys = 0; 289 290 cleanup: 291 for (i = 0; i < nkeys; i++) 292 krb5_free_keyblock_contents(context, &keys[i]); 293 free(keys); 294 kadm5_free_principal_ent(lhandle, &princ_rec); 295 return code; 296 } 297 298 static void 299 add_principal(void *lhandle, char *keytab_str, krb5_keytab keytab, 300 krb5_boolean keepold, int n_ks_tuple, 301 krb5_key_salt_tuple *ks_tuple, char *princ_str) 302 { 303 krb5_principal princ = NULL; 304 krb5_keytab_entry new_entry; 305 kadm5_key_data *key_data; 306 int code, nkeys, i; 307 308 princ = NULL; 309 key_data = NULL; 310 nkeys = 0; 311 312 code = krb5_parse_name(context, princ_str, &princ); 313 if (code != 0) { 314 com_err(whoami, code, _("while parsing -add principal name %s"), 315 princ_str); 316 goto cleanup; 317 } 318 319 if (norandkey) { 320 code = kadm5_get_principal_keys(handle, princ, 0, &key_data, &nkeys); 321 } else { 322 code = fetch_new_keys(handle, princ, keepold, n_ks_tuple, ks_tuple, 323 &key_data, &nkeys); 324 } 325 326 if (code != 0) { 327 if (code == KADM5_UNK_PRINC) { 328 fprintf(stderr, _("%s: Principal %s does not exist.\n"), 329 whoami, princ_str); 330 } else 331 com_err(whoami, code, _("while changing %s's key"), princ_str); 332 goto cleanup; 333 } 334 335 for (i = 0; i < nkeys; i++) { 336 memset(&new_entry, 0, sizeof(new_entry)); 337 new_entry.principal = princ; 338 new_entry.key = key_data[i].key; 339 new_entry.vno = key_data[i].kvno; 340 341 code = krb5_kt_add_entry(context, keytab, &new_entry); 342 if (code != 0) { 343 com_err(whoami, code, _("while adding key to keytab")); 344 goto cleanup; 345 } 346 347 if (!quiet) { 348 printf(_("Entry for principal %s with kvno %d, " 349 "encryption type %s added to keytab %s.\n"), 350 princ_str, key_data[i].kvno, 351 etype_string(key_data[i].key.enctype), keytab_str); 352 } 353 } 354 355 cleanup: 356 kadm5_free_kadm5_key_data(context, nkeys, key_data); 357 krb5_free_principal(context, princ); 358 } 359 360 static void 361 remove_principal(char *keytab_str, krb5_keytab keytab, 362 char *princ_str, char *kvno_str) 363 { 364 krb5_principal princ = NULL; 365 krb5_keytab_entry entry; 366 krb5_kt_cursor cursor = NULL; 367 enum { UNDEF, SPEC, HIGH, ALL, OLD } mode; 368 int code, did_something; 369 krb5_kvno kvno; 370 371 code = krb5_parse_name(context, princ_str, &princ); 372 if (code != 0) { 373 com_err(whoami, code, _("while parsing principal name %s"), princ_str); 374 goto cleanup; 375 } 376 377 mode = UNDEF; 378 if (kvno_str == NULL) { 379 mode = HIGH; 380 kvno = 0; 381 } else if (strcmp(kvno_str, "all") == 0) { 382 mode = ALL; 383 kvno = 0; 384 } else if (strcmp(kvno_str, "old") == 0) { 385 mode = OLD; 386 kvno = 0; 387 } else { 388 mode = SPEC; 389 kvno = atoi(kvno_str); 390 } 391 392 /* kvno is set to specified value for SPEC, 0 otherwise */ 393 code = krb5_kt_get_entry(context, keytab, princ, kvno, 0, &entry); 394 if (code != 0) { 395 if (code == ENOENT) { 396 fprintf(stderr, _("%s: Keytab %s does not exist.\n"), 397 whoami, keytab_str); 398 } else if (code == KRB5_KT_NOTFOUND) { 399 if (mode != SPEC) { 400 fprintf(stderr, _("%s: No entry for principal %s exists in " 401 "keytab %s\n"), 402 whoami, princ_str, keytab_str); 403 } else { 404 fprintf(stderr, _("%s: No entry for principal %s with kvno %d " 405 "exists in keytab %s\n"), 406 whoami, princ_str, kvno, keytab_str); 407 } 408 } else { 409 com_err(whoami, code, 410 _("while retrieving highest kvno from keytab")); 411 } 412 goto cleanup; 413 } 414 415 /* set kvno to spec'ed value for SPEC, highest kvno otherwise */ 416 if (mode != SPEC) 417 kvno = entry.vno; 418 krb5_kt_free_entry(context, &entry); 419 420 code = krb5_kt_start_seq_get(context, keytab, &cursor); 421 if (code != 0) { 422 com_err(whoami, code, _("while starting keytab scan")); 423 goto cleanup; 424 } 425 426 did_something = 0; 427 while ((code = krb5_kt_next_entry(context, keytab, &entry, 428 &cursor)) == 0) { 429 if (krb5_principal_compare(context, princ, entry.principal) && 430 ((mode == ALL) || 431 (mode == SPEC && entry.vno == kvno) || 432 (mode == OLD && entry.vno != kvno) || 433 (mode == HIGH && entry.vno == kvno))) { 434 435 /* 436 * Ack! What a kludge... the scanning functions lock 437 * the keytab so entries cannot be removed while they 438 * are operating. 439 */ 440 code = krb5_kt_end_seq_get(context, keytab, &cursor); 441 if (code != 0) { 442 com_err(whoami, code, 443 _("while temporarily ending keytab scan")); 444 goto cleanup; 445 } 446 cursor = NULL; 447 code = krb5_kt_remove_entry(context, keytab, &entry); 448 if (code != 0) { 449 com_err(whoami, code, _("while deleting entry from keytab")); 450 goto cleanup; 451 } 452 code = krb5_kt_start_seq_get(context, keytab, &cursor); 453 if (code != 0) { 454 com_err(whoami, code, _("while restarting keytab scan")); 455 goto cleanup; 456 } 457 458 did_something++; 459 if (!quiet) { 460 printf(_("Entry for principal %s with kvno %d removed from " 461 "keytab %s.\n"), princ_str, entry.vno, keytab_str); 462 } 463 } 464 krb5_kt_free_entry(context, &entry); 465 } 466 if (code && code != KRB5_KT_END) { 467 com_err(whoami, code, _("while scanning keytab")); 468 goto cleanup; 469 } 470 code = krb5_kt_end_seq_get(context, keytab, &cursor); 471 if (code) { 472 com_err(whoami, code, _("while ending keytab scan")); 473 goto cleanup; 474 } 475 cursor = NULL; 476 477 /* 478 * If !did_someting then mode must be OLD or we would have 479 * already returned with an error. But check it anyway just to 480 * prevent unexpected error messages... 481 */ 482 if (!did_something && mode == OLD) { 483 fprintf(stderr, _("%s: There is only one entry for principal %s in " 484 "keytab %s\n"), whoami, princ_str, keytab_str); 485 } 486 487 cleanup: 488 if (cursor != NULL) 489 (void)krb5_kt_end_seq_get(context, keytab, &cursor); 490 krb5_free_principal(context, princ); 491 } 492 493 /* 494 * etype_string(enctype): return a string representation of the 495 * encryption type. XXX copied from klist.c; this should be a 496 * library function, or perhaps just #defines 497 */ 498 static char * 499 etype_string(krb5_enctype enctype) 500 { 501 static char buf[100]; 502 krb5_error_code ret; 503 504 ret = krb5_enctype_to_name(enctype, FALSE, buf, sizeof(buf)); 505 if (ret) 506 snprintf(buf, sizeof(buf), "etype %d", enctype); 507 508 return buf; 509 } 510