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