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