1 /*
2 * Copyright (c) 1999-2005 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 KTH nor the names of its contributors may be
18 * used to endorse or promote products derived from this software without
19 * specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
32
33 #include "hdb_locl.h"
34 #include <hex.h>
35 #include <ctype.h>
36
37 /*
38 This is the present contents of a dump line. This might change at
39 any time. Fields are separated by white space.
40
41 principal
42 keyblock
43 kvno
44 keys...
45 mkvno
46 enctype
47 keyvalue
48 salt (- means use normal salt)
49 creation date and principal
50 modification date and principal
51 principal valid from date (not used)
52 principal valid end date (not used)
53 principal key expires (not used)
54 max ticket life
55 max renewable life
56 flags
57 generation number
58 */
59
60 /*
61 * These utility functions return the number of bytes written or -1, and
62 * they set an error in the context.
63 */
64 static ssize_t
append_string(krb5_context context,krb5_storage * sp,const char * fmt,...)65 append_string(krb5_context context, krb5_storage *sp, const char *fmt, ...)
66 {
67 ssize_t sz;
68 char *s;
69 int rc;
70 va_list ap;
71 va_start(ap, fmt);
72 rc = vasprintf(&s, fmt, ap);
73 va_end(ap);
74 if(rc < 0) {
75 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
76 return -1;
77 }
78 sz = krb5_storage_write(sp, s, strlen(s));
79 free(s);
80 return sz;
81 }
82
83 static krb5_error_code
append_hex(krb5_context context,krb5_storage * sp,int always_encode,int lower,krb5_data * data)84 append_hex(krb5_context context, krb5_storage *sp,
85 int always_encode, int lower, krb5_data *data)
86 {
87 ssize_t sz;
88 int printable = 1;
89 size_t i;
90 char *p;
91
92 p = data->data;
93 if (!always_encode) {
94 for (i = 0; i < data->length; i++) {
95 if (!isalnum((unsigned char)p[i]) && p[i] != '.'){
96 printable = 0;
97 break;
98 }
99 }
100 }
101 if (printable && !always_encode)
102 return append_string(context, sp, "\"%.*s\"",
103 data->length, data->data);
104 sz = hex_encode(data->data, data->length, &p);
105 if (sz == -1) return sz;
106 if (lower)
107 strlwr(p);
108 sz = append_string(context, sp, "%s", p);
109 free(p);
110 return sz;
111 }
112
113 static char *
time2str(time_t t)114 time2str(time_t t)
115 {
116 static char buf[128];
117 strftime(buf, sizeof(buf), "%Y%m%d%H%M%S", gmtime(&t));
118 return buf;
119 }
120
121 static ssize_t
append_event(krb5_context context,krb5_storage * sp,Event * ev)122 append_event(krb5_context context, krb5_storage *sp, Event *ev)
123 {
124 krb5_error_code ret;
125 ssize_t sz;
126 char *pr = NULL;
127 if(ev == NULL)
128 return append_string(context, sp, "- ");
129 if (ev->principal != NULL) {
130 ret = krb5_unparse_name(context, ev->principal, &pr);
131 if (ret) return -1; /* krb5_unparse_name() sets error info */
132 }
133 sz = append_string(context, sp, "%s:%s ", time2str(ev->time),
134 pr ? pr : "UNKNOWN");
135 free(pr);
136 return sz;
137 }
138
139 #define KRB5_KDB_SALTTYPE_NORMAL 0
140 #define KRB5_KDB_SALTTYPE_V4 1
141 #define KRB5_KDB_SALTTYPE_NOREALM 2
142 #define KRB5_KDB_SALTTYPE_ONLYREALM 3
143 #define KRB5_KDB_SALTTYPE_SPECIAL 4
144 #define KRB5_KDB_SALTTYPE_AFS3 5
145
146 static ssize_t
append_mit_key(krb5_context context,krb5_storage * sp,krb5_const_principal princ,unsigned int kvno,Key * key)147 append_mit_key(krb5_context context, krb5_storage *sp,
148 krb5_const_principal princ,
149 unsigned int kvno, Key *key)
150 {
151 krb5_error_code ret;
152 ssize_t sz;
153 size_t key_versions = key->salt ? 2 : 1;
154 size_t decrypted_key_length;
155 char buf[2];
156 krb5_data keylenbytes;
157 unsigned int salttype;
158
159 sz = append_string(context, sp, "\t%u\t%u\t%d\t%d\t", key_versions, kvno,
160 key->key.keytype, key->key.keyvalue.length + 2);
161 if (sz == -1) return sz;
162 ret = krb5_enctype_keysize(context, key->key.keytype, &decrypted_key_length);
163 if (ret) return -1; /* XXX we lose the error code */
164 buf[0] = decrypted_key_length & 0xff;
165 buf[1] = (decrypted_key_length & 0xff00) >> 8;
166 keylenbytes.data = buf;
167 keylenbytes.length = sizeof (buf);
168 sz = append_hex(context, sp, 1, 1, &keylenbytes);
169 if (sz == -1) return sz;
170 sz = append_hex(context, sp, 1, 1, &key->key.keyvalue);
171 if (!key->salt)
172 return sz;
173
174 /* Map salt to MIT KDB style */
175 if (key->salt->type == KRB5_PADATA_PW_SALT) {
176 krb5_salt k5salt;
177
178 /*
179 * Compute normal salt and then see whether it matches the stored one
180 */
181 ret = krb5_get_pw_salt(context, princ, &k5salt);
182 if (ret) return -1;
183 if (k5salt.saltvalue.length == key->salt->salt.length &&
184 memcmp(k5salt.saltvalue.data, key->salt->salt.data,
185 k5salt.saltvalue.length) == 0)
186 salttype = KRB5_KDB_SALTTYPE_NORMAL; /* matches */
187 else if (key->salt->salt.length == strlen(princ->realm) &&
188 memcmp(key->salt->salt.data, princ->realm,
189 key->salt->salt.length) == 0)
190 salttype = KRB5_KDB_SALTTYPE_ONLYREALM; /* matches realm */
191 else if (key->salt->salt.length == k5salt.saltvalue.length - strlen(princ->realm) &&
192 memcmp((char *)k5salt.saltvalue.data + strlen(princ->realm),
193 key->salt->salt.data, key->salt->salt.length) == 0)
194 salttype = KRB5_KDB_SALTTYPE_NOREALM; /* matches w/o realm */
195 else
196 salttype = KRB5_KDB_SALTTYPE_NORMAL; /* hope for best */
197
198 } else if (key->salt->type == KRB5_PADATA_AFS3_SALT) {
199 salttype = KRB5_KDB_SALTTYPE_AFS3;
200 }
201 sz = append_string(context, sp, "\t%u\t%u\t", salttype,
202 key->salt->salt.length);
203 if (sz == -1) return sz;
204 return append_hex(context, sp, 1, 1, &key->salt->salt);
205 }
206
207 static krb5_error_code
entry2string_int(krb5_context context,krb5_storage * sp,hdb_entry * ent)208 entry2string_int (krb5_context context, krb5_storage *sp, hdb_entry *ent)
209 {
210 char *p;
211 int i;
212 krb5_error_code ret;
213
214 /* --- principal */
215 ret = krb5_unparse_name(context, ent->principal, &p);
216 if(ret)
217 return ret;
218 append_string(context, sp, "%s ", p);
219 free(p);
220 /* --- kvno */
221 append_string(context, sp, "%d", ent->kvno);
222 /* --- keys */
223 for(i = 0; i < ent->keys.len; i++){
224 /* --- mkvno, keytype */
225 if(ent->keys.val[i].mkvno)
226 append_string(context, sp, ":%d:%d:",
227 *ent->keys.val[i].mkvno,
228 ent->keys.val[i].key.keytype);
229 else
230 append_string(context, sp, "::%d:",
231 ent->keys.val[i].key.keytype);
232 /* --- keydata */
233 append_hex(context, sp, 0, 0, &ent->keys.val[i].key.keyvalue);
234 append_string(context, sp, ":");
235 /* --- salt */
236 if(ent->keys.val[i].salt){
237 append_string(context, sp, "%u/", ent->keys.val[i].salt->type);
238 append_hex(context, sp, 0, 0, &ent->keys.val[i].salt->salt);
239 }else
240 append_string(context, sp, "-");
241 }
242 append_string(context, sp, " ");
243 /* --- created by */
244 append_event(context, sp, &ent->created_by);
245 /* --- modified by */
246 append_event(context, sp, ent->modified_by);
247
248 /* --- valid start */
249 if(ent->valid_start)
250 append_string(context, sp, "%s ", time2str(*ent->valid_start));
251 else
252 append_string(context, sp, "- ");
253
254 /* --- valid end */
255 if(ent->valid_end)
256 append_string(context, sp, "%s ", time2str(*ent->valid_end));
257 else
258 append_string(context, sp, "- ");
259
260 /* --- password ends */
261 if(ent->pw_end)
262 append_string(context, sp, "%s ", time2str(*ent->pw_end));
263 else
264 append_string(context, sp, "- ");
265
266 /* --- max life */
267 if(ent->max_life)
268 append_string(context, sp, "%d ", *ent->max_life);
269 else
270 append_string(context, sp, "- ");
271
272 /* --- max renewable life */
273 if(ent->max_renew)
274 append_string(context, sp, "%d ", *ent->max_renew);
275 else
276 append_string(context, sp, "- ");
277
278 /* --- flags */
279 append_string(context, sp, "%d ", HDBFlags2int(ent->flags));
280
281 /* --- generation number */
282 if(ent->generation) {
283 append_string(context, sp, "%s:%d:%d ", time2str(ent->generation->time),
284 ent->generation->usec,
285 ent->generation->gen);
286 } else
287 append_string(context, sp, "- ");
288
289 /* --- extensions */
290 if(ent->extensions && ent->extensions->len > 0) {
291 for(i = 0; i < ent->extensions->len; i++) {
292 void *d;
293 size_t size, sz = 0;
294
295 ASN1_MALLOC_ENCODE(HDB_extension, d, size,
296 &ent->extensions->val[i], &sz, ret);
297 if (ret) {
298 krb5_clear_error_message(context);
299 return ret;
300 }
301 if(size != sz)
302 krb5_abortx(context, "internal asn.1 encoder error");
303
304 if (hex_encode(d, size, &p) < 0) {
305 free(d);
306 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
307 return ENOMEM;
308 }
309
310 free(d);
311 append_string(context, sp, "%s%s", p,
312 ent->extensions->len - 1 != i ? ":" : "");
313 free(p);
314 }
315 } else
316 append_string(context, sp, "-");
317
318 return 0;
319 }
320
321 #define KRB5_KDB_DISALLOW_POSTDATED 0x00000001
322 #define KRB5_KDB_DISALLOW_FORWARDABLE 0x00000002
323 #define KRB5_KDB_DISALLOW_TGT_BASED 0x00000004
324 #define KRB5_KDB_DISALLOW_RENEWABLE 0x00000008
325 #define KRB5_KDB_DISALLOW_PROXIABLE 0x00000010
326 #define KRB5_KDB_DISALLOW_DUP_SKEY 0x00000020
327 #define KRB5_KDB_DISALLOW_ALL_TIX 0x00000040
328 #define KRB5_KDB_REQUIRES_PRE_AUTH 0x00000080
329 #define KRB5_KDB_REQUIRES_HW_AUTH 0x00000100
330 #define KRB5_KDB_REQUIRES_PWCHANGE 0x00000200
331 #define KRB5_KDB_DISALLOW_SVR 0x00001000
332 #define KRB5_KDB_PWCHANGE_SERVICE 0x00002000
333 #define KRB5_KDB_SUPPORT_DESMD5 0x00004000
334 #define KRB5_KDB_NEW_PRINC 0x00008000
335
336 static int
flags_to_attr(HDBFlags flags)337 flags_to_attr(HDBFlags flags)
338 {
339 int a = 0;
340
341 if (!flags.postdate)
342 a |= KRB5_KDB_DISALLOW_POSTDATED;
343 if (!flags.forwardable)
344 a |= KRB5_KDB_DISALLOW_FORWARDABLE;
345 if (flags.initial)
346 a |= KRB5_KDB_DISALLOW_TGT_BASED;
347 if (!flags.renewable)
348 a |= KRB5_KDB_DISALLOW_RENEWABLE;
349 if (!flags.proxiable)
350 a |= KRB5_KDB_DISALLOW_PROXIABLE;
351 if (flags.invalid)
352 a |= KRB5_KDB_DISALLOW_ALL_TIX;
353 if (flags.require_preauth)
354 a |= KRB5_KDB_REQUIRES_PRE_AUTH;
355 if (flags.require_hwauth)
356 a |= KRB5_KDB_REQUIRES_HW_AUTH;
357 if (!flags.server)
358 a |= KRB5_KDB_DISALLOW_SVR;
359 if (flags.change_pw)
360 a |= KRB5_KDB_PWCHANGE_SERVICE;
361 return a;
362 }
363
364 krb5_error_code
entry2mit_string_int(krb5_context context,krb5_storage * sp,hdb_entry * ent)365 entry2mit_string_int(krb5_context context, krb5_storage *sp, hdb_entry *ent)
366 {
367 krb5_error_code ret;
368 ssize_t sz;
369 size_t i, k;
370 size_t num_tl_data = 0;
371 size_t num_key_data = 0;
372 char *p;
373 HDB_Ext_KeySet *hist_keys = NULL;
374 HDB_extension *extp;
375 time_t last_pw_chg = 0;
376 time_t exp = 0;
377 time_t pwexp = 0;
378 unsigned int max_life = 0;
379 unsigned int max_renew = 0;
380
381 /* Always create a modified_by entry. */
382 num_tl_data++;
383
384 ret = hdb_entry_get_pw_change_time(ent, &last_pw_chg);
385 if (ret) return ret;
386 if (last_pw_chg)
387 num_tl_data++;
388
389 extp = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys);
390 if (extp)
391 hist_keys = &extp->data.u.hist_keys;
392
393 for (i = 0; i < ent->keys.len;i++) {
394 if (!mit_strong_etype(ent->keys.val[i].key.keytype))
395 continue;
396 num_key_data++;
397 }
398 if (hist_keys) {
399 for (i = 0; i < hist_keys->len; i++) {
400 /*
401 * MIT uses the highest kvno as the current kvno instead of
402 * tracking kvno separately, so we can't dump keysets with kvno
403 * higher than the entry's kvno.
404 */
405 if (hist_keys->val[i].kvno >= ent->kvno)
406 continue;
407 for (k = 0; k < hist_keys->val[i].keys.len; k++) {
408 if (ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD4 ||
409 ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD5)
410 continue;
411 num_key_data++;
412 }
413 }
414 }
415
416 ret = krb5_unparse_name(context, ent->principal, &p);
417 if (ret) return ret;
418 sz = append_string(context, sp, "princ\t38\t%u\t%u\t%u\t0\t%s\t%d",
419 strlen(p), num_tl_data, num_key_data, p,
420 flags_to_attr(ent->flags));
421 if (sz == -1) {
422 free(p);
423 return ENOMEM;
424 }
425
426 if (ent->max_life)
427 max_life = *ent->max_life;
428 if (ent->max_renew)
429 max_renew = *ent->max_renew;
430 if (ent->valid_end)
431 exp = *ent->valid_end;
432 if (ent->pw_end)
433 pwexp = *ent->pw_end;
434
435 sz = append_string(context, sp, "\t%u\t%u\t%u\t%u\t0\t0\t0",
436 max_life, max_renew, exp, pwexp);
437 if (sz == -1) {
438 free(p);
439 return ENOMEM;
440 }
441
442 /* Dump TL data we know: last pw chg and modified_by */
443 #define mit_KRB5_TL_LAST_PWD_CHANGE 1
444 #define mit_KRB5_TL_MOD_PRINC 2
445 if (last_pw_chg) {
446 krb5_data d;
447 time_t val;
448 unsigned char *ptr;
449
450 ptr = (unsigned char *)&last_pw_chg;
451 val = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
452 d.data = &val;
453 d.length = sizeof (last_pw_chg);
454 sz = append_string(context, sp, "\t%u\t%u\t",
455 mit_KRB5_TL_LAST_PWD_CHANGE, d.length);
456 if (sz == -1) {
457 free(p);
458 return ENOMEM;
459 }
460 sz = append_hex(context, sp, 1, 1, &d);
461 if (sz == -1) {
462 free(p);
463 return ENOMEM;
464 }
465 }
466 if (ent->modified_by) {
467 krb5_data d;
468 unsigned int val;
469 size_t plen;
470 unsigned char *ptr;
471 char *modby_p;
472
473 free(p);
474 ptr = (unsigned char *)&ent->modified_by->time;
475 val = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
476 d.data = &val;
477 d.length = sizeof (ent->modified_by->time);
478 ret = krb5_unparse_name(context, ent->modified_by->principal, &modby_p);
479 if (ret) return ret;
480 plen = strlen(modby_p);
481 sz = append_string(context, sp, "\t%u\t%u\t",
482 mit_KRB5_TL_MOD_PRINC,
483 d.length + plen + 1 /* NULL counted */);
484 if (sz == -1) {
485 free(modby_p);
486 return ENOMEM;
487 }
488 sz = append_hex(context, sp, 1, 1, &d);
489 if (sz == -1) {
490 free(modby_p);
491 return ENOMEM;
492 }
493 d.data = modby_p;
494 d.length = plen + 1;
495 sz = append_hex(context, sp, 1, 1, &d);
496 free(modby_p);
497 if (sz == -1) return ENOMEM;
498 } else {
499 krb5_data d;
500 unsigned int val;
501 size_t plen;
502 unsigned char *ptr;
503
504 /* Fake the entry to make MIT happy. */
505 ptr = (unsigned char *)&last_pw_chg;
506 val = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
507 d.data = &val;
508 d.length = sizeof (last_pw_chg);
509 plen = strlen(p);
510 sz = append_string(context, sp, "\t%u\t%u\t",
511 mit_KRB5_TL_MOD_PRINC,
512 d.length + plen + 1 /* NULL counted */);
513 if (sz == -1) {
514 free(p);
515 return ENOMEM;
516 }
517 sz = append_hex(context, sp, 1, 1, &d);
518 if (sz == -1) {
519 free(p);
520 return ENOMEM;
521 }
522 d.data = p;
523 d.length = plen + 1;
524 sz = append_hex(context, sp, 1, 1, &d);
525 free(p);
526 if (sz == -1) return ENOMEM;
527 }
528 /*
529 * Dump keys (remembering to not include any with kvno higher than
530 * the entry's because MIT doesn't track entry kvno separately from
531 * the entry's keys -- max kvno is it)
532 */
533 for (i = 0; i < ent->keys.len; i++) {
534 if (!mit_strong_etype(ent->keys.val[i].key.keytype))
535 continue;
536 sz = append_mit_key(context, sp, ent->principal, ent->kvno,
537 &ent->keys.val[i]);
538 if (sz == -1) return ENOMEM;
539 }
540 for (i = 0; hist_keys && i < ent->kvno; i++) {
541 size_t m;
542
543 /* dump historical keys */
544 for (k = 0; k < hist_keys->len; k++) {
545 if (hist_keys->val[k].kvno != ent->kvno - i)
546 continue;
547 for (m = 0; m < hist_keys->val[k].keys.len; m++) {
548 if (ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD4 ||
549 ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD5)
550 continue;
551 sz = append_mit_key(context, sp, ent->principal,
552 hist_keys->val[k].kvno,
553 &hist_keys->val[k].keys.val[m]);
554 if (sz == -1) return ENOMEM;
555 }
556 }
557 }
558 sz = append_string(context, sp, "\t-1;"); /* "extra data" */
559 if (sz == -1) return ENOMEM;
560 return 0;
561 }
562
563 krb5_error_code
hdb_entry2string(krb5_context context,hdb_entry * ent,char ** str)564 hdb_entry2string(krb5_context context, hdb_entry *ent, char **str)
565 {
566 krb5_error_code ret;
567 krb5_data data;
568 krb5_storage *sp;
569
570 sp = krb5_storage_emem();
571 if (sp == NULL) {
572 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
573 return ENOMEM;
574 }
575
576 ret = entry2string_int(context, sp, ent);
577 if (ret) {
578 krb5_storage_free(sp);
579 return ret;
580 }
581
582 krb5_storage_write(sp, "\0", 1);
583 krb5_storage_to_data(sp, &data);
584 krb5_storage_free(sp);
585 *str = data.data;
586 return 0;
587 }
588
589 /* print a hdb_entry to (FILE*)data; suitable for hdb_foreach */
590
591 krb5_error_code
hdb_print_entry(krb5_context context,HDB * db,hdb_entry_ex * entry,void * data)592 hdb_print_entry(krb5_context context, HDB *db, hdb_entry_ex *entry,
593 void *data)
594 {
595 struct hdb_print_entry_arg *parg = data;
596 krb5_error_code ret;
597 krb5_storage *sp;
598
599 fflush(parg->out);
600 sp = krb5_storage_from_fd(fileno(parg->out));
601 if (sp == NULL) {
602 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
603 return ENOMEM;
604 }
605
606 switch (parg->fmt) {
607 case HDB_DUMP_HEIMDAL:
608 ret = entry2string_int(context, sp, &entry->entry);
609 break;
610 case HDB_DUMP_MIT:
611 ret = entry2mit_string_int(context, sp, &entry->entry);
612 break;
613 default:
614 heim_abort("Only two dump formats supported: Heimdal and MIT");
615 }
616 if (ret) {
617 krb5_storage_free(sp);
618 return ret;
619 }
620
621 krb5_storage_write(sp, "\n", 1);
622 krb5_storage_free(sp);
623 return 0;
624 }
625