home | help | back | first | fref | pref | prev | next | nref | lref | last | post |
Date: Sat, 17 May 2014 19:54:29 -0400 From: Greg Hudson <ghudson@mit.edu> Message-Id: <201405172354.s4HNsTSg025030@drugstore.mit.edu> To: cvs-krb5@mit.edu Reply-To: krbdev@mit.edu MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: cvs-krb5-bounces@mit.edu https://github.com/krb5/krb5/commit/99e1d7a4b91676d968ecf9329a48ec2b3c0a193a commit 99e1d7a4b91676d968ecf9329a48ec2b3c0a193a Author: Greg Hudson <ghudson@mit.edu> Date: Mon May 5 00:06:38 2014 -0400 Add cred marshalling functions Add a new file ccmarshal.c containing functions to marshal and unmarshal credentials in file formats version 1-4. These will replace the functions in cc_file.c and cc_keyring.c, and can be used for KCM in the future. src/lib/krb5/ccache/Makefile.in | 3 + src/lib/krb5/ccache/cc-int.h | 16 ++ src/lib/krb5/ccache/ccmarshal.c | 476 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 495 insertions(+), 0 deletions(-) diff --git a/src/lib/krb5/ccache/Makefile.in b/src/lib/krb5/ccache/Makefile.in index ad53e65..19a8dc9 100644 --- a/src/lib/krb5/ccache/Makefile.in +++ b/src/lib/krb5/ccache/Makefile.in @@ -21,6 +21,7 @@ STLIBOBJS= \ cccursor.o \ ccdefault.o \ ccdefops.o \ + ccmarshal.o \ ccselect.o \ ccselect_k5identity.o \ ccselect_realm.o \ @@ -37,6 +38,7 @@ OBJS= $(OUTPRE)ccbase.$(OBJEXT) \ $(OUTPRE)cccursor.$(OBJEXT) \ $(OUTPRE)ccdefault.$(OBJEXT) \ $(OUTPRE)ccdefops.$(OBJEXT) \ + $(OUTPRE)ccmarshal.$(OBJEXT) \ $(OUTPRE)ccselect.$(OBJEXT) \ $(OUTPRE)ccselect_k5identity.$(OBJEXT) \ $(OUTPRE)ccselect_realm.$(OBJEXT) \ @@ -53,6 +55,7 @@ SRCS= $(srcdir)/ccbase.c \ $(srcdir)/cccursor.c \ $(srcdir)/ccdefault.c \ $(srcdir)/ccdefops.c \ + $(srcdir)/ccmarshal.c \ $(srcdir)/ccselect.c \ $(srcdir)/ccselect_k5identity.c \ $(srcdir)/ccselect_realm.c \ diff --git a/src/lib/krb5/ccache/cc-int.h b/src/lib/krb5/ccache/cc-int.h index b125d87..1aa42bb 100644 --- a/src/lib/krb5/ccache/cc-int.h +++ b/src/lib/krb5/ccache/cc-int.h @@ -131,6 +131,22 @@ krb5_error_code ccselect_k5identity_initvt(krb5_context context, int maj_ver, int min_ver, krb5_plugin_vtable vtable); +krb5_error_code +k5_unmarshal_cred(const unsigned char *data, size_t len, int version, + krb5_creds *creds); + +krb5_error_code +k5_unmarshal_princ(const unsigned char *data, size_t len, int version, + krb5_principal *princ_out); + +krb5_error_code +k5_marshal_cred(krb5_creds *creds, int version, unsigned char **bytes_out, + size_t *len_out); + +krb5_error_code +k5_marshal_princ(krb5_principal princ, int version, unsigned char **bytes_out, + size_t *len_out); + /* * Per-type ccache cursor. */ diff --git a/src/lib/krb5/ccache/ccmarshal.c b/src/lib/krb5/ccache/ccmarshal.c new file mode 100644 index 0000000..c27b43f --- /dev/null +++ b/src/lib/krb5/ccache/ccmarshal.c @@ -0,0 +1,476 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/ccache/ccmarshal.c - Functions for serializing creds */ +/* + * Copyright (C) 2014 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDER 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. + */ + +/* + * This file implements marshalling and unmarshalling of krb5 credentials and + * principals in versions 1 through 4 of the FILE ccache format. Version 4 is + * also used for the KEYRING ccache type. + * + * The FILE credential cache format uses fixed 16-bit or 32-bit representations + * of integers. In versions 1 and 2 these are in host byte order; in later + * versions they are in big-endian byte order. Variable-length fields are + * represented with a 32-bit length followed by the field value. There is no + * type tagging; field representations are simply concatenated together. + * + * A psuedo-BNF grammar for the credential and principal formats is: + * + * credential ::= + * client (principal) + * server (principal) + * keyblock (keyblock) + * authtime (32 bits) + * starttime (32 bits) + * endtime (32 bits) + * renew_till (32 bits) + * is_skey (1 byte, 0 or 1) + * ticket_flags (32 bits) + * addresses (addresses) + * authdata (authdata) + * ticket (data) + * second_ticket (data) + * + * principal ::= + * name type (32 bits) [omitted in version 1] + * count of components (32 bits) [includes realm in version 1] + * realm (data) + * component1 (data) + * component2 (data) + * ... + * + * keyblock ::= + * enctype (16 bits) [repeated twice in version 3; see below] + * data + * + * addresses ::= + * count (32 bits) + * address1 + * address2 + * ... + * + * address ::= + * addrtype (16 bits) + * data + * + * authdata ::= + * count (32 bits) + * authdata1 + * authdata2 + * ... + * + * authdata ::= + * ad_type (16 bits) + * data + * + * data ::= + * length (32 bits) + * value (length bytes) + * + * When version 3 was current (before release 1.0), the keyblock had separate + * key type and enctype fields, and both were recorded. At present we record + * the enctype field twice when writing the version 3 format and ignore the + * second value when reading it. + */ + +#include "k5-input.h" +#include "cc-int.h" + +/* Read a 16-bit integer in host byte order for versions 1 and 2, or in + * big-endian byte order for later versions.*/ +static uint16_t +get16(struct k5input *in, int version) +{ + return (version < 3) ? k5_input_get_uint16_n(in) : + k5_input_get_uint16_be(in); +} + +/* Read a 32-bit integer in host byte order for versions 1 and 2, or in + * big-endian byte order for later versions.*/ +static uint32_t +get32(struct k5input *in, int version) +{ + return (version < 3) ? k5_input_get_uint32_n(in) : + k5_input_get_uint32_be(in); +} + +/* Read a 32-bit length and make a copy of that many bytes. Return NULL on + * error. */ +static void * +get_len_bytes(struct k5input *in, int version, unsigned int *len_out) +{ + krb5_error_code ret; + unsigned int len = get32(in, version); + const void *bytes = k5_input_get_bytes(in, len); + void *copy; + + *len_out = 0; + if (bytes == NULL) + return NULL; + copy = k5memdup0(bytes, len, &ret); + if (copy == NULL) { + k5_input_set_status(in, ret); + return NULL; + } + *len_out = len; + return copy; +} + +/* Like get_len_bytes, but put the result in data. */ +static void +get_data(struct k5input *in, int version, krb5_data *data) +{ + unsigned int len; + void *bytes = get_len_bytes(in, version, &len); + + *data = (bytes == NULL) ? empty_data() : make_data(bytes, len); +} + +static krb5_principal +unmarshal_princ(struct k5input *in, int version) +{ + krb5_error_code ret; + krb5_principal princ; + uint32_t i, ncomps; + + princ = k5alloc(sizeof(krb5_principal_data), &ret); + if (princ == NULL) { + k5_input_set_status(in, ret); + return NULL; + } + princ->magic = KV5M_PRINCIPAL; + /* Version 1 does not store the principal name type, and counts the realm + * in the number of components. */ + princ->type = (version == 1) ? KRB5_NT_UNKNOWN : get32(in, version); + ncomps = get32(in, version); + if (version == 1) + ncomps--; + if (ncomps > in->len) { /* Sanity check to avoid large allocations */ + ret = EINVAL; + goto error; + } + if (ncomps != 0) { + princ->data = k5calloc(ncomps, sizeof(krb5_data), &ret); + if (princ->data == NULL) + goto error; + princ->length = ncomps; + } + get_data(in, version, &princ->realm); + for (i = 0; i < ncomps; i++) + get_data(in, version, &princ->data[i]); + return princ; + +error: + k5_input_set_status(in, ret); + krb5_free_principal(NULL, princ); + return NULL; +} + +static void +unmarshal_keyblock(struct k5input *in, int version, krb5_keyblock *kb) +{ + memset(kb, 0, sizeof(*kb)); + kb->magic = KV5M_KEYBLOCK; + kb->enctype = get16(in, version); + /* Version 3 stores the enctype twice. */ + if (version == 3) + (void)get16(in, version); + kb->contents = get_len_bytes(in, version, &kb->length); +} + +static krb5_address * +unmarshal_addr(struct k5input *in, int version) +{ + krb5_address *addr; + + addr = calloc(1, sizeof(*addr)); + if (addr == NULL) { + k5_input_set_status(in, ENOMEM); + return NULL; + } + addr->magic = KV5M_ADDRESS; + addr->addrtype = get16(in, version); + addr->contents = get_len_bytes(in, version, &addr->length); + return addr; +} + +static krb5_address ** +unmarshal_addrs(struct k5input *in, int version) +{ + krb5_address **addrs; + size_t i, count; + + count = get32(in, version); + if (count > in->len) { /* Sanity check to avoid large allocations */ + k5_input_set_status(in, EINVAL); + return NULL; + } + addrs = calloc(count + 1, sizeof(*addrs)); + if (addrs == NULL) { + k5_input_set_status(in, ENOMEM); + return NULL; + } + for (i = 0; i < count; i++) + addrs[i] = unmarshal_addr(in, version); + return addrs; +} + +static krb5_authdata * +unmarshal_authdatum(struct k5input *in, int version) +{ + krb5_authdata *ad; + + ad = calloc(1, sizeof(*ad)); + if (ad == NULL) { + k5_input_set_status(in, ENOMEM); + return NULL; + } + ad->magic = KV5M_ADDRESS; + /* Authdata types can be negative, so sign-extend the get16 result. */ + ad->ad_type = (int16_t)get16(in, version); + ad->contents = get_len_bytes(in, version, &ad->length); + return ad; +} + +static krb5_authdata ** +unmarshal_authdata(struct k5input *in, int version) +{ + krb5_authdata **authdata; + size_t i, count; + + count = get32(in, version); + if (count > in->len) { /* Sanity check to avoid large allocations */ + k5_input_set_status(in, EINVAL); + return NULL; + } + authdata = calloc(count + 1, sizeof(*authdata)); + if (authdata == NULL) { + k5_input_set_status(in, ENOMEM); + return NULL; + } + for (i = 0; i < count; i++) + authdata[i] = unmarshal_authdatum(in, version); + return authdata; +} + +/* Unmarshal a credential using the specified file ccache version (expressed as + * an integer from 1 to 4). Does not check for trailing garbage. */ +krb5_error_code +k5_unmarshal_cred(const unsigned char *data, size_t len, int version, + krb5_creds *creds) +{ + struct k5input in; + + k5_input_init(&in, data, len); + creds->client = unmarshal_princ(&in, version); + creds->server = unmarshal_princ(&in, version); + unmarshal_keyblock(&in, version, &creds->keyblock); + creds->times.authtime = get32(&in, version); + creds->times.starttime = get32(&in, version); + creds->times.endtime = get32(&in, version); + creds->times.renew_till = get32(&in, version); + creds->is_skey = k5_input_get_byte(&in); + creds->ticket_flags = get32(&in, version); + creds->addresses = unmarshal_addrs(&in, version); + creds->authdata = unmarshal_authdata(&in, version); + get_data(&in, version, &creds->ticket); + get_data(&in, version, &creds->second_ticket); + if (in.status) { + krb5_free_cred_contents(NULL, creds); + memset(creds, 0, sizeof(*creds)); + } + return (in.status == EINVAL) ? KRB5_CC_FORMAT : in.status; +} + +/* Unmarshal a principal using the specified file ccache version (expressed as + * an integer from 1 to 4). Does not check for trailing garbage. */ +krb5_error_code +k5_unmarshal_princ(const unsigned char *data, size_t len, int version, + krb5_principal *princ_out) +{ + struct k5input in; + krb5_principal princ; + + *princ_out = NULL; + k5_input_init(&in, data, len); + princ = unmarshal_princ(&in, version); + if (in.status) + krb5_free_principal(NULL, princ); + else + *princ_out = princ; + return (in.status == EINVAL) ? KRB5_CC_FORMAT : in.status; +} + +/* Store a 16-bit integer in host byte order for versions 1 and 2, or in + * big-endian byte order for later versions.*/ +static void +put16(struct k5buf *buf, int version, uint16_t num) +{ + char n[2]; + + if (version < 3) + store_16_n(num, n); + else + store_16_be(num, n); + k5_buf_add_len(buf, n, 2); +} + +/* Store a 32-bit integer in host byte order for versions 1 and 2, or in + * big-endian byte order for later versions.*/ +static void +put32(struct k5buf *buf, int version, uint32_t num) +{ + char n[4]; + + if (version < 3) + store_32_n(num, n); + else + store_32_be(num, n); + k5_buf_add_len(buf, n, 4); +} + +static void +put_len_bytes(struct k5buf *buf, int version, const void *bytes, + unsigned int len) +{ + put32(buf, version, len); + k5_buf_add_len(buf, bytes, len); +} + +static void +put_data(struct k5buf *buf, int version, krb5_data *data) +{ + put_len_bytes(buf, version, data->data, data->length); +} + +static void +marshal_princ(struct k5buf *buf, int version, krb5_principal princ) +{ + int32_t i, ncomps; + + /* Version 1 does not store the principal name type, and counts the realm + * in the number of components. */ + if (version != 1) + put32(buf, version, princ->type); + ncomps = princ->length + ((version == 1) ? 1 : 0); + put32(buf, version, ncomps); + put_data(buf, version, &princ->realm); + for (i = 0; i < princ->length; i++) + put_data(buf, version, &princ->data[i]); +} + +static void +marshal_keyblock(struct k5buf *buf, int version, krb5_keyblock *kb) +{ + put16(buf, version, kb->enctype); + /* Version 3 stores the enctype twice. */ + if (version == 3) + put16(buf, version, kb->enctype); + put_len_bytes(buf, version, kb->contents, kb->length); +} + +static void +marshal_addrs(struct k5buf *buf, int version, krb5_address **addrs) +{ + size_t i, count; + + for (count = 0; addrs != NULL && addrs[count] != NULL; count++); + put32(buf, version, count); + for (i = 0; i < count; i++) { + put16(buf, version, addrs[i]->addrtype); + put_len_bytes(buf, version, addrs[i]->contents, addrs[i]->length); + } +} + +static void +marshal_authdata(struct k5buf *buf, int version, krb5_authdata **authdata) +{ + size_t i, count; + + for (count = 0; authdata != NULL && authdata[count] != NULL; count++); + put32(buf, version, count); + for (i = 0; i < count; i++) { + put16(buf, version, authdata[i]->ad_type); + put_len_bytes(buf, version, authdata[i]->contents, + authdata[i]->length); + } +} + +/* Marshal a credential using the specified file ccache version (expressed as + * an integer from 1 to 4). */ +krb5_error_code +k5_marshal_cred(krb5_creds *creds, int version, unsigned char **bytes_out, + size_t *len_out) +{ + struct k5buf buf; + char is_skey; + + *bytes_out = NULL; + *len_out = 0; + k5_buf_init_dynamic(&buf); + marshal_princ(&buf, version, creds->client); + marshal_princ(&buf, version, creds->server); + marshal_keyblock(&buf, version, &creds->keyblock); + put32(&buf, version, creds->times.authtime); + put32(&buf, version, creds->times.starttime); + put32(&buf, version, creds->times.endtime); + put32(&buf, version, creds->times.renew_till); + is_skey = creds->is_skey; + k5_buf_add_len(&buf, &is_skey, 1); + put32(&buf, version, creds->ticket_flags); + marshal_addrs(&buf, version, creds->addresses); + marshal_authdata(&buf, version, creds->authdata); + put_data(&buf, version, &creds->ticket); + put_data(&buf, version, &creds->second_ticket); + if (k5_buf_data(&buf) == NULL) + return ENOMEM; + *bytes_out = (unsigned char *)k5_buf_data(&buf); + *len_out = k5_buf_len(&buf); + return 0; +} + +/* Marshal a principal using the specified file ccache version (expressed as an + * integer from 1 to 4). */ +krb5_error_code +k5_marshal_princ(krb5_principal princ, int version, unsigned char **bytes_out, + size_t *len_out) +{ + struct k5buf buf; + + *bytes_out = NULL; + *len_out = 0; + k5_buf_init_dynamic(&buf); + marshal_princ(&buf, version, princ); + if (k5_buf_data(&buf) == NULL) + return ENOMEM; + *bytes_out = (unsigned char *)k5_buf_data(&buf); + *len_out = k5_buf_len(&buf); + return 0; +} _______________________________________________ cvs-krb5 mailing list cvs-krb5@mit.edu https://mailman.mit.edu/mailman/listinfo/cvs-krb5
home | help | back | first | fref | pref | prev | next | nref | lref | last | post |