155682Smarkm/*
2102644Snectar * Copyright (c) 1999 - 2002 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "hdb_locl.h"
3555682Smarkm
3655682Smarkm/* keytab backend for HDB databases */
3755682Smarkm
38178825SdfrRCSID("$Id: keytab.c 18380 2006-10-09 12:36:40Z lha $");
3955682Smarkm
4055682Smarkmstruct hdb_data {
4155682Smarkm    char *dbname;
4255682Smarkm    char *mkey;
4355682Smarkm};
4455682Smarkm
4572445Sassar/*
4672445Sassar * the format for HDB keytabs is:
47178825Sdfr * HDB:[database:file:mkey]
4872445Sassar */
4972445Sassar
5055682Smarkmstatic krb5_error_code
5155682Smarkmhdb_resolve(krb5_context context, const char *name, krb5_keytab id)
5255682Smarkm{
5355682Smarkm    struct hdb_data *d;
5455682Smarkm    const char *db, *mkey;
5572445Sassar
5655682Smarkm    d = malloc(sizeof(*d));
5790926Snectar    if(d == NULL) {
5890926Snectar	krb5_set_error_string(context, "malloc: out of memory");
5955682Smarkm	return ENOMEM;
6090926Snectar    }
6155682Smarkm    db = name;
6255682Smarkm    mkey = strchr(name, ':');
6355682Smarkm    if(mkey == NULL || mkey[1] == '\0') {
6455682Smarkm	if(*name == '\0')
6555682Smarkm	    d->dbname = NULL;
6655682Smarkm	else {
6755682Smarkm	    d->dbname = strdup(name);
6855682Smarkm	    if(d->dbname == NULL) {
6955682Smarkm		free(d);
7090926Snectar		krb5_set_error_string(context, "malloc: out of memory");
7155682Smarkm		return ENOMEM;
7255682Smarkm	    }
7355682Smarkm	}
7455682Smarkm	d->mkey = NULL;
7555682Smarkm    } else {
7655682Smarkm	if((mkey - db) == 0) {
7755682Smarkm	    d->dbname = NULL;
7855682Smarkm	} else {
79178825Sdfr	    d->dbname = malloc(mkey - db + 1);
8055682Smarkm	    if(d->dbname == NULL) {
8155682Smarkm		free(d);
8290926Snectar		krb5_set_error_string(context, "malloc: out of memory");
8355682Smarkm		return ENOMEM;
8455682Smarkm	    }
8572445Sassar	    memmove(d->dbname, db, mkey - db);
8655682Smarkm	    d->dbname[mkey - db] = '\0';
8755682Smarkm	}
8855682Smarkm	d->mkey = strdup(mkey + 1);
8955682Smarkm	if(d->mkey == NULL) {
9055682Smarkm	    free(d->dbname);
9155682Smarkm	    free(d);
9290926Snectar	    krb5_set_error_string(context, "malloc: out of memory");
9355682Smarkm	    return ENOMEM;
9455682Smarkm	}
9555682Smarkm    }
9655682Smarkm    id->data = d;
9755682Smarkm    return 0;
9855682Smarkm}
9955682Smarkm
10055682Smarkmstatic krb5_error_code
10155682Smarkmhdb_close(krb5_context context, krb5_keytab id)
10255682Smarkm{
10355682Smarkm    struct hdb_data *d = id->data;
10472445Sassar
10572445Sassar    free(d->dbname);
10672445Sassar    free(d->mkey);
10755682Smarkm    free(d);
10855682Smarkm    return 0;
10955682Smarkm}
11055682Smarkm
11155682Smarkmstatic krb5_error_code
11255682Smarkmhdb_get_name(krb5_context context,
11355682Smarkm	     krb5_keytab id,
11455682Smarkm	     char *name,
11555682Smarkm	     size_t namesize)
11655682Smarkm{
11755682Smarkm    struct hdb_data *d = id->data;
11872445Sassar
11955682Smarkm    snprintf(name, namesize, "%s%s%s",
12055682Smarkm	     d->dbname ? d->dbname : "",
12155682Smarkm	     (d->dbname || d->mkey) ? ":" : "",
12255682Smarkm	     d->mkey ? d->mkey : "");
12355682Smarkm    return 0;
12455682Smarkm}
12555682Smarkm
12672445Sassarstatic void
12772445Sassarset_config (krb5_context context,
128178825Sdfr	    const krb5_config_binding *binding,
12972445Sassar	    const char **dbname,
13072445Sassar	    const char **mkey)
13172445Sassar{
13272445Sassar    *dbname = krb5_config_get_string(context, binding, "dbname", NULL);
13372445Sassar    *mkey   = krb5_config_get_string(context, binding, "mkey_file", NULL);
13472445Sassar}
13572445Sassar
13672445Sassar/*
13772445Sassar * try to figure out the database (`dbname') and master-key (`mkey')
13872445Sassar * that should be used for `principal'.
13972445Sassar */
14072445Sassar
14172445Sassarstatic void
14272445Sassarfind_db (krb5_context context,
14372445Sassar	 const char **dbname,
14472445Sassar	 const char **mkey,
14572445Sassar	 krb5_const_principal principal)
14672445Sassar{
147102644Snectar    const krb5_config_binding *top_bind = NULL;
148178825Sdfr    const krb5_config_binding *default_binding = NULL;
149178825Sdfr    const krb5_config_binding *db;
150178825Sdfr    krb5_realm *prealm = krb5_princ_realm(context, rk_UNCONST(principal));
15172445Sassar
15272445Sassar    *dbname = *mkey = NULL;
15372445Sassar
154178825Sdfr    while ((db =
15572445Sassar	    krb5_config_get_next(context,
15672445Sassar				 NULL,
15772445Sassar				 &top_bind,
15872445Sassar				 krb5_config_list,
15972445Sassar				 "kdc",
16072445Sassar				 "database",
16172445Sassar				 NULL)) != NULL) {
16272445Sassar	const char *p;
16372445Sassar
16472445Sassar	p = krb5_config_get_string (context, db, "realm", NULL);
16572445Sassar	if (p == NULL) {
16672445Sassar	    if(default_binding) {
16772445Sassar		krb5_warnx(context, "WARNING: more than one realm-less "
16872445Sassar			   "database specification");
16972445Sassar		krb5_warnx(context, "WARNING: using the first encountered");
17072445Sassar	    } else
17172445Sassar		default_binding = db;
17272445Sassar	} else if (strcmp (*prealm, p) == 0) {
17372445Sassar	    set_config (context, db, dbname, mkey);
17472445Sassar	    break;
17572445Sassar	}
17672445Sassar    }
17772445Sassar    if (*dbname == NULL && default_binding != NULL)
17872445Sassar	set_config (context, default_binding, dbname, mkey);
17972445Sassar    if (*dbname == NULL)
18072445Sassar	*dbname = HDB_DEFAULT_DB;
18172445Sassar}
18272445Sassar
18372445Sassar/*
18472445Sassar * find the keytab entry in `id' for `principal, kvno, enctype' and return
18572445Sassar * it in `entry'.  return 0 or an error code
18672445Sassar */
18772445Sassar
18855682Smarkmstatic krb5_error_code
18955682Smarkmhdb_get_entry(krb5_context context,
19055682Smarkm	      krb5_keytab id,
19155682Smarkm	      krb5_const_principal principal,
19255682Smarkm	      krb5_kvno kvno,
19355682Smarkm	      krb5_enctype enctype,
19455682Smarkm	      krb5_keytab_entry *entry)
19555682Smarkm{
196178825Sdfr    hdb_entry_ex ent;
19755682Smarkm    krb5_error_code ret;
19855682Smarkm    struct hdb_data *d = id->data;
19955682Smarkm    int i;
20072445Sassar    HDB *db;
20172445Sassar    const char *dbname = d->dbname;
20272445Sassar    const char *mkey   = d->mkey;
20355682Smarkm
204178825Sdfr    memset(&ent, 0, sizeof(ent));
205178825Sdfr
20672445Sassar    if (dbname == NULL)
20772445Sassar	find_db (context, &dbname, &mkey, principal);
20872445Sassar
20972445Sassar    ret = hdb_create (context, &db, dbname);
21055682Smarkm    if (ret)
21155682Smarkm	return ret;
21272445Sassar    ret = hdb_set_master_keyfile (context, db, mkey);
21372445Sassar    if (ret) {
214178825Sdfr	(*db->hdb_destroy)(context, db);
21572445Sassar	return ret;
21672445Sassar    }
21772445Sassar
218178825Sdfr    ret = (*db->hdb_open)(context, db, O_RDONLY, 0);
21972445Sassar    if (ret) {
220178825Sdfr	(*db->hdb_destroy)(context, db);
22172445Sassar	return ret;
22272445Sassar    }
223178825Sdfr    ret = (*db->hdb_fetch)(context, db, principal,
224178825Sdfr			   HDB_F_DECRYPT|
225178825Sdfr			   HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
226178825Sdfr			   &ent);
22772445Sassar
228178825Sdfr    if(ret == HDB_ERR_NOENTRY) {
229178825Sdfr	ret = KRB5_KT_NOTFOUND;
230178825Sdfr	goto out;
231178825Sdfr    }else if(ret)
232178825Sdfr	goto out;
233178825Sdfr
234178825Sdfr    if(kvno && ent.entry.kvno != kvno) {
23555682Smarkm	hdb_free_entry(context, &ent);
236178825Sdfr 	ret = KRB5_KT_NOTFOUND;
237178825Sdfr	goto out;
23855682Smarkm    }
23955682Smarkm    if(enctype == 0)
240178825Sdfr	if(ent.entry.keys.len > 0)
241178825Sdfr	    enctype = ent.entry.keys.val[0].key.keytype;
24255682Smarkm    ret = KRB5_KT_NOTFOUND;
243178825Sdfr    for(i = 0; i < ent.entry.keys.len; i++) {
244178825Sdfr	if(ent.entry.keys.val[i].key.keytype == enctype) {
24555682Smarkm	    krb5_copy_principal(context, principal, &entry->principal);
246178825Sdfr	    entry->vno = ent.entry.kvno;
24755682Smarkm	    krb5_copy_keyblock_contents(context,
248178825Sdfr					&ent.entry.keys.val[i].key,
24955682Smarkm					&entry->keyblock);
25055682Smarkm	    ret = 0;
25155682Smarkm	    break;
25255682Smarkm	}
25355682Smarkm    }
25455682Smarkm    hdb_free_entry(context, &ent);
255178825Sdfrout:
256178825Sdfr    (*db->hdb_close)(context, db);
257178825Sdfr    (*db->hdb_destroy)(context, db);
25855682Smarkm    return ret;
25955682Smarkm}
26055682Smarkm
26155682Smarkmkrb5_kt_ops hdb_kt_ops = {
26255682Smarkm    "HDB",
26355682Smarkm    hdb_resolve,
26455682Smarkm    hdb_get_name,
26555682Smarkm    hdb_close,
26655682Smarkm    hdb_get_entry,
26755682Smarkm    NULL,		/* start_seq_get */
26855682Smarkm    NULL,		/* next_entry */
26955682Smarkm    NULL,		/* end_seq_get */
27055682Smarkm    NULL,		/* add */
27155682Smarkm    NULL		/* remove */
27255682Smarkm};
273