Home | History | Annotate | Line # | Download | only in hdb
      1 /*	$NetBSD: ext.c,v 1.2 2017/01/28 21:31:48 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2004 - 2005 Kungliga Tekniska Hgskolan
      5  * (Royal Institute of Technology, Stockholm, Sweden).
      6  * 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 <krb5/der.h>
     38 
     39 krb5_error_code
     40 hdb_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 
     65 HDB_extension *
     66 hdb_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 
     85 krb5_error_code
     86 hdb_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 extension, 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 extension");
    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 extension");
    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 extension");
    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 
    180 krb5_error_code
    181 hdb_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 
    209 krb5_error_code
    210 hdb_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 
    223 krb5_error_code
    224 hdb_entry_get_pkinit_hash(const hdb_entry *entry, const HDB_Ext_PKINIT_hash **a)
    225 {
    226     const HDB_extension *ext;
    227 
    228     ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_cert_hash);
    229     if (ext)
    230 	*a = &ext->data.u.pkinit_cert_hash;
    231     else
    232 	*a = NULL;
    233 
    234     return 0;
    235 }
    236 
    237 krb5_error_code
    238 hdb_entry_get_pkinit_cert(const hdb_entry *entry, const HDB_Ext_PKINIT_cert **a)
    239 {
    240     const HDB_extension *ext;
    241 
    242     ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_cert);
    243     if (ext)
    244 	*a = &ext->data.u.pkinit_cert;
    245     else
    246 	*a = NULL;
    247 
    248     return 0;
    249 }
    250 
    251 krb5_error_code
    252 hdb_entry_get_pw_change_time(const hdb_entry *entry, time_t *t)
    253 {
    254     const HDB_extension *ext;
    255 
    256     ext = hdb_find_extension(entry, choice_HDB_extension_data_last_pw_change);
    257     if (ext)
    258 	*t = ext->data.u.last_pw_change;
    259     else
    260 	*t = 0;
    261 
    262     return 0;
    263 }
    264 
    265 krb5_error_code
    266 hdb_entry_set_pw_change_time(krb5_context context,
    267 			     hdb_entry *entry,
    268 			     time_t t)
    269 {
    270     HDB_extension ext;
    271 
    272     ext.mandatory = FALSE;
    273     ext.data.element = choice_HDB_extension_data_last_pw_change;
    274     if (t == 0)
    275 	t = time(NULL);
    276     ext.data.u.last_pw_change = t;
    277 
    278     return hdb_replace_extension(context, entry, &ext);
    279 }
    280 
    281 int
    282 hdb_entry_get_password(krb5_context context, HDB *db,
    283 		       const hdb_entry *entry, char **p)
    284 {
    285     HDB_extension *ext;
    286     char *str;
    287     int ret;
    288 
    289     ext = hdb_find_extension(entry, choice_HDB_extension_data_password);
    290     if (ext) {
    291 	heim_utf8_string xstr;
    292 	heim_octet_string pw;
    293 
    294 	if (db->hdb_master_key_set && ext->data.u.password.mkvno) {
    295 	    hdb_master_key key;
    296 
    297 	    key = _hdb_find_master_key(ext->data.u.password.mkvno,
    298 				       db->hdb_master_key);
    299 
    300 	    if (key == NULL) {
    301 		krb5_set_error_message(context, HDB_ERR_NO_MKEY,
    302 				       "master key %d missing",
    303 				       *ext->data.u.password.mkvno);
    304 		return HDB_ERR_NO_MKEY;
    305 	    }
    306 
    307 	    ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY,
    308 				    ext->data.u.password.password.data,
    309 				    ext->data.u.password.password.length,
    310 				    &pw);
    311 	} else {
    312 	    ret = der_copy_octet_string(&ext->data.u.password.password, &pw);
    313 	}
    314 	if (ret) {
    315 	    krb5_clear_error_message(context);
    316 	    return ret;
    317 	}
    318 
    319 	xstr = pw.data;
    320 	if (xstr[pw.length - 1] != '\0') {
    321 	    krb5_set_error_message(context, EINVAL, "malformed password");
    322 	    return EINVAL;
    323 	}
    324 
    325 	*p = strdup(xstr);
    326 
    327 	der_free_octet_string(&pw);
    328 	if (*p == NULL) {
    329 	    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
    330 	    return ENOMEM;
    331 	}
    332 	return 0;
    333     }
    334 
    335     ret = krb5_unparse_name(context, entry->principal, &str);
    336     if (ret == 0) {
    337 	krb5_set_error_message(context, ENOENT,
    338 			       "no password attribute for %s", str);
    339 	free(str);
    340     } else
    341 	krb5_clear_error_message(context);
    342 
    343     return ENOENT;
    344 }
    345 
    346 int
    347 hdb_entry_set_password(krb5_context context, HDB *db,
    348 		       hdb_entry *entry, const char *p)
    349 {
    350     HDB_extension ext;
    351     hdb_master_key key;
    352     int ret;
    353 
    354     ext.mandatory = FALSE;
    355     ext.data.element = choice_HDB_extension_data_password;
    356 
    357     if (db->hdb_master_key_set) {
    358 
    359 	key = _hdb_find_master_key(NULL, db->hdb_master_key);
    360 	if (key == NULL) {
    361 	    krb5_set_error_message(context, HDB_ERR_NO_MKEY,
    362 				   "hdb_entry_set_password: "
    363 				   "failed to find masterkey");
    364 	    return HDB_ERR_NO_MKEY;
    365 	}
    366 
    367 	ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY,
    368 				p, strlen(p) + 1,
    369 				&ext.data.u.password.password);
    370 	if (ret)
    371 	    return ret;
    372 
    373 	ext.data.u.password.mkvno =
    374 	    malloc(sizeof(*ext.data.u.password.mkvno));
    375 	if (ext.data.u.password.mkvno == NULL) {
    376 	    free_HDB_extension(&ext);
    377 	    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
    378 	    return ENOMEM;
    379 	}
    380 	*ext.data.u.password.mkvno = _hdb_mkey_version(key);
    381 
    382     } else {
    383 	ext.data.u.password.mkvno = NULL;
    384 
    385 	ret = krb5_data_copy(&ext.data.u.password.password,
    386 			     p, strlen(p) + 1);
    387 	if (ret) {
    388 	    krb5_set_error_message(context, ret, "malloc: out of memory");
    389 	    free_HDB_extension(&ext);
    390 	    return ret;
    391 	}
    392     }
    393 
    394     ret = hdb_replace_extension(context, entry, &ext);
    395 
    396     free_HDB_extension(&ext);
    397 
    398     return ret;
    399 }
    400 
    401 int
    402 hdb_entry_clear_password(krb5_context context, hdb_entry *entry)
    403 {
    404     return hdb_clear_extension(context, entry,
    405 			       choice_HDB_extension_data_password);
    406 }
    407 
    408 krb5_error_code
    409 hdb_entry_get_ConstrainedDelegACL(const hdb_entry *entry,
    410 				  const HDB_Ext_Constrained_delegation_acl **a)
    411 {
    412     const HDB_extension *ext;
    413 
    414     ext = hdb_find_extension(entry,
    415 			     choice_HDB_extension_data_allowed_to_delegate_to);
    416     if (ext)
    417 	*a = &ext->data.u.allowed_to_delegate_to;
    418     else
    419 	*a = NULL;
    420 
    421     return 0;
    422 }
    423 
    424 krb5_error_code
    425 hdb_entry_get_aliases(const hdb_entry *entry, const HDB_Ext_Aliases **a)
    426 {
    427     const HDB_extension *ext;
    428 
    429     ext = hdb_find_extension(entry, choice_HDB_extension_data_aliases);
    430     if (ext)
    431 	*a = &ext->data.u.aliases;
    432     else
    433 	*a = NULL;
    434 
    435     return 0;
    436 }
    437 
    438 unsigned int
    439 hdb_entry_get_kvno_diff_clnt(const hdb_entry *entry)
    440 {
    441     const HDB_extension *ext;
    442 
    443     ext = hdb_find_extension(entry,
    444 			     choice_HDB_extension_data_hist_kvno_diff_clnt);
    445     if (ext)
    446 	return ext->data.u.hist_kvno_diff_clnt;
    447     return 1;
    448 }
    449 
    450 krb5_error_code
    451 hdb_entry_set_kvno_diff_clnt(krb5_context context, hdb_entry *entry,
    452 			     unsigned int diff)
    453 {
    454     HDB_extension ext;
    455 
    456     if (diff > 16384)
    457 	return EINVAL;
    458     ext.mandatory = FALSE;
    459     ext.data.element = choice_HDB_extension_data_hist_kvno_diff_clnt;
    460     ext.data.u.hist_kvno_diff_clnt = diff;
    461     return hdb_replace_extension(context, entry, &ext);
    462 }
    463 
    464 krb5_error_code
    465 hdb_entry_clear_kvno_diff_clnt(krb5_context context, hdb_entry *entry)
    466 {
    467     return hdb_clear_extension(context, entry,
    468 			       choice_HDB_extension_data_hist_kvno_diff_clnt);
    469 }
    470 
    471 unsigned int
    472 hdb_entry_get_kvno_diff_svc(const hdb_entry *entry)
    473 {
    474     const HDB_extension *ext;
    475 
    476     ext = hdb_find_extension(entry,
    477 			     choice_HDB_extension_data_hist_kvno_diff_svc);
    478     if (ext)
    479 	return ext->data.u.hist_kvno_diff_svc;
    480     return 1024; /* max_life effectively provides a better default */
    481 }
    482 
    483 krb5_error_code
    484 hdb_entry_set_kvno_diff_svc(krb5_context context, hdb_entry *entry,
    485 			    unsigned int diff)
    486 {
    487     HDB_extension ext;
    488 
    489     if (diff > 16384)
    490 	return EINVAL;
    491     ext.mandatory = FALSE;
    492     ext.data.element = choice_HDB_extension_data_hist_kvno_diff_svc;
    493     ext.data.u.hist_kvno_diff_svc = diff;
    494     return hdb_replace_extension(context, entry, &ext);
    495 }
    496 
    497 krb5_error_code
    498 hdb_entry_clear_kvno_diff_svc(krb5_context context, hdb_entry *entry)
    499 {
    500     return hdb_clear_extension(context, entry,
    501 			       choice_HDB_extension_data_hist_kvno_diff_svc);
    502 }
    503 
    504 krb5_error_code
    505 hdb_set_last_modified_by(krb5_context context, hdb_entry *entry,
    506                          krb5_principal modby, time_t modtime)
    507 {
    508     krb5_error_code ret;
    509     Event *old_ev;
    510     Event *ev;
    511 
    512     old_ev = entry->modified_by;
    513 
    514     ev = calloc(1, sizeof (*ev));
    515     if (!ev)
    516         return ENOMEM;
    517     if (modby)
    518         ret = krb5_copy_principal(context, modby, &ev->principal);
    519     else
    520         ret = krb5_parse_name(context, "root/admin", &ev->principal);
    521     if (ret) {
    522         free(ev);
    523         return ret;
    524     }
    525     ev->time = modtime;
    526     if (!ev->time)
    527         time(&ev->time);
    528 
    529     entry->modified_by = ev;
    530     if (old_ev)
    531         free_Event(old_ev);
    532     return 0;
    533 }
    534 
    535