clean files
[heimdal.git] / lib / hdb / print.c
blob0d1e2855217d0f1cddfc4c90a2604ed35fb1a56d
1 /*
2 * Copyright (c) 1999-2005 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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. */
33 #include "hdb_locl.h"
34 #include <hex.h>
35 #include <ctype.h>
38 This is the present contents of a dump line. This might change at
39 any time. Fields are separated by white space.
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
61 * These utility functions return the number of bytes written or -1, and
62 * they set an error in the context.
64 static ssize_t
65 append_string(krb5_context context, krb5_storage *sp, const char *fmt, ...)
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;
78 sz = krb5_storage_write(sp, s, strlen(s));
79 free(s);
80 return sz;
83 static krb5_error_code
84 append_hex(krb5_context context, krb5_storage *sp,
85 int always_encode, int lower, krb5_data *data)
87 ssize_t sz;
88 int printable = 1;
89 size_t i;
90 char *p;
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;
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;
113 static char *
114 time2str(time_t t)
116 static char buf[128];
117 strftime(buf, sizeof(buf), "%Y%m%d%H%M%S", gmtime(&t));
118 return buf;
121 static ssize_t
122 append_event(krb5_context context, krb5_storage *sp, Event *ev)
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 */
133 sz = append_string(context, sp, "%s:%s ", time2str(ev->time),
134 pr ? pr : "UNKNOWN");
135 free(pr);
136 return sz;
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
146 static ssize_t
147 append_mit_key(krb5_context context, krb5_storage *sp,
148 krb5_const_principal princ,
149 unsigned int kvno, Key *key)
151 krb5_error_code ret;
152 krb5_salt k5salt;
153 ssize_t sz;
154 size_t key_versions = key->salt ? 2 : 1;
155 size_t decrypted_key_length;
156 char buf[2];
157 krb5_data keylenbytes;
158 unsigned int salttype;
160 sz = append_string(context, sp, "\t%u\t%u\t%d\t%d\t", key_versions, kvno,
161 key->key.keytype, key->key.keyvalue.length + 2);
162 if (sz == -1) return sz;
163 ret = krb5_enctype_keysize(context, key->key.keytype, &decrypted_key_length);
164 if (ret) return -1; /* XXX we lose the error code */
165 buf[0] = decrypted_key_length & 0xff;
166 buf[1] = (decrypted_key_length & 0xff00) >> 8;
167 keylenbytes.data = buf;
168 keylenbytes.length = sizeof (buf);
169 sz = append_hex(context, sp, 1, 1, &keylenbytes);
170 if (sz == -1) return sz;
171 sz = append_hex(context, sp, 1, 1, &key->key.keyvalue);
172 if (!key->salt)
173 return sz;
175 /* Map salt to MIT KDB style */
176 switch (key->salt->type) {
177 case KRB5_PADATA_PW_SALT:
180 * Compute normal salt and then see whether it matches the stored one
182 ret = krb5_get_pw_salt(context, princ, &k5salt);
183 if (ret) return -1;
184 if (k5salt.saltvalue.length == key->salt->salt.length &&
185 memcmp(k5salt.saltvalue.data, key->salt->salt.data,
186 k5salt.saltvalue.length) == 0)
187 salttype = KRB5_KDB_SALTTYPE_NORMAL; /* matches */
188 else if (key->salt->salt.length == strlen(princ->realm) &&
189 memcmp(key->salt->salt.data, princ->realm,
190 key->salt->salt.length) == 0)
191 salttype = KRB5_KDB_SALTTYPE_ONLYREALM; /* matches realm */
192 else if (key->salt->salt.length ==
193 k5salt.saltvalue.length - strlen(princ->realm) &&
194 memcmp((char *)k5salt.saltvalue.data + strlen(princ->realm),
195 key->salt->salt.data, key->salt->salt.length) == 0)
196 salttype = KRB5_KDB_SALTTYPE_NOREALM; /* matches w/o realm */
197 else
198 salttype = KRB5_KDB_SALTTYPE_NORMAL; /* hope for best */
200 break;
202 case KRB5_PADATA_AFS3_SALT:
203 salttype = KRB5_KDB_SALTTYPE_AFS3;
204 break;
206 default:
207 return -1;
210 sz = append_string(context, sp, "\t%u\t%u\t", salttype,
211 key->salt->salt.length);
212 if (sz == -1) return sz;
213 return append_hex(context, sp, 1, 1, &key->salt->salt);
216 static krb5_error_code
217 entry2string_int (krb5_context context, krb5_storage *sp, hdb_entry *ent)
219 char *p;
220 size_t i;
221 krb5_error_code ret;
223 /* --- principal */
224 ret = krb5_unparse_name(context, ent->principal, &p);
225 if(ret)
226 return ret;
227 append_string(context, sp, "%s ", p);
228 free(p);
229 /* --- kvno */
230 append_string(context, sp, "%d", ent->kvno);
231 /* --- keys */
232 for(i = 0; i < ent->keys.len; i++){
233 /* --- mkvno, keytype */
234 if(ent->keys.val[i].mkvno)
235 append_string(context, sp, ":%d:%d:",
236 *ent->keys.val[i].mkvno,
237 ent->keys.val[i].key.keytype);
238 else
239 append_string(context, sp, "::%d:",
240 ent->keys.val[i].key.keytype);
241 /* --- keydata */
242 append_hex(context, sp, 0, 0, &ent->keys.val[i].key.keyvalue);
243 append_string(context, sp, ":");
244 /* --- salt */
245 if(ent->keys.val[i].salt){
246 append_string(context, sp, "%u/", ent->keys.val[i].salt->type);
247 append_hex(context, sp, 0, 0, &ent->keys.val[i].salt->salt);
248 }else
249 append_string(context, sp, "-");
251 append_string(context, sp, " ");
252 /* --- created by */
253 append_event(context, sp, &ent->created_by);
254 /* --- modified by */
255 append_event(context, sp, ent->modified_by);
257 /* --- valid start */
258 if(ent->valid_start)
259 append_string(context, sp, "%s ", time2str(*ent->valid_start));
260 else
261 append_string(context, sp, "- ");
263 /* --- valid end */
264 if(ent->valid_end)
265 append_string(context, sp, "%s ", time2str(*ent->valid_end));
266 else
267 append_string(context, sp, "- ");
269 /* --- password ends */
270 if(ent->pw_end)
271 append_string(context, sp, "%s ", time2str(*ent->pw_end));
272 else
273 append_string(context, sp, "- ");
275 /* --- max life */
276 if(ent->max_life)
277 append_string(context, sp, "%d ", *ent->max_life);
278 else
279 append_string(context, sp, "- ");
281 /* --- max renewable life */
282 if(ent->max_renew)
283 append_string(context, sp, "%d ", *ent->max_renew);
284 else
285 append_string(context, sp, "- ");
287 /* --- flags */
288 append_string(context, sp, "%d ", HDBFlags2int(ent->flags));
290 /* --- generation number */
291 if(ent->generation) {
292 append_string(context, sp, "%s:%d:%d ", time2str(ent->generation->time),
293 ent->generation->usec,
294 ent->generation->gen);
295 } else
296 append_string(context, sp, "- ");
298 /* --- extensions */
299 if(ent->extensions && ent->extensions->len > 0) {
300 for(i = 0; i < ent->extensions->len; i++) {
301 void *d;
302 size_t size, sz = 0;
304 ASN1_MALLOC_ENCODE(HDB_extension, d, size,
305 &ent->extensions->val[i], &sz, ret);
306 if (ret) {
307 krb5_clear_error_message(context);
308 return ret;
310 if(size != sz)
311 krb5_abortx(context, "internal asn.1 encoder error");
313 if (hex_encode(d, size, &p) < 0) {
314 free(d);
315 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
316 return ENOMEM;
319 free(d);
320 append_string(context, sp, "%s%s", p,
321 ent->extensions->len - 1 != i ? ":" : "");
322 free(p);
324 } else
325 append_string(context, sp, "-");
327 return 0;
330 #define KRB5_KDB_DISALLOW_POSTDATED 0x00000001
331 #define KRB5_KDB_DISALLOW_FORWARDABLE 0x00000002
332 #define KRB5_KDB_DISALLOW_TGT_BASED 0x00000004
333 #define KRB5_KDB_DISALLOW_RENEWABLE 0x00000008
334 #define KRB5_KDB_DISALLOW_PROXIABLE 0x00000010
335 #define KRB5_KDB_DISALLOW_DUP_SKEY 0x00000020
336 #define KRB5_KDB_DISALLOW_ALL_TIX 0x00000040
337 #define KRB5_KDB_REQUIRES_PRE_AUTH 0x00000080
338 #define KRB5_KDB_REQUIRES_HW_AUTH 0x00000100
339 #define KRB5_KDB_REQUIRES_PWCHANGE 0x00000200
340 #define KRB5_KDB_DISALLOW_SVR 0x00001000
341 #define KRB5_KDB_PWCHANGE_SERVICE 0x00002000
342 #define KRB5_KDB_SUPPORT_DESMD5 0x00004000
343 #define KRB5_KDB_NEW_PRINC 0x00008000
345 static int
346 flags_to_attr(HDBFlags flags)
348 int a = 0;
350 if (!flags.postdate)
351 a |= KRB5_KDB_DISALLOW_POSTDATED;
352 if (!flags.forwardable)
353 a |= KRB5_KDB_DISALLOW_FORWARDABLE;
354 if (flags.initial)
355 a |= KRB5_KDB_DISALLOW_TGT_BASED;
356 if (!flags.renewable)
357 a |= KRB5_KDB_DISALLOW_RENEWABLE;
358 if (!flags.proxiable)
359 a |= KRB5_KDB_DISALLOW_PROXIABLE;
360 if (flags.invalid)
361 a |= KRB5_KDB_DISALLOW_ALL_TIX;
362 if (flags.require_preauth)
363 a |= KRB5_KDB_REQUIRES_PRE_AUTH;
364 if (flags.require_hwauth)
365 a |= KRB5_KDB_REQUIRES_HW_AUTH;
366 if (!flags.server)
367 a |= KRB5_KDB_DISALLOW_SVR;
368 if (flags.change_pw)
369 a |= KRB5_KDB_PWCHANGE_SERVICE;
370 return a;
373 krb5_error_code
374 entry2mit_string_int(krb5_context context, krb5_storage *sp, hdb_entry *ent)
376 krb5_error_code ret;
377 ssize_t sz;
378 size_t i, k;
379 size_t num_tl_data = 0;
380 size_t num_key_data = 0;
381 char *p;
382 HDB_Ext_KeySet *hist_keys = NULL;
383 HDB_extension *extp;
384 time_t last_pw_chg = 0;
385 time_t exp = 0;
386 time_t pwexp = 0;
387 unsigned int max_life = 0;
388 unsigned int max_renew = 0;
390 if (ent->modified_by)
391 num_tl_data++;
393 ret = hdb_entry_get_pw_change_time(ent, &last_pw_chg);
394 if (ret) return ret;
395 if (last_pw_chg)
396 num_tl_data++;
398 extp = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys);
399 if (extp)
400 hist_keys = &extp->data.u.hist_keys;
402 for (i = 0; i < ent->keys.len;i++) {
403 if (ent->keys.val[i].key.keytype == ETYPE_DES_CBC_MD4 ||
404 ent->keys.val[i].key.keytype == ETYPE_DES_CBC_MD5)
405 continue;
406 num_key_data++;
408 if (hist_keys) {
409 for (i = 0; i < hist_keys->len; i++) {
411 * MIT uses the highest kvno as the current kvno instead of
412 * tracking kvno separately, so we can't dump keysets with kvno
413 * higher than the entry's kvno.
415 if (hist_keys->val[i].kvno >= ent->kvno)
416 continue;
417 for (k = 0; k < hist_keys->val[i].keys.len; k++) {
418 if (ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD4 ||
419 ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD5)
420 continue;
421 num_key_data++;
426 ret = krb5_unparse_name(context, ent->principal, &p);
427 if (ret) return ret;
428 sz = append_string(context, sp, "princ\t38\t%u\t%u\t%u\t0\t%s\t%d",
429 strlen(p), num_tl_data, num_key_data, p,
430 flags_to_attr(ent->flags));
431 free(p);
432 if (sz == -1) return ENOMEM;
434 if (ent->max_life)
435 max_life = *ent->max_life;
436 if (ent->max_renew)
437 max_renew = *ent->max_renew;
438 if (ent->valid_end)
439 exp = *ent->valid_end;
440 if (ent->pw_end)
441 pwexp = *ent->pw_end;
443 sz = append_string(context, sp, "\t%u\t%u\t%u\t%u\t0\t0\t0",
444 max_life, max_renew, exp, pwexp);
445 if (sz == -1) return ENOMEM;
447 /* Dump TL data we know: last pw chg and modified_by */
448 #define mit_KRB5_TL_LAST_PWD_CHANGE 1
449 #define mit_KRB5_TL_MOD_PRINC 2
450 if (last_pw_chg) {
451 krb5_data d;
452 time_t val;
453 unsigned char *ptr;
455 ptr = (unsigned char *)&last_pw_chg;
456 val = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
457 d.data = &val;
458 d.length = sizeof (last_pw_chg);
459 sz = append_string(context, sp, "\t%u\t%u\t",
460 mit_KRB5_TL_LAST_PWD_CHANGE, d.length);
461 if (sz == -1) return ENOMEM;
462 sz = append_hex(context, sp, 1, 1, &d);
463 if (sz == -1) return ENOMEM;
465 if (ent->modified_by) {
466 krb5_data d;
467 unsigned int val;
468 size_t plen;
469 unsigned char *ptr;
470 char *modby_p;
472 ptr = (unsigned char *)&ent->modified_by->time;
473 val = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
474 d.data = &val;
475 d.length = sizeof (ent->modified_by->time);
476 ret = krb5_unparse_name(context, ent->modified_by->principal, &modby_p);
477 if (ret) return ret;
478 plen = strlen(modby_p);
479 sz = append_string(context, sp, "\t%u\t%u\t",
480 mit_KRB5_TL_MOD_PRINC,
481 d.length + plen + 1 /* NULL counted */);
482 if (sz == -1) return ENOMEM;
483 sz = append_hex(context, sp, 1, 1, &d);
484 if (sz == -1) {
485 free(modby_p);
486 return ENOMEM;
488 d.data = modby_p;
489 d.length = plen + 1;
490 sz = append_hex(context, sp, 1, 1, &d);
491 free(modby_p);
492 if (sz == -1) return ENOMEM;
495 * Dump keys (remembering to not include any with kvno higher than
496 * the entry's because MIT doesn't track entry kvno separately from
497 * the entry's keys -- max kvno is it)
499 for (i = 0; i < ent->keys.len; i++) {
500 if (ent->keys.val[i].key.keytype == ETYPE_DES_CBC_MD4 ||
501 ent->keys.val[i].key.keytype == ETYPE_DES_CBC_MD5)
502 continue;
503 sz = append_mit_key(context, sp, ent->principal, ent->kvno,
504 &ent->keys.val[i]);
505 if (sz == -1) return ENOMEM;
507 for (i = 0; hist_keys && i < ent->kvno; i++) {
508 size_t m;
510 /* dump historical keys */
511 for (k = 0; k < hist_keys->len; k++) {
512 if (hist_keys->val[k].kvno != ent->kvno - i)
513 continue;
514 for (m = 0; m < hist_keys->val[k].keys.len; m++) {
515 if (ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD4 ||
516 ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD5)
517 continue;
518 sz = append_mit_key(context, sp, ent->principal,
519 hist_keys->val[k].kvno,
520 &hist_keys->val[k].keys.val[m]);
521 if (sz == -1) return ENOMEM;
525 sz = append_string(context, sp, "\t-1;"); /* "extra data" */
526 if (sz == -1) return ENOMEM;
527 return 0;
530 krb5_error_code
531 hdb_entry2string(krb5_context context, hdb_entry *ent, char **str)
533 krb5_error_code ret;
534 krb5_data data;
535 krb5_storage *sp;
537 sp = krb5_storage_emem();
538 if (sp == NULL) {
539 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
540 return ENOMEM;
543 ret = entry2string_int(context, sp, ent);
544 if (ret) {
545 krb5_storage_free(sp);
546 return ret;
549 krb5_storage_write(sp, "\0", 1);
550 krb5_storage_to_data(sp, &data);
551 krb5_storage_free(sp);
552 *str = data.data;
553 return 0;
556 /* print a hdb_entry to (FILE*)data; suitable for hdb_foreach */
558 krb5_error_code
559 hdb_print_entry(krb5_context context, HDB *db, hdb_entry_ex *entry,
560 void *data)
562 struct hdb_print_entry_arg *parg = data;
563 krb5_error_code ret;
564 krb5_storage *sp;
566 fflush(parg->out);
567 sp = krb5_storage_from_fd(fileno(parg->out));
568 if (sp == NULL) {
569 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
570 return ENOMEM;
573 switch (parg->fmt) {
574 case HDB_DUMP_HEIMDAL:
575 ret = entry2string_int(context, sp, &entry->entry);
576 break;
577 case HDB_DUMP_MIT:
578 ret = entry2mit_string_int(context, sp, &entry->entry);
579 break;
580 default:
581 heim_abort("Only two dump formats supported: Heimdal and MIT");
583 if (ret) {
584 krb5_storage_free(sp);
585 return ret;
588 krb5_storage_write(sp, "\n", 1);
589 krb5_storage_free(sp);
590 return 0;