Files
heimdal/lib/kadm5/marshall.c
2022-12-22 18:14:07 -06:00

963 lines
28 KiB
C

/*
* 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, &params->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(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(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(*p))
p++;
if (*p == '#') {
p = nxt;
continue;
}
if (*p == '\0')
continue;
typ = p;
val = strpbrk(p, " \t");
if (val) {
*(val++) = '\0';
while (isspace(*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