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 */
ktutil_free_kt_list(context,list)40 krb5_error_code ktutil_free_kt_list(context, list)
41 krb5_context context;
42 krb5_kt_list list;
43 {
44 krb5_kt_list lp, prev;
45 krb5_error_code retval = 0;
46
47 for (lp = list; lp;) {
48 retval = krb5_kt_free_entry(context, lp->entry);
49 free(lp->entry);
50 if (retval)
51 break;
52 prev = lp;
53 lp = lp->next;
54 free(prev);
55 }
56 return retval;
57 }
58
59 /*
60 * Delete a numbered entry in a kt_list. Takes a pointer to a kt_list
61 * in case head gets deleted.
62 */
ktutil_delete(context,list,idx)63 krb5_error_code ktutil_delete(context, list, idx)
64 krb5_context context;
65 krb5_kt_list *list;
66 int idx;
67 {
68 krb5_kt_list lp, prev;
69 int i;
70
71 for (lp = *list, i = 1; lp; prev = lp, lp = lp->next, i++) {
72 if (i == idx) {
73 if (i == 1)
74 *list = lp->next;
75 else
76 prev->next = lp->next;
77 lp->next = NULL;
78 return ktutil_free_kt_list(context, lp);
79 }
80 }
81 return EINVAL;
82 }
83
84 /*
85 * Determine the enctype, salt, and s2kparams for princ based on the presence
86 * of the -f flag (fetch), the optionally specified salt string, and the
87 * optionally specified enctype. If the fetch flag is used, salt_str must not
88 * be given; if the fetch flag is not used, the enctype must be given.
89 */
90 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)91 get_etype_info(krb5_context context, krb5_principal princ, int fetch,
92 char *salt_str, krb5_enctype *enctype_inout,
93 krb5_data *salt_out, krb5_data *s2kparams_out)
94 {
95 krb5_error_code retval;
96 krb5_enctype enctype;
97 krb5_get_init_creds_opt *opt = NULL;
98 krb5_data salt;
99
100 *salt_out = empty_data();
101 *s2kparams_out = empty_data();
102
103 if (!fetch) {
104 /* Use the specified enctype and either the specified or default salt.
105 * Do not produce s2kparams. */
106 assert(*enctype_inout != ENCTYPE_NULL);
107 if (salt_str != NULL) {
108 salt = string2data(salt_str);
109 return krb5int_copy_data_contents(context, &salt, salt_out);
110 } else {
111 return krb5_principal2salt(context, princ, salt_out);
112 }
113 }
114
115 /* Get etype-info from the KDC. */
116 assert(salt_str == NULL);
117 if (*enctype_inout != ENCTYPE_NULL) {
118 retval = krb5_get_init_creds_opt_alloc(context, &opt);
119 if (retval)
120 return retval;
121 krb5_get_init_creds_opt_set_etype_list(opt, enctype_inout, 1);
122 }
123 retval = krb5_get_etype_info(context, princ, opt, &enctype, salt_out,
124 s2kparams_out);
125 krb5_get_init_creds_opt_free(context, opt);
126 if (retval)
127 return retval;
128 if (enctype == ENCTYPE_NULL)
129 return KRB5KDC_ERR_ETYPE_NOSUPP;
130
131 *enctype_inout = enctype;
132 return 0;
133 }
134
135 /*
136 * Create a new keytab entry and add it to the keytab list.
137 * Based on the value of use_pass, either prompt the user for a
138 * password or key. If the keytab list is NULL, allocate a new
139 * one first.
140 */
ktutil_add(context,list,princ_str,fetch,kvno,enctype_str,use_pass,salt_str)141 krb5_error_code ktutil_add(context, list, princ_str, fetch, kvno,
142 enctype_str, use_pass, salt_str)
143 krb5_context context;
144 krb5_kt_list *list;
145 char *princ_str;
146 int fetch;
147 krb5_kvno kvno;
148 char *enctype_str;
149 int use_pass;
150 char *salt_str;
151 {
152 krb5_keytab_entry *entry = NULL;
153 krb5_kt_list lp, *last;
154 krb5_principal princ;
155 krb5_enctype enctype = ENCTYPE_NULL;
156 krb5_timestamp now;
157 krb5_error_code retval;
158 krb5_data password = empty_data(), salt = empty_data();
159 krb5_data params = empty_data(), *s2kparams;
160 krb5_keyblock key;
161 char buf[BUFSIZ];
162 char promptstr[1024];
163 char *princ_full = NULL;
164 uint8_t *keybytes;
165 size_t keylen;
166 unsigned int pwsize = BUFSIZ;
167
168 retval = krb5_parse_name(context, princ_str, &princ);
169 if (retval)
170 goto cleanup;
171 /* now unparse in order to get the default realm appended
172 to princ_str, if no realm was specified */
173 retval = krb5_unparse_name(context, princ, &princ_full);
174 if (retval)
175 goto cleanup;
176 if (enctype_str != NULL) {
177 retval = krb5_string_to_enctype(enctype_str, &enctype);
178 if (retval) {
179 retval = KRB5_BAD_ENCTYPE;
180 goto cleanup;
181 }
182 }
183 retval = krb5_timeofday(context, &now);
184 if (retval)
185 goto cleanup;
186
187 entry = k5alloc(sizeof(*entry), &retval);
188 if (entry == NULL)
189 goto cleanup;
190
191 if (use_pass) {
192 retval = alloc_data(&password, pwsize);
193 if (retval)
194 goto cleanup;
195
196 snprintf(promptstr, sizeof(promptstr), _("Password for %.1000s"),
197 princ_full);
198 retval = krb5_read_password(context, promptstr, NULL, password.data,
199 &password.length);
200 if (retval)
201 goto cleanup;
202
203 retval = get_etype_info(context, princ, fetch, salt_str,
204 &enctype, &salt, ¶ms);
205 if (retval)
206 goto cleanup;
207 s2kparams = (params.length > 0) ? ¶ms : NULL;
208 retval = krb5_c_string_to_key_with_params(context, enctype, &password,
209 &salt, s2kparams, &key);
210 if (retval)
211 goto cleanup;
212 entry->key = key;
213 } else {
214 printf(_("Key for %s (hex): "), princ_full);
215 fgets(buf, BUFSIZ, stdin);
216 /*
217 * We need to get rid of the trailing '\n' from fgets.
218 * If we have an even number of hex digits (as we should),
219 * write a '\0' over the '\n'. If for some reason we have
220 * an odd number of hex digits, force an even number of hex
221 * digits by writing a '0' into the last position (the string
222 * will still be null-terminated).
223 */
224 buf[strlen(buf) - 1] = strlen(buf) % 2 ? '\0' : '0';
225 if (strlen(buf) == 0) {
226 fprintf(stderr, _("addent: Error reading key.\n"));
227 retval = 0;
228 goto cleanup;
229 }
230
231 retval = k5_hex_decode(buf, &keybytes, &keylen);
232 if (retval) {
233 if (retval == EINVAL) {
234 fprintf(stderr, _("addent: Illegal character in key.\n"));
235 retval = 0;
236 }
237 goto cleanup;
238 }
239
240 entry->key.enctype = enctype;
241 entry->key.contents = keybytes;
242 entry->key.length = keylen;
243 }
244 entry->principal = princ;
245 entry->vno = kvno;
246 entry->timestamp = now;
247
248 /* Add entry to the end of the list (or create a new list if empty). */
249 lp = k5alloc(sizeof(*lp), &retval);
250 if (lp == NULL)
251 goto cleanup;
252 lp->next = NULL;
253 lp->entry = entry;
254 entry = NULL;
255 for (last = list; *last != NULL; last = &(*last)->next);
256 *last = lp;
257
258 cleanup:
259 krb5_free_keytab_entry_contents(context, entry);
260 free(entry);
261 zapfree(password.data, password.length);
262 krb5_free_data_contents(context, &salt);
263 krb5_free_data_contents(context, ¶ms);
264 krb5_free_unparsed_name(context, princ_full);
265 return retval;
266 }
267
268 /*
269 * Read in a keytab and append it to list. If list starts as NULL,
270 * allocate a new one if necessary.
271 */
ktutil_read_keytab(context,name,list)272 krb5_error_code ktutil_read_keytab(context, name, list)
273 krb5_context context;
274 char *name;
275 krb5_kt_list *list;
276 {
277 krb5_kt_list lp = NULL, tail = NULL, back = NULL;
278 krb5_keytab kt;
279 krb5_keytab_entry *entry;
280 krb5_kt_cursor cursor;
281 krb5_error_code retval = 0;
282
283 if (*list) {
284 /* point lp at the tail of the list */
285 for (lp = *list; lp->next; lp = lp->next);
286 back = lp;
287 }
288 retval = krb5_kt_resolve(context, name, &kt);
289 if (retval)
290 return retval;
291 retval = krb5_kt_start_seq_get(context, kt, &cursor);
292 if (retval)
293 goto close_kt;
294 for (;;) {
295 entry = (krb5_keytab_entry *)malloc(sizeof (krb5_keytab_entry));
296 if (!entry) {
297 retval = ENOMEM;
298 break;
299 }
300 memset(entry, 0, sizeof (*entry));
301 retval = krb5_kt_next_entry(context, kt, entry, &cursor);
302 if (retval)
303 break;
304
305 if (!lp) { /* if list is empty, start one */
306 lp = (krb5_kt_list)malloc(sizeof (*lp));
307 if (!lp) {
308 retval = ENOMEM;
309 break;
310 }
311 } else {
312 lp->next = (krb5_kt_list)malloc(sizeof (*lp));
313 if (!lp->next) {
314 retval = ENOMEM;
315 break;
316 }
317 lp = lp->next;
318 }
319 if (!tail)
320 tail = lp;
321 lp->next = NULL;
322 lp->entry = entry;
323 }
324 if (entry)
325 free(entry);
326 if (retval) {
327 if (retval == KRB5_KT_END)
328 retval = 0;
329 else {
330 ktutil_free_kt_list(context, tail);
331 tail = NULL;
332 if (back)
333 back->next = NULL;
334 }
335 }
336 if (!*list)
337 *list = tail;
338 krb5_kt_end_seq_get(context, kt, &cursor);
339 close_kt:
340 krb5_kt_close(context, kt);
341 return retval;
342 }
343
344 /*
345 * Takes a kt_list and writes it to the named keytab.
346 */
ktutil_write_keytab(context,list,name)347 krb5_error_code ktutil_write_keytab(context, list, name)
348 krb5_context context;
349 krb5_kt_list list;
350 char *name;
351 {
352 krb5_kt_list lp;
353 krb5_keytab kt;
354 char ktname[MAXPATHLEN+sizeof("WRFILE:")+1];
355 krb5_error_code retval = 0;
356 int result;
357
358 result = snprintf(ktname, sizeof(ktname), "WRFILE:%s", name);
359 if (SNPRINTF_OVERFLOW(result, sizeof(ktname)))
360 return ENAMETOOLONG;
361 retval = krb5_kt_resolve(context, ktname, &kt);
362 if (retval)
363 return retval;
364 for (lp = list; lp; lp = lp->next) {
365 retval = krb5_kt_add_entry(context, kt, lp->entry);
366 if (retval)
367 break;
368 }
369 krb5_kt_close(context, kt);
370 return retval;
371 }
372