1178825Sdfr/* 2178825Sdfr * Copyright (c) 2004 - 2005 Kungliga Tekniska H�gskolan 3178825Sdfr * (Royal Institute of Technology, Stockholm, Sweden). 4178825Sdfr * All rights reserved. 5178825Sdfr * 6178825Sdfr * Redistribution and use in source and binary forms, with or without 7178825Sdfr * modification, are permitted provided that the following conditions 8178825Sdfr * are met: 9178825Sdfr * 10178825Sdfr * 1. Redistributions of source code must retain the above copyright 11178825Sdfr * notice, this list of conditions and the following disclaimer. 12178825Sdfr * 13178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright 14178825Sdfr * notice, this list of conditions and the following disclaimer in the 15178825Sdfr * documentation and/or other materials provided with the distribution. 16178825Sdfr * 17178825Sdfr * 3. Neither the name of the Institute nor the names of its contributors 18178825Sdfr * may be used to endorse or promote products derived from this software 19178825Sdfr * without specific prior written permission. 20178825Sdfr * 21178825Sdfr * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24178825Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31178825Sdfr * SUCH DAMAGE. 32178825Sdfr */ 33178825Sdfr 34178825Sdfr#include "hdb_locl.h" 35178825Sdfr#include <der.h> 36178825Sdfr 37178825SdfrRCSID("$Id: ext.c 21113 2007-06-18 12:59:32Z lha $"); 38178825Sdfr 39178825Sdfrkrb5_error_code 40178825Sdfrhdb_entry_check_mandatory(krb5_context context, const hdb_entry *ent) 41178825Sdfr{ 42178825Sdfr int i; 43178825Sdfr 44178825Sdfr if (ent->extensions == NULL) 45178825Sdfr return 0; 46178825Sdfr 47178825Sdfr /* 48178825Sdfr * check for unknown extensions and if they where tagged mandatory 49178825Sdfr */ 50178825Sdfr 51178825Sdfr for (i = 0; i < ent->extensions->len; i++) { 52178825Sdfr if (ent->extensions->val[i].data.element != 53178825Sdfr choice_HDB_extension_data_asn1_ellipsis) 54178825Sdfr continue; 55178825Sdfr if (ent->extensions->val[i].mandatory) { 56178825Sdfr krb5_set_error_string(context, "Principal have unknown " 57178825Sdfr "mandatory extension"); 58178825Sdfr return HDB_ERR_MANDATORY_OPTION; 59178825Sdfr } 60178825Sdfr } 61178825Sdfr return 0; 62178825Sdfr} 63178825Sdfr 64178825SdfrHDB_extension * 65178825Sdfrhdb_find_extension(const hdb_entry *entry, int type) 66178825Sdfr{ 67178825Sdfr int i; 68178825Sdfr 69178825Sdfr if (entry->extensions == NULL) 70178825Sdfr return NULL; 71178825Sdfr 72178825Sdfr for (i = 0; i < entry->extensions->len; i++) 73178825Sdfr if (entry->extensions->val[i].data.element == type) 74178825Sdfr return &entry->extensions->val[i]; 75178825Sdfr return NULL; 76178825Sdfr} 77178825Sdfr 78178825Sdfr/* 79178825Sdfr * Replace the extension `ext' in `entry'. Make a copy of the 80178825Sdfr * extension, so the caller must still free `ext' on both success and 81178825Sdfr * failure. Returns 0 or error code. 82178825Sdfr */ 83178825Sdfr 84178825Sdfrkrb5_error_code 85178825Sdfrhdb_replace_extension(krb5_context context, 86178825Sdfr hdb_entry *entry, 87178825Sdfr const HDB_extension *ext) 88178825Sdfr{ 89178825Sdfr HDB_extension *ext2; 90178825Sdfr HDB_extension *es; 91178825Sdfr int ret; 92178825Sdfr 93178825Sdfr ext2 = NULL; 94178825Sdfr 95178825Sdfr if (entry->extensions == NULL) { 96178825Sdfr entry->extensions = calloc(1, sizeof(*entry->extensions)); 97178825Sdfr if (entry->extensions == NULL) { 98178825Sdfr krb5_set_error_string(context, "malloc: out of memory"); 99178825Sdfr return ENOMEM; 100178825Sdfr } 101178825Sdfr } else if (ext->data.element != choice_HDB_extension_data_asn1_ellipsis) { 102178825Sdfr ext2 = hdb_find_extension(entry, ext->data.element); 103178825Sdfr } else { 104178825Sdfr /* 105178825Sdfr * This is an unknown extention, and we are asked to replace a 106178825Sdfr * possible entry in `entry' that is of the same type. This 107178825Sdfr * might seem impossible, but ASN.1 CHOICE comes to our 108178825Sdfr * rescue. The first tag in each branch in the CHOICE is 109178825Sdfr * unique, so just find the element in the list that have the 110178825Sdfr * same tag was we are putting into the list. 111178825Sdfr */ 112178825Sdfr Der_class replace_class, list_class; 113178825Sdfr Der_type replace_type, list_type; 114178825Sdfr unsigned int replace_tag, list_tag; 115178825Sdfr size_t size; 116178825Sdfr int i; 117178825Sdfr 118178825Sdfr ret = der_get_tag(ext->data.u.asn1_ellipsis.data, 119178825Sdfr ext->data.u.asn1_ellipsis.length, 120178825Sdfr &replace_class, &replace_type, &replace_tag, 121178825Sdfr &size); 122178825Sdfr if (ret) { 123178825Sdfr krb5_set_error_string(context, "hdb: failed to decode " 124178825Sdfr "replacement hdb extention"); 125178825Sdfr return ret; 126178825Sdfr } 127178825Sdfr 128178825Sdfr for (i = 0; i < entry->extensions->len; i++) { 129178825Sdfr HDB_extension *ext3 = &entry->extensions->val[i]; 130178825Sdfr 131178825Sdfr if (ext3->data.element != choice_HDB_extension_data_asn1_ellipsis) 132178825Sdfr continue; 133178825Sdfr 134178825Sdfr ret = der_get_tag(ext3->data.u.asn1_ellipsis.data, 135178825Sdfr ext3->data.u.asn1_ellipsis.length, 136178825Sdfr &list_class, &list_type, &list_tag, 137178825Sdfr &size); 138178825Sdfr if (ret) { 139178825Sdfr krb5_set_error_string(context, "hdb: failed to decode " 140178825Sdfr "present hdb extention"); 141178825Sdfr return ret; 142178825Sdfr } 143178825Sdfr 144178825Sdfr if (MAKE_TAG(replace_class,replace_type,replace_type) == 145178825Sdfr MAKE_TAG(list_class,list_type,list_type)) { 146178825Sdfr ext2 = ext3; 147178825Sdfr break; 148178825Sdfr } 149178825Sdfr } 150178825Sdfr } 151178825Sdfr 152178825Sdfr if (ext2) { 153178825Sdfr free_HDB_extension(ext2); 154178825Sdfr ret = copy_HDB_extension(ext, ext2); 155178825Sdfr if (ret) 156178825Sdfr krb5_set_error_string(context, "hdb: failed to copy replacement " 157178825Sdfr "hdb extention"); 158178825Sdfr return ret; 159178825Sdfr } 160178825Sdfr 161178825Sdfr es = realloc(entry->extensions->val, 162178825Sdfr (entry->extensions->len+1)*sizeof(entry->extensions->val[0])); 163178825Sdfr if (es == NULL) { 164178825Sdfr krb5_set_error_string(context, "malloc: out of memory"); 165178825Sdfr return ENOMEM; 166178825Sdfr } 167178825Sdfr entry->extensions->val = es; 168178825Sdfr 169178825Sdfr ret = copy_HDB_extension(ext, 170178825Sdfr &entry->extensions->val[entry->extensions->len]); 171178825Sdfr if (ret == 0) 172178825Sdfr entry->extensions->len++; 173178825Sdfr else 174178825Sdfr krb5_set_error_string(context, "hdb: failed to copy new extension"); 175178825Sdfr 176178825Sdfr return ret; 177178825Sdfr} 178178825Sdfr 179178825Sdfrkrb5_error_code 180178825Sdfrhdb_clear_extension(krb5_context context, 181178825Sdfr hdb_entry *entry, 182178825Sdfr int type) 183178825Sdfr{ 184178825Sdfr int i; 185178825Sdfr 186178825Sdfr if (entry->extensions == NULL) 187178825Sdfr return 0; 188178825Sdfr 189178825Sdfr for (i = 0; i < entry->extensions->len; i++) { 190178825Sdfr if (entry->extensions->val[i].data.element == type) { 191178825Sdfr free_HDB_extension(&entry->extensions->val[i]); 192178825Sdfr memmove(&entry->extensions->val[i], 193178825Sdfr &entry->extensions->val[i + 1], 194178825Sdfr sizeof(entry->extensions->val[i]) * (entry->extensions->len - i - 1)); 195178825Sdfr entry->extensions->len--; 196178825Sdfr } 197178825Sdfr } 198178825Sdfr if (entry->extensions->len == 0) { 199178825Sdfr free(entry->extensions->val); 200178825Sdfr free(entry->extensions); 201178825Sdfr entry->extensions = NULL; 202178825Sdfr } 203178825Sdfr 204178825Sdfr return 0; 205178825Sdfr} 206178825Sdfr 207178825Sdfr 208178825Sdfrkrb5_error_code 209178825Sdfrhdb_entry_get_pkinit_acl(const hdb_entry *entry, const HDB_Ext_PKINIT_acl **a) 210178825Sdfr{ 211178825Sdfr const HDB_extension *ext; 212178825Sdfr 213178825Sdfr ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_acl); 214178825Sdfr if (ext) 215178825Sdfr *a = &ext->data.u.pkinit_acl; 216178825Sdfr else 217178825Sdfr *a = NULL; 218178825Sdfr 219178825Sdfr return 0; 220178825Sdfr} 221178825Sdfr 222178825Sdfrkrb5_error_code 223178825Sdfrhdb_entry_get_pkinit_hash(const hdb_entry *entry, const HDB_Ext_PKINIT_hash **a) 224178825Sdfr{ 225178825Sdfr const HDB_extension *ext; 226178825Sdfr 227178825Sdfr ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_cert_hash); 228178825Sdfr if (ext) 229178825Sdfr *a = &ext->data.u.pkinit_cert_hash; 230178825Sdfr else 231178825Sdfr *a = NULL; 232178825Sdfr 233178825Sdfr return 0; 234178825Sdfr} 235178825Sdfr 236178825Sdfrkrb5_error_code 237178825Sdfrhdb_entry_get_pw_change_time(const hdb_entry *entry, time_t *t) 238178825Sdfr{ 239178825Sdfr const HDB_extension *ext; 240178825Sdfr 241178825Sdfr ext = hdb_find_extension(entry, choice_HDB_extension_data_last_pw_change); 242178825Sdfr if (ext) 243178825Sdfr *t = ext->data.u.last_pw_change; 244178825Sdfr else 245178825Sdfr *t = 0; 246178825Sdfr 247178825Sdfr return 0; 248178825Sdfr} 249178825Sdfr 250178825Sdfrkrb5_error_code 251178825Sdfrhdb_entry_set_pw_change_time(krb5_context context, 252178825Sdfr hdb_entry *entry, 253178825Sdfr time_t t) 254178825Sdfr{ 255178825Sdfr HDB_extension ext; 256178825Sdfr 257178825Sdfr ext.mandatory = FALSE; 258178825Sdfr ext.data.element = choice_HDB_extension_data_last_pw_change; 259178825Sdfr if (t == 0) 260178825Sdfr t = time(NULL); 261178825Sdfr ext.data.u.last_pw_change = t; 262178825Sdfr 263178825Sdfr return hdb_replace_extension(context, entry, &ext); 264178825Sdfr} 265178825Sdfr 266178825Sdfrint 267178825Sdfrhdb_entry_get_password(krb5_context context, HDB *db, 268178825Sdfr const hdb_entry *entry, char **p) 269178825Sdfr{ 270178825Sdfr HDB_extension *ext; 271178825Sdfr char *str; 272178825Sdfr int ret; 273178825Sdfr 274178825Sdfr ext = hdb_find_extension(entry, choice_HDB_extension_data_password); 275178825Sdfr if (ext) { 276178825Sdfr heim_utf8_string str; 277178825Sdfr heim_octet_string pw; 278178825Sdfr 279178825Sdfr if (db->hdb_master_key_set && ext->data.u.password.mkvno) { 280178825Sdfr hdb_master_key key; 281178825Sdfr 282178825Sdfr key = _hdb_find_master_key(ext->data.u.password.mkvno, 283178825Sdfr db->hdb_master_key); 284178825Sdfr 285178825Sdfr if (key == NULL) { 286178825Sdfr krb5_set_error_string(context, "master key %d missing", 287178825Sdfr *ext->data.u.password.mkvno); 288178825Sdfr return HDB_ERR_NO_MKEY; 289178825Sdfr } 290178825Sdfr 291178825Sdfr ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY, 292178825Sdfr ext->data.u.password.password.data, 293178825Sdfr ext->data.u.password.password.length, 294178825Sdfr &pw); 295178825Sdfr } else { 296178825Sdfr ret = der_copy_octet_string(&ext->data.u.password.password, &pw); 297178825Sdfr } 298178825Sdfr if (ret) { 299178825Sdfr krb5_clear_error_string(context); 300178825Sdfr return ret; 301178825Sdfr } 302178825Sdfr 303178825Sdfr str = pw.data; 304178825Sdfr if (str[pw.length - 1] != '\0') { 305178825Sdfr krb5_set_error_string(context, "password malformated"); 306178825Sdfr return EINVAL; 307178825Sdfr } 308178825Sdfr 309178825Sdfr *p = strdup(str); 310178825Sdfr 311178825Sdfr der_free_octet_string(&pw); 312178825Sdfr if (*p == NULL) { 313178825Sdfr krb5_set_error_string(context, "malloc: out of memory"); 314178825Sdfr return ENOMEM; 315178825Sdfr } 316178825Sdfr return 0; 317178825Sdfr } 318178825Sdfr 319178825Sdfr ret = krb5_unparse_name(context, entry->principal, &str); 320178825Sdfr if (ret == 0) { 321178825Sdfr krb5_set_error_string(context, "no password attributefor %s", str); 322178825Sdfr free(str); 323178825Sdfr } else 324178825Sdfr krb5_clear_error_string(context); 325178825Sdfr 326178825Sdfr return ENOENT; 327178825Sdfr} 328178825Sdfr 329178825Sdfrint 330178825Sdfrhdb_entry_set_password(krb5_context context, HDB *db, 331178825Sdfr hdb_entry *entry, const char *p) 332178825Sdfr{ 333178825Sdfr HDB_extension ext; 334178825Sdfr hdb_master_key key; 335178825Sdfr int ret; 336178825Sdfr 337178825Sdfr ext.mandatory = FALSE; 338178825Sdfr ext.data.element = choice_HDB_extension_data_password; 339178825Sdfr 340178825Sdfr if (db->hdb_master_key_set) { 341178825Sdfr 342178825Sdfr key = _hdb_find_master_key(NULL, db->hdb_master_key); 343178825Sdfr if (key == NULL) { 344178825Sdfr krb5_set_error_string(context, "hdb_entry_set_password: " 345178825Sdfr "failed to find masterkey"); 346178825Sdfr return HDB_ERR_NO_MKEY; 347178825Sdfr } 348178825Sdfr 349178825Sdfr ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY, 350178825Sdfr p, strlen(p) + 1, 351178825Sdfr &ext.data.u.password.password); 352178825Sdfr if (ret) 353178825Sdfr return ret; 354178825Sdfr 355178825Sdfr ext.data.u.password.mkvno = 356178825Sdfr malloc(sizeof(*ext.data.u.password.mkvno)); 357178825Sdfr if (ext.data.u.password.mkvno == NULL) { 358178825Sdfr free_HDB_extension(&ext); 359178825Sdfr krb5_set_error_string(context, "malloc: out of memory"); 360178825Sdfr return ENOMEM; 361178825Sdfr } 362178825Sdfr *ext.data.u.password.mkvno = _hdb_mkey_version(key); 363178825Sdfr 364178825Sdfr } else { 365178825Sdfr ext.data.u.password.mkvno = NULL; 366178825Sdfr 367178825Sdfr ret = krb5_data_copy(&ext.data.u.password.password, 368178825Sdfr p, strlen(p) + 1); 369178825Sdfr if (ret) { 370178825Sdfr krb5_set_error_string(context, "malloc: out of memory"); 371178825Sdfr free_HDB_extension(&ext); 372178825Sdfr return ret; 373178825Sdfr } 374178825Sdfr } 375178825Sdfr 376178825Sdfr ret = hdb_replace_extension(context, entry, &ext); 377178825Sdfr 378178825Sdfr free_HDB_extension(&ext); 379178825Sdfr 380178825Sdfr return ret; 381178825Sdfr} 382178825Sdfr 383178825Sdfrint 384178825Sdfrhdb_entry_clear_password(krb5_context context, hdb_entry *entry) 385178825Sdfr{ 386178825Sdfr return hdb_clear_extension(context, entry, 387178825Sdfr choice_HDB_extension_data_password); 388178825Sdfr} 389178825Sdfr 390178825Sdfrkrb5_error_code 391178825Sdfrhdb_entry_get_ConstrainedDelegACL(const hdb_entry *entry, 392178825Sdfr const HDB_Ext_Constrained_delegation_acl **a) 393178825Sdfr{ 394178825Sdfr const HDB_extension *ext; 395178825Sdfr 396178825Sdfr ext = hdb_find_extension(entry, 397178825Sdfr choice_HDB_extension_data_allowed_to_delegate_to); 398178825Sdfr if (ext) 399178825Sdfr *a = &ext->data.u.allowed_to_delegate_to; 400178825Sdfr else 401178825Sdfr *a = NULL; 402178825Sdfr 403178825Sdfr return 0; 404178825Sdfr} 405178825Sdfr 406178825Sdfrkrb5_error_code 407178825Sdfrhdb_entry_get_aliases(const hdb_entry *entry, const HDB_Ext_Aliases **a) 408178825Sdfr{ 409178825Sdfr const HDB_extension *ext; 410178825Sdfr 411178825Sdfr ext = hdb_find_extension(entry, choice_HDB_extension_data_aliases); 412178825Sdfr if (ext) 413178825Sdfr *a = &ext->data.u.aliases; 414178825Sdfr else 415178825Sdfr *a = NULL; 416178825Sdfr 417178825Sdfr return 0; 418178825Sdfr} 419