1 /* 2 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * kadmin/ktutil/ktutil_funcs.c 8 * 9 *(C) Copyright 1995, 1996 by the Massachusetts Institute of Technology. 10 * All Rights Reserved. 11 * 12 * Export of this software from the United States of America may 13 * require a specific license from the United States Government. 14 * It is the responsibility of any person or organization contemplating 15 * export to obtain such a license before exporting. 16 * 17 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 18 * distribute this software and its documentation for any purpose and 19 * without fee is hereby granted, provided that the above copyright 20 * notice appear in all copies and that both that copyright notice and 21 * this permission notice appear in supporting documentation, and that 22 * the name of M.I.T. not be used in advertising or publicity pertaining 23 * to distribution of the software without specific, written prior 24 * permission. Furthermore if you modify this software you must label 25 * your software as modified software and not distribute it in such a 26 * fashion that it might be confused with the original M.I.T. software. 27 * M.I.T. makes no representations about the suitability of 28 * this software for any purpose. It is provided "as is" without express 29 * or implied warranty. 30 * 31 * Utility functions for ktutil. 32 */ 33 34 #include "k5-int.h" 35 #include "ktutil.h" 36 #ifdef KRB5_KRB4_COMPAT 37 #include "kerberosIV/krb.h" 38 #include <stdio.h> 39 #endif 40 #include <string.h> 41 #include <ctype.h> 42 #include <libintl.h> 43 44 /* 45 * Free a kt_list 46 */ 47 krb5_error_code ktutil_free_kt_list(context, list) 48 krb5_context context; 49 krb5_kt_list list; 50 { 51 krb5_kt_list lp, prev; 52 krb5_error_code retval = 0; 53 54 for (lp = list; lp;) { 55 retval = krb5_kt_free_entry(context, lp->entry); 56 free((char *)lp->entry); 57 if (retval) 58 break; 59 prev = lp; 60 lp = lp->next; 61 free((char *)prev); 62 } 63 return retval; 64 } 65 66 /* 67 * Delete a numbered entry in a kt_list. Takes a pointer to a kt_list 68 * in case head gets deleted. 69 */ 70 krb5_error_code ktutil_delete(context, list, idx) 71 krb5_context context; 72 krb5_kt_list *list; 73 int idx; 74 { 75 krb5_kt_list lp, prev; 76 int i; 77 78 for (lp = *list, i = 1; lp; prev = lp, lp = lp->next, i++) { 79 if (i == idx) { 80 if (i == 1) 81 *list = lp->next; 82 else 83 prev->next = lp->next; 84 lp->next = NULL; 85 return ktutil_free_kt_list(context, lp); 86 } 87 } 88 return EINVAL; 89 } 90 91 /* 92 * Create a new keytab entry and add it to the keytab list. 93 * Based on the value of use_pass, either prompt the user for a 94 * password or key. If the keytab list is NULL, allocate a new 95 * one first. 96 */ 97 krb5_error_code ktutil_add(context, list, princ_str, kvno, 98 enctype_str, use_pass) 99 krb5_context context; 100 krb5_kt_list *list; 101 char *princ_str; 102 krb5_kvno kvno; 103 char *enctype_str; 104 int use_pass; 105 { 106 krb5_keytab_entry *entry; 107 krb5_kt_list lp = NULL, prev = NULL; 108 krb5_principal princ; 109 krb5_enctype enctype; 110 krb5_timestamp now; 111 krb5_error_code retval; 112 krb5_data password, salt; 113 krb5_keyblock key; 114 char buf[BUFSIZ]; 115 char promptstr[1024]; 116 117 char *cp; 118 int i, tmp; 119 unsigned int pwsize = BUFSIZ; 120 121 retval = krb5_parse_name(context, princ_str, &princ); 122 if (retval) 123 return retval; 124 /* now unparse in order to get the default realm appended 125 to princ_str, if no realm was specified */ 126 retval = krb5_unparse_name(context, princ, &princ_str); 127 if (retval) 128 return retval; 129 retval = krb5_string_to_enctype(enctype_str, &enctype); 130 if (retval) 131 return KRB5_BAD_ENCTYPE; 132 retval = krb5_timeofday(context, &now); 133 if (retval) 134 return retval; 135 136 if (*list) { 137 /* point lp at the tail of the list */ 138 for (lp = *list; lp->next; lp = lp->next); 139 } 140 entry = (krb5_keytab_entry *) malloc(sizeof(krb5_keytab_entry)); 141 if (!entry) { 142 return ENOMEM; 143 } 144 memset((char *) entry, 0, sizeof(*entry)); 145 146 if (!lp) { /* if list is empty, start one */ 147 lp = (krb5_kt_list) malloc(sizeof(*lp)); 148 if (!lp) { 149 return ENOMEM; 150 } 151 } else { 152 lp->next = (krb5_kt_list) malloc(sizeof(*lp)); 153 if (!lp->next) { 154 return ENOMEM; 155 } 156 prev = lp; 157 lp = lp->next; 158 } 159 lp->next = NULL; 160 lp->entry = entry; 161 162 if (use_pass) { 163 password.length = pwsize; 164 password.data = (char *) malloc(pwsize); 165 if (!password.data) { 166 retval = ENOMEM; 167 goto cleanup; 168 } 169 170 (void) snprintf(promptstr, sizeof(promptstr), 171 gettext("Password for %.1000s"), princ_str); 172 retval = krb5_read_password(context, promptstr, NULL, password.data, 173 &password.length); 174 if (retval) 175 goto cleanup; 176 retval = krb5_principal2salt(context, princ, &salt); 177 if (retval) 178 goto cleanup; 179 retval = krb5_c_string_to_key(context, enctype, &password, 180 &salt, &key); 181 if (retval) 182 goto cleanup; 183 memset(password.data, 0, password.length); 184 password.length = 0; 185 memcpy(&lp->entry->key, &key, sizeof(krb5_keyblock)); 186 } else { 187 printf(gettext("Key for %s (hex): "), princ_str); 188 fgets(buf, BUFSIZ, stdin); 189 /* 190 * We need to get rid of the trailing '\n' from fgets. 191 * If we have an even number of hex digits (as we should), 192 * write a '\0' over the '\n'. If for some reason we have 193 * an odd number of hex digits, force an even number of hex 194 * digits by writing a '0' into the last position (the string 195 * will still be null-terminated). 196 */ 197 buf[strlen(buf) - 1] = strlen(buf) % 2 ? '\0' : '0'; 198 if (strlen(buf) == 0) { 199 fprintf(stderr, "addent: %s", gettext("Error reading key.\n")); 200 retval = 0; 201 goto cleanup; 202 } 203 204 lp->entry->key.enctype = enctype; 205 lp->entry->key.contents = (krb5_octet *) malloc((strlen(buf) + 1) / 2); 206 if (!lp->entry->key.contents) { 207 retval = ENOMEM; 208 goto cleanup; 209 } 210 211 i = 0; 212 for (cp = buf; *cp; cp += 2) { 213 if (!isxdigit((int) cp[0]) || !isxdigit((int) cp[1])) { 214 fprintf(stderr, "addent: %s", 215 gettext("Illegal character in key.\n")); 216 retval = 0; 217 goto cleanup; 218 } 219 sscanf(cp, "%02x", &tmp); 220 lp->entry->key.contents[i++] = (krb5_octet) tmp; 221 } 222 lp->entry->key.length = i; 223 } 224 lp->entry->principal = princ; 225 lp->entry->vno = kvno; 226 lp->entry->timestamp = now; 227 228 if (!*list) 229 *list = lp; 230 231 return 0; 232 233 cleanup: 234 if (prev) 235 prev->next = NULL; 236 ktutil_free_kt_list(context, lp); 237 return retval; 238 } 239 240 /* 241 * Read in a keytab and append it to list. If list starts as NULL, 242 * allocate a new one if necessary. 243 */ 244 krb5_error_code ktutil_read_keytab(context, name, list) 245 krb5_context context; 246 char *name; 247 krb5_kt_list *list; 248 { 249 krb5_kt_list lp = NULL, tail = NULL, back = NULL; 250 krb5_keytab kt; 251 krb5_keytab_entry *entry; 252 krb5_kt_cursor cursor; 253 krb5_error_code retval = 0; 254 255 if (*list) { 256 /* point lp at the tail of the list */ 257 for (lp = *list; lp->next; lp = lp->next); 258 back = lp; 259 } 260 retval = krb5_kt_resolve(context, name, &kt); 261 if (retval) 262 return retval; 263 retval = krb5_kt_start_seq_get(context, kt, &cursor); 264 if (retval) 265 goto close_kt; 266 for (;;) { 267 entry = (krb5_keytab_entry *)malloc(sizeof (krb5_keytab_entry)); 268 if (!entry) { 269 retval = ENOMEM; 270 break; 271 } 272 memset((char *)entry, 0, sizeof (*entry)); 273 retval = krb5_kt_next_entry(context, kt, entry, &cursor); 274 if (retval) 275 break; 276 277 if (!lp) { /* if list is empty, start one */ 278 lp = (krb5_kt_list)malloc(sizeof (*lp)); 279 if (!lp) { 280 retval = ENOMEM; 281 break; 282 } 283 } else { 284 lp->next = (krb5_kt_list)malloc(sizeof (*lp)); 285 if (!lp->next) { 286 retval = ENOMEM; 287 break; 288 } 289 lp = lp->next; 290 } 291 if (!tail) 292 tail = lp; 293 lp->next = NULL; 294 lp->entry = entry; 295 } 296 if (entry) 297 free((char *)entry); 298 if (retval) { 299 if (retval == KRB5_KT_END) 300 retval = 0; 301 else { 302 ktutil_free_kt_list(context, tail); 303 tail = NULL; 304 if (back) 305 back->next = NULL; 306 } 307 } 308 if (!*list) 309 *list = tail; 310 krb5_kt_end_seq_get(context, kt, &cursor); 311 close_kt: 312 krb5_kt_close(context, kt); 313 return retval; 314 } 315 316 /* 317 * Takes a kt_list and writes it to the named keytab. 318 */ 319 krb5_error_code ktutil_write_keytab(context, list, name) 320 krb5_context context; 321 krb5_kt_list list; 322 char *name; 323 { 324 krb5_kt_list lp; 325 krb5_keytab kt; 326 char ktname[MAXPATHLEN+sizeof("WRFILE:")+1]; 327 krb5_error_code retval = 0; 328 329 strcpy(ktname, "WRFILE:"); 330 if (strlen (name) >= MAXPATHLEN) 331 return ENAMETOOLONG; 332 strncat (ktname, name, MAXPATHLEN); 333 retval = krb5_kt_resolve(context, ktname, &kt); 334 if (retval) 335 return retval; 336 for (lp = list; lp; lp = lp->next) { 337 retval = krb5_kt_add_entry(context, kt, lp->entry); 338 if (retval) 339 break; 340 } 341 krb5_kt_close(context, kt); 342 return retval; 343 } 344 345 #ifdef KRB5_KRB4_COMPAT 346 /* 347 * getstr() takes a file pointer, a string and a count. It reads from 348 * the file until either it has read "count" characters, or until it 349 * reads a null byte. When finished, what has been read exists in the 350 * given string "s". If "count" characters were actually read, the 351 * last is changed to a null, so the returned string is always null- 352 * terminated. getstr() returns the number of characters read, 353 * including the null terminator. 354 */ 355 356 static int getstr(fp, s, n) 357 FILE *fp; 358 register char *s; 359 int n; 360 { 361 register int count = n; 362 while (fread(s, 1, 1, fp) > 0 && --count) 363 if (*s++ == '\0') 364 return (n - count); 365 *s = '\0'; 366 return (n - count); 367 } 368 369 /* 370 * Read in a named krb4 srvtab and append to list. Allocate new list 371 * if needed. 372 */ 373 krb5_error_code ktutil_read_srvtab(context, name, list) 374 krb5_context context; 375 char *name; 376 krb5_kt_list *list; 377 { 378 krb5_kt_list lp = NULL, tail = NULL, back = NULL; 379 krb5_keytab_entry *entry; 380 krb5_error_code retval = 0; 381 char sname[SNAME_SZ]; /* name of service */ 382 char sinst[INST_SZ]; /* instance of service */ 383 char srealm[REALM_SZ]; /* realm of service */ 384 unsigned char kvno; /* key version number */ 385 des_cblock key; 386 FILE *fp; 387 388 if (*list) { 389 /* point lp at the tail of the list */ 390 for (lp = *list; lp->next; lp = lp->next); 391 back = lp; 392 } 393 fp = fopen(name, "r"); 394 if (!fp) 395 return EIO; 396 for (;;) { 397 entry = (krb5_keytab_entry *)malloc(sizeof (krb5_keytab_entry)); 398 if (!entry) { 399 retval = ENOMEM; 400 break; 401 } 402 memset((char *)entry, 0, sizeof (*entry)); 403 memset(sname, 0, sizeof (sname)); 404 memset(sinst, 0, sizeof (sinst)); 405 memset(srealm, 0, sizeof (srealm)); 406 if (!(getstr(fp, sname, SNAME_SZ) > 0 && 407 getstr(fp, sinst, INST_SZ) > 0 && 408 getstr(fp, srealm, REALM_SZ) > 0 && 409 fread(&kvno, 1, 1, fp) > 0 && 410 fread((char *)key, sizeof (key), 1, fp) > 0)) 411 break; 412 entry->magic = KV5M_KEYTAB_ENTRY; 413 entry->timestamp = 0; /* XXX */ 414 entry->vno = kvno; 415 retval = krb5_425_conv_principal(context, 416 sname, sinst, srealm, 417 &entry->principal); 418 if (retval) 419 break; 420 entry->key.magic = KV5M_KEYBLOCK; 421 entry->key.enctype = ENCTYPE_DES_CBC_CRC; 422 entry->key.length = sizeof (key); 423 entry->key.contents = (krb5_octet *)malloc(sizeof (key)); 424 if (!entry->key.contents) { 425 retval = ENOMEM; 426 break; 427 } 428 memcpy((char *)entry->key.contents, (char *)key, sizeof (key)); 429 if (!lp) { /* if list is empty, start one */ 430 lp = (krb5_kt_list)malloc(sizeof (*lp)); 431 if (!lp) { 432 retval = ENOMEM; 433 break; 434 } 435 } else { 436 lp->next = (krb5_kt_list)malloc(sizeof (*lp)); 437 if (!lp->next) { 438 retval = ENOMEM; 439 break; 440 } 441 lp = lp->next; 442 } 443 lp->next = NULL; 444 lp->entry = entry; 445 if (!tail) 446 tail = lp; 447 } 448 if (entry) { 449 if (entry->magic == KV5M_KEYTAB_ENTRY) 450 krb5_kt_free_entry(context, entry); 451 free((char *)entry); 452 } 453 if (retval) { 454 ktutil_free_kt_list(context, tail); 455 tail = NULL; 456 if (back) 457 back->next = NULL; 458 } 459 if (!*list) 460 *list = tail; 461 fclose(fp); 462 return retval; 463 } 464 465 /* 466 * Writes a kt_list out to a krb4 srvtab file. Note that it first 467 * prunes the kt_list so that it won't contain any keys that are not 468 * the most recent, and ignores keys that are not ENCTYPE_DES. 469 */ 470 krb5_error_code ktutil_write_srvtab(context, list, name) 471 krb5_context context; 472 krb5_kt_list list; 473 char *name; 474 { 475 krb5_kt_list lp, lp1, prev, pruned = NULL; 476 krb5_error_code retval = 0; 477 FILE *fp; 478 char sname[SNAME_SZ]; 479 char sinst[INST_SZ]; 480 char srealm[REALM_SZ]; 481 482 /* First do heinous stuff to prune the list. */ 483 for (lp = list; lp; lp = lp->next) { 484 if ((lp->entry->key.enctype != ENCTYPE_DES_CBC_CRC) && 485 (lp->entry->key.enctype != ENCTYPE_DES_CBC_MD5) && 486 (lp->entry->key.enctype != ENCTYPE_DES_CBC_MD4) && 487 (lp->entry->key.enctype != ENCTYPE_DES_CBC_RAW)) 488 continue; 489 490 for (lp1 = pruned; lp1; prev = lp1, lp1 = lp1->next) { 491 /* Hunt for the current principal in the pruned list */ 492 if (krb5_principal_compare(context, 493 lp->entry->principal, 494 lp1->entry->principal)) 495 break; 496 } 497 if (!lp1) { /* need to add entry to tail of pruned list */ 498 if (!pruned) { 499 pruned = (krb5_kt_list) malloc(sizeof (*pruned)); 500 if (!pruned) 501 return ENOMEM; 502 memset((char *) pruned, 0, sizeof(*pruned)); 503 lp1 = pruned; 504 } else { 505 prev->next 506 = (krb5_kt_list) malloc(sizeof (*pruned)); 507 if (!prev->next) { 508 retval = ENOMEM; 509 goto free_pruned; 510 } 511 memset((char *) prev->next, 0, sizeof(*pruned)); 512 lp1 = prev->next; 513 } 514 lp1->entry = lp->entry; 515 } else { 516 /* This heuristic should be roughly the same as in the 517 keytab-reading code in libkrb5. */ 518 int offset = 0; 519 if (lp1->entry->vno > 240 || lp->entry->vno > 240) { 520 offset = 128; 521 } 522 #define M(X) (((X) + offset) % 256) 523 if (M(lp1->entry->vno) < M(lp->entry->vno)) 524 /* Check if lp->entry is newer kvno; if so, update */ 525 lp1->entry = lp->entry; 526 } 527 } 528 umask(0077); /*Changing umask for all of ktutil is OK 529 * We don't ever write out anything that should use 530 * default umask.*/ 531 fp = fopen(name, "w"); 532 if (!fp) { 533 retval = EIO; 534 goto free_pruned; 535 } 536 for (lp = pruned; lp; lp = lp->next) { 537 unsigned char kvno; 538 kvno = (unsigned char) lp->entry->vno; 539 retval = krb5_524_conv_principal(context, 540 lp->entry->principal, 541 sname, sinst, srealm); 542 if (retval) 543 break; 544 fwrite(sname, strlen(sname) + 1, 1, fp); 545 fwrite(sinst, strlen(sinst) + 1, 1, fp); 546 fwrite(srealm, strlen(srealm) + 1, 1, fp); 547 fwrite((char *)&kvno, 1, 1, fp); 548 fwrite((char *)lp->entry->key.contents, 549 sizeof (des_cblock), 1, fp); 550 } 551 fclose(fp); 552 free_pruned: 553 /* 554 * Loop over and free the pruned list; don't use free_kt_list 555 * because that kills the entries. 556 */ 557 for (lp = pruned; lp;) { 558 prev = lp; 559 lp = lp->next; 560 free((char *)prev); 561 } 562 return retval; 563 } 564 #endif /* KRB5_KRB4_COMPAT */ 565