
Excluded: libtomath and libedit files, most of which appear to be testing or example code not involved in production, and which are derived from an upstream that should perhaps have patches submitted upstream instead. fix https://github.com/heimdal/heimdal/issues/1111
963 lines
28 KiB
C
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, ¶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
|