1 /* 2 * Copyright 2003 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 * kadmin/ktutil/ktutil_funcs.c 10 * 11 *(C) Copyright 1995, 1996 by the Massachusetts Institute of Technology. 12 * All Rights Reserved. 13 * 14 * Export of this software from the United States of America may 15 * require a specific license from the United States Government. 16 * It is the responsibility of any person or organization contemplating 17 * export to obtain such a license before exporting. 18 * 19 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 20 * distribute this software and its documentation for any purpose and 21 * without fee is hereby granted, provided that the above copyright 22 * notice appear in all copies and that both that copyright notice and 23 * this permission notice appear in supporting documentation, and that 24 * the name of M.I.T. not be used in advertising or publicity pertaining 25 * to distribution of the software without specific, written prior 26 * permission. Furthermore if you modify this software you must label 27 * your software as modified software and not distribute it in such a 28 * fashion that it might be confused with the original M.I.T. software. 29 * M.I.T. makes no representations about the suitability of 30 * this software for any purpose. It is provided "as is" without express 31 * or implied warranty. 32 * 33 * Utility functions for ktutil. 34 */ 35 36 #include "k5-int.h" 37 #include "ktutil.h" 38 #ifdef KRB5_KRB4_COMPAT 39 #include "kerberosIV/krb.h" 40 #include <stdio.h> 41 #endif 42 #include <string.h> 43 #include <ctype.h> 44 #include <libintl.h> 45 46 /* 47 * Free a kt_list 48 */ 49 krb5_error_code ktutil_free_kt_list(context, list) 50 krb5_context context; 51 krb5_kt_list list; 52 { 53 krb5_kt_list lp, prev; 54 krb5_error_code retval = 0; 55 56 for (lp = list; lp;) { 57 retval = krb5_kt_free_entry(context, lp->entry); 58 free((char *)lp->entry); 59 if (retval) 60 break; 61 prev = lp; 62 lp = lp->next; 63 free((char *)prev); 64 } 65 return retval; 66 } 67 68 /* 69 * Delete a numbered entry in a kt_list. Takes a pointer to a kt_list 70 * in case head gets deleted. 71 */ 72 krb5_error_code ktutil_delete(context, list, index) 73 krb5_context context; 74 krb5_kt_list *list; 75 int index; 76 { 77 krb5_kt_list lp, prev; 78 int i; 79 80 for (lp = *list, i = 1; lp; prev = lp, lp = lp->next, i++) { 81 if (i == index) { 82 if (i == 1) 83 *list = lp->next; 84 else 85 prev->next = lp->next; 86 lp->next = NULL; 87 return ktutil_free_kt_list(context, lp); 88 } 89 } 90 return EINVAL; 91 } 92 93 /* 94 * Create a new keytab entry and add it to the keytab list. 95 * Based on the value of use_pass, either prompt the user for a 96 * password or key. If the keytab list is NULL, allocate a new 97 * one first. 98 */ 99 krb5_error_code ktutil_add(context, list, princ_str, kvno, 100 enctype_str, use_pass) 101 krb5_context context; 102 krb5_kt_list *list; 103 char *princ_str; 104 krb5_kvno kvno; 105 char *enctype_str; 106 int use_pass; 107 { 108 krb5_keytab_entry *entry; 109 krb5_kt_list lp = NULL, prev = NULL; 110 krb5_principal princ; 111 krb5_enctype enctype; 112 krb5_timestamp now; 113 krb5_error_code retval; 114 krb5_data password, salt; 115 krb5_keyblock key; 116 char buf[BUFSIZ]; 117 char promptstr[1024]; 118 119 char *cp; 120 int i, tmp, pwsize = BUFSIZ; 121 122 retval = krb5_parse_name(context, princ_str, &princ); 123 if (retval) 124 return retval; 125 /* now unparse in order to get the default realm appended 126 to princ_str, if no realm was specified */ 127 retval = krb5_unparse_name(context, princ, &princ_str); 128 if (retval) 129 return retval; 130 retval = krb5_string_to_enctype(enctype_str, &enctype); 131 if (retval) 132 return KRB5_BAD_ENCTYPE; 133 retval = krb5_timeofday(context, &now); 134 if (retval) 135 return retval; 136 137 if (*list) { 138 /* point lp at the tail of the list */ 139 for (lp = *list; lp->next; lp = lp->next); 140 } 141 entry = (krb5_keytab_entry *) malloc(sizeof(krb5_keytab_entry)); 142 if (!entry) { 143 return ENOMEM; 144 } 145 memset((char *) entry, 0, sizeof(*entry)); 146 147 if (!lp) { /* if list is empty, start one */ 148 lp = (krb5_kt_list) malloc(sizeof(krb5_kt_list)); 149 if (!lp) { 150 return ENOMEM; 151 } 152 } else { 153 lp->next = (krb5_kt_list) malloc(sizeof(krb5_kt_list)); 154 if (!lp->next) { 155 return ENOMEM; 156 } 157 prev = lp; 158 lp = lp->next; 159 } 160 lp->next = NULL; 161 lp->entry = entry; 162 163 if (use_pass) { 164 password.length = pwsize; 165 password.data = (char *) malloc(pwsize); 166 if (!password.data) { 167 retval = ENOMEM; 168 goto cleanup; 169 } 170 171 (void) snprintf(promptstr, sizeof(promptstr), 172 gettext("Password for %.1000s: "), princ_str); 173 retval = krb5_read_password(context, promptstr, NULL, password.data, 174 &password.length); 175 if (retval) 176 goto cleanup; 177 retval = krb5_principal2salt(context, princ, &salt); 178 if (retval) 179 goto cleanup; 180 retval = krb5_c_string_to_key(context, enctype, &password, 181 &salt, &key); 182 if (retval) 183 goto cleanup; 184 memset(password.data, 0, password.length); 185 password.length = 0; 186 memcpy(&lp->entry->key, &key, sizeof(krb5_keyblock)); 187 } else { 188 printf(gettext("Key for %s (hex): "), princ_str); 189 fgets(buf, BUFSIZ, stdin); 190 /* 191 * We need to get rid of the trailing '\n' from fgets. 192 * If we have an even number of hex digits (as we should), 193 * write a '\0' over the '\n'. If for some reason we have 194 * an odd number of hex digits, force an even number of hex 195 * digits by writing a '0' into the last position (the string 196 * will still be null-terminated). 197 */ 198 buf[strlen(buf) - 1] = strlen(buf) % 2 ? '\0' : '0'; 199 if (strlen(buf) == 0) { 200 fprintf(stderr, "addent: %s", gettext("Error reading key.\n")); 201 retval = 0; 202 goto cleanup; 203 } 204 205 lp->entry->key.enctype = enctype; 206 lp->entry->key.contents = (krb5_octet *) malloc((strlen(buf) + 1) / 2); 207 if (!lp->entry->key.contents) { 208 retval = ENOMEM; 209 goto cleanup; 210 } 211 212 i = 0; 213 for (cp = buf; *cp; cp += 2) { 214 if (!isxdigit(cp[0]) || !isxdigit(cp[1])) { 215 fprintf(stderr, "addent: %s", 216 gettext("Illegal character in key.\n")); 217 retval = 0; 218 goto cleanup; 219 } 220 sscanf(cp, "%02x", &tmp); 221 lp->entry->key.contents[i++] = (krb5_octet) tmp; 222 } 223 lp->entry->key.length = i; 224 } 225 lp->entry->principal = princ; 226 lp->entry->vno = kvno; 227 lp->entry->timestamp = now; 228 229 if (!*list) 230 *list = lp; 231 232 return 0; 233 234 cleanup: 235 if (prev) 236 prev->next = NULL; 237 ktutil_free_kt_list(context, lp); 238 return retval; 239 } 240 241 /* 242 * Read in a keytab and append it to list. If list starts as NULL, 243 * allocate a new one if necessary. 244 */ 245 krb5_error_code ktutil_read_keytab(context, name, list) 246 krb5_context context; 247 char *name; 248 krb5_kt_list *list; 249 { 250 krb5_kt_list lp = NULL, tail = NULL, back = NULL; 251 krb5_keytab kt; 252 krb5_keytab_entry *entry; 253 krb5_kt_cursor cursor; 254 krb5_error_code retval = 0; 255 256 if (*list) { 257 /* point lp at the tail of the list */ 258 for (lp = *list; lp->next; lp = lp->next); 259 back = lp; 260 } 261 retval = krb5_kt_resolve(context, name, &kt); 262 if (retval) 263 return retval; 264 retval = krb5_kt_start_seq_get(context, kt, &cursor); 265 if (retval) 266 goto close_kt; 267 for (;;) { 268 entry = (krb5_keytab_entry *)malloc(sizeof (krb5_keytab_entry)); 269 if (!entry) { 270 retval = ENOMEM; 271 break; 272 } 273 memset((char *)entry, 0, sizeof (*entry)); 274 retval = krb5_kt_next_entry(context, kt, entry, &cursor); 275 if (retval) 276 break; 277 278 if (!lp) { /* if list is empty, start one */ 279 lp = (krb5_kt_list)malloc(sizeof (*lp)); 280 if (!lp) { 281 retval = ENOMEM; 282 break; 283 } 284 } else { 285 lp->next = (krb5_kt_list)malloc(sizeof (*lp)); 286 if (!lp->next) { 287 retval = ENOMEM; 288 break; 289 } 290 lp = lp->next; 291 } 292 if (!tail) 293 tail = lp; 294 lp->next = NULL; 295 lp->entry = entry; 296 } 297 if (entry) 298 free((char *)entry); 299 if (retval) 300 if (retval == KRB5_KT_END) 301 retval = 0; 302 else { 303 ktutil_free_kt_list(context, tail); 304 tail = NULL; 305 if (back) 306 back->next = NULL; 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 int getstr(fp, s, n) 357 FILE *fp; 358 register char *s; 359 int n; 360 { 361 register 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 if (lp1->entry->vno < lp->entry->vno) 516 /* Check if lp->entry is newer kvno; if so, update */ 517 lp1->entry = lp->entry; 518 } 519 fp = fopen(name, "w"); 520 if (!fp) { 521 retval = EIO; 522 goto free_pruned; 523 } 524 for (lp = pruned; lp; lp = lp->next) { 525 unsigned char kvno; 526 kvno = (unsigned char) lp->entry->vno; 527 retval = krb5_524_conv_principal(context, 528 lp->entry->principal, 529 sname, sinst, srealm); 530 if (retval) 531 break; 532 fwrite(sname, strlen(sname) + 1, 1, fp); 533 fwrite(sinst, strlen(sinst) + 1, 1, fp); 534 fwrite(srealm, strlen(srealm) + 1, 1, fp); 535 fwrite((char *)&kvno, 1, 1, fp); 536 fwrite((char *)lp->entry->key.contents, 537 sizeof (des_cblock), 1, fp); 538 } 539 fclose(fp); 540 free_pruned: 541 /* 542 * Loop over and free the pruned list; don't use free_kt_list 543 * because that kills the entries. 544 */ 545 for (lp = pruned; lp;) { 546 prev = lp; 547 lp = lp->next; 548 free((char *)prev); 549 } 550 return retval; 551 } 552 #endif /* KRB5_KRB4_COMPAT */ 553