Home | History | Annotate | Line # | Download | only in hdb
      1 /*	$NetBSD: common.c,v 1.2 2017/01/28 21:31:48 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997-2002 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 
     38 int
     39 hdb_principal2key(krb5_context context, krb5_const_principal p, krb5_data *key)
     40 {
     41     Principal new;
     42     size_t len = 0;
     43     int ret;
     44 
     45     ret = copy_Principal(p, &new);
     46     if(ret)
     47 	return ret;
     48     new.name.name_type = 0;
     49 
     50     ASN1_MALLOC_ENCODE(Principal, key->data, key->length, &new, &len, ret);
     51     if (ret == 0 && key->length != len)
     52 	krb5_abortx(context, "internal asn.1 encoder error");
     53     free_Principal(&new);
     54     return ret;
     55 }
     56 
     57 int
     58 hdb_key2principal(krb5_context context, krb5_data *key, krb5_principal p)
     59 {
     60     return decode_Principal(key->data, key->length, p, NULL);
     61 }
     62 
     63 int
     64 hdb_entry2value(krb5_context context, const hdb_entry *ent, krb5_data *value)
     65 {
     66     size_t len = 0;
     67     int ret;
     68 
     69     ASN1_MALLOC_ENCODE(hdb_entry, value->data, value->length, ent, &len, ret);
     70     if (ret == 0 && value->length != len)
     71 	krb5_abortx(context, "internal asn.1 encoder error");
     72     return ret;
     73 }
     74 
     75 int
     76 hdb_value2entry(krb5_context context, krb5_data *value, hdb_entry *ent)
     77 {
     78     return decode_hdb_entry(value->data, value->length, ent, NULL);
     79 }
     80 
     81 int
     82 hdb_entry_alias2value(krb5_context context,
     83 		      const hdb_entry_alias *alias,
     84 		      krb5_data *value)
     85 {
     86     size_t len = 0;
     87     int ret;
     88 
     89     ASN1_MALLOC_ENCODE(hdb_entry_alias, value->data, value->length,
     90 		       alias, &len, ret);
     91     if (ret == 0 && value->length != len)
     92 	krb5_abortx(context, "internal asn.1 encoder error");
     93     return ret;
     94 }
     95 
     96 int
     97 hdb_value2entry_alias(krb5_context context, krb5_data *value,
     98 		      hdb_entry_alias *ent)
     99 {
    100     return decode_hdb_entry_alias(value->data, value->length, ent, NULL);
    101 }
    102 
    103 krb5_error_code
    104 _hdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
    105 		unsigned flags, krb5_kvno kvno, hdb_entry_ex *entry)
    106 {
    107     krb5_principal enterprise_principal = NULL;
    108     krb5_data key, value;
    109     krb5_error_code ret;
    110 
    111     if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
    112 	if (principal->name.name_string.len != 1) {
    113 	    ret = KRB5_PARSE_MALFORMED;
    114 	    krb5_set_error_message(context, ret, "malformed principal: "
    115 				   "enterprise name with %d name components",
    116 				   principal->name.name_string.len);
    117 	    return ret;
    118 	}
    119 	ret = krb5_parse_name(context, principal->name.name_string.val[0],
    120 			      &enterprise_principal);
    121 	if (ret)
    122 	    return ret;
    123 	principal = enterprise_principal;
    124     }
    125 
    126     hdb_principal2key(context, principal, &key);
    127     if (enterprise_principal)
    128 	krb5_free_principal(context, enterprise_principal);
    129     ret = db->hdb__get(context, db, key, &value);
    130     krb5_data_free(&key);
    131     if(ret)
    132 	return ret;
    133     ret = hdb_value2entry(context, &value, &entry->entry);
    134     if (ret == ASN1_BAD_ID && (flags & HDB_F_CANON) == 0) {
    135 	krb5_data_free(&value);
    136 	return HDB_ERR_NOENTRY;
    137     } else if (ret == ASN1_BAD_ID) {
    138 	hdb_entry_alias alias;
    139 
    140 	ret = hdb_value2entry_alias(context, &value, &alias);
    141 	if (ret) {
    142 	    krb5_data_free(&value);
    143 	    return ret;
    144 	}
    145 	hdb_principal2key(context, alias.principal, &key);
    146 	krb5_data_free(&value);
    147 	free_hdb_entry_alias(&alias);
    148 
    149 	ret = db->hdb__get(context, db, key, &value);
    150 	krb5_data_free(&key);
    151 	if (ret)
    152 	    return ret;
    153 	ret = hdb_value2entry(context, &value, &entry->entry);
    154 	if (ret) {
    155 	    krb5_data_free(&value);
    156 	    return ret;
    157 	}
    158     }
    159     krb5_data_free(&value);
    160     if ((flags & HDB_F_DECRYPT) && (flags & HDB_F_ALL_KVNOS)) {
    161 	/* Decrypt the current keys */
    162 	ret = hdb_unseal_keys(context, db, &entry->entry);
    163 	if (ret) {
    164 	    hdb_free_entry(context, entry);
    165 	    return ret;
    166 	}
    167 	/* Decrypt the key history too */
    168 	ret = hdb_unseal_keys_kvno(context, db, 0, flags, &entry->entry);
    169 	if (ret) {
    170 	    hdb_free_entry(context, entry);
    171 	    return ret;
    172 	}
    173     } else if ((flags & HDB_F_DECRYPT)) {
    174 	if ((flags & HDB_F_KVNO_SPECIFIED) == 0 || kvno == entry->entry.kvno) {
    175 	    /* Decrypt the current keys */
    176 	    ret = hdb_unseal_keys(context, db, &entry->entry);
    177 	    if (ret) {
    178 		hdb_free_entry(context, entry);
    179 		return ret;
    180 	    }
    181 	} else {
    182 	    if ((flags & HDB_F_ALL_KVNOS))
    183 		kvno = 0;
    184 	    /*
    185 	     * Find and decrypt the keys from the history that we want,
    186 	     * and swap them with the current keys
    187 	     */
    188 	    ret = hdb_unseal_keys_kvno(context, db, kvno, flags, &entry->entry);
    189 	    if (ret) {
    190 		hdb_free_entry(context, entry);
    191 		return ret;
    192 	    }
    193 	}
    194     }
    195 
    196     return 0;
    197 }
    198 
    199 static krb5_error_code
    200 hdb_remove_aliases(krb5_context context, HDB *db, krb5_data *key)
    201 {
    202     const HDB_Ext_Aliases *aliases;
    203     krb5_error_code code;
    204     hdb_entry oldentry;
    205     krb5_data value;
    206     size_t i;
    207 
    208     code = db->hdb__get(context, db, *key, &value);
    209     if (code == HDB_ERR_NOENTRY)
    210 	return 0;
    211     else if (code)
    212 	return code;
    213 
    214     code = hdb_value2entry(context, &value, &oldentry);
    215     krb5_data_free(&value);
    216     if (code)
    217 	return code;
    218 
    219     code = hdb_entry_get_aliases(&oldentry, &aliases);
    220     if (code || aliases == NULL) {
    221 	free_hdb_entry(&oldentry);
    222 	return code;
    223     }
    224     for (i = 0; i < aliases->aliases.len; i++) {
    225 	krb5_data akey;
    226 
    227 	code = hdb_principal2key(context, &aliases->aliases.val[i], &akey);
    228         if (code == 0) {
    229             code = db->hdb__del(context, db, akey);
    230             krb5_data_free(&akey);
    231         }
    232 	if (code) {
    233 	    free_hdb_entry(&oldentry);
    234 	    return code;
    235 	}
    236     }
    237     free_hdb_entry(&oldentry);
    238     return 0;
    239 }
    240 
    241 static krb5_error_code
    242 hdb_add_aliases(krb5_context context, HDB *db,
    243 		unsigned flags, hdb_entry_ex *entry)
    244 {
    245     const HDB_Ext_Aliases *aliases;
    246     krb5_error_code code;
    247     krb5_data key, value;
    248     size_t i;
    249 
    250     code = hdb_entry_get_aliases(&entry->entry, &aliases);
    251     if (code || aliases == NULL)
    252 	return code;
    253 
    254     for (i = 0; i < aliases->aliases.len; i++) {
    255 	hdb_entry_alias entryalias;
    256 	entryalias.principal = entry->entry.principal;
    257 
    258 	code = hdb_entry_alias2value(context, &entryalias, &value);
    259 	if (code)
    260 	    return code;
    261 
    262 	code = hdb_principal2key(context, &aliases->aliases.val[i], &key);
    263         if (code == 0) {
    264             code = db->hdb__put(context, db, flags, key, value);
    265             krb5_data_free(&key);
    266         }
    267 	krb5_data_free(&value);
    268 	if (code)
    269 	    return code;
    270     }
    271     return 0;
    272 }
    273 
    274 static krb5_error_code
    275 hdb_check_aliases(krb5_context context, HDB *db, hdb_entry_ex *entry)
    276 {
    277     const HDB_Ext_Aliases *aliases;
    278     int code;
    279     size_t i;
    280 
    281     /* check if new aliases already is used */
    282 
    283     code = hdb_entry_get_aliases(&entry->entry, &aliases);
    284     if (code)
    285 	return code;
    286 
    287     for (i = 0; aliases && i < aliases->aliases.len; i++) {
    288 	hdb_entry_alias alias;
    289 	krb5_data akey, value;
    290 
    291 	code = hdb_principal2key(context, &aliases->aliases.val[i], &akey);
    292         if (code == 0) {
    293             code = db->hdb__get(context, db, akey, &value);
    294             krb5_data_free(&akey);
    295         }
    296 	if (code == HDB_ERR_NOENTRY)
    297 	    continue;
    298 	else if (code)
    299 	    return code;
    300 
    301 	code = hdb_value2entry_alias(context, &value, &alias);
    302 	krb5_data_free(&value);
    303 
    304 	if (code == ASN1_BAD_ID)
    305 	    return HDB_ERR_EXISTS;
    306 	else if (code)
    307 	    return code;
    308 
    309 	code = krb5_principal_compare(context, alias.principal,
    310 				      entry->entry.principal);
    311 	free_hdb_entry_alias(&alias);
    312 	if (code == 0)
    313 	    return HDB_ERR_EXISTS;
    314     }
    315     return 0;
    316 }
    317 
    318 krb5_error_code
    319 _hdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
    320 {
    321     krb5_data key, value;
    322     int code;
    323 
    324     if (entry->entry.flags.do_not_store)
    325 	return HDB_ERR_MISUSE;
    326     /* check if new aliases already is used */
    327     code = hdb_check_aliases(context, db, entry);
    328     if (code)
    329 	return code;
    330 
    331     if ((flags & HDB_F_PRECHECK) && (flags & HDB_F_REPLACE))
    332         return 0;
    333 
    334     if ((flags & HDB_F_PRECHECK)) {
    335         code = hdb_principal2key(context, entry->entry.principal, &key);
    336         if (code)
    337             return code;
    338         code = db->hdb__get(context, db, key, &value);
    339         krb5_data_free(&key);
    340         if (code == 0)
    341             krb5_data_free(&value);
    342         if (code == HDB_ERR_NOENTRY)
    343             return 0;
    344         return code ? code : HDB_ERR_EXISTS;
    345     }
    346 
    347     if(entry->entry.generation == NULL) {
    348 	struct timeval t;
    349 	entry->entry.generation = malloc(sizeof(*entry->entry.generation));
    350 	if(entry->entry.generation == NULL) {
    351 	    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
    352 	    return ENOMEM;
    353 	}
    354 	gettimeofday(&t, NULL);
    355 	entry->entry.generation->time = t.tv_sec;
    356 	entry->entry.generation->usec = t.tv_usec;
    357 	entry->entry.generation->gen = 0;
    358     } else
    359 	entry->entry.generation->gen++;
    360 
    361     code = hdb_seal_keys(context, db, &entry->entry);
    362     if (code)
    363 	return code;
    364 
    365     hdb_principal2key(context, entry->entry.principal, &key);
    366 
    367     /* remove aliases */
    368     code = hdb_remove_aliases(context, db, &key);
    369     if (code) {
    370 	krb5_data_free(&key);
    371 	return code;
    372     }
    373     hdb_entry2value(context, &entry->entry, &value);
    374     code = db->hdb__put(context, db, flags & HDB_F_REPLACE, key, value);
    375     krb5_data_free(&value);
    376     krb5_data_free(&key);
    377     if (code)
    378 	return code;
    379 
    380     code = hdb_add_aliases(context, db, flags, entry);
    381 
    382     return code;
    383 }
    384 
    385 krb5_error_code
    386 _hdb_remove(krb5_context context, HDB *db,
    387             unsigned flags, krb5_const_principal principal)
    388 {
    389     krb5_data key, value;
    390     int code;
    391 
    392     hdb_principal2key(context, principal, &key);
    393 
    394     if ((flags & HDB_F_PRECHECK)) {
    395         /*
    396          * We don't check that we can delete the aliases because we
    397          * assume that the DB is consistent.  If we did check for alias
    398          * consistency we'd also have to provide a way to fsck the DB,
    399          * otherwise admins would have no way to recover -- papering
    400          * over this here is less work, but we really ought to provide
    401          * an HDB fsck.
    402          */
    403         code = db->hdb__get(context, db, key, &value);
    404         krb5_data_free(&key);
    405         if (code == 0) {
    406             krb5_data_free(&value);
    407             return 0;
    408         }
    409         return code;
    410     }
    411 
    412     code = hdb_remove_aliases(context, db, &key);
    413     if (code) {
    414 	krb5_data_free(&key);
    415 	return code;
    416     }
    417     code = db->hdb__del(context, db, key);
    418     krb5_data_free(&key);
    419     return code;
    420 }
    421 
    422