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(ksaltlist,nksalts,enctype,salttype)270 krb5_keysalt_is_present(ksaltlist, nksalts, enctype, salttype)
271 krb5_key_salt_tuple *ksaltlist;
272 krb5_int32 nksalts;
273 krb5_enctype enctype;
274 krb5_int32 salttype;
275 {
276 krb5_boolean foundit;
277 int i;
278
279 foundit = 0;
280 if (ksaltlist) {
281 for (i=0; i<nksalts; i++) {
282 if ((ksaltlist[i].ks_enctype == enctype) &&
283 ((ksaltlist[i].ks_salttype == salttype) ||
284 (salttype < 0))) {
285 foundit = 1;
286 break;
287 }
288 }
289 }
290 return(foundit);
291 }
292
293 /* NOTE: This is a destructive parser (writes NULs). */
294 static krb5_error_code
string_to_keysalt(char * s,const char * ksaltseps,krb5_enctype * etype,krb5_int32 * stype)295 string_to_keysalt(char *s, const char *ksaltseps,
296 krb5_enctype *etype, krb5_int32 *stype)
297 {
298 char *sp;
299 const char *ksseps = (ksaltseps != NULL) ? ksaltseps : default_ksaltseps;
300 krb5_error_code ret = 0;
301
302 sp = strpbrk(s, ksseps);
303 if (sp != NULL) {
304 *sp++ = '\0';
305 }
306 ret = krb5_string_to_enctype(s, etype);
307 if (ret)
308 return ret;
309
310 /* Default to normal salt if omitted. */
311 *stype = KRB5_KDB_SALTTYPE_NORMAL;
312 if (sp == NULL)
313 return 0;
314 return krb5_string_to_salttype(sp, stype);
315 }
316
317 /*
318 * krb5_string_to_keysalts() - Convert a string representation to a list
319 * of key/salt tuples.
320 */
321 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)322 krb5_string_to_keysalts(const char *string, const char *tupleseps,
323 const char *ksaltseps, krb5_boolean dups,
324 krb5_key_salt_tuple **ksaltp, krb5_int32 *nksaltp)
325 {
326 char *copy, *p, *ksp;
327 char *tlasts = NULL;
328 const char *tseps = (tupleseps != NULL) ? tupleseps : default_tupleseps;
329 krb5_int32 nksalts = 0;
330 krb5_int32 stype;
331 krb5_enctype etype;
332 krb5_error_code ret = 0;
333 krb5_key_salt_tuple *ksalts = NULL, *ksalts_new = NULL;
334
335 *ksaltp = NULL;
336 *nksaltp = 0;
337 p = copy = strdup(string);
338 if (p == NULL)
339 return ENOMEM;
340 while ((ksp = strtok_r(p, tseps, &tlasts)) != NULL) {
341 /* Pass a null pointer to subsequent calls to strtok_r(). */
342 p = NULL;
343
344 /* Discard unrecognized keysalts. */
345 if (string_to_keysalt(ksp, ksaltseps, &etype, &stype) != 0)
346 continue;
347
348 /* Ignore duplicate keysalts if caller asks. */
349 if (!dups && krb5_keysalt_is_present(ksalts, nksalts, etype, stype))
350 continue;
351
352 ksalts_new = realloc(ksalts, (nksalts + 1) * sizeof(*ksalts));
353 if (ksalts_new == NULL) {
354 ret = ENOMEM;
355 goto cleanup;
356 }
357 ksalts = ksalts_new;
358 ksalts[nksalts].ks_enctype = etype;
359 ksalts[nksalts].ks_salttype = stype;
360 nksalts++;
361 }
362 *ksaltp = ksalts;
363 *nksaltp = nksalts;
364 cleanup:
365 if (ret)
366 free(ksalts);
367 free(copy);
368 return ret;
369 }
370
371 /*
372 * krb5_keysalt_iterate() - Do something for each unique key/salt
373 * combination.
374 *
375 * If ignoresalt set, then salttype is ignored.
376 */
377 krb5_error_code
krb5_keysalt_iterate(ksaltlist,nksalt,ignoresalt,iterator,arg)378 krb5_keysalt_iterate(ksaltlist, nksalt, ignoresalt, iterator, arg)
379 krb5_key_salt_tuple *ksaltlist;
380 krb5_int32 nksalt;
381 krb5_boolean ignoresalt;
382 krb5_error_code (*iterator) (krb5_key_salt_tuple *, krb5_pointer);
383 krb5_pointer arg;
384 {
385 int i;
386 krb5_error_code kret;
387 krb5_key_salt_tuple scratch;
388
389 kret = 0;
390 for (i=0; i<nksalt; i++) {
391 scratch.ks_enctype = ksaltlist[i].ks_enctype;
392 scratch.ks_salttype = (ignoresalt) ? -1 : ksaltlist[i].ks_salttype;
393 if (!krb5_keysalt_is_present(ksaltlist,
394 i,
395 scratch.ks_enctype,
396 scratch.ks_salttype)) {
397 kret = (*iterator)(&scratch, arg);
398 if (kret)
399 break;
400 }
401 }
402 return(kret);
403 }
404