1178825Sdfr/* 2233294Sstas * Copyright (c) 2004 - 2005 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 5178825Sdfr * 6233294Sstas * Redistribution and use in source and binary forms, with or without 7233294Sstas * modification, are permitted provided that the following conditions 8233294Sstas * are met: 9178825Sdfr * 10233294Sstas * 1. Redistributions of source code must retain the above copyright 11233294Sstas * notice, this list of conditions and the following disclaimer. 12178825Sdfr * 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 16178825Sdfr * 17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 18233294Sstas * may be used to endorse or promote products derived from this software 19233294Sstas * without specific prior written permission. 20178825Sdfr * 21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31233294Sstas * SUCH DAMAGE. 32178825Sdfr */ 33178825Sdfr 34178825Sdfr#include "hdb_locl.h" 35178825Sdfr#include <der.h> 36178825Sdfr 37178825Sdfrkrb5_error_code 38178825Sdfrhdb_entry_check_mandatory(krb5_context context, const hdb_entry *ent) 39178825Sdfr{ 40233294Sstas size_t i; 41178825Sdfr 42178825Sdfr if (ent->extensions == NULL) 43178825Sdfr return 0; 44178825Sdfr 45233294Sstas /* 46178825Sdfr * check for unknown extensions and if they where tagged mandatory 47178825Sdfr */ 48178825Sdfr 49178825Sdfr for (i = 0; i < ent->extensions->len; i++) { 50233294Sstas if (ent->extensions->val[i].data.element != 51178825Sdfr choice_HDB_extension_data_asn1_ellipsis) 52178825Sdfr continue; 53178825Sdfr if (ent->extensions->val[i].mandatory) { 54233294Sstas krb5_set_error_message(context, HDB_ERR_MANDATORY_OPTION, 55233294Sstas "Principal have unknown " 56233294Sstas "mandatory extension"); 57178825Sdfr return HDB_ERR_MANDATORY_OPTION; 58178825Sdfr } 59178825Sdfr } 60178825Sdfr return 0; 61178825Sdfr} 62178825Sdfr 63178825SdfrHDB_extension * 64178825Sdfrhdb_find_extension(const hdb_entry *entry, int type) 65178825Sdfr{ 66233294Sstas size_t i; 67178825Sdfr 68178825Sdfr if (entry->extensions == NULL) 69178825Sdfr return NULL; 70178825Sdfr 71178825Sdfr for (i = 0; i < entry->extensions->len; i++) 72233294Sstas if (entry->extensions->val[i].data.element == (unsigned)type) 73178825Sdfr return &entry->extensions->val[i]; 74178825Sdfr return NULL; 75178825Sdfr} 76178825Sdfr 77178825Sdfr/* 78178825Sdfr * Replace the extension `ext' in `entry'. Make a copy of the 79178825Sdfr * extension, so the caller must still free `ext' on both success and 80178825Sdfr * failure. Returns 0 or error code. 81178825Sdfr */ 82178825Sdfr 83178825Sdfrkrb5_error_code 84233294Sstashdb_replace_extension(krb5_context context, 85233294Sstas hdb_entry *entry, 86178825Sdfr const HDB_extension *ext) 87178825Sdfr{ 88178825Sdfr HDB_extension *ext2; 89178825Sdfr HDB_extension *es; 90178825Sdfr int ret; 91178825Sdfr 92178825Sdfr ext2 = NULL; 93178825Sdfr 94178825Sdfr if (entry->extensions == NULL) { 95178825Sdfr entry->extensions = calloc(1, sizeof(*entry->extensions)); 96178825Sdfr if (entry->extensions == NULL) { 97233294Sstas krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 98178825Sdfr return ENOMEM; 99178825Sdfr } 100178825Sdfr } else if (ext->data.element != choice_HDB_extension_data_asn1_ellipsis) { 101178825Sdfr ext2 = hdb_find_extension(entry, ext->data.element); 102178825Sdfr } else { 103233294Sstas /* 104178825Sdfr * This is an unknown extention, and we are asked to replace a 105178825Sdfr * possible entry in `entry' that is of the same type. This 106178825Sdfr * might seem impossible, but ASN.1 CHOICE comes to our 107178825Sdfr * rescue. The first tag in each branch in the CHOICE is 108178825Sdfr * unique, so just find the element in the list that have the 109178825Sdfr * same tag was we are putting into the list. 110178825Sdfr */ 111178825Sdfr Der_class replace_class, list_class; 112178825Sdfr Der_type replace_type, list_type; 113178825Sdfr unsigned int replace_tag, list_tag; 114178825Sdfr size_t size; 115233294Sstas size_t i; 116178825Sdfr 117178825Sdfr ret = der_get_tag(ext->data.u.asn1_ellipsis.data, 118178825Sdfr ext->data.u.asn1_ellipsis.length, 119178825Sdfr &replace_class, &replace_type, &replace_tag, 120178825Sdfr &size); 121178825Sdfr if (ret) { 122233294Sstas krb5_set_error_message(context, ret, "hdb: failed to decode " 123233294Sstas "replacement hdb extention"); 124178825Sdfr return ret; 125178825Sdfr } 126178825Sdfr 127178825Sdfr for (i = 0; i < entry->extensions->len; i++) { 128178825Sdfr HDB_extension *ext3 = &entry->extensions->val[i]; 129178825Sdfr 130178825Sdfr if (ext3->data.element != choice_HDB_extension_data_asn1_ellipsis) 131178825Sdfr continue; 132178825Sdfr 133178825Sdfr ret = der_get_tag(ext3->data.u.asn1_ellipsis.data, 134178825Sdfr ext3->data.u.asn1_ellipsis.length, 135178825Sdfr &list_class, &list_type, &list_tag, 136178825Sdfr &size); 137178825Sdfr if (ret) { 138233294Sstas krb5_set_error_message(context, ret, "hdb: failed to decode " 139233294Sstas "present hdb extention"); 140178825Sdfr return ret; 141178825Sdfr } 142178825Sdfr 143178825Sdfr if (MAKE_TAG(replace_class,replace_type,replace_type) == 144178825Sdfr MAKE_TAG(list_class,list_type,list_type)) { 145178825Sdfr ext2 = ext3; 146178825Sdfr break; 147178825Sdfr } 148178825Sdfr } 149178825Sdfr } 150178825Sdfr 151178825Sdfr if (ext2) { 152178825Sdfr free_HDB_extension(ext2); 153178825Sdfr ret = copy_HDB_extension(ext, ext2); 154178825Sdfr if (ret) 155233294Sstas krb5_set_error_message(context, ret, "hdb: failed to copy replacement " 156233294Sstas "hdb extention"); 157178825Sdfr return ret; 158178825Sdfr } 159178825Sdfr 160233294Sstas es = realloc(entry->extensions->val, 161178825Sdfr (entry->extensions->len+1)*sizeof(entry->extensions->val[0])); 162178825Sdfr if (es == NULL) { 163233294Sstas krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 164178825Sdfr return ENOMEM; 165178825Sdfr } 166178825Sdfr entry->extensions->val = es; 167178825Sdfr 168178825Sdfr ret = copy_HDB_extension(ext, 169178825Sdfr &entry->extensions->val[entry->extensions->len]); 170178825Sdfr if (ret == 0) 171178825Sdfr entry->extensions->len++; 172178825Sdfr else 173233294Sstas krb5_set_error_message(context, ret, "hdb: failed to copy new extension"); 174178825Sdfr 175178825Sdfr return ret; 176178825Sdfr} 177178825Sdfr 178178825Sdfrkrb5_error_code 179233294Sstashdb_clear_extension(krb5_context context, 180233294Sstas hdb_entry *entry, 181178825Sdfr int type) 182178825Sdfr{ 183233294Sstas size_t i; 184178825Sdfr 185178825Sdfr if (entry->extensions == NULL) 186178825Sdfr return 0; 187178825Sdfr 188178825Sdfr for (i = 0; i < entry->extensions->len; i++) { 189233294Sstas if (entry->extensions->val[i].data.element == (unsigned)type) { 190178825Sdfr free_HDB_extension(&entry->extensions->val[i]); 191178825Sdfr memmove(&entry->extensions->val[i], 192178825Sdfr &entry->extensions->val[i + 1], 193178825Sdfr sizeof(entry->extensions->val[i]) * (entry->extensions->len - i - 1)); 194178825Sdfr entry->extensions->len--; 195178825Sdfr } 196178825Sdfr } 197178825Sdfr if (entry->extensions->len == 0) { 198178825Sdfr free(entry->extensions->val); 199178825Sdfr free(entry->extensions); 200178825Sdfr entry->extensions = NULL; 201178825Sdfr } 202178825Sdfr 203178825Sdfr return 0; 204178825Sdfr} 205178825Sdfr 206178825Sdfr 207178825Sdfrkrb5_error_code 208178825Sdfrhdb_entry_get_pkinit_acl(const hdb_entry *entry, const HDB_Ext_PKINIT_acl **a) 209178825Sdfr{ 210178825Sdfr const HDB_extension *ext; 211178825Sdfr 212178825Sdfr ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_acl); 213178825Sdfr if (ext) 214178825Sdfr *a = &ext->data.u.pkinit_acl; 215178825Sdfr else 216178825Sdfr *a = NULL; 217178825Sdfr 218178825Sdfr return 0; 219178825Sdfr} 220178825Sdfr 221178825Sdfrkrb5_error_code 222178825Sdfrhdb_entry_get_pkinit_hash(const hdb_entry *entry, const HDB_Ext_PKINIT_hash **a) 223178825Sdfr{ 224178825Sdfr const HDB_extension *ext; 225178825Sdfr 226178825Sdfr ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_cert_hash); 227178825Sdfr if (ext) 228178825Sdfr *a = &ext->data.u.pkinit_cert_hash; 229178825Sdfr else 230178825Sdfr *a = NULL; 231178825Sdfr 232178825Sdfr return 0; 233178825Sdfr} 234178825Sdfr 235178825Sdfrkrb5_error_code 236233294Sstashdb_entry_get_pkinit_cert(const hdb_entry *entry, const HDB_Ext_PKINIT_cert **a) 237233294Sstas{ 238233294Sstas const HDB_extension *ext; 239233294Sstas 240233294Sstas ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_cert); 241233294Sstas if (ext) 242233294Sstas *a = &ext->data.u.pkinit_cert; 243233294Sstas else 244233294Sstas *a = NULL; 245233294Sstas 246233294Sstas return 0; 247233294Sstas} 248233294Sstas 249233294Sstaskrb5_error_code 250178825Sdfrhdb_entry_get_pw_change_time(const hdb_entry *entry, time_t *t) 251178825Sdfr{ 252178825Sdfr const HDB_extension *ext; 253178825Sdfr 254178825Sdfr ext = hdb_find_extension(entry, choice_HDB_extension_data_last_pw_change); 255178825Sdfr if (ext) 256178825Sdfr *t = ext->data.u.last_pw_change; 257178825Sdfr else 258178825Sdfr *t = 0; 259178825Sdfr 260178825Sdfr return 0; 261178825Sdfr} 262178825Sdfr 263178825Sdfrkrb5_error_code 264233294Sstashdb_entry_set_pw_change_time(krb5_context context, 265178825Sdfr hdb_entry *entry, 266178825Sdfr time_t t) 267178825Sdfr{ 268178825Sdfr HDB_extension ext; 269178825Sdfr 270178825Sdfr ext.mandatory = FALSE; 271178825Sdfr ext.data.element = choice_HDB_extension_data_last_pw_change; 272178825Sdfr if (t == 0) 273178825Sdfr t = time(NULL); 274178825Sdfr ext.data.u.last_pw_change = t; 275178825Sdfr 276178825Sdfr return hdb_replace_extension(context, entry, &ext); 277178825Sdfr} 278178825Sdfr 279178825Sdfrint 280233294Sstashdb_entry_get_password(krb5_context context, HDB *db, 281178825Sdfr const hdb_entry *entry, char **p) 282178825Sdfr{ 283178825Sdfr HDB_extension *ext; 284178825Sdfr char *str; 285178825Sdfr int ret; 286178825Sdfr 287178825Sdfr ext = hdb_find_extension(entry, choice_HDB_extension_data_password); 288178825Sdfr if (ext) { 289233294Sstas heim_utf8_string xstr; 290178825Sdfr heim_octet_string pw; 291178825Sdfr 292178825Sdfr if (db->hdb_master_key_set && ext->data.u.password.mkvno) { 293178825Sdfr hdb_master_key key; 294178825Sdfr 295233294Sstas key = _hdb_find_master_key(ext->data.u.password.mkvno, 296178825Sdfr db->hdb_master_key); 297178825Sdfr 298178825Sdfr if (key == NULL) { 299233294Sstas krb5_set_error_message(context, HDB_ERR_NO_MKEY, 300233294Sstas "master key %d missing", 301233294Sstas *ext->data.u.password.mkvno); 302178825Sdfr return HDB_ERR_NO_MKEY; 303178825Sdfr } 304178825Sdfr 305178825Sdfr ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY, 306178825Sdfr ext->data.u.password.password.data, 307178825Sdfr ext->data.u.password.password.length, 308178825Sdfr &pw); 309178825Sdfr } else { 310178825Sdfr ret = der_copy_octet_string(&ext->data.u.password.password, &pw); 311178825Sdfr } 312178825Sdfr if (ret) { 313233294Sstas krb5_clear_error_message(context); 314178825Sdfr return ret; 315178825Sdfr } 316178825Sdfr 317233294Sstas xstr = pw.data; 318233294Sstas if (xstr[pw.length - 1] != '\0') { 319233294Sstas krb5_set_error_message(context, EINVAL, "malformed password"); 320178825Sdfr return EINVAL; 321178825Sdfr } 322178825Sdfr 323233294Sstas *p = strdup(xstr); 324178825Sdfr 325178825Sdfr der_free_octet_string(&pw); 326178825Sdfr if (*p == NULL) { 327233294Sstas krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 328178825Sdfr return ENOMEM; 329178825Sdfr } 330178825Sdfr return 0; 331178825Sdfr } 332178825Sdfr 333178825Sdfr ret = krb5_unparse_name(context, entry->principal, &str); 334178825Sdfr if (ret == 0) { 335233294Sstas krb5_set_error_message(context, ENOENT, 336233294Sstas "no password attribute for %s", str); 337178825Sdfr free(str); 338233294Sstas } else 339233294Sstas krb5_clear_error_message(context); 340178825Sdfr 341178825Sdfr return ENOENT; 342178825Sdfr} 343178825Sdfr 344178825Sdfrint 345233294Sstashdb_entry_set_password(krb5_context context, HDB *db, 346178825Sdfr hdb_entry *entry, const char *p) 347178825Sdfr{ 348178825Sdfr HDB_extension ext; 349178825Sdfr hdb_master_key key; 350178825Sdfr int ret; 351178825Sdfr 352178825Sdfr ext.mandatory = FALSE; 353178825Sdfr ext.data.element = choice_HDB_extension_data_password; 354178825Sdfr 355178825Sdfr if (db->hdb_master_key_set) { 356178825Sdfr 357178825Sdfr key = _hdb_find_master_key(NULL, db->hdb_master_key); 358178825Sdfr if (key == NULL) { 359233294Sstas krb5_set_error_message(context, HDB_ERR_NO_MKEY, 360233294Sstas "hdb_entry_set_password: " 361233294Sstas "failed to find masterkey"); 362178825Sdfr return HDB_ERR_NO_MKEY; 363178825Sdfr } 364178825Sdfr 365178825Sdfr ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY, 366233294Sstas p, strlen(p) + 1, 367178825Sdfr &ext.data.u.password.password); 368178825Sdfr if (ret) 369178825Sdfr return ret; 370178825Sdfr 371233294Sstas ext.data.u.password.mkvno = 372178825Sdfr malloc(sizeof(*ext.data.u.password.mkvno)); 373178825Sdfr if (ext.data.u.password.mkvno == NULL) { 374178825Sdfr free_HDB_extension(&ext); 375233294Sstas krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 376178825Sdfr return ENOMEM; 377178825Sdfr } 378178825Sdfr *ext.data.u.password.mkvno = _hdb_mkey_version(key); 379178825Sdfr 380178825Sdfr } else { 381178825Sdfr ext.data.u.password.mkvno = NULL; 382178825Sdfr 383233294Sstas ret = krb5_data_copy(&ext.data.u.password.password, 384178825Sdfr p, strlen(p) + 1); 385178825Sdfr if (ret) { 386233294Sstas krb5_set_error_message(context, ret, "malloc: out of memory"); 387178825Sdfr free_HDB_extension(&ext); 388178825Sdfr return ret; 389178825Sdfr } 390178825Sdfr } 391178825Sdfr 392178825Sdfr ret = hdb_replace_extension(context, entry, &ext); 393178825Sdfr 394178825Sdfr free_HDB_extension(&ext); 395178825Sdfr 396178825Sdfr return ret; 397178825Sdfr} 398178825Sdfr 399178825Sdfrint 400178825Sdfrhdb_entry_clear_password(krb5_context context, hdb_entry *entry) 401178825Sdfr{ 402233294Sstas return hdb_clear_extension(context, entry, 403178825Sdfr choice_HDB_extension_data_password); 404178825Sdfr} 405178825Sdfr 406178825Sdfrkrb5_error_code 407233294Sstashdb_entry_get_ConstrainedDelegACL(const hdb_entry *entry, 408178825Sdfr const HDB_Ext_Constrained_delegation_acl **a) 409178825Sdfr{ 410178825Sdfr const HDB_extension *ext; 411178825Sdfr 412233294Sstas ext = hdb_find_extension(entry, 413178825Sdfr choice_HDB_extension_data_allowed_to_delegate_to); 414178825Sdfr if (ext) 415178825Sdfr *a = &ext->data.u.allowed_to_delegate_to; 416178825Sdfr else 417178825Sdfr *a = NULL; 418178825Sdfr 419178825Sdfr return 0; 420178825Sdfr} 421178825Sdfr 422178825Sdfrkrb5_error_code 423178825Sdfrhdb_entry_get_aliases(const hdb_entry *entry, const HDB_Ext_Aliases **a) 424178825Sdfr{ 425178825Sdfr const HDB_extension *ext; 426178825Sdfr 427178825Sdfr ext = hdb_find_extension(entry, choice_HDB_extension_data_aliases); 428178825Sdfr if (ext) 429178825Sdfr *a = &ext->data.u.aliases; 430178825Sdfr else 431178825Sdfr *a = NULL; 432178825Sdfr 433178825Sdfr return 0; 434178825Sdfr} 435