Line data Source code
1 : /*
2 : * Copyright (c) 1999 - 2002 Kungliga Tekniska Högskolan
3 : * (Royal Institute of Technology, Stockholm, Sweden).
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions
8 : * are met:
9 : *
10 : * 1. Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : *
13 : * 2. Redistributions in binary form must reproduce the above copyright
14 : * notice, this list of conditions and the following disclaimer in the
15 : * documentation and/or other materials provided with the distribution.
16 : *
17 : * 3. Neither the name of the Institute nor the names of its contributors
18 : * may be used to endorse or promote products derived from this software
19 : * without specific prior written permission.
20 : *
21 : * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 : * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 : * SUCH DAMAGE.
32 : */
33 :
34 : #include "hdb_locl.h"
35 :
36 : /* keytab backend for HDB databases */
37 :
38 : struct hdb_data {
39 : char *dbname;
40 : char *mkey;
41 : };
42 :
43 : struct hdb_cursor {
44 : HDB *db;
45 : hdb_entry hdb_entry;
46 : int first, next;
47 : int key_idx;
48 : };
49 :
50 : /*
51 : * the format for HDB keytabs is:
52 : * HDB:[HDBFORMAT:database-specific-data[:mkey=mkey-file]]
53 : */
54 :
55 : static krb5_error_code KRB5_CALLCONV
56 32 : hdb_resolve(krb5_context context, const char *name, krb5_keytab id)
57 : {
58 : struct hdb_data *d;
59 : const char *db, *mkey;
60 :
61 32 : d = malloc(sizeof(*d));
62 32 : if(d == NULL) {
63 0 : krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
64 0 : return ENOMEM;
65 : }
66 32 : db = name;
67 32 : mkey = strstr(name, ":mkey=");
68 32 : if(mkey == NULL || mkey[6] == '\0') {
69 32 : if(*name == '\0')
70 0 : d->dbname = NULL;
71 : else {
72 32 : d->dbname = strdup(name);
73 32 : if(d->dbname == NULL) {
74 0 : free(d);
75 0 : krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
76 0 : return ENOMEM;
77 : }
78 : }
79 32 : d->mkey = NULL;
80 : } else {
81 0 : d->dbname = malloc(mkey - db + 1);
82 0 : if(d->dbname == NULL) {
83 0 : free(d);
84 0 : krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
85 0 : return ENOMEM;
86 : }
87 0 : memmove(d->dbname, db, mkey - db);
88 0 : d->dbname[mkey - db] = '\0';
89 :
90 0 : d->mkey = strdup(mkey + 6);
91 0 : if(d->mkey == NULL) {
92 0 : free(d->dbname);
93 0 : free(d);
94 0 : krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
95 0 : return ENOMEM;
96 : }
97 : }
98 32 : id->data = d;
99 32 : return 0;
100 : }
101 :
102 : static krb5_error_code KRB5_CALLCONV
103 32 : hdb_close(krb5_context context, krb5_keytab id)
104 : {
105 32 : struct hdb_data *d = id->data;
106 :
107 32 : free(d->dbname);
108 32 : free(d->mkey);
109 32 : free(d);
110 32 : return 0;
111 : }
112 :
113 : static krb5_error_code KRB5_CALLCONV
114 0 : hdb_get_name(krb5_context context,
115 : krb5_keytab id,
116 : char *name,
117 : size_t namesize)
118 : {
119 0 : struct hdb_data *d = id->data;
120 :
121 0 : snprintf(name, namesize, "%s%s%s",
122 0 : d->dbname ? d->dbname : "",
123 0 : (d->dbname || d->mkey) ? ":" : "",
124 0 : d->mkey ? d->mkey : "");
125 0 : return 0;
126 : }
127 :
128 : /*
129 : * try to figure out the database (`dbname') and master-key (`mkey')
130 : * that should be used for `principal'.
131 : */
132 :
133 : static krb5_error_code
134 0 : find_db (krb5_context context,
135 : char **dbname,
136 : char **mkey,
137 : krb5_const_principal principal)
138 : {
139 0 : krb5_const_realm realm = krb5_principal_get_realm(context, principal);
140 : krb5_error_code ret;
141 0 : struct hdb_dbinfo *head, *dbinfo = NULL;
142 :
143 0 : *dbname = *mkey = NULL;
144 :
145 0 : ret = hdb_get_dbinfo(context, &head);
146 0 : if (ret)
147 0 : return ret;
148 :
149 0 : while ((dbinfo = hdb_dbinfo_get_next(head, dbinfo)) != NULL) {
150 0 : const char *p = hdb_dbinfo_get_realm(context, dbinfo);
151 0 : if (p && strcmp (realm, p) == 0) {
152 0 : p = hdb_dbinfo_get_dbname(context, dbinfo);
153 0 : if (p)
154 0 : *dbname = strdup(p);
155 0 : p = hdb_dbinfo_get_mkey_file(context, dbinfo);
156 0 : if (p)
157 0 : *mkey = strdup(p);
158 0 : break;
159 : }
160 : }
161 0 : hdb_free_dbinfo(context, &head);
162 0 : if (*dbname == NULL &&
163 0 : (*dbname = strdup(hdb_default_db(context))) == NULL) {
164 0 : free(*mkey);
165 0 : *mkey = NULL;
166 0 : return krb5_enomem(context);
167 : }
168 0 : return 0;
169 : }
170 :
171 : /*
172 : * find the keytab entry in `id' for `principal, kvno, enctype' and return
173 : * it in `entry'. return 0 or an error code
174 : */
175 :
176 : static krb5_error_code KRB5_CALLCONV
177 32 : hdb_get_entry(krb5_context context,
178 : krb5_keytab id,
179 : krb5_const_principal principal,
180 : krb5_kvno kvno,
181 : krb5_enctype enctype,
182 : krb5_keytab_entry *entry)
183 : {
184 : hdb_entry ent;
185 : krb5_error_code ret;
186 32 : struct hdb_data *d = id->data;
187 32 : const char *dbname = d->dbname;
188 32 : const char *mkey = d->mkey;
189 32 : char *fdbname = NULL, *fmkey = NULL;
190 : HDB *db;
191 : size_t i;
192 :
193 32 : if (!principal)
194 0 : return KRB5_KT_NOTFOUND;
195 :
196 32 : memset(&ent, 0, sizeof(ent));
197 :
198 32 : if (dbname == NULL) {
199 0 : ret = find_db(context, &fdbname, &fmkey, principal);
200 0 : if (ret)
201 0 : return ret;
202 0 : dbname = fdbname;
203 0 : mkey = fmkey;
204 : }
205 :
206 32 : ret = hdb_create (context, &db, dbname);
207 32 : if (ret)
208 0 : goto out2;
209 32 : ret = hdb_set_master_keyfile (context, db, mkey);
210 32 : if (ret) {
211 0 : (*db->hdb_destroy)(context, db);
212 0 : goto out2;
213 : }
214 :
215 32 : ret = (*db->hdb_open)(context, db, O_RDONLY, 0);
216 32 : if (ret) {
217 0 : (*db->hdb_destroy)(context, db);
218 0 : goto out2;
219 : }
220 :
221 32 : ret = hdb_fetch_kvno(context, db, principal,
222 : HDB_F_DECRYPT|HDB_F_KVNO_SPECIFIED|
223 : HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
224 : 0, 0, kvno, &ent);
225 :
226 32 : if(ret == HDB_ERR_NOENTRY) {
227 0 : ret = KRB5_KT_NOTFOUND;
228 0 : goto out;
229 32 : }else if(ret)
230 0 : goto out;
231 :
232 32 : if(kvno && (krb5_kvno)ent.kvno != kvno) {
233 0 : hdb_free_entry(context, db, &ent);
234 0 : ret = KRB5_KT_NOTFOUND;
235 0 : goto out;
236 : }
237 32 : if(enctype == 0)
238 0 : if(ent.keys.len > 0)
239 0 : enctype = ent.keys.val[0].key.keytype;
240 32 : ret = KRB5_KT_NOTFOUND;
241 32 : for(i = 0; i < ent.keys.len; i++) {
242 32 : if(ent.keys.val[i].key.keytype == enctype) {
243 32 : krb5_copy_principal(context, principal, &entry->principal);
244 32 : entry->vno = ent.kvno;
245 64 : krb5_copy_keyblock_contents(context,
246 32 : &ent.keys.val[i].key,
247 : &entry->keyblock);
248 32 : ret = 0;
249 32 : break;
250 : }
251 : }
252 32 : hdb_free_entry(context, db, &ent);
253 32 : out:
254 32 : (*db->hdb_close)(context, db);
255 32 : (*db->hdb_destroy)(context, db);
256 32 : out2:
257 32 : free(fdbname);
258 32 : free(fmkey);
259 32 : return ret;
260 : }
261 :
262 : /*
263 : * find the keytab entry in `id' for `principal, kvno, enctype' and return
264 : * it in `entry'. return 0 or an error code
265 : */
266 :
267 : static krb5_error_code KRB5_CALLCONV
268 0 : hdb_start_seq_get(krb5_context context,
269 : krb5_keytab id,
270 : krb5_kt_cursor *cursor)
271 : {
272 : krb5_error_code ret;
273 : struct hdb_cursor *c;
274 0 : struct hdb_data *d = id->data;
275 0 : const char *dbname = d->dbname;
276 0 : const char *mkey = d->mkey;
277 : HDB *db;
278 :
279 0 : if (dbname == NULL) {
280 : /*
281 : * We don't support enumerating without being told what
282 : * backend to enumerate on
283 : */
284 0 : ret = KRB5_KT_NOTFOUND;
285 0 : return ret;
286 : }
287 :
288 0 : ret = hdb_create (context, &db, dbname);
289 0 : if (ret)
290 0 : return ret;
291 0 : ret = hdb_set_master_keyfile (context, db, mkey);
292 0 : if (ret) {
293 0 : (*db->hdb_destroy)(context, db);
294 0 : return ret;
295 : }
296 :
297 0 : ret = (*db->hdb_open)(context, db, O_RDONLY, 0);
298 0 : if (ret) {
299 0 : (*db->hdb_destroy)(context, db);
300 0 : return ret;
301 : }
302 :
303 0 : cursor->data = c = malloc (sizeof(*c));
304 0 : if(c == NULL){
305 0 : (*db->hdb_close)(context, db);
306 0 : (*db->hdb_destroy)(context, db);
307 0 : krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
308 0 : return ENOMEM;
309 : }
310 :
311 0 : c->db = db;
312 0 : c->first = TRUE;
313 0 : c->next = TRUE;
314 0 : c->key_idx = 0;
315 :
316 0 : cursor->data = c;
317 0 : return ret;
318 : }
319 :
320 : static int KRB5_CALLCONV
321 0 : hdb_next_entry(krb5_context context,
322 : krb5_keytab id,
323 : krb5_keytab_entry *entry,
324 : krb5_kt_cursor *cursor)
325 : {
326 0 : struct hdb_cursor *c = cursor->data;
327 : krb5_error_code ret;
328 :
329 0 : memset(entry, 0, sizeof(*entry));
330 :
331 0 : if (c->first) {
332 0 : c->first = FALSE;
333 0 : ret = (c->db->hdb_firstkey)(context, c->db,
334 : HDB_F_DECRYPT|
335 : HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
336 : &c->hdb_entry);
337 0 : if (ret == HDB_ERR_NOENTRY)
338 0 : return KRB5_KT_END;
339 0 : else if (ret)
340 0 : return ret;
341 :
342 0 : if (c->hdb_entry.keys.len == 0)
343 0 : hdb_free_entry(context, c->db, &c->hdb_entry);
344 : else
345 0 : c->next = FALSE;
346 : }
347 :
348 0 : while (c->next) {
349 0 : ret = (c->db->hdb_nextkey)(context, c->db,
350 : HDB_F_DECRYPT|
351 : HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
352 : &c->hdb_entry);
353 0 : if (ret == HDB_ERR_NOENTRY)
354 0 : return KRB5_KT_END;
355 0 : else if (ret)
356 0 : return ret;
357 :
358 : /* If no keys on this entry, try again */
359 0 : if (c->hdb_entry.keys.len == 0)
360 0 : hdb_free_entry(context, c->db, &c->hdb_entry);
361 : else
362 0 : c->next = FALSE;
363 : }
364 :
365 : /*
366 : * Return next enc type (keytabs are one slot per key, while
367 : * hdb is one record per principal.
368 : */
369 :
370 0 : ret = krb5_copy_principal(context,
371 0 : c->hdb_entry.principal,
372 : &entry->principal);
373 0 : if (ret)
374 0 : return ret;
375 :
376 0 : entry->vno = c->hdb_entry.kvno;
377 0 : ret = krb5_copy_keyblock_contents(context,
378 0 : &c->hdb_entry.keys.val[c->key_idx].key,
379 : &entry->keyblock);
380 0 : if (ret) {
381 0 : krb5_free_principal(context, entry->principal);
382 0 : memset(entry, 0, sizeof(*entry));
383 0 : return ret;
384 : }
385 0 : c->key_idx++;
386 :
387 : /*
388 : * Once we get to the end of the list, signal that we want the
389 : * next entry
390 : */
391 :
392 0 : if ((size_t)c->key_idx == c->hdb_entry.keys.len) {
393 0 : hdb_free_entry(context, c->db, &c->hdb_entry);
394 0 : c->next = TRUE;
395 0 : c->key_idx = 0;
396 : }
397 :
398 0 : return 0;
399 : }
400 :
401 :
402 : static int KRB5_CALLCONV
403 0 : hdb_end_seq_get(krb5_context context,
404 : krb5_keytab id,
405 : krb5_kt_cursor *cursor)
406 : {
407 0 : struct hdb_cursor *c = cursor->data;
408 :
409 0 : if (!c->next)
410 0 : hdb_free_entry(context, c->db, &c->hdb_entry);
411 :
412 0 : (c->db->hdb_close)(context, c->db);
413 0 : (c->db->hdb_destroy)(context, c->db);
414 :
415 0 : free(c);
416 0 : return 0;
417 : }
418 :
419 : krb5_kt_ops hdb_kt_ops = {
420 : "HDB",
421 : hdb_resolve,
422 : hdb_get_name,
423 : hdb_close,
424 : NULL, /* destroy */
425 : hdb_get_entry,
426 : hdb_start_seq_get,
427 : hdb_next_entry,
428 : hdb_end_seq_get,
429 : NULL, /* add */
430 : NULL, /* remove */
431 : NULL,
432 : 0
433 : };
434 :
435 : krb5_kt_ops hdb_get_kt_ops = {
436 : "HDBGET",
437 : hdb_resolve,
438 : hdb_get_name,
439 : hdb_close,
440 : NULL,
441 : hdb_get_entry,
442 : NULL,
443 : NULL,
444 : NULL,
445 : NULL,
446 : NULL,
447 : NULL,
448 : 0
449 : };
|