Home | History | Annotate | Line # | Download | only in hdb
      1 /*	$NetBSD: keytab.c,v 1.3 2023/06/19 21:41:43 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1999 - 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 /* keytab backend for HDB databases */
     39 
     40 struct hdb_data {
     41     char *dbname;
     42     char *mkey;
     43 };
     44 
     45 struct hdb_cursor {
     46     HDB *db;
     47     hdb_entry_ex hdb_entry;
     48     int first, next;
     49     int key_idx;
     50 };
     51 
     52 /*
     53  * the format for HDB keytabs is:
     54  * HDB:[HDBFORMAT:database-specific-data[:mkey=mkey-file]]
     55  */
     56 
     57 static krb5_error_code KRB5_CALLCONV
     58 hdb_resolve(krb5_context context, const char *name, krb5_keytab id)
     59 {
     60     struct hdb_data *d;
     61     const char *db, *mkey;
     62 
     63     d = malloc(sizeof(*d));
     64     if(d == NULL) {
     65 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
     66 	return ENOMEM;
     67     }
     68     db = name;
     69     mkey = strstr(name, ":mkey=");
     70     if(mkey == NULL || mkey[6] == '\0') {
     71 	if(*name == '\0')
     72 	    d->dbname = NULL;
     73 	else {
     74 	    d->dbname = strdup(name);
     75 	    if(d->dbname == NULL) {
     76 		free(d);
     77 		krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
     78 		return ENOMEM;
     79 	    }
     80 	}
     81 	d->mkey = NULL;
     82     } else {
     83 	d->dbname = malloc(mkey - db + 1);
     84 	if(d->dbname == NULL) {
     85 	    free(d);
     86 	    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
     87 	    return ENOMEM;
     88 	}
     89 	memmove(d->dbname, db, mkey - db);
     90 	d->dbname[mkey - db] = '\0';
     91 
     92 	d->mkey = strdup(mkey + 6);
     93 	if(d->mkey == NULL) {
     94 	    free(d->dbname);
     95 	    free(d);
     96 	    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
     97 	    return ENOMEM;
     98 	}
     99     }
    100     id->data = d;
    101     return 0;
    102 }
    103 
    104 static krb5_error_code KRB5_CALLCONV
    105 hdb_close(krb5_context context, krb5_keytab id)
    106 {
    107     struct hdb_data *d = id->data;
    108 
    109     free(d->dbname);
    110     free(d->mkey);
    111     free(d);
    112     return 0;
    113 }
    114 
    115 static krb5_error_code KRB5_CALLCONV
    116 hdb_get_name(krb5_context context,
    117 	     krb5_keytab id,
    118 	     char *name,
    119 	     size_t namesize)
    120 {
    121     struct hdb_data *d = id->data;
    122 
    123     snprintf(name, namesize, "%s%s%s",
    124 	     d->dbname ? d->dbname : "",
    125 	     (d->dbname || d->mkey) ? ":" : "",
    126 	     d->mkey ? d->mkey : "");
    127     return 0;
    128 }
    129 
    130 /*
    131  * try to figure out the database (`dbname') and master-key (`mkey')
    132  * that should be used for `principal'.
    133  */
    134 
    135 static krb5_error_code
    136 find_db (krb5_context context,
    137 	 char **dbname,
    138 	 char **mkey,
    139 	 krb5_const_principal principal)
    140 {
    141     krb5_const_realm realm = krb5_principal_get_realm(context, principal);
    142     krb5_error_code ret;
    143     struct hdb_dbinfo *head, *dbinfo = NULL;
    144 
    145     *dbname = *mkey = NULL;
    146 
    147     ret = hdb_get_dbinfo(context, &head);
    148     if (ret)
    149 	return ret;
    150 
    151     while ((dbinfo = hdb_dbinfo_get_next(head, dbinfo)) != NULL) {
    152 	const char *p = hdb_dbinfo_get_realm(context, dbinfo);
    153 	if (p && strcmp (realm, p) == 0) {
    154 	    p = hdb_dbinfo_get_dbname(context, dbinfo);
    155 	    if (p)
    156 		*dbname = strdup(p);
    157 	    p = hdb_dbinfo_get_mkey_file(context, dbinfo);
    158 	    if (p)
    159 		*mkey = strdup(p);
    160 	    break;
    161 	}
    162     }
    163     hdb_free_dbinfo(context, &head);
    164     if (*dbname == NULL &&
    165 	(*dbname = strdup(HDB_DEFAULT_DB)) == NULL) {
    166         free(*mkey);
    167         *mkey = NULL;
    168         return krb5_enomem(context);
    169     }
    170     return 0;
    171 }
    172 
    173 /*
    174  * find the keytab entry in `id' for `principal, kvno, enctype' and return
    175  * it in `entry'.  return 0 or an error code
    176  */
    177 
    178 static krb5_error_code KRB5_CALLCONV
    179 hdb_get_entry(krb5_context context,
    180 	      krb5_keytab id,
    181 	      krb5_const_principal principal,
    182 	      krb5_kvno kvno,
    183 	      krb5_enctype enctype,
    184 	      krb5_keytab_entry *entry)
    185 {
    186     hdb_entry_ex ent;
    187     krb5_error_code ret;
    188     struct hdb_data *d = id->data;
    189     const char *dbname = d->dbname;
    190     const char *mkey   = d->mkey;
    191     char *fdbname = NULL, *fmkey = NULL;
    192     HDB *db;
    193     size_t i;
    194 
    195     memset(&ent, 0, sizeof(ent));
    196 
    197     if (dbname == NULL) {
    198 	ret = find_db(context, &fdbname, &fmkey, principal);
    199 	if (ret)
    200 	    return ret;
    201 	dbname = fdbname;
    202 	mkey = fmkey;
    203     }
    204 
    205     ret = hdb_create (context, &db, dbname);
    206     if (ret)
    207 	goto out2;
    208     ret = hdb_set_master_keyfile (context, db, mkey);
    209     if (ret) {
    210 	(*db->hdb_destroy)(context, db);
    211 	goto out2;
    212     }
    213 
    214     ret = (*db->hdb_open)(context, db, O_RDONLY, 0);
    215     if (ret) {
    216 	(*db->hdb_destroy)(context, db);
    217 	goto out2;
    218     }
    219 
    220     ret = (*db->hdb_fetch_kvno)(context, db, principal,
    221 				HDB_F_DECRYPT|HDB_F_KVNO_SPECIFIED|
    222 				HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
    223 				kvno, &ent);
    224 
    225     if(ret == HDB_ERR_NOENTRY) {
    226 	ret = KRB5_KT_NOTFOUND;
    227 	goto out;
    228     }else if(ret)
    229 	goto out;
    230 
    231     if(kvno && (krb5_kvno)ent.entry.kvno != kvno) {
    232 	hdb_free_entry(context, &ent);
    233  	ret = KRB5_KT_NOTFOUND;
    234 	goto out;
    235     }
    236     if(enctype == 0)
    237 	if(ent.entry.keys.len > 0)
    238 	    enctype = ent.entry.keys.val[0].key.keytype;
    239     ret = KRB5_KT_NOTFOUND;
    240     for(i = 0; i < ent.entry.keys.len; i++) {
    241 	if(ent.entry.keys.val[i].key.keytype == enctype) {
    242 	    krb5_copy_principal(context, principal, &entry->principal);
    243 	    entry->vno = ent.entry.kvno;
    244 	    krb5_copy_keyblock_contents(context,
    245 					&ent.entry.keys.val[i].key,
    246 					&entry->keyblock);
    247 	    ret = 0;
    248 	    break;
    249 	}
    250     }
    251     hdb_free_entry(context, &ent);
    252  out:
    253     (*db->hdb_close)(context, db);
    254     (*db->hdb_destroy)(context, db);
    255  out2:
    256     free(fdbname);
    257     free(fmkey);
    258     return ret;
    259 }
    260 
    261 /*
    262  * find the keytab entry in `id' for `principal, kvno, enctype' and return
    263  * it in `entry'.  return 0 or an error code
    264  */
    265 
    266 static krb5_error_code KRB5_CALLCONV
    267 hdb_start_seq_get(krb5_context context,
    268 		  krb5_keytab id,
    269 		  krb5_kt_cursor *cursor)
    270 {
    271     krb5_error_code ret;
    272     struct hdb_cursor *c;
    273     struct hdb_data *d = id->data;
    274     const char *dbname = d->dbname;
    275     const char *mkey   = d->mkey;
    276     HDB *db;
    277 
    278     if (dbname == NULL) {
    279 	/*
    280 	 * We don't support enumerating without being told what
    281 	 * backend to enumerate on
    282 	 */
    283   	ret = KRB5_KT_NOTFOUND;
    284 	return ret;
    285     }
    286 
    287     ret = hdb_create (context, &db, dbname);
    288     if (ret)
    289 	return ret;
    290     ret = hdb_set_master_keyfile (context, db, mkey);
    291     if (ret) {
    292 	(*db->hdb_destroy)(context, db);
    293 	return ret;
    294     }
    295 
    296     ret = (*db->hdb_open)(context, db, O_RDONLY, 0);
    297     if (ret) {
    298 	(*db->hdb_destroy)(context, db);
    299 	return ret;
    300     }
    301 
    302     cursor->data = c = malloc (sizeof(*c));
    303     if(c == NULL){
    304 	(*db->hdb_close)(context, db);
    305 	(*db->hdb_destroy)(context, db);
    306 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
    307 	return ENOMEM;
    308     }
    309 
    310     c->db = db;
    311     c->first = TRUE;
    312     c->next = TRUE;
    313     c->key_idx = 0;
    314 
    315     cursor->data = c;
    316     return ret;
    317 }
    318 
    319 static int KRB5_CALLCONV
    320 hdb_next_entry(krb5_context context,
    321 	       krb5_keytab id,
    322 	       krb5_keytab_entry *entry,
    323 	       krb5_kt_cursor *cursor)
    324 {
    325     struct hdb_cursor *c = cursor->data;
    326     krb5_error_code ret;
    327 
    328     memset(entry, 0, sizeof(*entry));
    329 
    330     if (c->first) {
    331 	c->first = FALSE;
    332 	ret = (c->db->hdb_firstkey)(context, c->db,
    333 				    HDB_F_DECRYPT|
    334 				    HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
    335 				    &c->hdb_entry);
    336 	if (ret == HDB_ERR_NOENTRY)
    337 	    return KRB5_KT_END;
    338 	else if (ret)
    339 	    return ret;
    340 
    341 	if (c->hdb_entry.entry.keys.len == 0)
    342 	    hdb_free_entry(context, &c->hdb_entry);
    343 	else
    344 	    c->next = FALSE;
    345     }
    346 
    347     while (c->next) {
    348 	ret = (c->db->hdb_nextkey)(context, c->db,
    349 				   HDB_F_DECRYPT|
    350 				   HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
    351 				   &c->hdb_entry);
    352 	if (ret == HDB_ERR_NOENTRY)
    353 	    return KRB5_KT_END;
    354 	else if (ret)
    355 	    return ret;
    356 
    357 	/* If no keys on this entry, try again */
    358 	if (c->hdb_entry.entry.keys.len == 0)
    359 	    hdb_free_entry(context, &c->hdb_entry);
    360 	else
    361 	    c->next = FALSE;
    362     }
    363 
    364     /*
    365      * Return next enc type (keytabs are one slot per key, while
    366      * hdb is one record per principal.
    367      */
    368 
    369     ret = krb5_copy_principal(context,
    370 			      c->hdb_entry.entry.principal,
    371 			      &entry->principal);
    372     if (ret)
    373 	return ret;
    374 
    375     entry->vno = c->hdb_entry.entry.kvno;
    376     ret = krb5_copy_keyblock_contents(context,
    377 				      &c->hdb_entry.entry.keys.val[c->key_idx].key,
    378 				      &entry->keyblock);
    379     if (ret) {
    380 	krb5_free_principal(context, entry->principal);
    381 	memset(entry, 0, sizeof(*entry));
    382 	return ret;
    383     }
    384     c->key_idx++;
    385 
    386     /*
    387      * Once we get to the end of the list, signal that we want the
    388      * next entry
    389      */
    390 
    391     if ((size_t)c->key_idx == c->hdb_entry.entry.keys.len) {
    392 	hdb_free_entry(context, &c->hdb_entry);
    393 	c->next = TRUE;
    394 	c->key_idx = 0;
    395     }
    396 
    397     return 0;
    398 }
    399 
    400 
    401 static int KRB5_CALLCONV
    402 hdb_end_seq_get(krb5_context context,
    403 		krb5_keytab id,
    404 		krb5_kt_cursor *cursor)
    405 {
    406     struct hdb_cursor *c = cursor->data;
    407 
    408     if (!c->next)
    409 	hdb_free_entry(context, &c->hdb_entry);
    410 
    411     (c->db->hdb_close)(context, c->db);
    412     (c->db->hdb_destroy)(context, c->db);
    413 
    414     free(c);
    415     return 0;
    416 }
    417 
    418 krb5_kt_ops hdb_kt_ops = {
    419     "HDB",
    420     hdb_resolve,
    421     hdb_get_name,
    422     hdb_close,
    423     NULL,		/* destroy */
    424     hdb_get_entry,
    425     hdb_start_seq_get,
    426     hdb_next_entry,
    427     hdb_end_seq_get,
    428     NULL,		/* add */
    429     NULL,		/* remove */
    430     NULL,
    431     0
    432 };
    433 
    434 krb5_kt_ops hdb_get_kt_ops = {
    435     "HDBGET",
    436     hdb_resolve,
    437     hdb_get_name,
    438     hdb_close,
    439     NULL,
    440     hdb_get_entry,
    441     NULL,
    442     NULL,
    443     NULL,
    444     NULL,
    445     NULL,
    446     NULL,
    447     0
    448 };
    449