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 68 : hdb_resolve(krb5_context context, const char *name, krb5_keytab id)
57 : {
58 0 : struct hdb_data *d;
59 0 : const char *db, *mkey;
60 :
61 68 : d = malloc(sizeof(*d));
62 68 : if(d == NULL) {
63 0 : krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
64 0 : return ENOMEM;
65 : }
66 68 : db = name;
67 68 : mkey = strstr(name, ":mkey=");
68 68 : if(mkey == NULL || mkey[6] == '\0') {
69 68 : if(*name == '\0')
70 0 : d->dbname = NULL;
71 : else {
72 68 : d->dbname = strdup(name);
73 68 : 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 68 : 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 68 : id->data = d;
99 68 : return 0;
100 : }
101 :
102 : static krb5_error_code KRB5_CALLCONV
103 68 : hdb_close(krb5_context context, krb5_keytab id)
104 : {
105 68 : struct hdb_data *d = id->data;
106 :
107 68 : free(d->dbname);
108 68 : free(d->mkey);
109 68 : free(d);
110 68 : 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 0 : 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 68 : 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 0 : hdb_entry ent;
185 0 : krb5_error_code ret;
186 68 : struct hdb_data *d = id->data;
187 68 : const char *dbname = d->dbname;
188 68 : const char *mkey = d->mkey;
189 68 : char *fdbname = NULL, *fmkey = NULL;
190 0 : HDB *db;
191 0 : size_t i;
192 :
193 68 : if (!principal)
194 0 : return KRB5_KT_NOTFOUND;
195 :
196 68 : memset(&ent, 0, sizeof(ent));
197 :
198 68 : 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 68 : ret = hdb_create (context, &db, dbname);
207 68 : if (ret)
208 0 : goto out2;
209 68 : ret = hdb_set_master_keyfile (context, db, mkey);
210 68 : if (ret) {
211 0 : (*db->hdb_destroy)(context, db);
212 0 : goto out2;
213 : }
214 :
215 68 : ret = (*db->hdb_open)(context, db, O_RDONLY, 0);
216 68 : if (ret) {
217 0 : (*db->hdb_destroy)(context, db);
218 0 : goto out2;
219 : }
220 :
221 68 : 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 68 : if (ret == HDB_ERR_WRONG_REALM || ret == HDB_ERR_NOENTRY)
227 0 : ret = KRB5_KT_NOTFOUND;
228 68 : if (ret)
229 0 : goto out;
230 :
231 68 : if(kvno && (krb5_kvno)ent.kvno != kvno) {
232 2 : hdb_free_entry(context, db, &ent);
233 2 : ret = KRB5_KT_NOTFOUND;
234 2 : goto out;
235 : }
236 66 : if(enctype == 0)
237 0 : if(ent.keys.len > 0)
238 0 : enctype = ent.keys.val[0].key.keytype;
239 66 : ret = KRB5_KT_NOTFOUND;
240 68 : for(i = 0; i < ent.keys.len; i++) {
241 66 : if(ent.keys.val[i].key.keytype == enctype) {
242 64 : krb5_copy_principal(context, principal, &entry->principal);
243 64 : entry->vno = ent.kvno;
244 64 : krb5_copy_keyblock_contents(context,
245 64 : &ent.keys.val[i].key,
246 : &entry->keyblock);
247 64 : ret = 0;
248 64 : break;
249 : }
250 : }
251 66 : hdb_free_entry(context, db, &ent);
252 68 : out:
253 68 : (*db->hdb_close)(context, db);
254 68 : (*db->hdb_destroy)(context, db);
255 68 : out2:
256 68 : free(fdbname);
257 68 : free(fmkey);
258 68 : return ret;
259 : }
260 :
261 : /*
262 : * find the keytab entry in `id' for `principal, kvno, enctype' and return
263 : * it in `entry'. return 0 or an error code
264 : */
265 :
266 : static krb5_error_code KRB5_CALLCONV
267 0 : hdb_start_seq_get(krb5_context context,
268 : krb5_keytab id,
269 : krb5_kt_cursor *cursor)
270 : {
271 0 : krb5_error_code ret;
272 0 : struct hdb_cursor *c;
273 0 : struct hdb_data *d = id->data;
274 0 : const char *dbname = d->dbname;
275 0 : const char *mkey = d->mkey;
276 0 : HDB *db;
277 :
278 0 : if (dbname == NULL) {
279 : /*
280 : * We don't support enumerating without being told what
281 : * backend to enumerate on
282 : */
283 0 : ret = KRB5_KT_NOTFOUND;
284 0 : return ret;
285 : }
286 :
287 0 : ret = hdb_create (context, &db, dbname);
288 0 : if (ret)
289 0 : return ret;
290 0 : ret = hdb_set_master_keyfile (context, db, mkey);
291 0 : if (ret) {
292 0 : (*db->hdb_destroy)(context, db);
293 0 : return ret;
294 : }
295 :
296 0 : ret = (*db->hdb_open)(context, db, O_RDONLY, 0);
297 0 : if (ret) {
298 0 : (*db->hdb_destroy)(context, db);
299 0 : return ret;
300 : }
301 :
302 0 : cursor->data = c = malloc (sizeof(*c));
303 0 : if(c == NULL){
304 0 : (*db->hdb_close)(context, db);
305 0 : (*db->hdb_destroy)(context, db);
306 0 : krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
307 0 : return ENOMEM;
308 : }
309 :
310 0 : c->db = db;
311 0 : c->first = TRUE;
312 0 : c->next = TRUE;
313 0 : c->key_idx = 0;
314 :
315 0 : cursor->data = c;
316 0 : return ret;
317 : }
318 :
319 : static int KRB5_CALLCONV
320 0 : hdb_next_entry(krb5_context context,
321 : krb5_keytab id,
322 : krb5_keytab_entry *entry,
323 : krb5_kt_cursor *cursor)
324 : {
325 0 : struct hdb_cursor *c = cursor->data;
326 0 : krb5_error_code ret;
327 :
328 0 : memset(entry, 0, sizeof(*entry));
329 :
330 0 : if (c->first) {
331 0 : c->first = FALSE;
332 0 : ret = (c->db->hdb_firstkey)(context, c->db,
333 : HDB_F_DECRYPT|
334 : HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
335 : &c->hdb_entry);
336 0 : if (ret == HDB_ERR_NOENTRY)
337 0 : return KRB5_KT_END;
338 0 : else if (ret)
339 0 : return ret;
340 :
341 0 : if (c->hdb_entry.keys.len == 0)
342 0 : hdb_free_entry(context, c->db, &c->hdb_entry);
343 : else
344 0 : c->next = FALSE;
345 : }
346 :
347 0 : while (c->next) {
348 0 : ret = (c->db->hdb_nextkey)(context, c->db,
349 : HDB_F_DECRYPT|
350 : HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
351 : &c->hdb_entry);
352 0 : if (ret == HDB_ERR_NOENTRY)
353 0 : return KRB5_KT_END;
354 0 : else if (ret)
355 0 : return ret;
356 :
357 : /* If no keys on this entry, try again */
358 0 : if (c->hdb_entry.keys.len == 0)
359 0 : hdb_free_entry(context, c->db, &c->hdb_entry);
360 : else
361 0 : c->next = FALSE;
362 : }
363 :
364 : /*
365 : * Return next enc type (keytabs are one slot per key, while
366 : * hdb is one record per principal.
367 : */
368 :
369 0 : ret = krb5_copy_principal(context,
370 0 : c->hdb_entry.principal,
371 : &entry->principal);
372 0 : if (ret)
373 0 : return ret;
374 :
375 0 : entry->vno = c->hdb_entry.kvno;
376 0 : ret = krb5_copy_keyblock_contents(context,
377 0 : &c->hdb_entry.keys.val[c->key_idx].key,
378 : &entry->keyblock);
379 0 : if (ret) {
380 0 : krb5_free_principal(context, entry->principal);
381 0 : memset(entry, 0, sizeof(*entry));
382 0 : return ret;
383 : }
384 0 : c->key_idx++;
385 :
386 : /*
387 : * Once we get to the end of the list, signal that we want the
388 : * next entry
389 : */
390 :
391 0 : if ((size_t)c->key_idx == c->hdb_entry.keys.len) {
392 0 : hdb_free_entry(context, c->db, &c->hdb_entry);
393 0 : c->next = TRUE;
394 0 : c->key_idx = 0;
395 : }
396 :
397 0 : return 0;
398 : }
399 :
400 :
401 : static int KRB5_CALLCONV
402 0 : hdb_end_seq_get(krb5_context context,
403 : krb5_keytab id,
404 : krb5_kt_cursor *cursor)
405 : {
406 0 : struct hdb_cursor *c = cursor->data;
407 :
408 0 : if (!c->next)
409 0 : hdb_free_entry(context, c->db, &c->hdb_entry);
410 :
411 0 : (c->db->hdb_close)(context, c->db);
412 0 : (c->db->hdb_destroy)(context, c->db);
413 :
414 0 : free(c);
415 0 : return 0;
416 : }
417 :
418 : krb5_kt_ops hdb_kt_ops = {
419 : "HDB",
420 : hdb_resolve,
421 : hdb_get_name,
422 : hdb_close,
423 : NULL, /* destroy */
424 : hdb_get_entry,
425 : hdb_start_seq_get,
426 : hdb_next_entry,
427 : hdb_end_seq_get,
428 : NULL, /* add */
429 : NULL, /* remove */
430 : NULL,
431 : 0
432 : };
433 :
434 : krb5_kt_ops hdb_get_kt_ops = {
435 : "HDBGET",
436 : hdb_resolve,
437 : hdb_get_name,
438 : hdb_close,
439 : NULL,
440 : hdb_get_entry,
441 : NULL,
442 : NULL,
443 : NULL,
444 : NULL,
445 : NULL,
446 : NULL,
447 : 0
448 : };
|