xref: /freebsd/crypto/krb5/src/lib/kadm5/str_conv.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/kadm5/str_conv.c */
3 /*
4  * Copyright (C) 1995-2015 by the Massachusetts Institute of Technology.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  *
14  * * Redistributions in binary form must reproduce the above copyright
15  *   notice, this list of conditions and the following disclaimer in
16  *   the documentation and/or other materials provided with the
17  *   distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30  * OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /* Convert between strings and Kerberos internal data. */
34 
35 #include "k5-int.h"
36 #include "admin_internal.h"
37 #include "adm_proto.h"
38 
39 #include <ctype.h>
40 
41 static const char default_tupleseps[]   = ", \t";
42 static const char default_ksaltseps[]   = ":";
43 
44 struct flag_table_row {
45     const char *spec;           /* Input specifier string */
46     krb5_flags flag;            /* Flag */
47     int invert;                 /* Whether to invert the sense */
48 };
49 
50 static const struct flag_table_row ftbl[] = {
51     {"allow_postdated",         KRB5_KDB_DISALLOW_POSTDATED,    1},
52     {"postdateable",            KRB5_KDB_DISALLOW_POSTDATED,    1},
53     {"disallow_postdated",      KRB5_KDB_DISALLOW_POSTDATED,    0},
54     {"allow_forwardable",       KRB5_KDB_DISALLOW_FORWARDABLE,  1},
55     {"forwardable",             KRB5_KDB_DISALLOW_FORWARDABLE,  1},
56     {"disallow_forwardable",    KRB5_KDB_DISALLOW_FORWARDABLE,  0},
57     {"allow_tgs_req",           KRB5_KDB_DISALLOW_TGT_BASED,    1},
58     {"tgt_based",               KRB5_KDB_DISALLOW_TGT_BASED,    1},
59     {"disallow_tgt_based",      KRB5_KDB_DISALLOW_TGT_BASED,    0},
60     {"allow_renewable",         KRB5_KDB_DISALLOW_RENEWABLE,    1},
61     {"renewable",               KRB5_KDB_DISALLOW_RENEWABLE,    1},
62     {"disallow_renewable",      KRB5_KDB_DISALLOW_RENEWABLE,    0},
63     {"allow_proxiable",         KRB5_KDB_DISALLOW_PROXIABLE,    1},
64     {"proxiable",               KRB5_KDB_DISALLOW_PROXIABLE,    1},
65     {"disallow_proxiable",      KRB5_KDB_DISALLOW_PROXIABLE,    0},
66     {"allow_dup_skey",          KRB5_KDB_DISALLOW_DUP_SKEY,     1},
67     {"dup_skey",                KRB5_KDB_DISALLOW_DUP_SKEY,     1},
68     {"disallow_dup_skey",       KRB5_KDB_DISALLOW_DUP_SKEY,     0},
69     {"allow_tickets",           KRB5_KDB_DISALLOW_ALL_TIX,      1},
70     {"allow_tix",               KRB5_KDB_DISALLOW_ALL_TIX,      1},
71     {"disallow_all_tix",        KRB5_KDB_DISALLOW_ALL_TIX,      0},
72     {"preauth",                 KRB5_KDB_REQUIRES_PRE_AUTH,     0},
73     {"requires_pre_auth",       KRB5_KDB_REQUIRES_PRE_AUTH,     0},
74     {"requires_preauth",        KRB5_KDB_REQUIRES_PRE_AUTH,     0},
75     {"hwauth",                  KRB5_KDB_REQUIRES_HW_AUTH,      0},
76     {"requires_hw_auth",        KRB5_KDB_REQUIRES_HW_AUTH,      0},
77     {"requires_hwauth",         KRB5_KDB_REQUIRES_HW_AUTH,      0},
78     {"needchange",              KRB5_KDB_REQUIRES_PWCHANGE,     0},
79     {"pwchange",                KRB5_KDB_REQUIRES_PWCHANGE,     0},
80     {"requires_pwchange",       KRB5_KDB_REQUIRES_PWCHANGE,     0},
81     {"allow_svr",               KRB5_KDB_DISALLOW_SVR,          1},
82     {"service",                 KRB5_KDB_DISALLOW_SVR,          1},
83     {"disallow_svr",            KRB5_KDB_DISALLOW_SVR,          0},
84     {"password_changing_service", KRB5_KDB_PWCHANGE_SERVICE,    0},
85     {"pwchange_service",        KRB5_KDB_PWCHANGE_SERVICE,      0},
86     {"pwservice",               KRB5_KDB_PWCHANGE_SERVICE,      0},
87     {"md5",                     KRB5_KDB_SUPPORT_DESMD5,        0},
88     {"support_desmd5",          KRB5_KDB_SUPPORT_DESMD5,        0},
89     {"new_princ",               KRB5_KDB_NEW_PRINC,             0},
90     {"ok_as_delegate",          KRB5_KDB_OK_AS_DELEGATE,        0},
91     {"ok_to_auth_as_delegate",  KRB5_KDB_OK_TO_AUTH_AS_DELEGATE, 0},
92     {"no_auth_data_required",   KRB5_KDB_NO_AUTH_DATA_REQUIRED, 0},
93     {"lockdown_keys",           KRB5_KDB_LOCKDOWN_KEYS,         0},
94 };
95 #define NFTBL (sizeof(ftbl) / sizeof(ftbl[0]))
96 
97 static const char *outflags[] = {
98     "DISALLOW_POSTDATED",       /* 0x00000001 */
99     "DISALLOW_FORWARDABLE",     /* 0x00000002 */
100     "DISALLOW_TGT_BASED",       /* 0x00000004 */
101     "DISALLOW_RENEWABLE",       /* 0x00000008 */
102     "DISALLOW_PROXIABLE",       /* 0x00000010 */
103     "DISALLOW_DUP_SKEY",        /* 0x00000020 */
104     "DISALLOW_ALL_TIX",         /* 0x00000040 */
105     "REQUIRES_PRE_AUTH",        /* 0x00000080 */
106     "REQUIRES_HW_AUTH",         /* 0x00000100 */
107     "REQUIRES_PWCHANGE",        /* 0x00000200 */
108     NULL,                       /* 0x00000400 */
109     NULL,                       /* 0x00000800 */
110     "DISALLOW_SVR",             /* 0x00001000 */
111     "PWCHANGE_SERVICE",         /* 0x00002000 */
112     "SUPPORT_DESMD5",           /* 0x00004000 */
113     "NEW_PRINC",                /* 0x00008000 */
114     NULL,                       /* 0x00010000 */
115     NULL,                       /* 0x00020000 */
116     NULL,                       /* 0x00040000 */
117     NULL,                       /* 0x00080000 */
118     "OK_AS_DELEGATE",           /* 0x00100000 */
119     "OK_TO_AUTH_AS_DELEGATE",   /* 0x00200000 */
120     "NO_AUTH_DATA_REQUIRED",    /* 0x00400000 */
121     "LOCKDOWN_KEYS",            /* 0x00800000 */
122 };
123 #define NOUTFLAGS (sizeof(outflags) / sizeof(outflags[0]))
124 
125 /*
126  * Given s, which is a normalized flagspec with the prefix stripped off, and
127  * req_neg indicating whether the flagspec is negated, update the toset and
128  * toclear masks.
129  */
130 static krb5_error_code
raw_flagspec_to_mask(const char * s,int req_neg,krb5_flags * toset,krb5_flags * toclear)131 raw_flagspec_to_mask(const char *s, int req_neg, krb5_flags *toset,
132                      krb5_flags *toclear)
133 {
134     int found = 0, invert = 0;
135     size_t i;
136     krb5_flags flag;
137     unsigned long ul;
138 
139     for (i = 0; !found && i < NFTBL; i++) {
140         if (strcmp(s, ftbl[i].spec) != 0)
141             continue;
142         /* Found a match */
143         found = 1;
144         invert = ftbl[i].invert;
145         flag = ftbl[i].flag;
146     }
147     /* Accept hexadecimal numbers. */
148     if (!found && strncmp(s, "0x", 2) == 0) {
149         /* Assume that krb5_flags are 32 bits long. */
150         ul = strtoul(s, NULL, 16) & 0xffffffff;
151         flag = (krb5_flags)ul;
152         found = 1;
153     }
154     if (!found)
155         return EINVAL;
156     if (req_neg)
157         invert = !invert;
158     if (invert)
159         *toclear &= ~flag;
160     else
161         *toset |= flag;
162     return 0;
163 }
164 
165 /*
166  * Update the toset and toclear flag masks according to flag specifier string
167  * spec, which is of the form {+|-}flagname.  toset and toclear can point to
168  * the same flag word.
169  */
170 krb5_error_code
krb5_flagspec_to_mask(const char * spec,krb5_flags * toset,krb5_flags * toclear)171 krb5_flagspec_to_mask(const char *spec, krb5_flags *toset, krb5_flags *toclear)
172 {
173     int req_neg = 0;
174     char *copy, *cp, *s;
175     krb5_error_code retval;
176 
177     s = copy = strdup(spec);
178     if (s == NULL)
179         return ENOMEM;
180 
181     if (*s == '-') {
182         req_neg = 1;
183         s++;
184     } else if (*s == '+')
185         s++;
186 
187     for (cp = s; *cp != '\0'; cp++) {
188         /* Transform hyphens to underscores.*/
189         if (*cp == '-')
190             *cp = '_';
191         /* Downcase. */
192         if (isupper((unsigned char)*cp))
193             *cp = tolower((unsigned char)*cp);
194     }
195     retval = raw_flagspec_to_mask(s, req_neg, toset, toclear);
196     free(copy);
197     return retval;
198 }
199 
200 /*
201  * Copy the flag name of flagnum to outstr.  On error, outstr points to a null
202  * pointer.
203  */
204 krb5_error_code
krb5_flagnum_to_string(int flagnum,char ** outstr)205 krb5_flagnum_to_string(int flagnum, char **outstr)
206 {
207     const char *s = NULL;
208 
209     *outstr = NULL;
210     if ((unsigned int)flagnum < NOUTFLAGS)
211         s = outflags[flagnum];
212     if (s == NULL) {
213         /* Assume that krb5_flags are 32 bits long. */
214         if (asprintf(outstr, "0x%08lx", 1UL << flagnum) == -1)
215             *outstr = NULL;
216     } else {
217         *outstr = strdup(s);
218     }
219     if (*outstr == NULL)
220         return ENOMEM;
221     return 0;
222 }
223 
224 /*
225  * Create a null-terminated array of string representations of flags.  Store a
226  * null pointer into outarray if there would be no strings.
227  */
228 krb5_error_code
krb5_flags_to_strings(krb5_int32 flags,char *** outarray)229 krb5_flags_to_strings(krb5_int32 flags, char ***outarray)
230 {
231     char **a = NULL, **a_new = NULL, **ap;
232     size_t amax = 0, i;
233     krb5_error_code retval;
234 
235     *outarray = NULL;
236 
237     /* Assume that krb5_flags are 32 bits long. */
238     for (i = 0; i < 32; i++) {
239         if (!(flags & (1UL << i)))
240             continue;
241 
242         a_new = realloc(a, (amax + 2) * sizeof(*a));
243         if (a_new == NULL) {
244             retval = ENOMEM;
245             goto cleanup;
246         }
247         a = a_new;
248         retval = krb5_flagnum_to_string(i, &a[amax++]);
249         a[amax] = NULL;
250         if (retval)
251             goto cleanup;
252     }
253     *outarray = a;
254     return 0;
255 cleanup:
256     for (ap = a; ap != NULL && *ap != NULL; ap++) {
257         free(*ap);
258     }
259     free(a);
260     return retval;
261 }
262 
263 /*
264  * krb5_keysalt_is_present()    - Determine if a key/salt pair is present
265  *                                in a list of key/salt tuples.
266  *
267  *      Salttype may be negative to indicate a search for only a enctype.
268  */
269 krb5_boolean
krb5_keysalt_is_present(krb5_key_salt_tuple * ksaltlist,krb5_int32 nksalts,krb5_enctype enctype,krb5_int32 salttype)270 krb5_keysalt_is_present(krb5_key_salt_tuple *ksaltlist, krb5_int32 nksalts,
271                         krb5_enctype enctype, krb5_int32 salttype)
272 {
273     krb5_boolean        foundit;
274     int                 i;
275 
276     foundit = 0;
277     if (ksaltlist) {
278         for (i=0; i<nksalts; i++) {
279             if ((ksaltlist[i].ks_enctype == enctype) &&
280                 ((ksaltlist[i].ks_salttype == salttype) ||
281                  (salttype < 0))) {
282                 foundit = 1;
283                 break;
284             }
285         }
286     }
287     return(foundit);
288 }
289 
290 /* NOTE: This is a destructive parser (writes NULs). */
291 static krb5_error_code
string_to_keysalt(char * s,const char * ksaltseps,krb5_enctype * etype,krb5_int32 * stype)292 string_to_keysalt(char *s, const char *ksaltseps,
293                   krb5_enctype *etype, krb5_int32 *stype)
294 {
295     char *sp;
296     const char *ksseps = (ksaltseps != NULL) ? ksaltseps : default_ksaltseps;
297     krb5_error_code ret = 0;
298 
299     sp = strpbrk(s, ksseps);
300     if (sp != NULL) {
301         *sp++ = '\0';
302     }
303     ret = krb5_string_to_enctype(s, etype);
304     if (ret)
305         return ret;
306 
307     /* Default to normal salt if omitted. */
308     *stype = KRB5_KDB_SALTTYPE_NORMAL;
309     if (sp == NULL)
310         return 0;
311     return krb5_string_to_salttype(sp, stype);
312 }
313 
314 /*
315  * krb5_string_to_keysalts()    - Convert a string representation to a list
316  *                                of key/salt tuples.
317  */
318 krb5_error_code
krb5_string_to_keysalts(const char * string,const char * tupleseps,const char * ksaltseps,krb5_boolean dups,krb5_key_salt_tuple ** ksaltp,krb5_int32 * nksaltp)319 krb5_string_to_keysalts(const char *string, const char *tupleseps,
320                         const char *ksaltseps, krb5_boolean dups,
321                         krb5_key_salt_tuple **ksaltp, krb5_int32 *nksaltp)
322 {
323     char *copy, *p, *ksp;
324     char *tlasts = NULL;
325     const char *tseps = (tupleseps != NULL) ? tupleseps : default_tupleseps;
326     krb5_int32 nksalts = 0;
327     krb5_int32 stype;
328     krb5_enctype etype;
329     krb5_error_code ret = 0;
330     krb5_key_salt_tuple *ksalts = NULL, *ksalts_new = NULL;
331 
332     *ksaltp = NULL;
333     *nksaltp = 0;
334     p = copy = strdup(string);
335     if (p == NULL)
336         return ENOMEM;
337     while ((ksp = strtok_r(p, tseps, &tlasts)) != NULL) {
338         /* Pass a null pointer to subsequent calls to strtok_r(). */
339         p = NULL;
340 
341         /* Discard unrecognized keysalts. */
342         if (string_to_keysalt(ksp, ksaltseps, &etype, &stype) != 0)
343             continue;
344 
345         /* Ignore duplicate keysalts if caller asks. */
346         if (!dups && krb5_keysalt_is_present(ksalts, nksalts, etype, stype))
347             continue;
348 
349         ksalts_new = realloc(ksalts, (nksalts + 1) * sizeof(*ksalts));
350         if (ksalts_new == NULL) {
351             ret = ENOMEM;
352             goto cleanup;
353         }
354         ksalts = ksalts_new;
355         ksalts[nksalts].ks_enctype = etype;
356         ksalts[nksalts].ks_salttype = stype;
357         nksalts++;
358     }
359     *ksaltp = ksalts;
360     *nksaltp = nksalts;
361 cleanup:
362     if (ret)
363         free(ksalts);
364     free(copy);
365     return ret;
366 }
367 
368 /*
369  * krb5_keysalt_iterate()       - Do something for each unique key/salt
370  *                                combination.
371  *
372  * If ignoresalt set, then salttype is ignored.
373  */
374 krb5_error_code
krb5_keysalt_iterate(krb5_key_salt_tuple * ksaltlist,krb5_int32 nksalt,krb5_boolean ignoresalt,krb5_error_code (* iterator)(krb5_key_salt_tuple *,void *),void * arg)375 krb5_keysalt_iterate(krb5_key_salt_tuple *ksaltlist, krb5_int32 nksalt,
376                      krb5_boolean ignoresalt,
377                      krb5_error_code (*iterator)(krb5_key_salt_tuple *,
378                                                  void *),
379                      void *arg)
380 {
381     int                 i;
382     krb5_error_code     kret;
383     krb5_key_salt_tuple scratch;
384 
385     kret = 0;
386     for (i=0; i<nksalt; i++) {
387         scratch.ks_enctype = ksaltlist[i].ks_enctype;
388         scratch.ks_salttype = (ignoresalt) ? -1 : ksaltlist[i].ks_salttype;
389         if (!krb5_keysalt_is_present(ksaltlist,
390                                      i,
391                                      scratch.ks_enctype,
392                                      scratch.ks_salttype)) {
393             kret = (*iterator)(&scratch, arg);
394             if (kret)
395                 break;
396         }
397     }
398     return(kret);
399 }
400