1 /* 2 * Copyright 2004 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.26 2000/02/19 01:57:07 tlyu 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.26 2000/02/19 01:57:07 tlyu 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 <k5-int.h> 52 #include <kadm5/admin.h> 53 54 static int add_principal(void *handle, char *keytab_str, krb5_keytab keytab, 55 int 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 62 extern char *krb5_defkeyname; 63 extern char *whoami; 64 extern krb5_context context; 65 extern void *handle; 66 static int quiet; 67 68 void 69 add_usage() 70 { 71 fprintf(stderr, "%s: %s\n", gettext("Usage"), 72 "ktadd [-k[eytab] keytab] [-q] [-e keysaltlist] " 73 "[principal | -glob princ-exp] [...]\n"); 74 } 75 76 void 77 rem_usage() 78 { 79 fprintf(stderr, "%s: %s\n", 80 gettext("Usage"), 81 "ktremove [-k[eytab] keytab] [-q] principal " 82 "[kvno|\"all\"|\"old\"]\n"); 83 } 84 85 int 86 process_keytab(krb5_context context, char **keytab_str, 87 krb5_keytab *keytab) 88 { 89 int code; 90 char buf[BUFSIZ]; 91 92 if (*keytab_str == NULL) { 93 if (code = krb5_kt_default(context, keytab)) { 94 com_err(whoami, code, gettext("while opening default keytab")); 95 return (1); 96 } 97 if (code = krb5_kt_get_name(context, *keytab, buf, BUFSIZ)) { 98 com_err(whoami, code, gettext("while retrieving keytab name")); 99 return (1); 100 } 101 if (!(*keytab_str = strdup(buf))) { 102 com_err(whoami, ENOMEM, gettext("while creating keytab name")); 103 return(1); 104 } 105 } else { 106 if (strchr(*keytab_str, ':') != NULL) { 107 *keytab_str = strdup(*keytab_str); 108 if (*keytab_str == NULL) { 109 com_err(whoami, ENOMEM, 110 gettext("while creating keytab name")); 111 return (1); 112 } 113 } else { 114 char *tmp = *keytab_str; 115 116 *keytab_str = (char *) 117 malloc(strlen("WRFILE:")+strlen(tmp)+1); 118 if (*keytab_str == NULL) { 119 com_err(whoami, ENOMEM, 120 gettext("while creating keytab name")); 121 return (1); 122 } 123 sprintf(*keytab_str, "WRFILE:%s", tmp); 124 } 125 126 code = krb5_kt_resolve(context, *keytab_str, keytab); 127 if (code != 0) { 128 com_err(whoami, code, 129 gettext("while resolving keytab %s"), *keytab_str); 130 free(keytab_str); 131 return (1); 132 } 133 } 134 135 return (0); 136 } 137 138 139 void 140 kadmin_keytab_add(int argc, char **argv) 141 { 142 krb5_keytab keytab = 0; 143 char *princ_str, *keytab_str = NULL, **princs; 144 int code, num, i; 145 krb5_error_code retval; 146 int keepold = 0, n_ks_tuple = 0; 147 krb5_key_salt_tuple *ks_tuple = NULL; 148 149 argc--; 150 argv++; 151 quiet = 0; 152 while (argc) { 153 if (strncmp(*argv, "-k", 2) == 0) { 154 argc--; 155 argv++; 156 if (!argc || keytab_str) { 157 add_usage(); 158 return; 159 } 160 keytab_str = *argv; 161 } else if (strcmp(*argv, "-q") == 0) { 162 quiet++; 163 } else if (strcmp(*argv, "-e") == 0) { 164 argc--; 165 if (argc < 1) { 166 add_usage(); 167 return; 168 } 169 retval = krb5_string_to_keysalts(*++argv, ", \t", ":.-", 0, 170 &ks_tuple, &n_ks_tuple); 171 if (retval) { 172 com_err("ktadd", retval, 173 gettext("while parsing keysalts %s"), 174 *argv); 175 176 return; 177 } 178 } else 179 break; 180 argc--; 181 argv++; 182 } 183 184 if (argc == 0) { 185 add_usage(); 186 return; 187 } 188 189 if (process_keytab(context, &keytab_str, &keytab)) 190 return; 191 192 while (*argv) { 193 if (strcmp(*argv, "-glob") == 0) { 194 if (*++argv == NULL) { 195 add_usage(); 196 break; 197 } 198 if (code = kadm5_get_principals(handle, *argv, 199 &princs, &num)) { 200 com_err(whoami, code, 201 gettext("while expanding expression " 202 "\"%s\"."), 203 *argv); 204 argv++; 205 continue; 206 } 207 208 for (i = 0; i < num; i++) 209 (void) add_principal(handle, keytab_str, keytab, 210 keepold, n_ks_tuple, ks_tuple, 211 princs[i]); 212 kadm5_free_name_list(handle, princs, num); 213 } else 214 (void) add_principal(handle, keytab_str, keytab, 215 keepold, n_ks_tuple, ks_tuple, 216 *argv); 217 argv++; 218 } 219 220 code = krb5_kt_close(context, keytab); 221 if (code != 0) 222 com_err(whoami, code, gettext("while closing keytab")); 223 224 free(keytab_str); 225 } 226 227 void 228 kadmin_keytab_remove(int argc, char **argv) 229 { 230 krb5_keytab keytab = 0; 231 char *princ_str, *keytab_str = NULL; 232 int code; 233 234 argc--; 235 argv++; 236 quiet = 0; 237 while (argc) { 238 if (strncmp(*argv, "-k", 2) == 0) { 239 argc--; 240 argv++; 241 if (!argc || keytab_str) { 242 rem_usage(); 243 return; 244 } 245 keytab_str = *argv; 246 } else if (strcmp(*argv, "-q") == 0) { 247 quiet++; 248 } else 249 break; 250 argc--; 251 argv++; 252 } 253 254 if (argc != 1 && argc != 2) { 255 rem_usage(); 256 return; 257 } 258 if (process_keytab(context, &keytab_str, &keytab)) 259 return; 260 261 (void) remove_principal(keytab_str, keytab, argv[0], argv[1]); 262 263 code = krb5_kt_close(context, keytab); 264 if (code != 0) 265 com_err(whoami, code, gettext("while closing keytab")); 266 267 free(keytab_str); 268 } 269 270 int add_principal(void *handle, char *keytab_str, krb5_keytab keytab, 271 int keepold, int n_ks_tuple, 272 krb5_key_salt_tuple *ks_tuple, 273 char *princ_str) 274 { 275 kadm5_principal_ent_rec princ_rec; 276 krb5_principal princ; 277 krb5_keytab_entry new_entry; 278 krb5_keyblock *keys; 279 int code, code2, mask, nkeys, i; 280 int nktypes = 0; 281 krb5_key_salt_tuple *permitted_etypes = NULL; 282 283 (void) memset((char *)&princ_rec, 0, sizeof(princ_rec)); 284 285 princ = NULL; 286 keys = NULL; 287 nkeys = 0; 288 289 code = krb5_parse_name(context, princ_str, &princ); 290 if (code != 0) { 291 com_err(whoami, code, 292 gettext("while parsing -add principal name %s"), 293 princ_str); 294 goto cleanup; 295 } 296 297 if (ks_tuple == NULL) { 298 krb5_enctype *ptr, *ktypes = NULL; 299 300 code = krb5_get_permitted_enctypes(context, &ktypes); 301 if (!code && ktypes && *ktypes) { 302 krb5_int32 salttype; 303 /* 304 * Count the results. This is stupid, the API above 305 * should have included an output param to indicate 306 * the size of the list that is returned. 307 */ 308 for (ptr = ktypes; *ptr; ptr++) nktypes++; 309 310 /* Allocate a new key-salt tuple set */ 311 permitted_etypes = (krb5_key_salt_tuple *)malloc ( 312 sizeof (krb5_key_salt_tuple) * nktypes); 313 if (permitted_etypes == NULL) { 314 free(ktypes); 315 return (ENOMEM); 316 } 317 318 /* 319 * Because the keysalt parameter doesn't matter for 320 * keys stored in the keytab, use the default "normal" 321 * salt for all keys 322 */ 323 (void) krb5_string_to_salttype("normal", &salttype); 324 for (i = 0; i < nktypes; i++) { 325 permitted_etypes[i].ks_enctype = ktypes[i]; 326 permitted_etypes[i].ks_salttype = salttype; 327 } 328 free(ktypes); 329 } else { 330 if (ktypes) 331 free(ktypes); 332 goto cleanup; 333 } 334 } else { 335 permitted_etypes = ks_tuple; 336 nktypes = n_ks_tuple; 337 } 338 339 code = kadm5_randkey_principal_3(handle, princ, 340 keepold, nktypes, permitted_etypes, 341 &keys, &nkeys); 342 343 #ifndef _KADMIN_LOCAL_ 344 /* this block is not needed in the kadmin.local client */ 345 346 /* 347 * If the above call failed, we may be talking to an older 348 * admin server, so try the older API. 349 */ 350 if (code == KADM5_RPC_ERROR) { 351 code = kadm5_randkey_principal_old(handle, princ, &keys, &nkeys); 352 } 353 #endif /* !KADMIN_LOCAL */ 354 if (code != 0) { 355 if (code == KADM5_UNK_PRINC) { 356 fprintf(stderr, 357 gettext("%s: Principal %s does not exist.\n"), 358 whoami, princ_str); 359 } else { 360 com_err(whoami, code, 361 gettext("while changing %s's key"), 362 princ_str); 363 } 364 goto cleanup; 365 } 366 367 code = kadm5_get_principal(handle, princ, &princ_rec, 368 KADM5_PRINCIPAL_NORMAL_MASK); 369 if (code != 0) { 370 com_err(whoami, code, gettext("while retrieving principal")); 371 goto cleanup; 372 } 373 374 for (i = 0; i < nkeys; i++) { 375 memset((char *) &new_entry, 0, sizeof(new_entry)); 376 new_entry.principal = princ; 377 new_entry.key = keys[i]; 378 new_entry.vno = princ_rec.kvno; 379 380 code = krb5_kt_add_entry(context, keytab, &new_entry); 381 if (code != 0) { 382 com_err(whoami, code, 383 gettext("while adding key to keytab")); 384 (void) kadm5_free_principal_ent(handle, &princ_rec); 385 goto cleanup; 386 } 387 388 if (!quiet) 389 printf(gettext("Entry for principal %s with kvno %d, " 390 "encryption type %s added to keytab %s.\n"), 391 princ_str, princ_rec.kvno, 392 etype_string(keys[i].enctype), keytab_str); 393 } 394 395 code = kadm5_free_principal_ent(handle, &princ_rec); 396 if (code != 0) { 397 com_err(whoami, code, gettext("while freeing principal entry")); 398 goto cleanup; 399 } 400 401 cleanup: 402 if (nkeys) { 403 for (i = 0; i < nkeys; i++) 404 krb5_free_keyblock_contents(context, &keys[i]); 405 free(keys); 406 } 407 if (princ) 408 krb5_free_principal(context, princ); 409 410 if (permitted_etypes != NULL && ks_tuple == NULL) 411 free(permitted_etypes); 412 413 return (code); 414 } 415 416 int 417 remove_principal(char *keytab_str, krb5_keytab keytab, char 418 *princ_str, char *kvno_str) 419 { 420 krb5_principal princ; 421 krb5_keytab_entry entry; 422 krb5_kt_cursor cursor; 423 enum { 424 UNDEF, SPEC, HIGH, ALL, OLD 425 } mode; 426 int code, kvno, did_something; 427 428 code = krb5_parse_name(context, princ_str, &princ); 429 if (code != 0) { 430 com_err(whoami, code, 431 gettext("while parsing principal name %s"), 432 princ_str); 433 return (code); 434 } 435 mode = UNDEF; 436 if (kvno_str == NULL) { 437 mode = HIGH; 438 kvno = 0; 439 } else if (strcmp(kvno_str, "all") == 0) { 440 mode = ALL; 441 kvno = 0; 442 } else if (strcmp(kvno_str, "old") == 0) { 443 mode = OLD; 444 kvno = 0; 445 } else { 446 mode = SPEC; 447 kvno = atoi(kvno_str); 448 } 449 450 /* kvno is set to specified value for SPEC, 0 otherwise */ 451 code = krb5_kt_get_entry(context, keytab, princ, kvno, 0, &entry); 452 if (code != 0) { 453 if (code == ENOENT) { 454 fprintf(stderr, 455 gettext("%s: Keytab %s does not exist.\n"), 456 whoami, keytab_str); 457 } else if (code == KRB5_KT_NOTFOUND) { 458 if (mode != SPEC) 459 fprintf(stderr, 460 gettext("%s: No entry for principal " 461 "%s exists in keytab %s\n"), 462 whoami, princ_str, keytab_str); 463 else 464 fprintf(stderr, 465 gettext("%s: No entry for principal " 466 "%s with kvno %d exists in " 467 "keytab %s.\n"), 468 whoami, princ_str, kvno, keytab_str); 469 } else { 470 com_err(whoami, code, 471 gettext("while retrieving highest " 472 "kvno from keytab")); 473 } 474 return (code); 475 } 476 /* set kvno to spec'ed value for SPEC, highest kvno otherwise */ 477 kvno = entry.vno; 478 krb5_kt_free_entry(context, &entry); 479 480 code = krb5_kt_start_seq_get(context, keytab, &cursor); 481 if (code != 0) { 482 com_err(whoami, code, gettext("while starting keytab scan")); 483 return (code); 484 } 485 did_something = 0; 486 while ((code = krb5_kt_next_entry(context, 487 keytab, &entry, &cursor)) == 0) { 488 if (krb5_principal_compare(context, princ, entry.principal) && 489 ((mode == ALL) || 490 (mode == SPEC && entry.vno == kvno) || 491 (mode == OLD && entry.vno != kvno) || 492 (mode == HIGH && entry.vno == kvno))) { 493 494 /* 495 * Ack! What a kludge... the scanning functions 496 * lock the keytab so entries cannot be removed 497 * while they are operating. 498 */ 499 code = krb5_kt_end_seq_get(context, keytab, &cursor); 500 if (code != 0) { 501 com_err(whoami, code, 502 gettext("while temporarily " 503 "ending keytab scan")); 504 return (code); 505 } 506 code = krb5_kt_remove_entry(context, keytab, &entry); 507 if (code != 0) { 508 com_err(whoami, code, 509 gettext("while deleting entry " 510 "from keytab")); 511 return (code); 512 } 513 code = krb5_kt_start_seq_get(context, keytab, &cursor); 514 if (code != 0) { 515 com_err(whoami, code, 516 gettext("while restarting keytab scan")); 517 return (code); 518 } 519 did_something++; 520 if (!quiet) 521 printf(gettext("Entry for principal " 522 "%s with kvno %d " 523 "removed from keytab %s.\n"), 524 princ_str, entry.vno, keytab_str); 525 } 526 krb5_kt_free_entry(context, &entry); 527 } 528 if (code && code != KRB5_KT_END) { 529 com_err(whoami, code, gettext("while scanning keytab")); 530 return (code); 531 } 532 if (code = krb5_kt_end_seq_get(context, keytab, &cursor)) { 533 com_err(whoami, code, gettext("while ending keytab scan")); 534 return (code); 535 } 536 /* 537 * If !did_someting then mode must be OLD or we would have already 538 * returned with an error. But check it anyway just to prevent 539 * unexpected error messages... 540 */ 541 if (!did_something && mode == OLD) { 542 fprintf(stderr, 543 gettext("%s: There is only one entry for principal " 544 "%s in keytab %s\n"), 545 whoami, princ_str, keytab_str); 546 return (1); 547 } 548 return (0); 549 } 550 551 /* 552 * etype_string(enctype): return a string representation of the 553 * encryption type. XXX copied from klist.c; this should be a 554 * library function, or perhaps just #defines 555 */ 556 static char * 557 etype_string(enctype) 558 krb5_enctype enctype; 559 { 560 static char buf[100]; 561 krb5_error_code ret; 562 563 if (ret = krb5_enctype_to_string(enctype, buf, sizeof(buf))) 564 sprintf(buf, "etype %d", enctype); 565 566 return (buf); 567 } 568