diff options
Diffstat (limited to 'third_party/heimdal/kadmin/load.c')
-rw-r--r-- | third_party/heimdal/kadmin/load.c | 650 |
1 files changed, 650 insertions, 0 deletions
diff --git a/third_party/heimdal/kadmin/load.c b/third_party/heimdal/kadmin/load.c new file mode 100644 index 0000000..f62f8b9 --- /dev/null +++ b/third_party/heimdal/kadmin/load.c @@ -0,0 +1,650 @@ +/* + * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <limits.h> + +#include "kadmin_locl.h" +#include "kadmin-commands.h" +#include <kadm5/private.h> + +struct entry { + char *principal; + char *key; + char *max_life; + char *max_renew; + char *created; + char *modified; + char *valid_start; + char *valid_end; + char *pw_end; + char *flags; + char *generation; + char *extensions; +}; + +static char * +skip_next(char *p) +{ + while(*p && !isspace((unsigned char)*p)) + p++; + *p++ = 0; + while(*p && isspace((unsigned char)*p)) + p++; + return p; +} + +/* + * Parse the time in `s', returning: + * -1 if error parsing + * 0 if none present + * 1 if parsed ok + */ + +static int +parse_time_string(time_t *t, const char *s) +{ + int year, month, date, hour, minute, second; + struct tm tm; + + if(strcmp(s, "-") == 0) + return 0; + if(sscanf(s, "%04d%02d%02d%02d%02d%02d", + &year, &month, &date, &hour, &minute, &second) != 6) + return -1; + tm.tm_year = year - 1900; + tm.tm_mon = month - 1; + tm.tm_mday = date; + tm.tm_hour = hour; + tm.tm_min = minute; + tm.tm_sec = second; + tm.tm_isdst = 0; + *t = timegm(&tm); + return 1; +} + +/* + * parse time, allocating space in *t if it's there + */ + +static int +parse_time_string_alloc (time_t **t, const char *s) +{ + time_t tmp; + int ret; + + *t = NULL; + ret = parse_time_string (&tmp, s); + if (ret == 1) { + *t = malloc (sizeof (**t)); + if (*t == NULL) + krb5_errx (context, 1, "malloc: out of memory"); + **t = tmp; + } + return ret; +} + +/* + * see parse_time_string for calling convention + */ + +static int +parse_integer(unsigned int *u, const char *s) +{ + if(strcmp(s, "-") == 0) + return 0; + if (sscanf(s, "%u", u) != 1) + return -1; + return 1; +} + +static int +parse_integer_alloc (unsigned int **u, const char *s) +{ + unsigned int tmp; + int ret; + + *u = NULL; + ret = parse_integer (&tmp, s); + if (ret == 1) { + *u = malloc (sizeof (**u)); + if (*u == NULL) + krb5_errx (context, 1, "malloc: out of memory"); + **u = tmp; + } + return ret; +} + +/* + * Parse dumped keys in `str' and store them in `ent' + * return -1 if parsing failed + */ + +static int +parse_keys(hdb_entry *ent, char *str) +{ + krb5_error_code ret; + int tmp; + char *p; + size_t i; + + p = strsep(&str, ":"); + if (sscanf(p, "%d", &tmp) != 1) + return 1; + ent->kvno = tmp; + p = strsep(&str, ":"); + while(p){ + Key *key; + key = realloc(ent->keys.val, + (ent->keys.len + 1) * sizeof(*ent->keys.val)); + if(key == NULL) + krb5_errx (context, 1, "realloc: out of memory"); + ent->keys.val = key; + key = ent->keys.val + ent->keys.len; + ent->keys.len++; + memset(key, 0, sizeof(*key)); + if(sscanf(p, "%d", &tmp) == 1) { + key->mkvno = malloc(sizeof(*key->mkvno)); + *key->mkvno = tmp; + } else + key->mkvno = NULL; + p = strsep(&str, ":"); + if (sscanf(p, "%d", &tmp) != 1) + return 1; + key->key.keytype = tmp; + p = strsep(&str, ":"); + ret = krb5_data_alloc(&key->key.keyvalue, (strlen(p) - 1) / 2 + 1); + if (ret) + krb5_err (context, 1, ret, "krb5_data_alloc"); + for(i = 0; i < strlen(p); i += 2) { + if(sscanf(p + i, "%02x", &tmp) != 1) + return 1; + ((u_char*)key->key.keyvalue.data)[i / 2] = tmp; + } + p = strsep(&str, ":"); + if(strcmp(p, "-") != 0){ + unsigned type; + size_t p_len; + + if(sscanf(p, "%u/", &type) != 1) + return 1; + p = strchr(p, '/'); + if(p == NULL) + return 1; + p++; + p_len = strlen(p); + + key->salt = calloc(1, sizeof(*key->salt)); + if (key->salt == NULL) + krb5_errx (context, 1, "malloc: out of memory"); + key->salt->type = type; + + if (p_len) { + if(*p == '\"') { + ret = krb5_data_copy(&key->salt->salt, p + 1, p_len - 2); + if (ret) + krb5_err (context, 1, ret, "krb5_data_copy"); + } else { + ret = krb5_data_alloc(&key->salt->salt, + (p_len - 1) / 2 + 1); + if (ret) + krb5_err (context, 1, ret, "krb5_data_alloc"); + for(i = 0; i < p_len; i += 2){ + if (sscanf(p + i, "%02x", &tmp) != 1) + return 1; + ((u_char*)key->salt->salt.data)[i / 2] = tmp; + } + } + } else + krb5_data_zero (&key->salt->salt); + } + p = strsep(&str, ":"); + } + return 0; +} + +/* + * see parse_time_string for calling convention + */ + +static int +parse_event(Event *ev, char *s) +{ + krb5_error_code ret; + char *p; + + if(strcmp(s, "-") == 0) + return 0; + memset(ev, 0, sizeof(*ev)); + p = strsep(&s, ":"); + if(parse_time_string(&ev->time, p) != 1) + return -1; + p = strsep(&s, ":"); + ret = krb5_parse_name(context, p, &ev->principal); + if (ret) + return -1; + return 1; +} + +static int +parse_event_alloc (Event **ev, char *s) +{ + Event tmp; + int ret; + + *ev = NULL; + ret = parse_event (&tmp, s); + if (ret == 1) { + *ev = malloc (sizeof (**ev)); + if (*ev == NULL) + krb5_errx (context, 1, "malloc: out of memory"); + **ev = tmp; + } + return ret; +} + +static int +parse_hdbflags2int(HDBFlags *f, const char *s) +{ + int ret; + unsigned int tmp; + + ret = parse_integer (&tmp, s); + if (ret == 1) + *f = int2HDBFlags (tmp); + return ret; +} + +static int +parse_generation(char *str, GENERATION **gen) +{ + char *p; + int v; + + if(strcmp(str, "-") == 0 || *str == '\0') { + *gen = NULL; + return 0; + } + *gen = calloc(1, sizeof(**gen)); + + p = strsep(&str, ":"); + if(parse_time_string(&(*gen)->time, p) != 1) + return -1; + p = strsep(&str, ":"); + if(sscanf(p, "%d", &v) != 1) + return -1; + (*gen)->usec = v; + p = strsep(&str, ":"); + if(sscanf(p, "%d", &v) != 1) + return -1; + (*gen)->gen = v - 1; /* XXX gets bumped in _hdb_store */ + return 0; +} + +/* On error modify strp to point to the problem element */ +static int +parse_extensions(char **strp, HDB_extensions **e) +{ + char *str = *strp; + char *p; + int ret; + + if(strcmp(str, "-") == 0 || *str == '\0') { + *e = NULL; + return 0; + } + *e = calloc(1, sizeof(**e)); + + p = strsep(&str, ":"); + + while (p) { + HDB_extension ext; + ssize_t len; + void *d; + + len = strlen(p); + d = emalloc(len); + + len = hex_decode(p, d, len); + if (len < 0) { + free(d); + *strp = p; + return -1; + } + + ret = decode_HDB_extension(d, len, &ext, NULL); + free(d); + if (ret) { + *strp = p; + return -1; + } + d = realloc((*e)->val, ((*e)->len + 1) * sizeof((*e)->val[0])); + if (d == NULL) + abort(); + (*e)->val = d; + (*e)->val[(*e)->len] = ext; + (*e)->len++; + + p = strsep(&str, ":"); + } + + return 0; +} + +/* XXX: Principal names with '\n' cannot be dumped or loaded */ +static int +my_fgetln(FILE *f, char **bufp, size_t *szp, size_t *lenp) +{ + size_t len; + size_t sz = *szp; + char *buf = *bufp; + char *n; + + if (!buf) { + buf = malloc(sz ? sz : 8192); + if (!buf) + return ENOMEM; + if (!sz) + sz = 8192; + } + + len = 0; + while (fgets(&buf[len], sz-len, f) != NULL) { + len += strlen(&buf[len]); + if (buf[len-1] == '\n') + break; + if (feof(f)) + break; + if (sz > SIZE_MAX/2 || + (n = realloc(buf, sz += 1 + (sz >> 1))) == NULL) { + free(buf); + *bufp = NULL; + *szp = 0; + *lenp = 0; + return ENOMEM; + } + buf = n; + } + *bufp = buf; + *szp = sz; + *lenp = len; + return 0; /* *len == 0 || no EOL -> EOF */ +} + +/* + * Parse the dump file in `filename' and create the database (merging + * iff merge) + */ + +static int +doit(const char *filename, int mergep) +{ + krb5_error_code ret = 0; + krb5_error_code ret2 = 0; + FILE *f; + char *line = NULL; + size_t linesz = 0; + size_t linelen = 0; + char *p; + int lineno; + int flags = O_RDWR; + struct entry e; + hdb_entry ent; + HDB *db = _kadm5_s_get_db(kadm_handle); + + f = fopen(filename, "r"); + if (f == NULL) { + krb5_warn(context, errno, "fopen(%s)", filename); + return 1; + } + /* + * We don't have a version number in the dump, so we don't know which iprop + * log entries to keep, if any. We throw the log away. + * + * We could merge the ipropd-master/slave dump/load here as an option, in + * which case we would first load the dump. + * + * If we're merging, first recover unconfirmed records in the existing log. + */ + if (mergep) + ret = kadm5_log_init(kadm_handle); + if (ret == 0) + ret = kadm5_log_reinit(kadm_handle, 0); + if (ret) { + fclose (f); + krb5_warn(context, ret, "kadm5_log_reinit"); + return 1; + } + + if (!mergep) + flags |= O_CREAT | O_TRUNC; + ret = db->hdb_open(context, db, flags, 0600); + if (ret){ + krb5_warn(context, ret, "hdb_open"); + fclose(f); + return 1; + } + (void) db->hdb_set_sync(context, db, 0); + for (lineno = 1; + (ret2 = my_fgetln(f, &line, &linesz, &linelen)) == 0 && linelen > 0; + ++lineno) { + p = line; + while (isspace((unsigned char)*p)) + p++; + + e.principal = p; + for (p = line; *p; p++){ + if (*p == '\\') /* Support '\n' escapes??? */ + p++; + else if (isspace((unsigned char)*p)) { + *p = 0; + break; + } + } + p = skip_next(p); + + e.key = p; + p = skip_next(p); + + e.created = p; + p = skip_next(p); + + e.modified = p; + p = skip_next(p); + + e.valid_start = p; + p = skip_next(p); + + e.valid_end = p; + p = skip_next(p); + + e.pw_end = p; + p = skip_next(p); + + e.max_life = p; + p = skip_next(p); + + e.max_renew = p; + p = skip_next(p); + + e.flags = p; + p = skip_next(p); + + e.generation = p; + p = skip_next(p); + + e.extensions = p; + skip_next(p); + + memset(&ent, 0, sizeof(ent)); + ret2 = krb5_parse_name(context, e.principal, &ent.principal); + if (ret2) { + const char *msg = krb5_get_error_message(context, ret); + fprintf(stderr, "%s:%d:%s (%s)\n", + filename, lineno, msg, e.principal); + krb5_free_error_message(context, msg); + ret = 1; + continue; + } + + if (parse_keys(&ent, e.key)) { + fprintf (stderr, "%s:%d:error parsing keys (%s)\n", + filename, lineno, e.key); + hdb_free_entry (context, db, &ent); + ret = 1; + continue; + } + + if (parse_event(&ent.created_by, e.created) == -1) { + fprintf (stderr, "%s:%d:error parsing created event (%s)\n", + filename, lineno, e.created); + hdb_free_entry (context, db, &ent); + ret = 1; + continue; + } + if (parse_event_alloc (&ent.modified_by, e.modified) == -1) { + fprintf (stderr, "%s:%d:error parsing event (%s)\n", + filename, lineno, e.modified); + hdb_free_entry (context, db, &ent); + ret = 1; + continue; + } + if (parse_time_string_alloc (&ent.valid_start, e.valid_start) == -1) { + fprintf (stderr, "%s:%d:error parsing time (%s)\n", + filename, lineno, e.valid_start); + hdb_free_entry (context, db, &ent); + ret = 1; + continue; + } + if (parse_time_string_alloc (&ent.valid_end, e.valid_end) == -1) { + fprintf (stderr, "%s:%d:error parsing time (%s)\n", + filename, lineno, e.valid_end); + hdb_free_entry (context, db, &ent); + ret = 1; + continue; + } + if (parse_time_string_alloc (&ent.pw_end, e.pw_end) == -1) { + fprintf (stderr, "%s:%d:error parsing time (%s)\n", + filename, lineno, e.pw_end); + hdb_free_entry (context, db, &ent); + ret = 1; + continue; + } + + if (parse_integer_alloc (&ent.max_life, e.max_life) == -1) { + fprintf (stderr, "%s:%d:error parsing lifetime (%s)\n", + filename, lineno, e.max_life); + hdb_free_entry (context, db, &ent); + ret = 1; + continue; + + } + if (parse_integer_alloc (&ent.max_renew, e.max_renew) == -1) { + fprintf (stderr, "%s:%d:error parsing lifetime (%s)\n", + filename, lineno, e.max_renew); + hdb_free_entry (context, db, &ent); + ret = 1; + continue; + } + + if (parse_hdbflags2int (&ent.flags, e.flags) != 1) { + fprintf (stderr, "%s:%d:error parsing flags (%s)\n", + filename, lineno, e.flags); + hdb_free_entry (context, db, &ent); + ret = 1; + continue; + } + + if(parse_generation(e.generation, &ent.generation) == -1) { + fprintf (stderr, "%s:%d:error parsing generation (%s)\n", + filename, lineno, e.generation); + hdb_free_entry (context, db, &ent); + ret = 1; + continue; + } + + if (parse_extensions(&e.extensions, &ent.extensions) == -1) { + fprintf (stderr, "%s:%d:error parsing extension (%s)\n", + filename, lineno, e.extensions); + hdb_free_entry (context, db, &ent); + ret = 1; + continue; + } + + ret2 = db->hdb_store(context, db, HDB_F_REPLACE, &ent); + hdb_free_entry (context, db, &ent); + if (ret2) { + krb5_warn(context, ret2, "db_store"); + break; + } + } + free(line); + if (ret2) + ret = ret2; + ret2 = db->hdb_set_sync(context, db, 1); + if (ret2) { + krb5_err(context, 1, ret2, "failed to sync the HDB"); + ret = ret2; + } + (void) kadm5_log_end(kadm_handle); + ret2 = db->hdb_close(context, db); + if (ret2) + ret = ret2; + fclose(f); + return ret != 0; +} + + +extern int local_flag; + +static int +loadit(int mergep, const char *name, int argc, char **argv) +{ + if(!local_flag) { + krb5_warnx(context, "%s is only available in local (-l) mode", name); + return 0; + } + + return doit(argv[0], mergep); +} + +int +load(void *opt, int argc, char **argv) +{ + return loadit(0, "load", argc, argv); +} + +int +merge(void *opt, int argc, char **argv) +{ + return loadit(1, "merge", argc, argv); +} |