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