krb5 commit: Add cred marshalling functions
Greg Hudson
ghudson at MIT.EDU
Sat May 17 19:54:29 EDT 2014
https://github.com/krb5/krb5/commit/99e1d7a4b91676d968ecf9329a48ec2b3c0a193a
commit 99e1d7a4b91676d968ecf9329a48ec2b3c0a193a
Author: Greg Hudson <ghudson at 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;
+}
More information about the cvs-krb5
mailing list