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 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 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 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 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 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 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 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 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