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