diff options
Diffstat (limited to 'third_party/heimdal/lib/kadm5/marshall.c')
-rw-r--r-- | third_party/heimdal/lib/kadm5/marshall.c | 962 |
1 files changed, 962 insertions, 0 deletions
diff --git a/third_party/heimdal/lib/kadm5/marshall.c b/third_party/heimdal/lib/kadm5/marshall.c new file mode 100644 index 0000000..849698f --- /dev/null +++ b/third_party/heimdal/lib/kadm5/marshall.c @@ -0,0 +1,962 @@ +/* + * 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 <getarg.h> +#include <krb5-protos.h> +#include <hex.h> + +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 |