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