1 /*	$NetBSD: keytab.c,v 1.1.1.2 2014/04/24 12:45:28 pettai Exp $	*/
2 
3 /*
4  * Copyright (c) 1999 - 2002 Kungliga Tekniska Högskolan
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
hdb_resolve(krb5_context context,const char * name,krb5_keytab id)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[5] == '\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 + 5);
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
hdb_close(krb5_context context,krb5_keytab id)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
hdb_get_name(krb5_context context,krb5_keytab id,char * name,size_t namesize)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
find_db(krb5_context context,char ** dbname,char ** mkey,krb5_const_principal principal)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);
166     return 0;
167 }
168 
169 /*
170  * find the keytab entry in `id' for `principal, kvno, enctype' and return
171  * it in `entry'.  return 0 or an error code
172  */
173 
174 static krb5_error_code KRB5_CALLCONV
hdb_get_entry(krb5_context context,krb5_keytab id,krb5_const_principal principal,krb5_kvno kvno,krb5_enctype enctype,krb5_keytab_entry * entry)175 hdb_get_entry(krb5_context context,
176 	      krb5_keytab id,
177 	      krb5_const_principal principal,
178 	      krb5_kvno kvno,
179 	      krb5_enctype enctype,
180 	      krb5_keytab_entry *entry)
181 {
182     hdb_entry_ex ent;
183     krb5_error_code ret;
184     struct hdb_data *d = id->data;
185     const char *dbname = d->dbname;
186     const char *mkey   = d->mkey;
187     char *fdbname = NULL, *fmkey = NULL;
188     HDB *db;
189     size_t i;
190 
191     memset(&ent, 0, sizeof(ent));
192 
193     if (dbname == NULL) {
194 	ret = find_db(context, &fdbname, &fmkey, principal);
195 	if (ret)
196 	    return ret;
197 	dbname = fdbname;
198 	mkey = fmkey;
199     }
200 
201     ret = hdb_create (context, &db, dbname);
202     if (ret)
203 	goto out2;
204     ret = hdb_set_master_keyfile (context, db, mkey);
205     if (ret) {
206 	(*db->hdb_destroy)(context, db);
207 	goto out2;
208     }
209 
210     ret = (*db->hdb_open)(context, db, O_RDONLY, 0);
211     if (ret) {
212 	(*db->hdb_destroy)(context, db);
213 	goto out2;
214     }
215 
216     ret = (*db->hdb_fetch_kvno)(context, db, principal,
217 				HDB_F_DECRYPT|HDB_F_KVNO_SPECIFIED|
218 				HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
219 				kvno, &ent);
220 
221     if(ret == HDB_ERR_NOENTRY) {
222 	ret = KRB5_KT_NOTFOUND;
223 	goto out;
224     }else if(ret)
225 	goto out;
226 
227     if(kvno && (krb5_kvno)ent.entry.kvno != kvno) {
228 	hdb_free_entry(context, &ent);
229  	ret = KRB5_KT_NOTFOUND;
230 	goto out;
231     }
232     if(enctype == 0)
233 	if(ent.entry.keys.len > 0)
234 	    enctype = ent.entry.keys.val[0].key.keytype;
235     ret = KRB5_KT_NOTFOUND;
236     for(i = 0; i < ent.entry.keys.len; i++) {
237 	if(ent.entry.keys.val[i].key.keytype == enctype) {
238 	    krb5_copy_principal(context, principal, &entry->principal);
239 	    entry->vno = ent.entry.kvno;
240 	    krb5_copy_keyblock_contents(context,
241 					&ent.entry.keys.val[i].key,
242 					&entry->keyblock);
243 	    ret = 0;
244 	    break;
245 	}
246     }
247     hdb_free_entry(context, &ent);
248  out:
249     (*db->hdb_close)(context, db);
250     (*db->hdb_destroy)(context, db);
251  out2:
252     free(fdbname);
253     free(fmkey);
254     return ret;
255 }
256 
257 /*
258  * find the keytab entry in `id' for `principal, kvno, enctype' and return
259  * it in `entry'.  return 0 or an error code
260  */
261 
262 static krb5_error_code KRB5_CALLCONV
hdb_start_seq_get(krb5_context context,krb5_keytab id,krb5_kt_cursor * cursor)263 hdb_start_seq_get(krb5_context context,
264 		  krb5_keytab id,
265 		  krb5_kt_cursor *cursor)
266 {
267     krb5_error_code ret;
268     struct hdb_cursor *c;
269     struct hdb_data *d = id->data;
270     const char *dbname = d->dbname;
271     const char *mkey   = d->mkey;
272     HDB *db;
273 
274     if (dbname == NULL) {
275 	/*
276 	 * We don't support enumerating without being told what
277 	 * backend to enumerate on
278 	 */
279   	ret = KRB5_KT_NOTFOUND;
280 	return ret;
281     }
282 
283     ret = hdb_create (context, &db, dbname);
284     if (ret)
285 	return ret;
286     ret = hdb_set_master_keyfile (context, db, mkey);
287     if (ret) {
288 	(*db->hdb_destroy)(context, db);
289 	return ret;
290     }
291 
292     ret = (*db->hdb_open)(context, db, O_RDONLY, 0);
293     if (ret) {
294 	(*db->hdb_destroy)(context, db);
295 	return ret;
296     }
297 
298     cursor->data = c = malloc (sizeof(*c));
299     if(c == NULL){
300 	(*db->hdb_close)(context, db);
301 	(*db->hdb_destroy)(context, db);
302 	krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
303 	return ENOMEM;
304     }
305 
306     c->db = db;
307     c->first = TRUE;
308     c->next = TRUE;
309     c->key_idx = 0;
310 
311     cursor->data = c;
312     return ret;
313 }
314 
315 static int KRB5_CALLCONV
hdb_next_entry(krb5_context context,krb5_keytab id,krb5_keytab_entry * entry,krb5_kt_cursor * cursor)316 hdb_next_entry(krb5_context context,
317 	       krb5_keytab id,
318 	       krb5_keytab_entry *entry,
319 	       krb5_kt_cursor *cursor)
320 {
321     struct hdb_cursor *c = cursor->data;
322     krb5_error_code ret;
323 
324     memset(entry, 0, sizeof(*entry));
325 
326     if (c->first) {
327 	c->first = FALSE;
328 	ret = (c->db->hdb_firstkey)(context, c->db,
329 				    HDB_F_DECRYPT|
330 				    HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
331 				    &c->hdb_entry);
332 	if (ret == HDB_ERR_NOENTRY)
333 	    return KRB5_KT_END;
334 	else if (ret)
335 	    return ret;
336 
337 	if (c->hdb_entry.entry.keys.len == 0)
338 	    hdb_free_entry(context, &c->hdb_entry);
339 	else
340 	    c->next = FALSE;
341     }
342 
343     while (c->next) {
344 	ret = (c->db->hdb_nextkey)(context, c->db,
345 				   HDB_F_DECRYPT|
346 				   HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
347 				   &c->hdb_entry);
348 	if (ret == HDB_ERR_NOENTRY)
349 	    return KRB5_KT_END;
350 	else if (ret)
351 	    return ret;
352 
353 	/* If no keys on this entry, try again */
354 	if (c->hdb_entry.entry.keys.len == 0)
355 	    hdb_free_entry(context, &c->hdb_entry);
356 	else
357 	    c->next = FALSE;
358     }
359 
360     /*
361      * Return next enc type (keytabs are one slot per key, while
362      * hdb is one record per principal.
363      */
364 
365     ret = krb5_copy_principal(context,
366 			      c->hdb_entry.entry.principal,
367 			      &entry->principal);
368     if (ret)
369 	return ret;
370 
371     entry->vno = c->hdb_entry.entry.kvno;
372     ret = krb5_copy_keyblock_contents(context,
373 				      &c->hdb_entry.entry.keys.val[c->key_idx].key,
374 				      &entry->keyblock);
375     if (ret) {
376 	krb5_free_principal(context, entry->principal);
377 	memset(entry, 0, sizeof(*entry));
378 	return ret;
379     }
380     c->key_idx++;
381 
382     /*
383      * Once we get to the end of the list, signal that we want the
384      * next entry
385      */
386 
387     if ((size_t)c->key_idx == c->hdb_entry.entry.keys.len) {
388 	hdb_free_entry(context, &c->hdb_entry);
389 	c->next = TRUE;
390 	c->key_idx = 0;
391     }
392 
393     return 0;
394 }
395 
396 
397 static int KRB5_CALLCONV
hdb_end_seq_get(krb5_context context,krb5_keytab id,krb5_kt_cursor * cursor)398 hdb_end_seq_get(krb5_context context,
399 		krb5_keytab id,
400 		krb5_kt_cursor *cursor)
401 {
402     struct hdb_cursor *c = cursor->data;
403 
404     if (!c->next)
405 	hdb_free_entry(context, &c->hdb_entry);
406 
407     (c->db->hdb_close)(context, c->db);
408     (c->db->hdb_destroy)(context, c->db);
409 
410     free(c);
411     return 0;
412 }
413 
414 krb5_kt_ops hdb_kt_ops = {
415     "HDB",
416     hdb_resolve,
417     hdb_get_name,
418     hdb_close,
419     NULL,		/* destroy */
420     hdb_get_entry,
421     hdb_start_seq_get,
422     hdb_next_entry,
423     hdb_end_seq_get,
424     NULL,		/* add */
425     NULL		/* remove */
426 };
427