xref: /netbsd-src/crypto/external/bsd/heimdal/dist/kadmin/load.c (revision 241bea01a19bbb306af27777a870b86d41cb3fda)
1 /*	$NetBSD: load.c,v 1.3 2019/12/15 22:50:46 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1997-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 the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <limits.h>
37 
38 #include "kadmin_locl.h"
39 #include "kadmin-commands.h"
40 #include <kadm5/private.h>
41 
42 struct entry {
43     char *principal;
44     char *key;
45     char *max_life;
46     char *max_renew;
47     char *created;
48     char *modified;
49     char *valid_start;
50     char *valid_end;
51     char *pw_end;
52     char *flags;
53     char *generation;
54     char *extensions;
55 };
56 
57 static char *
skip_next(char * p)58 skip_next(char *p)
59 {
60     while(*p && !isspace((unsigned char)*p))
61 	p++;
62     *p++ = 0;
63     while(*p && isspace((unsigned char)*p))
64 	p++;
65     return p;
66 }
67 
68 /*
69  * Parse the time in `s', returning:
70  * -1 if error parsing
71  * 0  if none  present
72  * 1  if parsed ok
73  */
74 
75 static int
parse_time_string(time_t * t,const char * s)76 parse_time_string(time_t *t, const char *s)
77 {
78     int year, month, date, hour, minute, second;
79     struct tm tm;
80 
81     if(strcmp(s, "-") == 0)
82 	return 0;
83     if(sscanf(s, "%04d%02d%02d%02d%02d%02d",
84 	      &year, &month, &date, &hour, &minute, &second) != 6)
85 	return -1;
86     tm.tm_year  = year - 1900;
87     tm.tm_mon   = month - 1;
88     tm.tm_mday  = date;
89     tm.tm_hour  = hour;
90     tm.tm_min   = minute;
91     tm.tm_sec   = second;
92     tm.tm_isdst = 0;
93     *t = timegm(&tm);
94     return 1;
95 }
96 
97 /*
98  * parse time, allocating space in *t if it's there
99  */
100 
101 static int
parse_time_string_alloc(time_t ** t,const char * s)102 parse_time_string_alloc (time_t **t, const char *s)
103 {
104     time_t tmp;
105     int ret;
106 
107     *t = NULL;
108     ret = parse_time_string (&tmp, s);
109     if (ret == 1) {
110 	*t = malloc (sizeof (**t));
111 	if (*t == NULL)
112 	    krb5_errx (context, 1, "malloc: out of memory");
113 	**t = tmp;
114     }
115     return ret;
116 }
117 
118 /*
119  * see parse_time_string for calling convention
120  */
121 
122 static int
parse_integer(unsigned int * u,const char * s)123 parse_integer(unsigned int *u, const char *s)
124 {
125     if(strcmp(s, "-") == 0)
126 	return 0;
127     if (sscanf(s, "%u", u) != 1)
128 	return -1;
129     return 1;
130 }
131 
132 static int
parse_integer_alloc(unsigned int ** u,const char * s)133 parse_integer_alloc (unsigned int **u, const char *s)
134 {
135     unsigned int tmp;
136     int ret;
137 
138     *u = NULL;
139     ret = parse_integer (&tmp, s);
140     if (ret == 1) {
141 	*u = malloc (sizeof (**u));
142 	if (*u == NULL)
143 	    krb5_errx (context, 1, "malloc: out of memory");
144 	**u = tmp;
145     }
146     return ret;
147 }
148 
149 /*
150  * Parse dumped keys in `str' and store them in `ent'
151  * return -1 if parsing failed
152  */
153 
154 static int
parse_keys(hdb_entry * ent,char * str)155 parse_keys(hdb_entry *ent, char *str)
156 {
157     krb5_error_code ret;
158     int tmp;
159     char *p;
160     size_t i;
161 
162     p = strsep(&str, ":");
163     if (sscanf(p, "%d", &tmp) != 1)
164 	return 1;
165     ent->kvno = tmp;
166     p = strsep(&str, ":");
167     while(p){
168 	Key *key;
169 	key = realloc(ent->keys.val,
170 		      (ent->keys.len + 1) * sizeof(*ent->keys.val));
171 	if(key == NULL)
172 	    krb5_errx (context, 1, "realloc: out of memory");
173 	ent->keys.val = key;
174 	key = ent->keys.val + ent->keys.len;
175 	ent->keys.len++;
176 	memset(key, 0, sizeof(*key));
177 	if(sscanf(p, "%d", &tmp) == 1) {
178 	    key->mkvno = malloc(sizeof(*key->mkvno));
179 	    *key->mkvno = tmp;
180 	} else
181 	    key->mkvno = NULL;
182 	p = strsep(&str, ":");
183 	if (sscanf(p, "%d", &tmp) != 1)
184 	    return 1;
185 	key->key.keytype = tmp;
186 	p = strsep(&str, ":");
187 	ret = krb5_data_alloc(&key->key.keyvalue, (strlen(p) - 1) / 2 + 1);
188 	if (ret)
189 	    krb5_err (context, 1, ret, "krb5_data_alloc");
190 	for(i = 0; i < strlen(p); i += 2) {
191 	    if(sscanf(p + i, "%02x", &tmp) != 1)
192 		return 1;
193 	    ((u_char*)key->key.keyvalue.data)[i / 2] = tmp;
194 	}
195 	p = strsep(&str, ":");
196 	if(strcmp(p, "-") != 0){
197 	    unsigned type;
198 	    size_t p_len;
199 
200 	    if(sscanf(p, "%u/", &type) != 1)
201 		return 1;
202 	    p = strchr(p, '/');
203 	    if(p == NULL)
204 		return 1;
205 	    p++;
206 	    p_len = strlen(p);
207 
208 	    key->salt = calloc(1, sizeof(*key->salt));
209 	    if (key->salt == NULL)
210 		krb5_errx (context, 1, "malloc: out of memory");
211 	    key->salt->type = type;
212 
213 	    if (p_len) {
214 		if(*p == '\"') {
215 		    ret = krb5_data_copy(&key->salt->salt, p + 1, p_len - 2);
216 		    if (ret)
217 			krb5_err (context, 1, ret, "krb5_data_copy");
218 		} else {
219 		    ret = krb5_data_alloc(&key->salt->salt,
220 					  (p_len - 1) / 2 + 1);
221 		    if (ret)
222 			krb5_err (context, 1, ret, "krb5_data_alloc");
223 		    for(i = 0; i < p_len; i += 2){
224 			if (sscanf(p + i, "%02x", &tmp) != 1)
225 			    return 1;
226 			((u_char*)key->salt->salt.data)[i / 2] = tmp;
227 		    }
228 		}
229 	    } else
230 		krb5_data_zero (&key->salt->salt);
231 	}
232 	p = strsep(&str, ":");
233     }
234     return 0;
235 }
236 
237 /*
238  * see parse_time_string for calling convention
239  */
240 
241 static int
parse_event(Event * ev,char * s)242 parse_event(Event *ev, char *s)
243 {
244     krb5_error_code ret;
245     char *p;
246 
247     if(strcmp(s, "-") == 0)
248 	return 0;
249     memset(ev, 0, sizeof(*ev));
250     p = strsep(&s, ":");
251     if(parse_time_string(&ev->time, p) != 1)
252 	return -1;
253     p = strsep(&s, ":");
254     ret = krb5_parse_name(context, p, &ev->principal);
255     if (ret)
256 	return -1;
257     return 1;
258 }
259 
260 static int
parse_event_alloc(Event ** ev,char * s)261 parse_event_alloc (Event **ev, char *s)
262 {
263     Event tmp;
264     int ret;
265 
266     *ev = NULL;
267     ret = parse_event (&tmp, s);
268     if (ret == 1) {
269 	*ev = malloc (sizeof (**ev));
270 	if (*ev == NULL)
271 	    krb5_errx (context, 1, "malloc: out of memory");
272 	**ev = tmp;
273     }
274     return ret;
275 }
276 
277 static int
parse_hdbflags2int(HDBFlags * f,const char * s)278 parse_hdbflags2int(HDBFlags *f, const char *s)
279 {
280     int ret;
281     unsigned int tmp;
282 
283     ret = parse_integer (&tmp, s);
284     if (ret == 1)
285 	*f = int2HDBFlags (tmp);
286     return ret;
287 }
288 
289 static int
parse_generation(char * str,GENERATION ** gen)290 parse_generation(char *str, GENERATION **gen)
291 {
292     char *p;
293     int v;
294 
295     if(strcmp(str, "-") == 0 || *str == '\0') {
296 	*gen = NULL;
297 	return 0;
298     }
299     *gen = calloc(1, sizeof(**gen));
300 
301     p = strsep(&str, ":");
302     if(parse_time_string(&(*gen)->time, p) != 1)
303 	return -1;
304     p = strsep(&str, ":");
305     if(sscanf(p, "%d", &v) != 1)
306 	return -1;
307     (*gen)->usec = v;
308     p = strsep(&str, ":");
309     if(sscanf(p, "%d", &v) != 1)
310 	return -1;
311     (*gen)->gen = v - 1; /* XXX gets bumped in _hdb_store */
312     return 0;
313 }
314 
315 /* On error modify strp to point to the problem element */
316 static int
parse_extensions(char ** strp,HDB_extensions ** e)317 parse_extensions(char **strp, HDB_extensions **e)
318 {
319     char *str = *strp;
320     char *p;
321     int ret;
322 
323     if(strcmp(str, "-") == 0 || *str == '\0') {
324 	*e = NULL;
325 	return 0;
326     }
327     *e = calloc(1, sizeof(**e));
328 
329     p = strsep(&str, ":");
330 
331     while (p) {
332 	HDB_extension ext;
333 	ssize_t len;
334 	void *d;
335 
336 	len = strlen(p);
337 	d = emalloc(len);
338 
339 	len = hex_decode(p, d, len);
340 	if (len < 0) {
341 	    free(d);
342             *strp = p;
343 	    return -1;
344 	}
345 
346 	ret = decode_HDB_extension(d, len, &ext, NULL);
347 	free(d);
348 	if (ret) {
349             *strp = p;
350 	    return -1;
351         }
352 	d = realloc((*e)->val, ((*e)->len + 1) * sizeof((*e)->val[0]));
353 	if (d == NULL)
354 	    abort();
355 	(*e)->val = d;
356 	(*e)->val[(*e)->len] = ext;
357 	(*e)->len++;
358 
359 	p = strsep(&str, ":");
360     }
361 
362     return 0;
363 }
364 
365 /* XXX: Principal names with '\n' cannot be dumped or loaded */
366 static int
my_fgetln(FILE * f,char ** bufp,size_t * szp,size_t * lenp)367 my_fgetln(FILE *f, char **bufp, size_t *szp, size_t *lenp)
368 {
369     size_t len;
370     size_t sz = *szp;
371     char *buf = *bufp;
372     char *p, *n;
373 
374     if (!buf) {
375         buf = malloc(sz ? sz : 8192);
376         if (!buf)
377             return ENOMEM;
378         if (!sz)
379             sz = 8192;
380     }
381 
382     len = 0;
383     while ((p = fgets(&buf[len], sz-len, f)) != NULL) {
384         len += strlen(&buf[len]);
385         if (buf[len-1] == '\n')
386             break;
387         if (feof(f))
388             break;
389         if (sz > SIZE_MAX/2 ||
390             (n = realloc(buf, sz += 1 + (sz >> 1))) == NULL) {
391             free(buf);
392             *bufp = NULL;
393             *szp = 0;
394             *lenp = 0;
395             return ENOMEM;
396         }
397         buf = n;
398     }
399     *bufp = buf;
400     *szp = sz;
401     *lenp = len;
402     return 0; /* *len == 0 || no EOL -> EOF */
403 }
404 
405 /*
406  * Parse the dump file in `filename' and create the database (merging
407  * iff merge)
408  */
409 
410 static int
doit(const char * filename,int mergep)411 doit(const char *filename, int mergep)
412 {
413     krb5_error_code ret = 0;
414     krb5_error_code ret2 = 0;
415     FILE *f;
416     char *line = NULL;
417     size_t linesz = 0;
418     size_t linelen = 0;
419     char *p;
420     int lineno;
421     int flags = O_RDWR;
422     struct entry e;
423     hdb_entry_ex ent;
424     HDB *db = _kadm5_s_get_db(kadm_handle);
425 
426     f = fopen(filename, "r");
427     if (f == NULL) {
428 	krb5_warn(context, errno, "fopen(%s)", filename);
429 	return 1;
430     }
431     /*
432      * We don't have a version number in the dump, so we don't know which iprop
433      * log entries to keep, if any.  We throw the log away.
434      *
435      * We could merge the ipropd-master/slave dump/load here as an option, in
436      * which case we would first load the dump.
437      *
438      * If we're merging, first recover unconfirmed records in the existing log.
439      */
440     if (mergep)
441         ret = kadm5_log_init(kadm_handle);
442     if (ret == 0)
443         ret = kadm5_log_reinit(kadm_handle, 0);
444     if (ret) {
445 	fclose (f);
446 	krb5_warn(context, ret, "kadm5_log_reinit");
447 	return 1;
448     }
449 
450     if (!mergep)
451 	flags |= O_CREAT | O_TRUNC;
452     ret = db->hdb_open(context, db, flags, 0600);
453     if (ret){
454 	krb5_warn(context, ret, "hdb_open");
455 	fclose(f);
456 	return 1;
457     }
458     (void) db->hdb_set_sync(context, db, 0);
459     for (lineno = 1;
460          (ret2 = my_fgetln(f, &line, &linesz, &linelen)) == 0 && linelen > 0;
461 	 ++lineno) {
462 	p = line;
463 	while (isspace((unsigned char)*p))
464 	    p++;
465 
466 	e.principal = p;
467 	for (p = line; *p; p++){
468 	    if (*p == '\\') /* Support '\n' escapes??? */
469 		p++;
470 	    else if (isspace((unsigned char)*p)) {
471 		*p = 0;
472 		break;
473 	    }
474 	}
475 	p = skip_next(p);
476 
477 	e.key = p;
478 	p = skip_next(p);
479 
480 	e.created = p;
481 	p = skip_next(p);
482 
483 	e.modified = p;
484 	p = skip_next(p);
485 
486 	e.valid_start = p;
487 	p = skip_next(p);
488 
489 	e.valid_end = p;
490 	p = skip_next(p);
491 
492 	e.pw_end = p;
493 	p = skip_next(p);
494 
495 	e.max_life = p;
496 	p = skip_next(p);
497 
498 	e.max_renew = p;
499 	p = skip_next(p);
500 
501 	e.flags = p;
502 	p = skip_next(p);
503 
504 	e.generation = p;
505 	p = skip_next(p);
506 
507 	e.extensions = p;
508 	skip_next(p);
509 
510 	memset(&ent, 0, sizeof(ent));
511 	ret2 = krb5_parse_name(context, e.principal, &ent.entry.principal);
512 	if (ret2) {
513 	    const char *msg = krb5_get_error_message(context, ret);
514 	    fprintf(stderr, "%s:%d:%s (%s)\n",
515 		    filename, lineno, msg, e.principal);
516 	    krb5_free_error_message(context, msg);
517             ret = 1;
518 	    continue;
519 	}
520 
521 	if (parse_keys(&ent.entry, e.key)) {
522 	    fprintf (stderr, "%s:%d:error parsing keys (%s)\n",
523 		     filename, lineno, e.key);
524 	    hdb_free_entry (context, &ent);
525             ret = 1;
526 	    continue;
527 	}
528 
529 	if (parse_event(&ent.entry.created_by, e.created) == -1) {
530 	    fprintf (stderr, "%s:%d:error parsing created event (%s)\n",
531 		     filename, lineno, e.created);
532 	    hdb_free_entry (context, &ent);
533             ret = 1;
534 	    continue;
535 	}
536 	if (parse_event_alloc (&ent.entry.modified_by, e.modified) == -1) {
537 	    fprintf (stderr, "%s:%d:error parsing event (%s)\n",
538 		     filename, lineno, e.modified);
539 	    hdb_free_entry (context, &ent);
540             ret = 1;
541 	    continue;
542 	}
543 	if (parse_time_string_alloc (&ent.entry.valid_start, e.valid_start) == -1) {
544 	    fprintf (stderr, "%s:%d:error parsing time (%s)\n",
545 		     filename, lineno, e.valid_start);
546 	    hdb_free_entry (context, &ent);
547             ret = 1;
548 	    continue;
549 	}
550 	if (parse_time_string_alloc (&ent.entry.valid_end,   e.valid_end) == -1) {
551 	    fprintf (stderr, "%s:%d:error parsing time (%s)\n",
552 		     filename, lineno, e.valid_end);
553 	    hdb_free_entry (context, &ent);
554             ret = 1;
555 	    continue;
556 	}
557 	if (parse_time_string_alloc (&ent.entry.pw_end,      e.pw_end) == -1) {
558 	    fprintf (stderr, "%s:%d:error parsing time (%s)\n",
559 		     filename, lineno, e.pw_end);
560 	    hdb_free_entry (context, &ent);
561             ret = 1;
562 	    continue;
563 	}
564 
565 	if (parse_integer_alloc (&ent.entry.max_life,  e.max_life) == -1) {
566 	    fprintf (stderr, "%s:%d:error parsing lifetime (%s)\n",
567 		     filename, lineno, e.max_life);
568 	    hdb_free_entry (context, &ent);
569             ret = 1;
570 	    continue;
571 
572 	}
573 	if (parse_integer_alloc (&ent.entry.max_renew, e.max_renew) == -1) {
574 	    fprintf (stderr, "%s:%d:error parsing lifetime (%s)\n",
575 		     filename, lineno, e.max_renew);
576 	    hdb_free_entry (context, &ent);
577             ret = 1;
578 	    continue;
579 	}
580 
581 	if (parse_hdbflags2int (&ent.entry.flags, e.flags) != 1) {
582 	    fprintf (stderr, "%s:%d:error parsing flags (%s)\n",
583 		     filename, lineno, e.flags);
584 	    hdb_free_entry (context, &ent);
585             ret = 1;
586 	    continue;
587 	}
588 
589 	if(parse_generation(e.generation, &ent.entry.generation) == -1) {
590 	    fprintf (stderr, "%s:%d:error parsing generation (%s)\n",
591 		     filename, lineno, e.generation);
592 	    hdb_free_entry (context, &ent);
593             ret = 1;
594 	    continue;
595 	}
596 
597 	if (parse_extensions(&e.extensions, &ent.entry.extensions) == -1) {
598 	    fprintf (stderr, "%s:%d:error parsing extension (%s)\n",
599 		     filename, lineno, e.extensions);
600 	    hdb_free_entry (context, &ent);
601             ret = 1;
602 	    continue;
603 	}
604 
605 	ret2 = db->hdb_store(context, db, HDB_F_REPLACE, &ent);
606 	hdb_free_entry (context, &ent);
607 	if (ret2) {
608 	    krb5_warn(context, ret2, "db_store");
609 	    break;
610 	}
611     }
612     free(line);
613     if (ret2)
614 	ret = ret2;
615     ret2 = db->hdb_set_sync(context, db, 1);
616     if (ret2)
617         krb5_err(context, 1, ret, "failed to sync the HDB");
618     (void) kadm5_log_end(kadm_handle);
619     ret2 = db->hdb_close(context, db);
620     if (ret2)
621         ret = ret2;
622     fclose(f);
623     return ret != 0;
624 }
625 
626 
627 extern int local_flag;
628 
629 static int
loadit(int mergep,const char * name,int argc,char ** argv)630 loadit(int mergep, const char *name, int argc, char **argv)
631 {
632     if(!local_flag) {
633 	krb5_warnx(context, "%s is only available in local (-l) mode", name);
634 	return 0;
635     }
636 
637     return doit(argv[0], mergep);
638 }
639 
640 int
load(void * opt,int argc,char ** argv)641 load(void *opt, int argc, char **argv)
642 {
643     return loadit(0, "load", argc, argv);
644 }
645 
646 int
merge(void * opt,int argc,char ** argv)647 merge(void *opt, int argc, char **argv)
648 {
649     return loadit(1, "merge", argc, argv);
650 }
651