1/* 2 * Copyright (c) 2004 - 2005 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2010 Apple Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include "hdb_locl.h" 37#include <der.h> 38 39krb5_error_code 40hdb_entry_check_mandatory(krb5_context context, const hdb_entry *ent) 41{ 42 size_t i; 43 44 if (ent->extensions == NULL) 45 return 0; 46 47 /* 48 * check for unknown extensions and if they where tagged mandatory 49 */ 50 51 for (i = 0; i < ent->extensions->len; i++) { 52 if (ent->extensions->val[i].data.element != 53 choice_HDB_extension_data_asn1_ellipsis) 54 continue; 55 if (ent->extensions->val[i].mandatory) { 56 krb5_set_error_message(context, HDB_ERR_MANDATORY_OPTION, 57 "Principal have unknown " 58 "mandatory extension"); 59 return HDB_ERR_MANDATORY_OPTION; 60 } 61 } 62 return 0; 63} 64 65HDB_extension * 66hdb_find_extension(const hdb_entry *entry, int type) 67{ 68 size_t i; 69 70 if (entry->extensions == NULL) 71 return NULL; 72 73 for (i = 0; i < entry->extensions->len; i++) 74 if (entry->extensions->val[i].data.element == (unsigned)type) 75 return &entry->extensions->val[i]; 76 return NULL; 77} 78 79/* 80 * Replace the extension `ext' in `entry'. Make a copy of the 81 * extension, so the caller must still free `ext' on both success and 82 * failure. Returns 0 or error code. 83 */ 84 85krb5_error_code 86hdb_replace_extension(krb5_context context, 87 hdb_entry *entry, 88 const HDB_extension *ext) 89{ 90 HDB_extension *ext2; 91 HDB_extension *es; 92 int ret; 93 94 ext2 = NULL; 95 96 if (entry->extensions == NULL) { 97 entry->extensions = calloc(1, sizeof(*entry->extensions)); 98 if (entry->extensions == NULL) { 99 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 100 return ENOMEM; 101 } 102 } else if (ext->data.element != choice_HDB_extension_data_asn1_ellipsis) { 103 ext2 = hdb_find_extension(entry, ext->data.element); 104 } else { 105 /* 106 * This is an unknown extention, and we are asked to replace a 107 * possible entry in `entry' that is of the same type. This 108 * might seem impossible, but ASN.1 CHOICE comes to our 109 * rescue. The first tag in each branch in the CHOICE is 110 * unique, so just find the element in the list that have the 111 * same tag was we are putting into the list. 112 */ 113 Der_class replace_class, list_class; 114 Der_type replace_type, list_type; 115 unsigned int replace_tag, list_tag; 116 size_t size; 117 size_t i; 118 119 ret = der_get_tag(ext->data.u.asn1_ellipsis.data, 120 ext->data.u.asn1_ellipsis.length, 121 &replace_class, &replace_type, &replace_tag, 122 &size); 123 if (ret) { 124 krb5_set_error_message(context, ret, "hdb: failed to decode " 125 "replacement hdb extention"); 126 return ret; 127 } 128 129 for (i = 0; i < entry->extensions->len; i++) { 130 HDB_extension *ext3 = &entry->extensions->val[i]; 131 132 if (ext3->data.element != choice_HDB_extension_data_asn1_ellipsis) 133 continue; 134 135 ret = der_get_tag(ext3->data.u.asn1_ellipsis.data, 136 ext3->data.u.asn1_ellipsis.length, 137 &list_class, &list_type, &list_tag, 138 &size); 139 if (ret) { 140 krb5_set_error_message(context, ret, "hdb: failed to decode " 141 "present hdb extention"); 142 return ret; 143 } 144 145 if (MAKE_TAG(replace_class,replace_type,replace_type) == 146 MAKE_TAG(list_class,list_type,list_type)) { 147 ext2 = ext3; 148 break; 149 } 150 } 151 } 152 153 if (ext2) { 154 free_HDB_extension(ext2); 155 ret = copy_HDB_extension(ext, ext2); 156 if (ret) 157 krb5_set_error_message(context, ret, "hdb: failed to copy replacement " 158 "hdb extention"); 159 return ret; 160 } 161 162 es = realloc(entry->extensions->val, 163 (entry->extensions->len+1)*sizeof(entry->extensions->val[0])); 164 if (es == NULL) { 165 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 166 return ENOMEM; 167 } 168 entry->extensions->val = es; 169 170 ret = copy_HDB_extension(ext, 171 &entry->extensions->val[entry->extensions->len]); 172 if (ret == 0) 173 entry->extensions->len++; 174 else 175 krb5_set_error_message(context, ret, "hdb: failed to copy new extension"); 176 177 return ret; 178} 179 180krb5_error_code 181hdb_clear_extension(krb5_context context, 182 hdb_entry *entry, 183 int type) 184{ 185 size_t i; 186 187 if (entry->extensions == NULL) 188 return 0; 189 190 for (i = 0; i < entry->extensions->len; i++) { 191 if (entry->extensions->val[i].data.element == (unsigned)type) { 192 free_HDB_extension(&entry->extensions->val[i]); 193 memmove(&entry->extensions->val[i], 194 &entry->extensions->val[i + 1], 195 sizeof(entry->extensions->val[i]) * (entry->extensions->len - i - 1)); 196 entry->extensions->len--; 197 } 198 } 199 if (entry->extensions->len == 0) { 200 free(entry->extensions->val); 201 free(entry->extensions); 202 entry->extensions = NULL; 203 } 204 205 return 0; 206} 207 208 209krb5_error_code 210hdb_entry_get_pkinit_acl(const hdb_entry *entry, const HDB_Ext_PKINIT_acl **a) 211{ 212 const HDB_extension *ext; 213 214 ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_acl); 215 if (ext) 216 *a = &ext->data.u.pkinit_acl; 217 else 218 *a = NULL; 219 220 return 0; 221} 222 223krb5_error_code 224hdb_entry_set_pkinit_acl(hdb_entry *entry, 225 const char *subject, 226 const char *issuer, 227 const char *anchor) 228{ 229 HDB_extension *ext; 230 HDB_Ext_PKINIT_acl *a; 231 void *ptr; 232 233 ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_acl); 234 if (ext == NULL) { 235 if (entry->extensions == NULL) { 236 entry->extensions = calloc(1, sizeof(*entry->extensions)); 237 if (entry->extensions == NULL) 238 return ENOMEM; 239 } 240 241 ptr = realloc(entry->extensions->val, 242 (entry->extensions->len + 1) * 243 sizeof(entry->extensions->val[0])); 244 if (ptr == NULL) 245 return ENOMEM; 246 entry->extensions->val = ptr; 247 248 memset(&entry->extensions->val[entry->extensions->len], 0, 249 sizeof(entry->extensions->val[0])); 250 251 entry->extensions->val[entry->extensions->len].data.element = 252 choice_HDB_extension_data_pkinit_acl; 253 a = &entry->extensions->val[entry->extensions->len].data.u.pkinit_acl; 254 entry->extensions->len++; 255 } else { 256 a = &ext->data.u.pkinit_acl; 257 } 258 259 ptr = realloc(a->val, (a->len + 1) * sizeof(a->val[0])); 260 if (ptr == NULL) 261 return ENOMEM; 262 a->val = ptr; 263 memset(&a->val[a->len], 0, sizeof(a->val[0])); 264 265 a->val[a->len].subject = strdup(subject); 266 267 if (issuer) { 268 a->val[a->len].issuer = malloc(sizeof(a->val[a->len].subject)); 269 *(a->val[a->len].issuer) = strdup(issuer); 270 } 271 272 if (anchor) { 273 a->val[a->len].anchor = malloc(sizeof(a->val[a->len].anchor)); 274 *(a->val[a->len].anchor) = strdup(anchor); 275 } 276 a->len++; 277 278 return 0; 279} 280 281krb5_error_code 282hdb_entry_get_pkinit_hash(const hdb_entry *entry, const HDB_Ext_PKINIT_hash **a) 283{ 284 const HDB_extension *ext; 285 286 ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_cert_hash); 287 if (ext) 288 *a = &ext->data.u.pkinit_cert_hash; 289 else 290 *a = NULL; 291 292 return 0; 293} 294 295krb5_error_code 296hdb_entry_get_pkinit_cert(const hdb_entry *entry, const HDB_Ext_PKINIT_cert **a) 297{ 298 const HDB_extension *ext; 299 300 ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_cert); 301 if (ext) 302 *a = &ext->data.u.pkinit_cert; 303 else 304 *a = NULL; 305 306 return 0; 307} 308 309krb5_error_code 310hdb_entry_get_pw_change_time(const hdb_entry *entry, time_t *t) 311{ 312 const HDB_extension *ext; 313 314 ext = hdb_find_extension(entry, choice_HDB_extension_data_last_pw_change); 315 if (ext) 316 *t = ext->data.u.last_pw_change; 317 else 318 *t = 0; 319 320 return 0; 321} 322 323krb5_error_code 324hdb_entry_set_pw_change_time(krb5_context context, 325 hdb_entry *entry, 326 time_t t) 327{ 328 HDB_extension ext; 329 330 ext.mandatory = FALSE; 331 ext.data.element = choice_HDB_extension_data_last_pw_change; 332 if (t == 0) 333 t = time(NULL); 334 ext.data.u.last_pw_change = t; 335 336 return hdb_replace_extension(context, entry, &ext); 337} 338 339int 340hdb_entry_get_password(krb5_context context, HDB *db, 341 const hdb_entry *entry, char **p) 342{ 343 heim_utf8_string str; 344 heim_octet_string pw; 345 HDB_extension *ext; 346 int ret; 347 348 *p = NULL; 349 350 ext = hdb_find_extension(entry, choice_HDB_extension_data_password); 351 if (ext == NULL) 352 return 0; 353 354 if (db->hdb_master_key_set && ext->data.u.password.mkvno) { 355 hdb_master_key key; 356 357 key = _hdb_find_master_key(ext->data.u.password.mkvno, 358 db->hdb_master_key); 359 360 if (key == NULL) { 361 krb5_set_error_message(context, HDB_ERR_NO_MKEY, 362 "master key %d missing", 363 *ext->data.u.password.mkvno); 364 return HDB_ERR_NO_MKEY; 365 } 366 367 ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY, 368 ext->data.u.password.password.data, 369 ext->data.u.password.password.length, 370 &pw); 371 } else { 372 ret = der_copy_octet_string(&ext->data.u.password.password, &pw); 373 } 374 if (ret) { 375 krb5_clear_error_message(context); 376 return ret; 377 } 378 379 str = pw.data; 380 if (str[pw.length - 1] != '\0') { 381 krb5_set_error_message(context, EINVAL, "malformed password"); 382 return EINVAL; 383 } 384 385 *p = strdup(str); 386 387 der_free_octet_string(&pw); 388 if (*p == NULL) { 389 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 390 return ENOMEM; 391 } 392 return 0; 393} 394 395int 396hdb_entry_set_password(krb5_context context, HDB *db, 397 hdb_entry *entry, const char *p) 398{ 399 HDB_extension ext; 400 hdb_master_key key; 401 int ret; 402 403 ext.mandatory = FALSE; 404 ext.data.element = choice_HDB_extension_data_password; 405 406 if (db->hdb_master_key_set) { 407 408 key = _hdb_find_master_key(NULL, db->hdb_master_key); 409 if (key == NULL) { 410 krb5_set_error_message(context, HDB_ERR_NO_MKEY, 411 "hdb_entry_set_password: " 412 "failed to find masterkey"); 413 return HDB_ERR_NO_MKEY; 414 } 415 416 ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY, 417 p, strlen(p) + 1, 418 &ext.data.u.password.password); 419 if (ret) 420 return ret; 421 422 ext.data.u.password.mkvno = 423 malloc(sizeof(*ext.data.u.password.mkvno)); 424 if (ext.data.u.password.mkvno == NULL) { 425 free_HDB_extension(&ext); 426 krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 427 return ENOMEM; 428 } 429 *ext.data.u.password.mkvno = _hdb_mkey_version(key); 430 431 } else { 432 ext.data.u.password.mkvno = NULL; 433 434 ret = krb5_data_copy(&ext.data.u.password.password, 435 p, strlen(p) + 1); 436 if (ret) { 437 krb5_set_error_message(context, ret, "malloc: out of memory"); 438 free_HDB_extension(&ext); 439 return ret; 440 } 441 } 442 443 ret = hdb_replace_extension(context, entry, &ext); 444 445 free_HDB_extension(&ext); 446 447 return ret; 448} 449 450int 451hdb_entry_clear_password(krb5_context context, hdb_entry *entry) 452{ 453 return hdb_clear_extension(context, entry, 454 choice_HDB_extension_data_password); 455} 456 457krb5_error_code 458hdb_entry_get_ConstrainedDelegACL(const hdb_entry *entry, 459 const HDB_Ext_Constrained_delegation_acl **a) 460{ 461 const HDB_extension *ext; 462 463 ext = hdb_find_extension(entry, 464 choice_HDB_extension_data_allowed_to_delegate_to); 465 if (ext) 466 *a = &ext->data.u.allowed_to_delegate_to; 467 else 468 *a = NULL; 469 470 return 0; 471} 472 473krb5_error_code 474hdb_entry_get_aliases(const hdb_entry *entry, const HDB_Ext_Aliases **a) 475{ 476 const HDB_extension *ext; 477 478 ext = hdb_find_extension(entry, choice_HDB_extension_data_aliases); 479 if (ext) 480 *a = &ext->data.u.aliases; 481 else 482 *a = NULL; 483 484 return 0; 485} 486 487unsigned int 488hdb_entry_get_kvno_diff_clnt(const hdb_entry *entry) 489{ 490 const HDB_extension *ext; 491 492 ext = hdb_find_extension(entry, 493 choice_HDB_extension_data_hist_kvno_diff_clnt); 494 if (ext) 495 return ext->data.u.hist_kvno_diff_clnt; 496 return 1; 497} 498 499krb5_error_code 500hdb_entry_set_kvno_diff_clnt(krb5_context context, hdb_entry *entry, 501 unsigned int diff) 502{ 503 HDB_extension ext; 504 505 if (diff > 16384) 506 return EINVAL; 507 ext.data.element = choice_HDB_extension_data_hist_kvno_diff_clnt; 508 ext.data.u.hist_kvno_diff_clnt = diff; 509 return hdb_replace_extension(context, entry, &ext); 510} 511 512krb5_error_code 513hdb_entry_clear_kvno_diff_clnt(krb5_context context, hdb_entry *entry) 514{ 515 return hdb_clear_extension(context, entry, 516 choice_HDB_extension_data_hist_kvno_diff_clnt); 517} 518 519unsigned int 520hdb_entry_get_kvno_diff_svc(const hdb_entry *entry) 521{ 522 const HDB_extension *ext; 523 524 ext = hdb_find_extension(entry, 525 choice_HDB_extension_data_hist_kvno_diff_svc); 526 if (ext) 527 return ext->data.u.hist_kvno_diff_svc; 528 return 1024; /* max_life effectively provides a better default */ 529} 530 531krb5_error_code 532hdb_entry_set_kvno_diff_svc(krb5_context context, hdb_entry *entry, 533 unsigned int diff) 534{ 535 HDB_extension ext; 536 537 if (diff > 16384) 538 return EINVAL; 539 ext.data.element = choice_HDB_extension_data_hist_kvno_diff_svc; 540 ext.data.u.hist_kvno_diff_svc = diff; 541 return hdb_replace_extension(context, entry, &ext); 542} 543 544krb5_error_code 545hdb_entry_clear_kvno_diff_svc(krb5_context context, hdb_entry *entry) 546{ 547 return hdb_clear_extension(context, entry, 548 choice_HDB_extension_data_hist_kvno_diff_svc); 549} 550