1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kadmin/ktutil/ktutil_funcs.c */
3 /*
4 *(C) Copyright 1995, 1996 by the Massachusetts Institute of Technology.
5 * All Rights Reserved.
6 *
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
11 *
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
25 */
26
27 /*
28 * Utility functions for ktutil.
29 */
30
31 #include "k5-int.h"
32 #include "k5-hex.h"
33 #include "ktutil.h"
34 #include <string.h>
35 #include <ctype.h>
36
37 /*
38 * Free a kt_list
39 */
40 krb5_error_code
ktutil_free_kt_list(krb5_context context,krb5_kt_list list)41 ktutil_free_kt_list(krb5_context context, krb5_kt_list list)
42 {
43 krb5_kt_list lp, prev;
44 krb5_error_code retval = 0;
45
46 for (lp = list; lp;) {
47 retval = krb5_kt_free_entry(context, lp->entry);
48 free(lp->entry);
49 if (retval)
50 break;
51 prev = lp;
52 lp = lp->next;
53 free(prev);
54 }
55 return retval;
56 }
57
58 /*
59 * Delete a numbered entry in a kt_list. Takes a pointer to a kt_list
60 * in case head gets deleted.
61 */
62 krb5_error_code
ktutil_delete(krb5_context context,krb5_kt_list * list,int idx)63 ktutil_delete(krb5_context context, krb5_kt_list *list, int idx)
64 {
65 krb5_kt_list lp, prev;
66 int i;
67
68 for (lp = *list, i = 1; lp; prev = lp, lp = lp->next, i++) {
69 if (i == idx) {
70 if (i == 1)
71 *list = lp->next;
72 else
73 prev->next = lp->next;
74 lp->next = NULL;
75 return ktutil_free_kt_list(context, lp);
76 }
77 }
78 return EINVAL;
79 }
80
81 /*
82 * Determine the enctype, salt, and s2kparams for princ based on the presence
83 * of the -f flag (fetch), the optionally specified salt string, and the
84 * optionally specified enctype. If the fetch flag is used, salt_str must not
85 * be given; if the fetch flag is not used, the enctype must be given.
86 */
87 static krb5_error_code
get_etype_info(krb5_context context,krb5_principal princ,int fetch,char * salt_str,krb5_enctype * enctype_inout,krb5_data * salt_out,krb5_data * s2kparams_out)88 get_etype_info(krb5_context context, krb5_principal princ, int fetch,
89 char *salt_str, krb5_enctype *enctype_inout,
90 krb5_data *salt_out, krb5_data *s2kparams_out)
91 {
92 krb5_error_code retval;
93 krb5_enctype enctype;
94 krb5_get_init_creds_opt *opt = NULL;
95 krb5_data salt;
96
97 *salt_out = empty_data();
98 *s2kparams_out = empty_data();
99
100 if (!fetch) {
101 /* Use the specified enctype and either the specified or default salt.
102 * Do not produce s2kparams. */
103 assert(*enctype_inout != ENCTYPE_NULL);
104 if (salt_str != NULL) {
105 salt = string2data(salt_str);
106 return krb5int_copy_data_contents(context, &salt, salt_out);
107 } else {
108 return krb5_principal2salt(context, princ, salt_out);
109 }
110 }
111
112 /* Get etype-info from the KDC. */
113 assert(salt_str == NULL);
114 if (*enctype_inout != ENCTYPE_NULL) {
115 retval = krb5_get_init_creds_opt_alloc(context, &opt);
116 if (retval)
117 return retval;
118 krb5_get_init_creds_opt_set_etype_list(opt, enctype_inout, 1);
119 }
120 retval = krb5_get_etype_info(context, princ, opt, &enctype, salt_out,
121 s2kparams_out);
122 krb5_get_init_creds_opt_free(context, opt);
123 if (retval)
124 return retval;
125 if (enctype == ENCTYPE_NULL)
126 return KRB5KDC_ERR_ETYPE_NOSUPP;
127
128 *enctype_inout = enctype;
129 return 0;
130 }
131
132 /*
133 * Create a new keytab entry and add it to the keytab list.
134 * Based on the value of use_pass, either prompt the user for a
135 * password or key. If the keytab list is NULL, allocate a new
136 * one first.
137 */
138 krb5_error_code
ktutil_add(krb5_context context,krb5_kt_list * list,char * princ_str,int fetch,krb5_kvno kvno,char * enctype_str,int use_pass,char * salt_str)139 ktutil_add(krb5_context context, krb5_kt_list *list, char *princ_str,
140 int fetch, krb5_kvno kvno, char *enctype_str, int use_pass,
141 char *salt_str)
142 {
143 krb5_keytab_entry *entry = NULL;
144 krb5_kt_list lp, *last;
145 krb5_principal princ;
146 krb5_enctype enctype = ENCTYPE_NULL;
147 krb5_timestamp now;
148 krb5_error_code retval;
149 krb5_data password = empty_data(), salt = empty_data();
150 krb5_data params = empty_data(), *s2kparams;
151 krb5_keyblock key;
152 char buf[BUFSIZ];
153 char promptstr[1024];
154 char *princ_full = NULL;
155 uint8_t *keybytes;
156 size_t keylen;
157 unsigned int pwsize = BUFSIZ;
158
159 retval = krb5_parse_name(context, princ_str, &princ);
160 if (retval)
161 goto cleanup;
162 /* now unparse in order to get the default realm appended
163 to princ_str, if no realm was specified */
164 retval = krb5_unparse_name(context, princ, &princ_full);
165 if (retval)
166 goto cleanup;
167 if (enctype_str != NULL) {
168 retval = krb5_string_to_enctype(enctype_str, &enctype);
169 if (retval) {
170 retval = KRB5_BAD_ENCTYPE;
171 goto cleanup;
172 }
173 }
174 retval = krb5_timeofday(context, &now);
175 if (retval)
176 goto cleanup;
177
178 entry = k5alloc(sizeof(*entry), &retval);
179 if (entry == NULL)
180 goto cleanup;
181
182 if (use_pass) {
183 retval = alloc_data(&password, pwsize);
184 if (retval)
185 goto cleanup;
186
187 snprintf(promptstr, sizeof(promptstr), _("Password for %.1000s"),
188 princ_full);
189 retval = krb5_read_password(context, promptstr, NULL, password.data,
190 &password.length);
191 if (retval)
192 goto cleanup;
193
194 retval = get_etype_info(context, princ, fetch, salt_str,
195 &enctype, &salt, ¶ms);
196 if (retval)
197 goto cleanup;
198 s2kparams = (params.length > 0) ? ¶ms : NULL;
199 retval = krb5_c_string_to_key_with_params(context, enctype, &password,
200 &salt, s2kparams, &key);
201 if (retval)
202 goto cleanup;
203 entry->key = key;
204 } else {
205 printf(_("Key for %s (hex): "), princ_full);
206 fgets(buf, BUFSIZ, stdin);
207 /*
208 * We need to get rid of the trailing '\n' from fgets.
209 * If we have an even number of hex digits (as we should),
210 * write a '\0' over the '\n'. If for some reason we have
211 * an odd number of hex digits, force an even number of hex
212 * digits by writing a '0' into the last position (the string
213 * will still be null-terminated).
214 */
215 buf[strlen(buf) - 1] = strlen(buf) % 2 ? '\0' : '0';
216 if (strlen(buf) == 0) {
217 fprintf(stderr, _("addent: Error reading key.\n"));
218 retval = 0;
219 goto cleanup;
220 }
221
222 retval = k5_hex_decode(buf, &keybytes, &keylen);
223 if (retval) {
224 if (retval == EINVAL) {
225 fprintf(stderr, _("addent: Illegal character in key.\n"));
226 retval = 0;
227 }
228 goto cleanup;
229 }
230
231 entry->key.enctype = enctype;
232 entry->key.contents = keybytes;
233 entry->key.length = keylen;
234 }
235 entry->principal = princ;
236 entry->vno = kvno;
237 entry->timestamp = now;
238
239 /* Add entry to the end of the list (or create a new list if empty). */
240 lp = k5alloc(sizeof(*lp), &retval);
241 if (lp == NULL)
242 goto cleanup;
243 lp->next = NULL;
244 lp->entry = entry;
245 entry = NULL;
246 for (last = list; *last != NULL; last = &(*last)->next);
247 *last = lp;
248
249 cleanup:
250 krb5_free_keytab_entry_contents(context, entry);
251 free(entry);
252 zapfree(password.data, password.length);
253 krb5_free_data_contents(context, &salt);
254 krb5_free_data_contents(context, ¶ms);
255 krb5_free_unparsed_name(context, princ_full);
256 return retval;
257 }
258
259 /*
260 * Read in a keytab and append it to list. If list starts as NULL,
261 * allocate a new one if necessary.
262 */
263 krb5_error_code
ktutil_read_keytab(krb5_context context,char * name,krb5_kt_list * list)264 ktutil_read_keytab(krb5_context context, char *name, krb5_kt_list *list)
265 {
266 krb5_kt_list lp = NULL, tail = NULL, back = NULL;
267 krb5_keytab kt;
268 krb5_keytab_entry *entry;
269 krb5_kt_cursor cursor;
270 krb5_error_code retval = 0;
271
272 if (*list) {
273 /* point lp at the tail of the list */
274 for (lp = *list; lp->next; lp = lp->next);
275 back = lp;
276 }
277 retval = krb5_kt_resolve(context, name, &kt);
278 if (retval)
279 return retval;
280 retval = krb5_kt_start_seq_get(context, kt, &cursor);
281 if (retval)
282 goto close_kt;
283 for (;;) {
284 entry = (krb5_keytab_entry *)malloc(sizeof (krb5_keytab_entry));
285 if (!entry) {
286 retval = ENOMEM;
287 break;
288 }
289 memset(entry, 0, sizeof (*entry));
290 retval = krb5_kt_next_entry(context, kt, entry, &cursor);
291 if (retval)
292 break;
293
294 if (!lp) { /* if list is empty, start one */
295 lp = (krb5_kt_list)malloc(sizeof (*lp));
296 if (!lp) {
297 retval = ENOMEM;
298 break;
299 }
300 } else {
301 lp->next = (krb5_kt_list)malloc(sizeof (*lp));
302 if (!lp->next) {
303 retval = ENOMEM;
304 break;
305 }
306 lp = lp->next;
307 }
308 if (!tail)
309 tail = lp;
310 lp->next = NULL;
311 lp->entry = entry;
312 }
313 if (entry)
314 free(entry);
315 if (retval) {
316 if (retval == KRB5_KT_END)
317 retval = 0;
318 else {
319 ktutil_free_kt_list(context, tail);
320 tail = NULL;
321 if (back)
322 back->next = NULL;
323 }
324 }
325 if (!*list)
326 *list = tail;
327 krb5_kt_end_seq_get(context, kt, &cursor);
328 close_kt:
329 krb5_kt_close(context, kt);
330 return retval;
331 }
332
333 /*
334 * Takes a kt_list and writes it to the named keytab.
335 */
336 krb5_error_code
ktutil_write_keytab(krb5_context context,krb5_kt_list list,char * name)337 ktutil_write_keytab(krb5_context context, krb5_kt_list list, char *name)
338 {
339 krb5_kt_list lp;
340 krb5_keytab kt;
341 char ktname[MAXPATHLEN+sizeof("WRFILE:")+1];
342 krb5_error_code retval = 0;
343 int result;
344
345 result = snprintf(ktname, sizeof(ktname), "WRFILE:%s", name);
346 if (SNPRINTF_OVERFLOW(result, sizeof(ktname)))
347 return ENAMETOOLONG;
348 retval = krb5_kt_resolve(context, ktname, &kt);
349 if (retval)
350 return retval;
351 for (lp = list; lp; lp = lp->next) {
352 retval = krb5_kt_add_entry(context, kt, lp->entry);
353 if (retval)
354 break;
355 }
356 krb5_kt_close(context, kt);
357 return retval;
358 }
359