2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * src/lib/krb5/asn.1/asn1_encode.c
2N/A *
2N/A * Copyright 1994, 2008 by the Massachusetts Institute of Technology.
2N/A * All Rights Reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A */
2N/A
2N/A/* ASN.1 primitive encoders */
2N/A
2N/A#include "asn1_encode.h"
2N/A#include "asn1_make.h"
2N/A
2N/Aasn1_error_code
2N/Aasn1_encode_boolean(asn1buf *buf, asn1_intmax val, unsigned int *retlen)
2N/A{
2N/A asn1_error_code retval;
2N/A unsigned int length = 0;
2N/A unsigned int partlen = 1;
2N/A asn1_octet bval;
2N/A
2N/A bval = val ? 0xFF : 0x00;
2N/A
2N/A retval = asn1buf_insert_octet(buf, bval);
2N/A if (retval) return retval;
2N/A
2N/A length = partlen;
2N/A retval = asn1_make_tag(buf, UNIVERSAL, PRIMITIVE, ASN1_BOOLEAN, length, &partlen);
2N/A if (retval) return retval;
2N/A length += partlen;
2N/A
2N/A *retlen = length;
2N/A return 0;
2N/A}
2N/A
2N/Astatic asn1_error_code
2N/Aasn1_encode_integer_internal(asn1buf *buf, asn1_intmax val,
2N/A unsigned int *retlen)
2N/A{
2N/A asn1_error_code retval;
2N/A unsigned int length = 0;
2N/A long valcopy;
2N/A int digit;
2N/A
2N/A valcopy = val;
2N/A do {
2N/A digit = (int) (valcopy&0xFF);
2N/A retval = asn1buf_insert_octet(buf,(asn1_octet) digit);
2N/A if (retval) return retval;
2N/A length++;
2N/A valcopy = valcopy >> 8;
2N/A } while (valcopy != 0 && valcopy != ~0);
2N/A
2N/A if ((val > 0) && ((digit&0x80) == 0x80)) { /* make sure the high bit is */
2N/A retval = asn1buf_insert_octet(buf,0); /* of the proper signed-ness */
2N/A if (retval) return retval;
2N/A length++;
2N/A } else if ((val < 0) && ((digit&0x80) != 0x80)) {
2N/A retval = asn1buf_insert_octet(buf,0xFF);
2N/A if (retval) return retval;
2N/A length++;
2N/A }
2N/A
2N/A
2N/A *retlen = length;
2N/A return 0;
2N/A}
2N/A
2N/Aasn1_error_code
2N/Aasn1_encode_integer(asn1buf * buf, asn1_intmax val, unsigned int *retlen)
2N/A{
2N/A asn1_error_code retval;
2N/A unsigned int length = 0;
2N/A unsigned int partlen;
2N/A retval = asn1_encode_integer_internal(buf, val, &partlen);
2N/A if (retval) return retval;
2N/A
2N/A length = partlen;
2N/A retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_INTEGER,length, &partlen);
2N/A if (retval) return retval;
2N/A length += partlen;
2N/A
2N/A *retlen = length;
2N/A return 0;
2N/A}
2N/A
2N/A#if 0
2N/Aasn1_error_code
2N/Aasn1_encode_enumerated(asn1buf * buf, long val,
2N/A unsigned int *retlen)
2N/A{
2N/A asn1_error_code retval;
2N/A unsigned int length = 0;
2N/A unsigned int partlen;
2N/A retval = asn1_encode_integer_internal(buf, val, &partlen);
2N/A if (retval) return retval;
2N/A
2N/A length = partlen;
2N/A retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_ENUMERATED,length, &partlen);
2N/A if (retval) return retval;
2N/A length += partlen;
2N/A
2N/A *retlen = length;
2N/A return 0;
2N/A}
2N/A#endif
2N/A
2N/Aasn1_error_code
2N/Aasn1_encode_unsigned_integer(asn1buf *buf, asn1_uintmax val,
2N/A unsigned int *retlen)
2N/A{
2N/A asn1_error_code retval;
2N/A unsigned int length = 0;
2N/A unsigned int partlen;
2N/A unsigned long valcopy;
2N/A int digit;
2N/A
2N/A valcopy = val;
2N/A do {
2N/A digit = (int) (valcopy&0xFF);
2N/A retval = asn1buf_insert_octet(buf,(asn1_octet) digit);
2N/A if (retval) return retval;
2N/A length++;
2N/A valcopy = valcopy >> 8;
2N/A } while (valcopy != 0);
2N/A
2N/A if (digit&0x80) { /* make sure the high bit is */
2N/A retval = asn1buf_insert_octet(buf,0); /* of the proper signed-ness */
2N/A if (retval) return retval;
2N/A length++;
2N/A }
2N/A
2N/A retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_INTEGER,length, &partlen);
2N/A if (retval) return retval;
2N/A length += partlen;
2N/A
2N/A *retlen = length;
2N/A return 0;
2N/A}
2N/A
2N/Astatic asn1_error_code
2N/Aencode_bytestring_with_tag(asn1buf *buf, unsigned int len,
2N/A const void *val, int tag,
2N/A unsigned int *retlen)
2N/A{
2N/A asn1_error_code retval;
2N/A unsigned int length;
2N/A
2N/A if (len > 0 && val == 0) return ASN1_MISSING_FIELD;
2N/A retval = asn1buf_insert_octetstring(buf, len, val);
2N/A if (retval) return retval;
2N/A retval = asn1_make_tag(buf, UNIVERSAL, PRIMITIVE, tag,
2N/A len, &length);
2N/A if (retval) return retval;
2N/A
2N/A *retlen = len + length;
2N/A return 0;
2N/A}
2N/A
2N/Aasn1_error_code
2N/Aasn1_encode_oid(asn1buf *buf, unsigned int len, const asn1_octet *val,
2N/A unsigned int *retlen)
2N/A{
2N/A return encode_bytestring_with_tag(buf, len, val, ASN1_OBJECTIDENTIFIER,
2N/A retlen);
2N/A}
2N/A
2N/Aasn1_error_code
2N/Aasn1_encode_octetstring(asn1buf *buf, unsigned int len, const void *val,
2N/A unsigned int *retlen)
2N/A{
2N/A return encode_bytestring_with_tag(buf, len, val, ASN1_OCTETSTRING,
2N/A retlen);
2N/A}
2N/A
2N/A#if 0
2N/Aasn1_error_code asn1_encode_null(asn1buf *buf, int *retlen)
2N/A{
2N/A asn1_error_code retval;
2N/A
2N/A retval = asn1buf_insert_octet(buf,0x00);
2N/A if (retval) return retval;
2N/A retval = asn1buf_insert_octet(buf,0x05);
2N/A if (retval) return retval;
2N/A
2N/A *retlen = 2;
2N/A return 0;
2N/A}
2N/A
2N/Aasn1_error_code asn1_encode_printablestring(asn1buf *buf, unsigned int len,
2N/A const char *val, int *retlen)
2N/A{
2N/A return encode_bytestring_with_tag(buf, len, val, ASN1_PRINTABLESTRING,
2N/A retlen);
2N/A}
2N/A
2N/Aasn1_error_code asn1_encode_ia5string(asn1buf *buf, unsigned int len,
2N/A const char *val, int *retlen)
2N/A{
2N/A return encode_bytestring_with_tag(buf, len, val, ASN1_IA5STRING,
2N/A retlen);
2N/A}
2N/A#endif
2N/A
2N/Aasn1_error_code
2N/Aasn1_encode_generaltime(asn1buf *buf, time_t val, unsigned int *retlen)
2N/A{
2N/A struct tm *gtime, gtimebuf;
2N/A char s[16], *sp;
2N/A time_t gmt_time = val;
2N/A
2N/A /*
2N/A * Time encoding: YYYYMMDDhhmmssZ
2N/A */
2N/A if (gmt_time == 0) {
2N/A sp = "19700101000000Z";
2N/A } else {
2N/A int len;
2N/A
2N/A /*
2N/A * Sanity check this just to be paranoid, as gmtime can return NULL,
2N/A * and some bogus implementations might overrun on the sprintf.
2N/A */
2N/A#ifdef HAVE_GMTIME_R
2N/A# ifdef GMTIME_R_RETURNS_INT
2N/A if (gmtime_r(&gmt_time, &gtimebuf) != 0)
2N/A return ASN1_BAD_GMTIME;
2N/A# else
2N/A if (gmtime_r(&gmt_time, &gtimebuf) == NULL)
2N/A return ASN1_BAD_GMTIME;
2N/A# endif
2N/A#else
2N/A gtime = gmtime(&gmt_time);
2N/A if (gtime == NULL)
2N/A return ASN1_BAD_GMTIME;
2N/A memcpy(&gtimebuf, gtime, sizeof(gtimebuf));
2N/A#endif
2N/A gtime = &gtimebuf;
2N/A
2N/A if (gtime->tm_year > 8099 || gtime->tm_mon > 11 ||
2N/A gtime->tm_mday > 31 || gtime->tm_hour > 23 ||
2N/A gtime->tm_min > 59 || gtime->tm_sec > 59)
2N/A return ASN1_BAD_GMTIME;
2N/A len = snprintf(s, sizeof(s), "%04d%02d%02d%02d%02d%02dZ",
2N/A 1900+gtime->tm_year, gtime->tm_mon+1,
2N/A gtime->tm_mday, gtime->tm_hour,
2N/A gtime->tm_min, gtime->tm_sec);
2N/A if (SNPRINTF_OVERFLOW(len, sizeof(s)))
2N/A /* Shouldn't be possible given above tests. */
2N/A return ASN1_BAD_GMTIME;
2N/A sp = s;
2N/A }
2N/A
2N/A return encode_bytestring_with_tag(buf, 15, sp, ASN1_GENERALTIME,
2N/A retlen);
2N/A}
2N/A
2N/Aasn1_error_code
2N/Aasn1_encode_generalstring(asn1buf *buf, unsigned int len, const void *val,
2N/A unsigned int *retlen)
2N/A{
2N/A return encode_bytestring_with_tag(buf, len, val, ASN1_GENERALSTRING,
2N/A retlen);
2N/A}
2N/A
2N/Aasn1_error_code
2N/Aasn1_encode_bitstring(asn1buf *buf, unsigned int len, const void *val,
2N/A unsigned int *retlen)
2N/A{
2N/A asn1_error_code retval;
2N/A unsigned int length;
2N/A
2N/A retval = asn1buf_insert_octetstring(buf, len, val);
2N/A if (retval) return retval;
2N/A retval = asn1buf_insert_octet(buf, 0);
2N/A if (retval) return retval;
2N/A retval = asn1_make_tag(buf, UNIVERSAL, PRIMITIVE, ASN1_BITSTRING,
2N/A len+1, &length);
2N/A if (retval) return retval;
2N/A *retlen = len + 1 + length;
2N/A return 0;
2N/A}
2N/A
2N/Aasn1_error_code
2N/Aasn1_encode_opaque(asn1buf *buf, unsigned int len, const void *val,
2N/A unsigned int *retlen)
2N/A{
2N/A asn1_error_code retval;
2N/A
2N/A retval = asn1buf_insert_octetstring(buf, len, val);
2N/A if (retval) return retval;
2N/A *retlen = len;
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * ASN.1 constructed type encoder engine
2N/A *
2N/A * Two entry points here:
2N/A *
2N/A * krb5int_asn1_encode_a_thing: Incrementally adds the partial
2N/A * encoding of an object to an already-initialized asn1buf.
2N/A *
2N/A * krb5int_asn1_do_full_encode: Returns a completed encoding, in the
2N/A * correct byte order, in an allocated krb5_data.
2N/A */
2N/A
2N/A#ifdef POINTERS_ARE_ALL_THE_SAME
2N/A#define LOADPTR(PTR,TYPE) \
2N/A (*(const void *const *)(PTR))
2N/A#else
2N/A#define LOADPTR(PTR,TYPE) \
2N/A (assert((TYPE)->loadptr != NULL), (TYPE)->loadptr(PTR))
2N/A#endif
2N/A
2N/Astatic int
2N/Aget_nullterm_sequence_len(const void *valp, const struct atype_info *seq)
2N/A{
2N/A int i;
2N/A const struct atype_info *a;
2N/A const void *elt, *eltptr;
2N/A
2N/A a = seq;
2N/A i = 0;
2N/A assert(a->type == atype_ptr);
2N/A assert(seq->size != 0);
2N/A
2N/A while (1) {
2N/A eltptr = (const char *) valp + i * seq->size;
2N/A elt = LOADPTR(eltptr, a);
2N/A if (elt == NULL)
2N/A break;
2N/A i++;
2N/A }
2N/A return i;
2N/A}
2N/Astatic asn1_error_code
2N/Aencode_sequence_of(asn1buf *buf, int seqlen, const void *val,
2N/A const struct atype_info *eltinfo,
2N/A unsigned int *retlen);
2N/A
2N/Astatic asn1_error_code
2N/Aencode_nullterm_sequence_of(asn1buf *buf, const void *val,
2N/A const struct atype_info *type,
2N/A int can_be_empty,
2N/A unsigned int *retlen)
2N/A{
2N/A int length = get_nullterm_sequence_len(val, type);
2N/A if (!can_be_empty && length == 0) return ASN1_MISSING_FIELD;
2N/A return encode_sequence_of(buf, length, val, type, retlen);
2N/A}
2N/A
2N/Astatic asn1_error_code
2N/Ajust_encode_sequence(asn1buf *buf, const void *val,
2N/A const struct seq_info *seq,
2N/A unsigned int *retlen);
2N/Astatic asn1_error_code
2N/Aencode_a_field(asn1buf *buf, const void *val,
2N/A const struct field_info *field,
2N/A unsigned int *retlen);
2N/A
2N/Aasn1_error_code
2N/Akrb5int_asn1_encode_a_thing(asn1buf *buf, const void *val,
2N/A const struct atype_info *a, unsigned int *retlen)
2N/A{
2N/A switch (a->type) {
2N/A case atype_fn:
2N/A assert(a->enc != NULL);
2N/A return a->enc(buf, val, retlen);
2N/A case atype_sequence:
2N/A assert(a->seq != NULL);
2N/A return just_encode_sequence(buf, val, a->seq, retlen);
2N/A case atype_ptr:
2N/A assert(a->basetype != NULL);
2N/A return krb5int_asn1_encode_a_thing(buf, LOADPTR(val, a),
2N/A a->basetype, retlen);
2N/A case atype_field:
2N/A assert(a->field != NULL);
2N/A return encode_a_field(buf, val, a->field, retlen);
2N/A case atype_nullterm_sequence_of:
2N/A case atype_nonempty_nullterm_sequence_of:
2N/A assert(a->basetype != NULL);
2N/A return encode_nullterm_sequence_of(buf, val, a->basetype,
2N/A a->type == atype_nullterm_sequence_of,
2N/A retlen);
2N/A case atype_tagged_thing:
2N/A {
2N/A asn1_error_code retval;
2N/A unsigned int length, sum = 0;
2N/A retval = krb5int_asn1_encode_a_thing(buf, val, a->basetype, &length);
2N/A if (retval) return retval;
2N/A sum = length;
2N/A retval = asn1_make_etag(buf, a->tagtype, a->tagval, sum, &length);
2N/A if (retval) return retval;
2N/A sum += length;
2N/A *retlen = sum;
2N/A return 0;
2N/A }
2N/A case atype_int:
2N/A assert(a->loadint != NULL);
2N/A return asn1_encode_integer(buf, a->loadint(val), retlen);
2N/A case atype_uint:
2N/A assert(a->loaduint != NULL);
2N/A return asn1_encode_unsigned_integer(buf, a->loaduint(val), retlen);
2N/A case atype_min:
2N/A case atype_max:
2N/A case atype_fn_len:
2N/A default:
2N/A assert(a->type > atype_min);
2N/A assert(a->type < atype_max);
2N/A assert(a->type != atype_fn_len);
2N/A abort();
2N/A }
2N/A}
2N/A
2N/Astatic asn1_error_code
2N/Aencode_a_field(asn1buf *buf, const void *val,
2N/A const struct field_info *field,
2N/A unsigned int *retlen)
2N/A{
2N/A asn1_error_code retval;
2N/A unsigned int sum = 0;
2N/A
2N/A if (val == NULL) return ASN1_MISSING_FIELD;
2N/A
2N/A switch (field->ftype) {
2N/A case field_immediate:
2N/A {
2N/A unsigned int length;
2N/A
2N/A retval = asn1_encode_integer(buf, (asn1_intmax) field->dataoff,
2N/A &length);
2N/A if (retval) return retval;
2N/A sum += length;
2N/A break;
2N/A }
2N/A case field_sequenceof_len:
2N/A {
2N/A const void *dataptr, *lenptr;
2N/A int slen;
2N/A unsigned int length;
2N/A const struct atype_info *a;
2N/A
2N/A /*
2N/A * The field holds a pointer to the array of objects. So the
2N/A * address we compute is a pointer-to-pointer, and that's what
2N/A * field->atype must help us dereference.
2N/A */
2N/A dataptr = (const char *)val + field->dataoff;
2N/A lenptr = (const char *)val + field->lenoff;
2N/A assert(field->atype->type == atype_ptr);
2N/A dataptr = LOADPTR(dataptr, field->atype);
2N/A a = field->atype->basetype;
2N/A assert(field->lentype != 0);
2N/A assert(field->lentype->type == atype_int || field->lentype->type == atype_uint);
2N/A assert(sizeof(int) <= sizeof(asn1_intmax));
2N/A assert(sizeof(unsigned int) <= sizeof(asn1_uintmax));
2N/A if (field->lentype->type == atype_int) {
2N/A asn1_intmax xlen = field->lentype->loadint(lenptr);
2N/A if (xlen < 0)
2N/A return EINVAL;
2N/A if ((unsigned int) xlen != (asn1_uintmax) xlen)
2N/A return EINVAL;
2N/A if ((unsigned int) xlen > INT_MAX)
2N/A return EINVAL;
2N/A slen = (int) xlen;
2N/A } else {
2N/A asn1_uintmax xlen = field->lentype->loaduint(lenptr);
2N/A if ((unsigned int) xlen != xlen)
2N/A return EINVAL;
2N/A if (xlen > INT_MAX)
2N/A return EINVAL;
2N/A slen = (int) xlen;
2N/A }
2N/A if (slen != 0 && dataptr == NULL)
2N/A return ASN1_MISSING_FIELD;
2N/A retval = encode_sequence_of(buf, slen, dataptr, a, &length);
2N/A if (retval) return retval;
2N/A sum += length;
2N/A break;
2N/A }
2N/A case field_normal:
2N/A {
2N/A const void *dataptr;
2N/A const struct atype_info *a;
2N/A unsigned int length;
2N/A
2N/A dataptr = (const char *)val + field->dataoff;
2N/A
2N/A a = field->atype;
2N/A assert(a->type != atype_fn_len);
2N/A retval = krb5int_asn1_encode_a_thing(buf, dataptr, a, &length);
2N/A if (retval) {
2N/A return retval;
2N/A }
2N/A sum += length;
2N/A break;
2N/A }
2N/A case field_string:
2N/A {
2N/A const void *dataptr, *lenptr;
2N/A const struct atype_info *a;
2N/A size_t slen;
2N/A unsigned int length;
2N/A
2N/A dataptr = (const char *)val + field->dataoff;
2N/A lenptr = (const char *)val + field->lenoff;
2N/A
2N/A a = field->atype;
2N/A assert(a->type == atype_fn_len);
2N/A assert(field->lentype != 0);
2N/A assert(field->lentype->type == atype_int || field->lentype->type == atype_uint);
2N/A assert(sizeof(int) <= sizeof(asn1_intmax));
2N/A assert(sizeof(unsigned int) <= sizeof(asn1_uintmax));
2N/A if (field->lentype->type == atype_int) {
2N/A asn1_intmax xlen = field->lentype->loadint(lenptr);
2N/A if (xlen < 0)
2N/A return EINVAL;
2N/A if ((size_t) xlen != (asn1_uintmax) xlen)
2N/A return EINVAL;
2N/A slen = (size_t) xlen;
2N/A } else {
2N/A asn1_uintmax xlen = field->lentype->loaduint(lenptr);
2N/A if ((size_t) xlen != xlen)
2N/A return EINVAL;
2N/A slen = (size_t) xlen;
2N/A }
2N/A
2N/A dataptr = LOADPTR(dataptr, a);
2N/A if (slen == SIZE_MAX)
2N/A /* Error - negative or out of size_t range. */
2N/A return EINVAL;
2N/A if (dataptr == NULL && slen != 0)
2N/A return ASN1_MISSING_FIELD;
2N/A /*
2N/A * Currently our string encoders want "unsigned int" for
2N/A * lengths.
2N/A */
2N/A if (slen != (unsigned int) slen)
2N/A return EINVAL;
2N/A assert(a->enclen != NULL);
2N/A retval = a->enclen(buf, (unsigned int) slen, dataptr, &length);
2N/A if (retval) {
2N/A return retval;
2N/A }
2N/A sum += length;
2N/A break;
2N/A }
2N/A default:
2N/A assert(field->ftype > field_min);
2N/A assert(field->ftype < field_max);
2N/A assert(__LINE__ == 0);
2N/A abort();
2N/A }
2N/A if (field->tag >= 0) {
2N/A unsigned int length;
2N/A retval = asn1_make_etag(buf, CONTEXT_SPECIFIC, field->tag, sum,
2N/A &length);
2N/A if (retval) {
2N/A return retval;
2N/A }
2N/A sum += length;
2N/A }
2N/A *retlen = sum;
2N/A return 0;
2N/A}
2N/A
2N/Astatic asn1_error_code
2N/Aencode_fields(asn1buf *buf, const void *val,
2N/A const struct field_info *fields, size_t nfields,
2N/A unsigned int optional,
2N/A unsigned int *retlen)
2N/A{
2N/A size_t i;
2N/A unsigned int sum = 0;
2N/A for (i = nfields; i > 0; i--) {
2N/A const struct field_info *f = fields+i-1;
2N/A unsigned int length;
2N/A asn1_error_code retval;
2N/A int present;
2N/A
2N/A if (f->opt == -1)
2N/A present = 1;
2N/A else if ((1u << f->opt) & optional)
2N/A present = 1;
2N/A else
2N/A present = 0;
2N/A if (present) {
2N/A retval = encode_a_field(buf, val, f, &length);
2N/A if (retval) return retval;
2N/A sum += length;
2N/A }
2N/A }
2N/A *retlen = sum;
2N/A return 0;
2N/A}
2N/A
2N/Astatic asn1_error_code
2N/Ajust_encode_sequence(asn1buf *buf, const void *val,
2N/A const struct seq_info *seq,
2N/A unsigned int *retlen)
2N/A{
2N/A const struct field_info *fields = seq->fields;
2N/A size_t nfields = seq->n_fields;
2N/A unsigned int optional;
2N/A asn1_error_code retval;
2N/A unsigned int sum = 0;
2N/A
2N/A if (seq->optional)
2N/A optional = seq->optional(val);
2N/A else
2N/A /*
2N/A * In this case, none of the field descriptors should indicate
2N/A * that we examine any bits of this value.
2N/A */
2N/A optional = 0;
2N/A {
2N/A unsigned int length;
2N/A retval = encode_fields(buf, val, fields, nfields, optional, &length);
2N/A if (retval) return retval;
2N/A sum += length;
2N/A }
2N/A {
2N/A unsigned int length;
2N/A retval = asn1_make_sequence(buf, sum, &length);
2N/A if (retval) return retval;
2N/A sum += length;
2N/A }
2N/A *retlen = sum;
2N/A return 0;
2N/A}
2N/A
2N/Astatic asn1_error_code
2N/Aencode_sequence_of(asn1buf *buf, int seqlen, const void *val,
2N/A const struct atype_info *eltinfo,
2N/A unsigned int *retlen)
2N/A{
2N/A asn1_error_code retval;
2N/A unsigned int sum = 0;
2N/A int i;
2N/A
2N/A for (i = seqlen-1; i >= 0; i--) {
2N/A const void *eltptr;
2N/A unsigned int length;
2N/A const struct atype_info *a = eltinfo;
2N/A
2N/A assert(eltinfo->size != 0);
2N/A eltptr = (const char *)val + i * eltinfo->size;
2N/A retval = krb5int_asn1_encode_a_thing(buf, eltptr, a, &length);
2N/A if (retval) return retval;
2N/A sum += length;
2N/A }
2N/A {
2N/A unsigned int length;
2N/A retval = asn1_make_sequence(buf, sum, &length);
2N/A if (retval) return retval;
2N/A sum += length;
2N/A }
2N/A *retlen = sum;
2N/A return 0;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5int_asn1_do_full_encode(const void *rep, krb5_data **code,
2N/A const struct atype_info *a)
2N/A{
2N/A unsigned int length;
2N/A asn1_error_code retval;
2N/A asn1buf *buf = NULL;
2N/A krb5_data *d;
2N/A
2N/A *code = NULL;
2N/A
2N/A if (rep == NULL)
2N/A return ASN1_MISSING_FIELD;
2N/A
2N/A retval = asn1buf_create(&buf);
2N/A if (retval)
2N/A return retval;
2N/A
2N/A retval = krb5int_asn1_encode_a_thing(buf, rep, a, &length);
2N/A if (retval)
2N/A goto cleanup;
2N/A retval = asn12krb5_buf(buf, &d);
2N/A if (retval)
2N/A goto cleanup;
2N/A *code = d;
2N/Acleanup:
2N/A asn1buf_destroy(&buf);
2N/A return retval;
2N/A}