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