1 /* 2 * Copyright 2006 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 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 } else 349 com_err(whoami, code, 350 gettext("while changing %s's key"), 351 princ_str); 352 goto cleanup; 353 } 354 355 code = kadm5_get_principal(lhandle, princ, &princ_rec, 356 KADM5_PRINCIPAL_NORMAL_MASK); 357 if (code != 0) { 358 com_err(whoami, code, gettext("while retrieving principal")); 359 goto cleanup; 360 } 361 362 for (i = 0; i < nkeys; i++) { 363 memset((char *) &new_entry, 0, sizeof(new_entry)); 364 new_entry.principal = princ; 365 new_entry.key = keys[i]; 366 new_entry.vno = princ_rec.kvno; 367 368 code = krb5_kt_add_entry(context, keytab, &new_entry); 369 if (code != 0) { 370 com_err(whoami, code, 371 gettext("while adding key to keytab")); 372 (void) kadm5_free_principal_ent(lhandle, &princ_rec); 373 goto cleanup; 374 } 375 376 if (!quiet) 377 printf(gettext("Entry for principal %s with kvno %d, " 378 "encryption type %s added to keytab %s.\n"), 379 princ_str, princ_rec.kvno, 380 etype_string(keys[i].enctype), keytab_str); 381 } 382 383 code = kadm5_free_principal_ent(lhandle, &princ_rec); 384 if (code != 0) { 385 com_err(whoami, code, gettext("while freeing principal entry")); 386 goto cleanup; 387 } 388 389 cleanup: 390 if (nkeys) { 391 for (i = 0; i < nkeys; i++) 392 krb5_free_keyblock_contents(context, &keys[i]); 393 free(keys); 394 } 395 if (princ) 396 krb5_free_principal(context, princ); 397 398 if (permitted_etypes != NULL && ks_tuple == NULL) 399 free(permitted_etypes); 400 401 return code; 402 } 403 404 int remove_principal(char *keytab_str, krb5_keytab keytab, char 405 *princ_str, char *kvno_str) 406 { 407 krb5_principal princ; 408 krb5_keytab_entry entry; 409 krb5_kt_cursor cursor; 410 enum { UNDEF, SPEC, HIGH, ALL, OLD } mode; 411 int code, did_something; 412 krb5_kvno kvno; 413 414 code = krb5_parse_name(context, princ_str, &princ); 415 if (code != 0) { 416 com_err(whoami, code, 417 gettext("while parsing principal name %s"), 418 princ_str); 419 return code; 420 } 421 422 mode = UNDEF; 423 if (kvno_str == NULL) { 424 mode = HIGH; 425 kvno = 0; 426 } else if (strcmp(kvno_str, "all") == 0) { 427 mode = ALL; 428 kvno = 0; 429 } else if (strcmp(kvno_str, "old") == 0) { 430 mode = OLD; 431 kvno = 0; 432 } else { 433 mode = SPEC; 434 kvno = atoi(kvno_str); 435 } 436 437 /* kvno is set to specified value for SPEC, 0 otherwise */ 438 code = krb5_kt_get_entry(context, keytab, princ, kvno, 0, &entry); 439 if (code != 0) { 440 if (code == ENOENT) { 441 fprintf(stderr, 442 gettext("%s: Keytab %s does not exist.\n"), 443 whoami, keytab_str); 444 } else if (code == KRB5_KT_NOTFOUND) { 445 if (mode != SPEC) 446 fprintf(stderr, 447 gettext("%s: No entry for principal " 448 "%s exists in keytab %s\n"), 449 whoami, princ_str, keytab_str); 450 else 451 fprintf(stderr, 452 gettext("%s: No entry for principal " 453 "%s with kvno %d exists in " 454 "keytab %s.\n"), 455 whoami, princ_str, kvno, keytab_str); 456 } else { 457 com_err(whoami, code, 458 gettext("while retrieving highest " 459 "kvno from keytab")); 460 } 461 return code; 462 } 463 464 /* set kvno to spec'ed value for SPEC, highest kvno otherwise */ 465 kvno = entry.vno; 466 krb5_kt_free_entry(context, &entry); 467 468 code = krb5_kt_start_seq_get(context, keytab, &cursor); 469 if (code != 0) { 470 com_err(whoami, code, gettext("while starting keytab scan")); 471 return code; 472 } 473 474 did_something = 0; 475 while ((code = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) { 476 if (krb5_principal_compare(context, princ, entry.principal) && 477 ((mode == ALL) || 478 (mode == SPEC && entry.vno == kvno) || 479 (mode == OLD && entry.vno != kvno) || 480 (mode == HIGH && entry.vno == kvno))) { 481 482 /* 483 * Ack! What a kludge... the scanning functions lock 484 * the keytab so entries cannot be removed while they 485 * are operating. 486 */ 487 code = krb5_kt_end_seq_get(context, keytab, &cursor); 488 if (code != 0) { 489 com_err(whoami, code, 490 gettext("while temporarily " 491 "ending keytab scan")); 492 return code; 493 } 494 code = krb5_kt_remove_entry(context, keytab, &entry); 495 if (code != 0) { 496 com_err(whoami, code, 497 gettext("while deleting entry " 498 "from keytab")); 499 return code; 500 } 501 code = krb5_kt_start_seq_get(context, keytab, &cursor); 502 if (code != 0) { 503 com_err(whoami, code, 504 gettext("while restarting keytab scan")); 505 return code; 506 } 507 508 did_something++; 509 if (!quiet) 510 printf(gettext("Entry for principal " 511 "%s with kvno %d " 512 "removed from keytab %s.\n"), 513 princ_str, entry.vno, keytab_str); 514 } 515 krb5_kt_free_entry(context, &entry); 516 } 517 if (code && code != KRB5_KT_END) { 518 com_err(whoami, code, gettext("while scanning keytab")); 519 return code; 520 } 521 if ((code = krb5_kt_end_seq_get(context, keytab, &cursor))) { 522 com_err(whoami, code, gettext("while ending keytab scan")); 523 return code; 524 } 525 526 /* 527 * If !did_someting then mode must be OLD or we would have 528 * already returned with an error. But check it anyway just to 529 * prevent unexpected error messages... 530 */ 531 if (!did_something && mode == OLD) { 532 fprintf(stderr, 533 gettext("%s: There is only one entry for principal " 534 "%s in keytab %s\n"), 535 whoami, princ_str, keytab_str); 536 return 1; 537 } 538 539 return 0; 540 } 541 542 /* 543 * etype_string(enctype): return a string representation of the 544 * encryption type. XXX copied from klist.c; this should be a 545 * library function, or perhaps just #defines 546 */ 547 static char *etype_string(enctype) 548 krb5_enctype enctype; 549 { 550 static char buf[100]; 551 krb5_error_code ret; 552 553 if ((ret = krb5_enctype_to_string(enctype, buf, sizeof(buf)))) 554 sprintf(buf, "etype %d", enctype); 555 556 return buf; 557 } 558