keytab.c revision 233294
1118611Snjl/*
2118611Snjl * Copyright (c) 1999 - 2002 Kungliga Tekniska H��gskolan
3118611Snjl * (Royal Institute of Technology, Stockholm, Sweden).
4118611Snjl * All rights reserved.
5118611Snjl *
6118611Snjl * Redistribution and use in source and binary forms, with or without
7118611Snjl * modification, are permitted provided that the following conditions
8118611Snjl * are met:
9118611Snjl *
10118611Snjl * 1. Redistributions of source code must retain the above copyright
11118611Snjl *    notice, this list of conditions and the following disclaimer.
12202771Sjkim *
13118611Snjl * 2. Redistributions in binary form must reproduce the above copyright
14118611Snjl *    notice, this list of conditions and the following disclaimer in the
15118611Snjl *    documentation and/or other materials provided with the distribution.
16118611Snjl *
17118611Snjl * 3. Neither the name of the Institute nor the names of its contributors
18118611Snjl *    may be used to endorse or promote products derived from this software
19118611Snjl *    without specific prior written permission.
20118611Snjl *
21118611Snjl * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22118611Snjl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23118611Snjl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24118611Snjl * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25118611Snjl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26118611Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27118611Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28118611Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29118611Snjl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30118611Snjl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31118611Snjl * SUCH DAMAGE.
32118611Snjl */
33118611Snjl
34118611Snjl#include "hdb_locl.h"
35118611Snjl
36118611Snjl/* keytab backend for HDB databases */
37118611Snjl
38118611Snjlstruct hdb_data {
39118611Snjl    char *dbname;
40118611Snjl    char *mkey;
41118611Snjl};
42118611Snjl
43118611Snjlstruct hdb_cursor {
44118611Snjl    HDB *db;
45118611Snjl    hdb_entry_ex hdb_entry;
46118611Snjl    int first, next;
47118611Snjl    int key_idx;
48118611Snjl};
49118611Snjl
50118611Snjl/*
51118611Snjl * the format for HDB keytabs is:
52118611Snjl * HDB:[HDBFORMAT:database-specific-data[:mkey=mkey-file]]
53118611Snjl */
54118611Snjl
55118611Snjlstatic krb5_error_code KRB5_CALLCONV
56118611Snjlhdb_resolve(krb5_context context, const char *name, krb5_keytab id)
57118611Snjl{
58118611Snjl    struct hdb_data *d;
59118611Snjl    const char *db, *mkey;
60118611Snjl
61118611Snjl    d = malloc(sizeof(*d));
62118611Snjl    if(d == NULL) {
63118611Snjl	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
64118611Snjl	return ENOMEM;
65118611Snjl    }
66118611Snjl    db = name;
67118611Snjl    mkey = strstr(name, ":mkey=");
68118611Snjl    if(mkey == NULL || mkey[5] == '\0') {
69118611Snjl	if(*name == '\0')
70118611Snjl	    d->dbname = NULL;
71118611Snjl	else {
72118611Snjl	    d->dbname = strdup(name);
73118611Snjl	    if(d->dbname == NULL) {
74118611Snjl		free(d);
75118611Snjl		krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
76118611Snjl		return ENOMEM;
77118611Snjl	    }
78118611Snjl	}
79118611Snjl	d->mkey = NULL;
80118611Snjl    } else {
81118611Snjl	d->dbname = malloc(mkey - db + 1);
82118611Snjl	if(d->dbname == NULL) {
83118611Snjl	    free(d);
84118611Snjl	    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
85118611Snjl	    return ENOMEM;
86118611Snjl	}
87118611Snjl	memmove(d->dbname, db, mkey - db);
88118611Snjl	d->dbname[mkey - db] = '\0';
89118611Snjl
90118611Snjl	d->mkey = strdup(mkey + 5);
91118611Snjl	if(d->mkey == NULL) {
92118611Snjl	    free(d->dbname);
93118611Snjl	    free(d);
94118611Snjl	    krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
95118611Snjl	    return ENOMEM;
96118611Snjl	}
97118611Snjl    }
98118611Snjl    id->data = d;
99118611Snjl    return 0;
100118611Snjl}
101118611Snjl
102118611Snjlstatic krb5_error_code KRB5_CALLCONV
103118611Snjlhdb_close(krb5_context context, krb5_keytab id)
104118611Snjl{
105118611Snjl    struct hdb_data *d = id->data;
106118611Snjl
107118611Snjl    free(d->dbname);
108118611Snjl    free(d->mkey);
109118611Snjl    free(d);
110118611Snjl    return 0;
111118611Snjl}
112118611Snjl
113118611Snjlstatic krb5_error_code KRB5_CALLCONV
114118611Snjlhdb_get_name(krb5_context context,
115118611Snjl	     krb5_keytab id,
116118611Snjl	     char *name,
117118611Snjl	     size_t namesize)
118151937Sjkim{
119118611Snjl    struct hdb_data *d = id->data;
120118611Snjl
121118611Snjl    snprintf(name, namesize, "%s%s%s",
122118611Snjl	     d->dbname ? d->dbname : "",
123118611Snjl	     (d->dbname || d->mkey) ? ":" : "",
124151937Sjkim	     d->mkey ? d->mkey : "");
125118611Snjl    return 0;
126151937Sjkim}
127151937Sjkim
128151937Sjkim/*
129151937Sjkim * try to figure out the database (`dbname') and master-key (`mkey')
130151937Sjkim * that should be used for `principal'.
131151937Sjkim */
132151937Sjkim
133151937Sjkimstatic krb5_error_code
134151937Sjkimfind_db (krb5_context context,
135151937Sjkim	 char **dbname,
136151937Sjkim	 char **mkey,
137151937Sjkim	 krb5_const_principal principal)
138151937Sjkim{
139151937Sjkim    krb5_const_realm realm = krb5_principal_get_realm(context, principal);
140151937Sjkim    krb5_error_code ret;
141151937Sjkim    struct hdb_dbinfo *head, *dbinfo = NULL;
142151937Sjkim
143151937Sjkim    *dbname = *mkey = NULL;
144151937Sjkim
145151937Sjkim    ret = hdb_get_dbinfo(context, &head);
146151937Sjkim    if (ret)
147151937Sjkim	return ret;
148151937Sjkim
149151937Sjkim    while ((dbinfo = hdb_dbinfo_get_next(head, dbinfo)) != NULL) {
150151937Sjkim	const char *p = hdb_dbinfo_get_realm(context, dbinfo);
151151937Sjkim	if (p && strcmp (realm, p) == 0) {
152151937Sjkim	    p = hdb_dbinfo_get_dbname(context, dbinfo);
153151937Sjkim	    if (p)
154151937Sjkim		*dbname = strdup(p);
155151937Sjkim	    p = hdb_dbinfo_get_mkey_file(context, dbinfo);
156151937Sjkim	    if (p)
157151937Sjkim		*mkey = strdup(p);
158151937Sjkim	    break;
159151937Sjkim	}
160151937Sjkim    }
161151937Sjkim    hdb_free_dbinfo(context, &head);
162151937Sjkim    if (*dbname == NULL)
163151937Sjkim	*dbname = strdup(HDB_DEFAULT_DB);
164118611Snjl    return 0;
165118611Snjl}
166118611Snjl
167118611Snjl/*
168151937Sjkim * find the keytab entry in `id' for `principal, kvno, enctype' and return
169151937Sjkim * it in `entry'.  return 0 or an error code
170151937Sjkim */
171151937Sjkim
172118611Snjlstatic krb5_error_code KRB5_CALLCONV
173151937Sjkimhdb_get_entry(krb5_context context,
174118611Snjl	      krb5_keytab id,
175138287Smarks	      krb5_const_principal principal,
176151937Sjkim	      krb5_kvno kvno,
177118611Snjl	      krb5_enctype enctype,
178118611Snjl	      krb5_keytab_entry *entry)
179118611Snjl{
180151937Sjkim    hdb_entry_ex ent;
181118611Snjl    krb5_error_code ret;
182151937Sjkim    struct hdb_data *d = id->data;
183151937Sjkim    const char *dbname = d->dbname;
184118611Snjl    const char *mkey   = d->mkey;
185118611Snjl    char *fdbname = NULL, *fmkey = NULL;
186118611Snjl    HDB *db;
187118611Snjl    size_t i;
188151937Sjkim
189118611Snjl    memset(&ent, 0, sizeof(ent));
190118611Snjl
191151937Sjkim    if (dbname == NULL) {
192151937Sjkim	ret = find_db(context, &fdbname, &fmkey, principal);
193151937Sjkim	if (ret)
194118611Snjl	    return ret;
195118611Snjl	dbname = fdbname;
196151937Sjkim	mkey = fmkey;
197138287Smarks    }
198151937Sjkim
199138287Smarks    ret = hdb_create (context, &db, dbname);
200151937Sjkim    if (ret)
201138287Smarks	goto out2;
202151937Sjkim    ret = hdb_set_master_keyfile (context, db, mkey);
203138287Smarks    if (ret) {
204151937Sjkim	(*db->hdb_destroy)(context, db);
205138287Smarks	goto out2;
206151937Sjkim    }
207118611Snjl
208151937Sjkim    ret = (*db->hdb_open)(context, db, O_RDONLY, 0);
209118611Snjl    if (ret) {
210151937Sjkim	(*db->hdb_destroy)(context, db);
211151937Sjkim	goto out2;
212151937Sjkim    }
213118611Snjl
214151937Sjkim    ret = (*db->hdb_fetch_kvno)(context, db, principal,
215118611Snjl				HDB_F_DECRYPT|HDB_F_KVNO_SPECIFIED|
216118611Snjl				HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
217118611Snjl				kvno, &ent);
218118611Snjl
219118611Snjl    if(ret == HDB_ERR_NOENTRY) {
220118611Snjl	ret = KRB5_KT_NOTFOUND;
221118611Snjl	goto out;
222151937Sjkim    }else if(ret)
223118611Snjl	goto out;
224118611Snjl
225118611Snjl    if(kvno && (krb5_kvno)ent.entry.kvno != kvno) {
226118611Snjl	hdb_free_entry(context, &ent);
227118611Snjl 	ret = KRB5_KT_NOTFOUND;
228118611Snjl	goto out;
229118611Snjl    }
230118611Snjl    if(enctype == 0)
231151937Sjkim	if(ent.entry.keys.len > 0)
232118611Snjl	    enctype = ent.entry.keys.val[0].key.keytype;
233118611Snjl    ret = KRB5_KT_NOTFOUND;
234118611Snjl    for(i = 0; i < ent.entry.keys.len; i++) {
235118611Snjl	if(ent.entry.keys.val[i].key.keytype == enctype) {
236118611Snjl	    krb5_copy_principal(context, principal, &entry->principal);
237118611Snjl	    entry->vno = ent.entry.kvno;
238118611Snjl	    krb5_copy_keyblock_contents(context,
239118611Snjl					&ent.entry.keys.val[i].key,
240118611Snjl					&entry->keyblock);
241118611Snjl	    ret = 0;
242118611Snjl	    break;
243118611Snjl	}
244118611Snjl    }
245118611Snjl    hdb_free_entry(context, &ent);
246118611Snjl out:
247118611Snjl    (*db->hdb_close)(context, db);
248118611Snjl    (*db->hdb_destroy)(context, db);
249151937Sjkim out2:
250118611Snjl    free(fdbname);
251118611Snjl    free(fmkey);
252118611Snjl    return ret;
253118611Snjl}
254118611Snjl
255118611Snjl/*
256118611Snjl * find the keytab entry in `id' for `principal, kvno, enctype' and return
257118611Snjl * it in `entry'.  return 0 or an error code
258151937Sjkim */
259118611Snjl
260118611Snjlstatic krb5_error_code KRB5_CALLCONV
261118611Snjlhdb_start_seq_get(krb5_context context,
262118611Snjl		  krb5_keytab id,
263118611Snjl		  krb5_kt_cursor *cursor)
264118611Snjl{
265118611Snjl    krb5_error_code ret;
266118611Snjl    struct hdb_cursor *c;
267118611Snjl    struct hdb_data *d = id->data;
268118611Snjl    const char *dbname = d->dbname;
269118611Snjl    const char *mkey   = d->mkey;
270118611Snjl    HDB *db;
271118611Snjl
272118611Snjl    if (dbname == NULL) {
273151937Sjkim	/*
274118611Snjl	 * We don't support enumerating without being told what
275118611Snjl	 * backend to enumerate on
276118611Snjl	 */
277118611Snjl  	ret = KRB5_KT_NOTFOUND;
278118611Snjl	return ret;
279118611Snjl    }
280118611Snjl
281118611Snjl    ret = hdb_create (context, &db, dbname);
282151937Sjkim    if (ret)
283118611Snjl	return ret;
284118611Snjl    ret = hdb_set_master_keyfile (context, db, mkey);
285118611Snjl    if (ret) {
286118611Snjl	(*db->hdb_destroy)(context, db);
287118611Snjl	return ret;
288118611Snjl    }
289118611Snjl
290118611Snjl    ret = (*db->hdb_open)(context, db, O_RDONLY, 0);
291118611Snjl    if (ret) {
292118611Snjl	(*db->hdb_destroy)(context, db);
293118611Snjl	return ret;
294118611Snjl    }
295118611Snjl
296118611Snjl    cursor->data = c = malloc (sizeof(*c));
297118611Snjl    if(c == NULL){
298118611Snjl	(*db->hdb_close)(context, db);
299118611Snjl	(*db->hdb_destroy)(context, db);
300118611Snjl	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
301118611Snjl	return ENOMEM;
302118611Snjl    }
303151937Sjkim
304118611Snjl    c->db = db;
305118611Snjl    c->first = TRUE;
306118611Snjl    c->next = TRUE;
307118611Snjl    c->key_idx = 0;
308118611Snjl
309118611Snjl    cursor->data = c;
310118611Snjl    return ret;
311118611Snjl}
312151937Sjkim
313118611Snjlstatic int KRB5_CALLCONV
314118611Snjlhdb_next_entry(krb5_context context,
315118611Snjl	       krb5_keytab id,
316118611Snjl	       krb5_keytab_entry *entry,
317118611Snjl	       krb5_kt_cursor *cursor)
318118611Snjl{
319118611Snjl    struct hdb_cursor *c = cursor->data;
320118611Snjl    krb5_error_code ret;
321118611Snjl
322118611Snjl    memset(entry, 0, sizeof(*entry));
323118611Snjl
324118611Snjl    if (c->first) {
325118611Snjl	c->first = FALSE;
326118611Snjl	ret = (c->db->hdb_firstkey)(context, c->db,
327118611Snjl				    HDB_F_DECRYPT|
328118611Snjl				    HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
329118611Snjl				    &c->hdb_entry);
330118611Snjl	if (ret == HDB_ERR_NOENTRY)
331118611Snjl	    return KRB5_KT_END;
332118611Snjl	else if (ret)
333118611Snjl	    return ret;
334118611Snjl
335118611Snjl	if (c->hdb_entry.entry.keys.len == 0)
336118611Snjl	    hdb_free_entry(context, &c->hdb_entry);
337118611Snjl	else
338118611Snjl	    c->next = FALSE;
339118611Snjl    }
340118611Snjl
341118611Snjl    while (c->next) {
342118611Snjl	ret = (c->db->hdb_nextkey)(context, c->db,
343118611Snjl				   HDB_F_DECRYPT|
344118611Snjl				   HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
345118611Snjl				   &c->hdb_entry);
346118611Snjl	if (ret == HDB_ERR_NOENTRY)
347118611Snjl	    return KRB5_KT_END;
348118611Snjl	else if (ret)
349118611Snjl	    return ret;
350118611Snjl
351118611Snjl	/* If no keys on this entry, try again */
352118611Snjl	if (c->hdb_entry.entry.keys.len == 0)
353118611Snjl	    hdb_free_entry(context, &c->hdb_entry);
354118611Snjl	else
355118611Snjl	    c->next = FALSE;
356118611Snjl    }
357118611Snjl
358118611Snjl    /*
359118611Snjl     * Return next enc type (keytabs are one slot per key, while
360118611Snjl     * hdb is one record per principal.
361118611Snjl     */
362151937Sjkim
363118611Snjl    ret = krb5_copy_principal(context,
364118611Snjl			      c->hdb_entry.entry.principal,
365118611Snjl			      &entry->principal);
366118611Snjl    if (ret)
367118611Snjl	return ret;
368118611Snjl
369118611Snjl    entry->vno = c->hdb_entry.entry.kvno;
370118611Snjl    ret = krb5_copy_keyblock_contents(context,
371118611Snjl				      &c->hdb_entry.entry.keys.val[c->key_idx].key,
372118611Snjl				      &entry->keyblock);
373118611Snjl    if (ret) {
374118611Snjl	krb5_free_principal(context, entry->principal);
375118611Snjl	memset(entry, 0, sizeof(*entry));
376118611Snjl	return ret;
377118611Snjl    }
378118611Snjl    c->key_idx++;
379118611Snjl
380118611Snjl    /*
381118611Snjl     * Once we get to the end of the list, signal that we want the
382151937Sjkim     * next entry
383151937Sjkim     */
384151937Sjkim
385151937Sjkim    if ((size_t)c->key_idx == c->hdb_entry.entry.keys.len) {
386151937Sjkim	hdb_free_entry(context, &c->hdb_entry);
387151937Sjkim	c->next = TRUE;
388151937Sjkim	c->key_idx = 0;
389151937Sjkim    }
390151937Sjkim
391118611Snjl    return 0;
392118611Snjl}
393118611Snjl
394118611Snjl
395118611Snjlstatic int KRB5_CALLCONV
396118611Snjlhdb_end_seq_get(krb5_context context,
397118611Snjl		krb5_keytab id,
398118611Snjl		krb5_kt_cursor *cursor)
399118611Snjl{
400118611Snjl    struct hdb_cursor *c = cursor->data;
401118611Snjl
402118611Snjl    if (!c->next)
403118611Snjl	hdb_free_entry(context, &c->hdb_entry);
404118611Snjl
405118611Snjl    (c->db->hdb_close)(context, c->db);
406118611Snjl    (c->db->hdb_destroy)(context, c->db);
407118611Snjl
408118611Snjl    free(c);
409118611Snjl    return 0;
410118611Snjl}
411118611Snjl
412151937Sjkimkrb5_kt_ops hdb_kt_ops = {
413118611Snjl    "HDB",
414118611Snjl    hdb_resolve,
415118611Snjl    hdb_get_name,
416118611Snjl    hdb_close,
417118611Snjl    NULL,		/* destroy */
418118611Snjl    hdb_get_entry,
419118611Snjl    hdb_start_seq_get,
420118611Snjl    hdb_next_entry,
421118611Snjl    hdb_end_seq_get,
422118611Snjl    NULL,		/* add */
423118611Snjl    NULL		/* remove */
424151937Sjkim};
425151937Sjkim