/* * Copyright (c) 1997 - 1999 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 "kadm5_locl.h" #define CHECK(e) do { if ((ret = e)) goto out; } while (0) int kadm5_some_keys_are_bogus(size_t n_keys, krb5_key_data *keys) { size_t i; for (i = 0; i < n_keys; i++) { krb5_key_data *key = &keys[i]; if (key->key_data_length[0] == sizeof(KADM5_BOGUS_KEY_DATA) - 1 && ct_memcmp(key->key_data_contents[1], KADM5_BOGUS_KEY_DATA, key->key_data_length[0]) == 0) return 1; } return 0; } int kadm5_all_keys_are_bogus(size_t n_keys, krb5_key_data *keys) { size_t i; if (n_keys == 0) return 0; for (i = 0; i < n_keys; i++) { krb5_key_data *key = &keys[i]; if (key->key_data_length[0] != sizeof(KADM5_BOGUS_KEY_DATA) - 1 || ct_memcmp(key->key_data_contents[1], KADM5_BOGUS_KEY_DATA, key->key_data_length[0]) != 0) return 0; } return 1; } kadm5_ret_t kadm5_store_key_data(krb5_storage *sp, krb5_key_data *key) { kadm5_ret_t ret; krb5_data c; CHECK(krb5_store_int32(sp, key->key_data_ver)); CHECK(krb5_store_int32(sp, key->key_data_kvno)); CHECK(krb5_store_int32(sp, key->key_data_type[0])); c.length = key->key_data_length[0]; c.data = key->key_data_contents[0]; CHECK(krb5_store_data(sp, c)); CHECK(krb5_store_int32(sp, key->key_data_type[1])); c.length = key->key_data_length[1]; c.data = key->key_data_contents[1]; CHECK(krb5_store_data(sp, c)); out: return ret; } kadm5_ret_t kadm5_store_fake_key_data(krb5_storage *sp, krb5_key_data *key) { kadm5_ret_t ret; krb5_data c; CHECK(krb5_store_int32(sp, key->key_data_ver)); CHECK(krb5_store_int32(sp, key->key_data_kvno)); CHECK(krb5_store_int32(sp, key->key_data_type[0])); /* * This is the key contents. We want it to be obvious to the client * (if it really did want the keys) that the key won't work. * 32-bit keys are no good for any enctype, so that should do. * Clients that didn't need keys will ignore this, and clients that * did want keys will either fail or they'll, say, create bogus * keytab entries that will subsequently fail to be useful. */ c.length = sizeof (KADM5_BOGUS_KEY_DATA) - 1; c.data = KADM5_BOGUS_KEY_DATA; CHECK(krb5_store_data(sp, c)); /* This is the salt -- no need to send garbage */ CHECK(krb5_store_int32(sp, key->key_data_type[1])); c.length = key->key_data_length[1]; c.data = key->key_data_contents[1]; CHECK(krb5_store_data(sp, c)); out: return ret; } kadm5_ret_t kadm5_ret_key_data(krb5_storage *sp, krb5_key_data *key) { kadm5_ret_t ret; krb5_data c; int32_t tmp; ret = krb5_ret_int32(sp, &tmp); if (ret == 0) { key->key_data_ver = tmp; ret = krb5_ret_int32(sp, &tmp); } if (ret == 0) { key->key_data_kvno = tmp; ret = krb5_ret_int32(sp, &tmp); } if (ret == 0) { key->key_data_type[0] = tmp; ret = krb5_ret_data(sp, &c); } if (ret == 0) { key->key_data_length[0] = c.length; key->key_data_contents[0] = c.data; ret = krb5_ret_int32(sp, &tmp); } if (ret == 0) { key->key_data_type[1] = tmp; ret = krb5_ret_data(sp, &c); } if (ret == 0) { key->key_data_length[1] = c.length; key->key_data_contents[1] = c.data; return 0; } return KADM5_FAILURE; } kadm5_ret_t kadm5_store_tl_data(krb5_storage *sp, krb5_tl_data *tl) { kadm5_ret_t ret; krb5_data c; CHECK(krb5_store_int32(sp, tl->tl_data_type)); c.length = tl->tl_data_length; c.data = tl->tl_data_contents; CHECK(krb5_store_data(sp, c)); out: return ret; } kadm5_ret_t kadm5_ret_tl_data(krb5_storage *sp, krb5_tl_data *tl) { kadm5_ret_t ret; krb5_data c; int32_t tmp; CHECK(krb5_ret_int32(sp, &tmp)); tl->tl_data_type = tmp; CHECK(krb5_ret_data(sp, &c)); tl->tl_data_length = c.length; tl->tl_data_contents = c.data; out: return ret; } static kadm5_ret_t store_principal_ent(krb5_storage *sp, kadm5_principal_ent_t princ, uint32_t mask, int wkeys) { kadm5_ret_t ret = 0; int i; if (mask & KADM5_PRINCIPAL) CHECK(krb5_store_principal(sp, princ->principal)); if (mask & KADM5_PRINC_EXPIRE_TIME) CHECK(krb5_store_int32(sp, princ->princ_expire_time)); if (mask & KADM5_PW_EXPIRATION) CHECK(krb5_store_int32(sp, princ->pw_expiration)); if (mask & KADM5_LAST_PWD_CHANGE) CHECK(krb5_store_int32(sp, princ->last_pwd_change)); if (mask & KADM5_MAX_LIFE) CHECK(krb5_store_int32(sp, princ->max_life)); if (mask & KADM5_MOD_NAME) { CHECK(krb5_store_int32(sp, princ->mod_name != NULL)); if(princ->mod_name) CHECK(krb5_store_principal(sp, princ->mod_name)); } if (mask & KADM5_MOD_TIME) CHECK(krb5_store_int32(sp, princ->mod_date)); if (mask & KADM5_ATTRIBUTES) CHECK(krb5_store_int32(sp, princ->attributes)); if (mask & KADM5_KVNO) CHECK(krb5_store_int32(sp, princ->kvno)); if (mask & KADM5_MKVNO) CHECK(krb5_store_int32(sp, princ->mkvno)); if (mask & KADM5_POLICY) { CHECK(krb5_store_int32(sp, princ->policy != NULL)); if(princ->policy) CHECK(krb5_store_string(sp, princ->policy)); } if (mask & KADM5_AUX_ATTRIBUTES) CHECK(krb5_store_int32(sp, princ->aux_attributes)); if (mask & KADM5_MAX_RLIFE) CHECK(krb5_store_int32(sp, princ->max_renewable_life)); if (mask & KADM5_LAST_SUCCESS) CHECK(krb5_store_int32(sp, princ->last_success)); if (mask & KADM5_LAST_FAILED) CHECK(krb5_store_int32(sp, princ->last_failed)); if (mask & KADM5_FAIL_AUTH_COUNT) CHECK(krb5_store_int32(sp, princ->fail_auth_count)); if (mask & KADM5_KEY_DATA) { CHECK(krb5_store_int32(sp, princ->n_key_data)); for(i = 0; i < princ->n_key_data; i++) { if (wkeys) CHECK(kadm5_store_key_data(sp, &princ->key_data[i])); else CHECK(kadm5_store_fake_key_data(sp, &princ->key_data[i])); } } if (mask & KADM5_TL_DATA) { krb5_tl_data *tp; CHECK(krb5_store_int32(sp, princ->n_tl_data)); for (tp = princ->tl_data; tp; tp = tp->tl_data_next) CHECK(kadm5_store_tl_data(sp, tp)); } out: return ret; } kadm5_ret_t kadm5_store_principal_ent(krb5_storage *sp, kadm5_principal_ent_t princ) { return store_principal_ent (sp, princ, ~0, 1); } kadm5_ret_t kadm5_store_principal_ent_nokeys(krb5_storage *sp, kadm5_principal_ent_t princ) { return store_principal_ent (sp, princ, ~0, 0); } kadm5_ret_t kadm5_store_principal_ent_mask(krb5_storage *sp, kadm5_principal_ent_t princ, uint32_t mask) { kadm5_ret_t ret; ret = krb5_store_int32(sp, mask); if (ret == 0) ret = store_principal_ent(sp, princ, mask, 1); return ret; } static kadm5_ret_t ret_principal_ent(krb5_storage *sp, kadm5_principal_ent_t princ, uint32_t mask) { kadm5_ret_t ret = 0; int i; int32_t tmp; if (mask & KADM5_PRINCIPAL) CHECK(krb5_ret_principal(sp, &princ->principal)); if (mask & KADM5_PRINC_EXPIRE_TIME) { CHECK(krb5_ret_int32(sp, &tmp)); princ->princ_expire_time = tmp; } if (mask & KADM5_PW_EXPIRATION) { CHECK(krb5_ret_int32(sp, &tmp)); princ->pw_expiration = tmp; } if (mask & KADM5_LAST_PWD_CHANGE) { CHECK(krb5_ret_int32(sp, &tmp)); princ->last_pwd_change = tmp; } if (mask & KADM5_MAX_LIFE) { CHECK(krb5_ret_int32(sp, &tmp)); princ->max_life = tmp; } if (mask & KADM5_MOD_NAME) { CHECK(krb5_ret_int32(sp, &tmp)); if(tmp) CHECK(krb5_ret_principal(sp, &princ->mod_name)); else princ->mod_name = NULL; } if (mask & KADM5_MOD_TIME) { CHECK(krb5_ret_int32(sp, &tmp)); princ->mod_date = tmp; } if (mask & KADM5_ATTRIBUTES) { CHECK(krb5_ret_int32(sp, &tmp)); princ->attributes = tmp; } if (mask & KADM5_KVNO) { CHECK(krb5_ret_int32(sp, &tmp)); princ->kvno = tmp; } if (mask & KADM5_MKVNO) { CHECK(krb5_ret_int32(sp, &tmp)); princ->mkvno = tmp; } if (mask & KADM5_POLICY) { CHECK(krb5_ret_int32(sp, &tmp)); if(tmp) CHECK(krb5_ret_string(sp, &princ->policy)); else princ->policy = NULL; } if (mask & KADM5_AUX_ATTRIBUTES) { CHECK(krb5_ret_int32(sp, &tmp)); princ->aux_attributes = tmp; } if (mask & KADM5_MAX_RLIFE) { CHECK(krb5_ret_int32(sp, &tmp)); princ->max_renewable_life = tmp; } if (mask & KADM5_LAST_SUCCESS) { CHECK(krb5_ret_int32(sp, &tmp)); princ->last_success = tmp; } if (mask & KADM5_LAST_FAILED) { CHECK(krb5_ret_int32(sp, &tmp)); princ->last_failed = tmp; } if (mask & KADM5_FAIL_AUTH_COUNT) { CHECK(krb5_ret_int32(sp, &tmp)); princ->fail_auth_count = tmp; } if (mask & KADM5_KEY_DATA) { CHECK(krb5_ret_int32(sp, &tmp)); princ->n_key_data = tmp; princ->key_data = calloc(princ->n_key_data, sizeof(*princ->key_data)); if (princ->key_data == NULL && princ->n_key_data != 0) return ENOMEM; for(i = 0; i < princ->n_key_data; i++) CHECK(kadm5_ret_key_data(sp, &princ->key_data[i])); } if (mask & KADM5_TL_DATA) { CHECK(krb5_ret_int32(sp, &tmp)); princ->n_tl_data = tmp; princ->tl_data = NULL; for(i = 0; i < princ->n_tl_data; i++){ krb5_tl_data *tp = malloc(sizeof(*tp)); if (tp == NULL) { ret = ENOMEM; goto out; } ret = kadm5_ret_tl_data(sp, tp); if (ret == 0) { tp->tl_data_next = princ->tl_data; princ->tl_data = tp; } else { free(tp); goto out; } } } out: /* Can't free princ here -- we don't have a context */ return ret; } kadm5_ret_t kadm5_ret_principal_ent(krb5_storage *sp, kadm5_principal_ent_t princ) { return ret_principal_ent (sp, princ, ~0); } kadm5_ret_t kadm5_ret_principal_ent_mask(krb5_storage *sp, kadm5_principal_ent_t princ, uint32_t *mask) { kadm5_ret_t ret; int32_t tmp; ret = krb5_ret_int32 (sp, &tmp); if (ret) { *mask = 0; return ret; } *mask = tmp; return ret_principal_ent (sp, princ, *mask); } kadm5_ret_t _kadm5_marshal_params(krb5_context context, kadm5_config_params *params, krb5_data *out) { kadm5_ret_t ret; krb5_storage *sp = krb5_storage_emem(); if (sp == NULL) return krb5_enomem(context); ret = krb5_store_int32(sp, params->mask & (KADM5_CONFIG_REALM)); if (ret == 0 && (params->mask & KADM5_CONFIG_REALM)) ret = krb5_store_string(sp, params->realm); if (ret == 0) ret = krb5_storage_to_data(sp, out); krb5_storage_free(sp); return ret; } kadm5_ret_t _kadm5_unmarshal_params(krb5_context context, krb5_data *in, kadm5_config_params *params) { kadm5_ret_t ret; krb5_storage *sp; int32_t mask; sp = krb5_storage_from_data(in); if (sp == NULL) return ENOMEM; ret = krb5_ret_int32(sp, &mask); if (ret) goto out; params->mask = mask; if(params->mask & KADM5_CONFIG_REALM) ret = krb5_ret_string(sp, ¶ms->realm); out: krb5_storage_free(sp); return ret; } #ifdef TEST #include #include #include static int version_flag; static int help_flag; static int verbose_flag; static int in_text_flag = 0; static int in_binary_flag = 0; static int out_hex_flag = 0; static int out_binary_flag = 0; static int must_round_trip_flag = 0; static char *byteorder_string_in_string; static char *byteorder_string_out_string; static struct getargs args[] = { { "version", '\0', arg_flag, &version_flag, "Version", NULL }, { "help", '\0', arg_flag, &help_flag, "Show this message", NULL }, { "verbose", 'v', arg_flag, &verbose_flag, NULL, NULL }, { "in-text", '\0', arg_flag, &in_text_flag, "Input is a text \"recipe\"", NULL }, { "in-binary", '\0', arg_flag, &in_binary_flag, "Input is binary", NULL }, { "out-hex", '\0', arg_flag, &out_hex_flag, "Output hex", NULL }, { "out-binary", '\0', arg_flag, &out_binary_flag, "Output binary", NULL }, { "must-round-trip", '\0', arg_flag, &must_round_trip_flag, "Check that encoding and decoding round-trip", NULL }, { "byte-order-out", '\0', arg_string, &byteorder_string_out_string, "Output byte order", "host, network, be, or le" }, { "byte-order-in", '\0', arg_string, &byteorder_string_in_string, "Input byte order", "host, network, packed, be, or le" }, }; #define DO_TYPE1(t, r, s) \ if (strcmp(type, #t) == 0) { \ t v; \ ret = r(in, &v); \ if (ret == 0) \ ret = s(out, v); \ return ret; \ } #define DO_TYPE2(t, r, s) \ if (strcmp(type, #t) == 0) { \ t v; \ ret = r(in, &v); \ if (ret == 0) \ ret = s(out, &v); \ return ret; \ } static krb5_error_code reencode(const char *type, krb5_storage *in, krb5_storage *out) { krb5_error_code ret; krb5_storage_seek(in, 0, SEEK_SET); /* * TODO: When --verbose print a visual representation of the value. * * We have functionality in lib/krb5 for that for krb5_principal and * krb5_address, but not any of the others. Adding krb5_print_*() * and kadm5_print_*() functions just for this program to use seems * annoying. */ DO_TYPE1(krb5_keyblock, krb5_ret_keyblock, krb5_store_keyblock); DO_TYPE1(krb5_principal, krb5_ret_principal, krb5_store_principal); DO_TYPE1(krb5_times, krb5_ret_times, krb5_store_times); DO_TYPE1(krb5_address, krb5_ret_address, krb5_store_address); DO_TYPE1(krb5_addresses, krb5_ret_addrs, krb5_store_addrs); DO_TYPE1(krb5_authdata, krb5_ret_authdata, krb5_store_authdata); DO_TYPE2(krb5_creds, krb5_ret_creds, krb5_store_creds); DO_TYPE2(krb5_key_data, kadm5_ret_key_data, kadm5_store_key_data); DO_TYPE2(krb5_tl_data, kadm5_ret_tl_data, kadm5_store_tl_data); DO_TYPE2(kadm5_principal_ent_rec, kadm5_ret_principal_ent, kadm5_store_principal_ent); return ENOTSUP; } static krb5_error_code eval_recipe1(krb5_storage *sp, const char *typ, const char *val) { krb5_error_code ret; uint64_t vu = 0; int64_t vi = 0; int consumed = 0; if (strncmp(typ, "int", sizeof("int") - 1) == 0) { if (sscanf(val, "%"PRIi64"%n", &vi, &consumed) != 1) return EINVAL; if (consumed < 1) return EINVAL; while (isspace((unsigned char)val[consumed])) consumed++; if (val[consumed] != '\0') return EINVAL; } else if (strncmp(typ, "uint", sizeof("uint") - 1) == 0) { /* There's no equally-useful equivalent of %i for unsigned */ if (val[0] == '0') { if (val[1] == 'x') { if (sscanf(val, "%"PRIx64"%n", &vu, &consumed) != 1) return EINVAL; } else { if (sscanf(val, "%"PRIo64"%n", &vu, &consumed) != 1) return EINVAL; } } else { if (sscanf(val, "%"PRIu64"%n", &vu, &consumed) != 1) return EINVAL; } if (consumed < 1) return EINVAL; while (isspace((unsigned char)val[consumed])) consumed++; if (val[consumed] != '\0') return EINVAL; vi = (int64_t)vu; } #define DO_INTn(n) \ if (strcmp(typ, "int" #n) == 0) { \ if (n < 64 && vi < INT ## n ## _MIN) \ return EOVERFLOW; \ if (n < 64 && vi > INT ## n ## _MAX) \ return EOVERFLOW; \ return krb5_store_int ## n (sp, vi); \ } DO_INTn(8); DO_INTn(16); DO_INTn(32); DO_INTn(64); #define DO_UINTn(n) \ if (strcmp(typ, "uint" #n) == 0) { \ if (n < 64 && vu > INT ## n ## _MAX) \ return EOVERFLOW; \ return krb5_store_int ## n (sp, vi); \ } DO_UINTn(8); DO_UINTn(16); DO_UINTn(32); DO_UINTn(64); if (strcmp(typ, "string") == 0) return krb5_store_string(sp, val); if (strcmp(typ, "stringz") == 0) return krb5_store_stringz(sp, val); if (strcmp(typ, "stringnl") == 0) return krb5_store_stringnl(sp, val); if (strcmp(typ, "data") == 0) { ssize_t dsz = strlen(val); krb5_data d; /* * 'data' as in 'krb5_data'. * * krb5_store_data() stores the length then the data. */ if (krb5_data_alloc(&d, dsz)) return ENOMEM; dsz = hex_decode(val, d.data, d.length); if (dsz < 0) return EINVAL; d.length = dsz; ret = krb5_store_data(sp, d); krb5_data_free(&d); return ret; } if (strcmp(typ, "rawdata") == 0) { ssize_t dsz = strlen(val); void *d; /* Store the data w/o a length prefix */ d = malloc(dsz); if (d == NULL) return ENOMEM; dsz = hex_decode(val, d, dsz); if (dsz < 0) return EINVAL; ret = krb5_store_datalen(sp, d, dsz); free(d); return ret; } return ENOTSUP; } static krb5_storage * eval_recipe(char *r, int spflags) { krb5_error_code ret; krb5_storage *sp; unsigned int lineno = 0; char *nxt = NULL; char *p; sp = krb5_storage_emem(); if (sp == NULL) errx(1, "Out of memory"); krb5_storage_set_flags(sp, spflags); for (p = r; p && *p; p = nxt) { char *typ; char *val; lineno++; /* Terminate p at \n */ nxt = p; do { nxt = strpbrk(nxt, "\r\n"); if (nxt && *nxt == '\r') { if (*(++nxt) != '\n') continue; } if (nxt && *nxt == '\n') { *(nxt++) = '\0'; break; } } while (nxt); while (isspace((unsigned char)*p)) p++; if (*p == '#') { p = nxt; continue; } if (*p == '\0') continue; typ = p; val = strpbrk(p, " \t"); if (val) { *(val++) = '\0'; while (isspace((unsigned char)*val)) val++; } ret = eval_recipe1(sp, typ, val); if (ret) krb5_err(NULL, 1, ret, "Error at line %u", lineno); } return sp; } static void usage(int code) { if (code) dup2(STDERR_FILENO, STDOUT_FILENO); arg_printusage(args, sizeof(args) / sizeof(args[0]), "test_marshall", "Usage: test_marshal [options] TYPE-NAME INPUT-FILE " "[OUTPUT-FILE]\n" "\tText inputs must be of the form:\n\n" "\t\tsimpletype literalvalue\n\n" "\twhere {simpletype} is one of:\n\n" "\t\tint8\n" "\t\tint16\n" "\t\tint32\n" "\t\tint64\n" "\t\tuint8\n" "\t\tuint16\n" "\t\tuint32\n" "\t\tuint64\n" "\t\tstring\n" "\t\tstringz\n" "\t\tstringnl\n" "\t\tdata\n" "\t\trawdata\n\n" "\tand {literalvalue} is as appropriate for the {simpletype}:\n\n" "\t - For int types the value can be decimal, octal, or hexadecimal.\n" "\t - For string types the string ends at the end of the line.\n" "\t - For {data} the value is hex and will be encoded as a 32-bit\n" "\t length then the raw binary data.\n" "\t - For {rawdata} the value is hex and will be encoded as just the\n" "\t raw binary data.\n\n" "\tThe {TYPE} must be one of: krb5_keyblock, krb5_principal,\n" "\tkrb5_times, krb5_address, krb5_addresses, krb5_authdata,\n" "\tkrb5_creds, krb5_key_data, krb5_tl_data, or\n" "\tkadm5_principal_ent_rec.\n\n" "Options:\n"); exit(code); } static krb5_flags byteorder_flags(const char *s) { if (s == NULL) return KRB5_STORAGE_BYTEORDER_BE; if (strcasecmp(s, "packed") == 0) return KRB5_STORAGE_BYTEORDER_PACKED; if (strcasecmp(s, "host") == 0) return KRB5_STORAGE_BYTEORDER_HOST; if (strcasecmp(s, "network") == 0) return KRB5_STORAGE_BYTEORDER_BE; if (strcasecmp(s, "be") == 0) return KRB5_STORAGE_BYTEORDER_BE; if (strcasecmp(s, "le") == 0) return KRB5_STORAGE_BYTEORDER_LE; return 0; } /* * This program is intended to make fuzzing of krb5_ret_*() and kadm5_ret_*() * possible. * * Inputs are either binary encodings or simplistic textual representations of * XDR-ish data structures normally coded with {kadm5,krb5}_{ret,store}_*() * functions. * * A textual representation of these structures looks like: * * type value * .. * * where type is one of char, int32, etc., and where value is an appropriate * literal for type. */ int main(int argc, char **argv) { krb5_error_code ret = 0; krb5_storage *insp = NULL; krb5_storage *insp2 = NULL; krb5_storage *outsp = NULL; krb5_flags spflags_in = 0; krb5_flags spflags_out = 0; krb5_data i, i2, o; size_t insz = 0; char *hexout = NULL; char *hexin = NULL; char *intxt = NULL; void *inbin = NULL; int optidx = 0; if (getarg(args, sizeof(args)/sizeof(args[0]), argc, argv, &optidx)) usage(1); if (help_flag) usage(0); argc -= optidx; argv += optidx; if (argc < 1) errx(1, "Missing type name argument"); if (argc < 2) errx(1, "Missing input file argument"); if (argc > 3) errx(1, "Too many arguments"); if ((in_text_flag && in_binary_flag) || (!in_text_flag && !in_binary_flag)) errx(1, "One and only one of --in-text and --in-binary must be given"); if (out_hex_flag && out_binary_flag) errx(1, "At most one of --out-text and --out-binary must be given"); if (!out_hex_flag && !out_binary_flag) { if (isatty(STDOUT_FILENO)) { warnx("Will output hex because stdout is a terminal"); out_hex_flag = 1; } else { warnx("Will output binary"); out_binary_flag = 1; } } spflags_in |= byteorder_flags(byteorder_string_in_string); spflags_out |= byteorder_flags(byteorder_string_out_string); /* Read the input */ if (in_text_flag) errno = rk_undumptext(argv[1], &intxt, NULL); else errno = rk_undumpdata(argv[1], &inbin, &insz); if (errno) err(1, "Could not read %s", argv[1]); /* If the input is a recipe, evaluate it */ if (intxt) insp = eval_recipe(intxt, spflags_in); else insp = krb5_storage_from_mem(inbin, insz); if (insp == NULL) errx(1, "Out of memory"); krb5_storage_set_flags(insp, spflags_in); ret = krb5_storage_to_data(insp, &i); if (ret) krb5_err(NULL, 1, ret, "Could not check round-tripping"); if (out_hex_flag) { char *hexstr = NULL; if (hex_encode(i.data, i.length, &hexstr) == -1) err(1, "Could not hex-encode output"); if (argv[2]) { FILE *f; f = fopen(argv[2], "w"); if (f == NULL) err(1, "Could not open %s for writing", argv[2]); if (fprintf(f, "%s\n", hexstr) < 0 || fclose(f)) err(1, "Could write to %s", argv[2]); } else { if (printf("%s\n", hexstr) < 0) err(1, "Could not write to stdout"); } free(hexstr); } else { if (argv[2]) { rk_dumpdata(argv[2], i.data, i.length); } else { if (fwrite(i.data, i.length, 1, stdout) != 1 || fflush(stdout) != 0) err(1, "Could not output encoding"); } } outsp = krb5_storage_emem(); if (outsp == NULL) errx(1, "Out of memory"); krb5_storage_set_flags(outsp, spflags_out); ret = reencode(argv[0], insp, outsp); if (ret) krb5_err(NULL, 1, ret, "Could not decode and re-encode"); if (i.length == o.length && memcmp(i.data, o.data, i.length) == 0) { if (verbose_flag) fprintf(stderr, "Encoding round-trips!\n"); goto out; } ret = krb5_storage_to_data(outsp, &o); if (ret) krb5_err(NULL, 1, ret, "Out of memory"); /* * The encoding did not round trip. Sadly kadm5_ret_principal_ent() * reverses the TL data list. So try to re-encode one more time. */ if (strcmp(argv[0], "kadm5_principal_ent_rec") == 0) { insp2 = krb5_storage_emem(); if (insp2 == NULL) errx(1, "Out of memory"); krb5_storage_set_flags(insp2, spflags_in); ret = reencode(argv[0], outsp, insp2); if (ret == 0) ret = krb5_storage_to_data(insp2, &i2); if (ret) krb5_err(NULL, 1, ret, "Could not decode and re-encode"); if (i.length == i2.length && memcmp(i.data, i2.data, i.length) == 0) { if (verbose_flag) fprintf(stderr, "Encoding round-trips!\n"); goto out; } } if (hex_encode(i.data, i.length, &hexin) < 0) errx(1, "Out of memory"); if (hex_encode(o.data, o.length, &hexout) < 0) errx(1, "Out of memory"); if (must_round_trip_flag) { errx(1, "Encoding does not round-trip\n(in: %s)\n(out: %s)", hexin, hexout); } else { warnx("Encoding does not round-trip\n(in: %s)\n(out: %s)", hexin, hexout); } out: free(hexin); free(hexout); krb5_data_free(&o); krb5_data_free(&i); krb5_data_free(&i2); krb5_storage_free(insp); krb5_storage_free(outsp); krb5_storage_free(insp2); return ret; } #endif