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