keytab.c revision 178826
1254885Sdumbbell/*
2254885Sdumbbell * Copyright (c) 1999 - 2002 Kungliga Tekniska H�gskolan
3254885Sdumbbell * (Royal Institute of Technology, Stockholm, Sweden).
4254885Sdumbbell * All rights reserved.
5254885Sdumbbell *
6254885Sdumbbell * Redistribution and use in source and binary forms, with or without
7254885Sdumbbell * modification, are permitted provided that the following conditions
8254885Sdumbbell * are met:
9254885Sdumbbell *
10254885Sdumbbell * 1. Redistributions of source code must retain the above copyright
11254885Sdumbbell *    notice, this list of conditions and the following disclaimer.
12254885Sdumbbell *
13254885Sdumbbell * 2. Redistributions in binary form must reproduce the above copyright
14254885Sdumbbell *    notice, this list of conditions and the following disclaimer in the
15254885Sdumbbell *    documentation and/or other materials provided with the distribution.
16254885Sdumbbell *
17254885Sdumbbell * 3. Neither the name of the Institute nor the names of its contributors
18254885Sdumbbell *    may be used to endorse or promote products derived from this software
19254885Sdumbbell *    without specific prior written permission.
20254885Sdumbbell *
21254885Sdumbbell * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22254885Sdumbbell * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23254885Sdumbbell * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24254885Sdumbbell * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25254885Sdumbbell * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26254885Sdumbbell * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27254885Sdumbbell * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28254885Sdumbbell * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29254885Sdumbbell * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30254885Sdumbbell * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31254885Sdumbbell * SUCH DAMAGE.
32254885Sdumbbell */
33254885Sdumbbell
34254885Sdumbbell#include "hdb_locl.h"
35254885Sdumbbell
36254885Sdumbbell/* keytab backend for HDB databases */
37254885Sdumbbell
38254885SdumbbellRCSID("$Id: keytab.c 18380 2006-10-09 12:36:40Z lha $");
39254885Sdumbbell
40254885Sdumbbellstruct hdb_data {
41254885Sdumbbell    char *dbname;
42254885Sdumbbell    char *mkey;
43254885Sdumbbell};
44254885Sdumbbell
45254885Sdumbbell/*
46254885Sdumbbell * the format for HDB keytabs is:
47254885Sdumbbell * HDB:[database:file:mkey]
48254885Sdumbbell */
49254885Sdumbbell
50254885Sdumbbellstatic krb5_error_code
51254885Sdumbbellhdb_resolve(krb5_context context, const char *name, krb5_keytab id)
52254885Sdumbbell{
53254885Sdumbbell    struct hdb_data *d;
54254885Sdumbbell    const char *db, *mkey;
55254885Sdumbbell
56254885Sdumbbell    d = malloc(sizeof(*d));
57254885Sdumbbell    if(d == NULL) {
58254885Sdumbbell	krb5_set_error_string(context, "malloc: out of memory");
59254885Sdumbbell	return ENOMEM;
60254885Sdumbbell    }
61254885Sdumbbell    db = name;
62254885Sdumbbell    mkey = strchr(name, ':');
63254885Sdumbbell    if(mkey == NULL || mkey[1] == '\0') {
64254885Sdumbbell	if(*name == '\0')
65254885Sdumbbell	    d->dbname = NULL;
66254885Sdumbbell	else {
67254885Sdumbbell	    d->dbname = strdup(name);
68254885Sdumbbell	    if(d->dbname == NULL) {
69254885Sdumbbell		free(d);
70254885Sdumbbell		krb5_set_error_string(context, "malloc: out of memory");
71254885Sdumbbell		return ENOMEM;
72254885Sdumbbell	    }
73254885Sdumbbell	}
74254885Sdumbbell	d->mkey = NULL;
75254885Sdumbbell    } else {
76254885Sdumbbell	if((mkey - db) == 0) {
77254885Sdumbbell	    d->dbname = NULL;
78254885Sdumbbell	} else {
79254885Sdumbbell	    d->dbname = malloc(mkey - db + 1);
80254885Sdumbbell	    if(d->dbname == NULL) {
81254885Sdumbbell		free(d);
82254885Sdumbbell		krb5_set_error_string(context, "malloc: out of memory");
83254885Sdumbbell		return ENOMEM;
84254885Sdumbbell	    }
85254885Sdumbbell	    memmove(d->dbname, db, mkey - db);
86254885Sdumbbell	    d->dbname[mkey - db] = '\0';
87254885Sdumbbell	}
88254885Sdumbbell	d->mkey = strdup(mkey + 1);
89254885Sdumbbell	if(d->mkey == NULL) {
90254885Sdumbbell	    free(d->dbname);
91254885Sdumbbell	    free(d);
92254885Sdumbbell	    krb5_set_error_string(context, "malloc: out of memory");
93254885Sdumbbell	    return ENOMEM;
94254885Sdumbbell	}
95254885Sdumbbell    }
96254885Sdumbbell    id->data = d;
97254885Sdumbbell    return 0;
98254885Sdumbbell}
99254885Sdumbbell
100254885Sdumbbellstatic krb5_error_code
101254885Sdumbbellhdb_close(krb5_context context, krb5_keytab id)
102254885Sdumbbell{
103254885Sdumbbell    struct hdb_data *d = id->data;
104254885Sdumbbell
105254885Sdumbbell    free(d->dbname);
106254885Sdumbbell    free(d->mkey);
107254885Sdumbbell    free(d);
108254885Sdumbbell    return 0;
109254885Sdumbbell}
110254885Sdumbbell
111254885Sdumbbellstatic krb5_error_code
112254885Sdumbbellhdb_get_name(krb5_context context,
113254885Sdumbbell	     krb5_keytab id,
114254885Sdumbbell	     char *name,
115254885Sdumbbell	     size_t namesize)
116254885Sdumbbell{
117254885Sdumbbell    struct hdb_data *d = id->data;
118254885Sdumbbell
119254885Sdumbbell    snprintf(name, namesize, "%s%s%s",
120254885Sdumbbell	     d->dbname ? d->dbname : "",
121254885Sdumbbell	     (d->dbname || d->mkey) ? ":" : "",
122254885Sdumbbell	     d->mkey ? d->mkey : "");
123254885Sdumbbell    return 0;
124254885Sdumbbell}
125254885Sdumbbell
126254885Sdumbbellstatic void
127254885Sdumbbellset_config (krb5_context context,
128254885Sdumbbell	    const krb5_config_binding *binding,
129254885Sdumbbell	    const char **dbname,
130254885Sdumbbell	    const char **mkey)
131254885Sdumbbell{
132254885Sdumbbell    *dbname = krb5_config_get_string(context, binding, "dbname", NULL);
133254885Sdumbbell    *mkey   = krb5_config_get_string(context, binding, "mkey_file", NULL);
134254885Sdumbbell}
135254885Sdumbbell
136254885Sdumbbell/*
137254885Sdumbbell * try to figure out the database (`dbname') and master-key (`mkey')
138254885Sdumbbell * that should be used for `principal'.
139254885Sdumbbell */
140254885Sdumbbell
141254885Sdumbbellstatic void
142254885Sdumbbellfind_db (krb5_context context,
143282199Sdumbbell	 const char **dbname,
144254885Sdumbbell	 const char **mkey,
145254885Sdumbbell	 krb5_const_principal principal)
146254885Sdumbbell{
147254885Sdumbbell    const krb5_config_binding *top_bind = NULL;
148254885Sdumbbell    const krb5_config_binding *default_binding = NULL;
149254885Sdumbbell    const krb5_config_binding *db;
150254885Sdumbbell    krb5_realm *prealm = krb5_princ_realm(context, rk_UNCONST(principal));
151254885Sdumbbell
152254885Sdumbbell    *dbname = *mkey = NULL;
153254885Sdumbbell
154254885Sdumbbell    while ((db =
155254885Sdumbbell	    krb5_config_get_next(context,
156254885Sdumbbell				 NULL,
157254885Sdumbbell				 &top_bind,
158254885Sdumbbell				 krb5_config_list,
159254885Sdumbbell				 "kdc",
160254885Sdumbbell				 "database",
161254885Sdumbbell				 NULL)) != NULL) {
162254885Sdumbbell	const char *p;
163254885Sdumbbell
164254885Sdumbbell	p = krb5_config_get_string (context, db, "realm", NULL);
165254885Sdumbbell	if (p == NULL) {
166254885Sdumbbell	    if(default_binding) {
167254885Sdumbbell		krb5_warnx(context, "WARNING: more than one realm-less "
168254885Sdumbbell			   "database specification");
169254885Sdumbbell		krb5_warnx(context, "WARNING: using the first encountered");
170254885Sdumbbell	    } else
171254885Sdumbbell		default_binding = db;
172254885Sdumbbell	} else if (strcmp (*prealm, p) == 0) {
173254885Sdumbbell	    set_config (context, db, dbname, mkey);
174254885Sdumbbell	    break;
175254885Sdumbbell	}
176282199Sdumbbell    }
177254885Sdumbbell    if (*dbname == NULL && default_binding != NULL)
178254885Sdumbbell	set_config (context, default_binding, dbname, mkey);
179254885Sdumbbell    if (*dbname == NULL)
180254885Sdumbbell	*dbname = HDB_DEFAULT_DB;
181254885Sdumbbell}
182254885Sdumbbell
183254885Sdumbbell/*
184254885Sdumbbell * find the keytab entry in `id' for `principal, kvno, enctype' and return
185254885Sdumbbell * it in `entry'.  return 0 or an error code
186254885Sdumbbell */
187254885Sdumbbell
188254885Sdumbbellstatic krb5_error_code
189254885Sdumbbellhdb_get_entry(krb5_context context,
190254885Sdumbbell	      krb5_keytab id,
191254885Sdumbbell	      krb5_const_principal principal,
192254885Sdumbbell	      krb5_kvno kvno,
193254885Sdumbbell	      krb5_enctype enctype,
194254885Sdumbbell	      krb5_keytab_entry *entry)
195254885Sdumbbell{
196254885Sdumbbell    hdb_entry_ex ent;
197254885Sdumbbell    krb5_error_code ret;
198254885Sdumbbell    struct hdb_data *d = id->data;
199254885Sdumbbell    int i;
200254885Sdumbbell    HDB *db;
201254885Sdumbbell    const char *dbname = d->dbname;
202254885Sdumbbell    const char *mkey   = d->mkey;
203254885Sdumbbell
204254885Sdumbbell    memset(&ent, 0, sizeof(ent));
205254885Sdumbbell
206254885Sdumbbell    if (dbname == NULL)
207254885Sdumbbell	find_db (context, &dbname, &mkey, principal);
208254885Sdumbbell
209254885Sdumbbell    ret = hdb_create (context, &db, dbname);
210254885Sdumbbell    if (ret)
211254885Sdumbbell	return ret;
212254885Sdumbbell    ret = hdb_set_master_keyfile (context, db, mkey);
213254885Sdumbbell    if (ret) {
214254885Sdumbbell	(*db->hdb_destroy)(context, db);
215254885Sdumbbell	return ret;
216254885Sdumbbell    }
217254885Sdumbbell
218254885Sdumbbell    ret = (*db->hdb_open)(context, db, O_RDONLY, 0);
219254885Sdumbbell    if (ret) {
220254885Sdumbbell	(*db->hdb_destroy)(context, db);
221254885Sdumbbell	return ret;
222254885Sdumbbell    }
223254885Sdumbbell    ret = (*db->hdb_fetch)(context, db, principal,
224254885Sdumbbell			   HDB_F_DECRYPT|
225254885Sdumbbell			   HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
226254885Sdumbbell			   &ent);
227254885Sdumbbell
228254885Sdumbbell    if(ret == HDB_ERR_NOENTRY) {
229254885Sdumbbell	ret = KRB5_KT_NOTFOUND;
230254885Sdumbbell	goto out;
231254885Sdumbbell    }else if(ret)
232254885Sdumbbell	goto out;
233254885Sdumbbell
234254885Sdumbbell    if(kvno && ent.entry.kvno != kvno) {
235254885Sdumbbell	hdb_free_entry(context, &ent);
236254885Sdumbbell 	ret = KRB5_KT_NOTFOUND;
237254885Sdumbbell	goto out;
238254885Sdumbbell    }
239254885Sdumbbell    if(enctype == 0)
240254885Sdumbbell	if(ent.entry.keys.len > 0)
241254885Sdumbbell	    enctype = ent.entry.keys.val[0].key.keytype;
242254885Sdumbbell    ret = KRB5_KT_NOTFOUND;
243254885Sdumbbell    for(i = 0; i < ent.entry.keys.len; i++) {
244254885Sdumbbell	if(ent.entry.keys.val[i].key.keytype == enctype) {
245254885Sdumbbell	    krb5_copy_principal(context, principal, &entry->principal);
246254885Sdumbbell	    entry->vno = ent.entry.kvno;
247254885Sdumbbell	    krb5_copy_keyblock_contents(context,
248254885Sdumbbell					&ent.entry.keys.val[i].key,
249254885Sdumbbell					&entry->keyblock);
250254885Sdumbbell	    ret = 0;
251254885Sdumbbell	    break;
252254885Sdumbbell	}
253254885Sdumbbell    }
254254885Sdumbbell    hdb_free_entry(context, &ent);
255254885Sdumbbellout:
256254885Sdumbbell    (*db->hdb_close)(context, db);
257254885Sdumbbell    (*db->hdb_destroy)(context, db);
258254885Sdumbbell    return ret;
259254885Sdumbbell}
260254885Sdumbbell
261254885Sdumbbellkrb5_kt_ops hdb_kt_ops = {
262254885Sdumbbell    "HDB",
263254885Sdumbbell    hdb_resolve,
264282199Sdumbbell    hdb_get_name,
265254885Sdumbbell    hdb_close,
266254885Sdumbbell    hdb_get_entry,
267254885Sdumbbell    NULL,		/* start_seq_get */
268254885Sdumbbell    NULL,		/* next_entry */
269254885Sdumbbell    NULL,		/* end_seq_get */
270254885Sdumbbell    NULL,		/* add */
271254885Sdumbbell    NULL		/* remove */
272254885Sdumbbell};
273254885Sdumbbell