xref: /freebsd/crypto/krb5/src/kadmin/ktutil/ktutil_funcs.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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, &params);
196         if (retval)
197             goto cleanup;
198         s2kparams = (params.length > 0) ? &params : 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, &params);
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