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