root/usr/src/lib/gss_mechs/mech_krb5/krb5/asn.1/asn1_k_decode.c
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * src/lib/krb5/asn.1/asn1_k_decode.c
 *
 * Copyright 1994 by the Massachusetts Institute of Technology.
 * All Rights Reserved.
 *
 * Export of this software from the United States of America may
 *   require a specific license from the United States Government.
 *   It is the responsibility of any person or organization contemplating
 *   export to obtain such a license before exporting.
 *
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
 * distribute this software and its documentation for any purpose and
 * without fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation, and that
 * the name of M.I.T. not be used in advertising or publicity pertaining
 * to distribution of the software without specific, written prior
 * permission.  Furthermore if you modify this software you must label
 * your software as modified software and not distribute it in such a
 * fashion that it might be confused with the original M.I.T. software.
 * M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is" without express
 * or implied warranty.
 */

#include "asn1_k_decode.h"
#include "asn1_decode.h"
#include "asn1_get.h"
#include "asn1_misc.h"

/* Declare useful decoder variables. */
#define setup()                                 \
  asn1_error_code retval;                       \
  asn1_class asn1class;                         \
  asn1_construction construction;               \
  asn1_tagnum tagnum;                           \
  unsigned int length, taglen

#define unused_var(x) if (0) { x = 0; x = x - x; }

/* This is used for prefetch of next tag in sequence. */
#define next_tag()                                                              \
{ taginfo t2;                                                                   \
  retval = asn1_get_tag_2(&subbuf, &t2);                                        \
  if (retval) return retval;                                                    \
  /* Copy out to match previous functionality, until better integrated.  */     \
  asn1class = t2.asn1class;                                                     \
  construction = t2.construction;                                               \
  tagnum = t2.tagnum;                                                           \
  taglen = t2.length;                                                           \
  indef = t2.indef;                                                             \
}

/* Force check for EOC tag. */
#define get_eoc()                                                                       \
    {                                                                                   \
        taginfo t3;                                                                     \
        retval = asn1_get_tag_2(&subbuf, &t3);                                          \
        if(retval) return retval;                                                       \
        if (t3.asn1class != UNIVERSAL || t3.tagnum || t3.indef)                         \
            return ASN1_MISSING_EOC;                                                    \
        /* Copy out to match previous functionality, until better integrated.  */       \
        asn1class = t3.asn1class;                                                       \
        construction = t3.construction;                                                 \
        tagnum = t3.tagnum;                                                             \
        taglen = t3.length;                                                             \
        indef = t3.indef;                                                               \
    }

#define alloc_field(var, type)                  \
  var = (type*)calloc(1, sizeof(type));         \
  if ((var) == NULL) return ENOMEM

/* Fetch an expected APPLICATION class tag and verify. */
#define apptag(tagexpect)                                                               \
  {                                                                                     \
      taginfo t1;                                                                       \
      retval = asn1_get_tag_2(buf, &t1);                                                \
      if (retval) return retval;                                                        \
      if (t1.asn1class != APPLICATION || t1.construction != CONSTRUCTED ||              \
          t1.tagnum != (tagexpect)) return ASN1_BAD_ID;                                 \
      /* Copy out to match previous functionality, until better integrated.  */         \
      asn1class = t1.asn1class;                                                         \
      construction = t1.construction;                                                   \
      tagnum = t1.tagnum;                                                               \
      applen = t1.length;                                                               \
  }

/**** normal fields ****/

/*
 * get_field_body
 *
 * Get bare field.  This also prefetches the next tag.  The call to
 * get_eoc() assumes that any values fetched by this macro are
 * enclosed in a context-specific tag.
 */
#define get_field_body(var, decoder)            \
  retval = decoder(&subbuf, &(var));            \
  if (retval) return retval;                    \
  if (!taglen && indef) { get_eoc(); }          \
  next_tag()

/*
 * get_field
 *
 * Get field having an expected context specific tag.  This assumes
 * that context-specific tags are monotonically increasing in its
 * verification of tag numbers.
 */
#define get_field(var, tagexpect, decoder)                              \
  if (tagnum > (tagexpect)) return ASN1_MISSING_FIELD;                  \
  if (tagnum < (tagexpect)) return ASN1_MISPLACED_FIELD;                \
  if ((asn1class != CONTEXT_SPECIFIC || construction != CONSTRUCTED)    \
      && (tagnum || taglen || asn1class != UNIVERSAL))                  \
    return ASN1_BAD_ID;                                                 \
  get_field_body(var,decoder)

/*
 * opt_field
 *
 * Get an optional field with an expected context specific tag.
 * Assumes that OPTVAL will have the default value, thus failing to
 * distinguish between absent optional values and present optional
 * values that happen to have the value of OPTVAL.
 */
#define opt_field(var, tagexpect, decoder, optvalue)                    \
  if (asn1buf_remains(&subbuf, seqindef)) {                             \
    if ((asn1class != CONTEXT_SPECIFIC || construction != CONSTRUCTED)  \
        && (tagnum || taglen || asn1class != UNIVERSAL))                \
      return ASN1_BAD_ID;                                               \
    if (tagnum == (tagexpect)) {                                        \
      get_field_body(var, decoder);                                     \
    } else var = optvalue;                                              \
  }

/**** fields w/ length ****/

/* similar to get_field_body */
#define get_lenfield_body(len, var, decoder)    \
  retval = decoder(&subbuf, &(len), &(var));    \
  if (retval) return retval;                    \
  if (!taglen && indef) { get_eoc(); }          \
  next_tag()

/* similar to get_field_body */
#define get_lenfield(len, var, tagexpect, decoder)                      \
  if (tagnum > (tagexpect)) return ASN1_MISSING_FIELD;                  \
  if (tagnum < (tagexpect)) return ASN1_MISPLACED_FIELD;                \
  if ((asn1class != CONTEXT_SPECIFIC || construction != CONSTRUCTED)    \
      && (tagnum || taglen || asn1class != UNIVERSAL))                  \
    return ASN1_BAD_ID;                                                 \
  get_lenfield_body(len, var, decoder)

/* similar to opt_field */
#define opt_lenfield(len, var, tagexpect, decoder)      \
  if (tagnum == (tagexpect)) {                          \
    get_lenfield_body(len, var, decoder);               \
  } else { len = 0; var = 0; }

/*
 * Deal with implicitly tagged fields
 */
#define get_implicit_octet_string(len, var, tagexpect)              \
  if (tagnum != (tagexpect)) return ASN1_MISSING_FIELD;             \
  if (asn1class != CONTEXT_SPECIFIC || construction != PRIMITIVE)   \
     return ASN1_BAD_ID;                                            \
  retval = asn1buf_remove_octetstring(&subbuf, taglen, &(var));     \
  if (retval) return retval;                                        \
  (len) = taglen;                                                   \
  next_tag()

#define opt_implicit_octet_string(len, var, tagexpect)              \
  if (tagnum == (tagexpect)) {                                      \
    if (asn1class != CONTEXT_SPECIFIC || construction != PRIMITIVE) \
        return ASN1_BAD_ID;                                         \
    retval = asn1buf_remove_octetstring(&subbuf, taglen, &(var));   \
    if (retval) return retval;                                      \
    (len) = taglen;                                                 \
    next_tag();                                                     \
  } else { (len) = 0; (var) = NULL; }

/*
 * begin_structure
 *
 * Declares some variables for decoding SEQUENCE types.  This is meant
 * to be called in an inner block that ends with a call to
 * end_structure().
 */
#define begin_structure()                                       \
  asn1buf subbuf;                                               \
  int seqindef;                                                 \
  int indef;                                                    \
  retval = asn1_get_sequence(buf, &length, &seqindef);          \
  if (retval) return retval;                                    \
  retval = asn1buf_imbed(&subbuf, buf, length, seqindef);       \
  if (retval) return retval;                                    \
  next_tag()

/*
 * This is used for structures which have no tagging.
 * It is the same as begin_structure() except next_tag()
 * is not called.
 */
#define begin_structure_no_tag()                                \
  asn1buf subbuf;                                               \
  int seqindef;                                                 \
  int indef;                                                    \
  retval = asn1_get_sequence(buf, &length, &seqindef);          \
  if (retval) return retval;                                    \
  retval = asn1buf_imbed(&subbuf, buf, length, seqindef);       \
  if (retval) return retval

/* skip trailing garbage */
#define end_structure()                                         \
  retval = asn1buf_sync(buf, &subbuf, asn1class, tagnum,        \
                        length, indef, seqindef);               \
  if (retval) return retval

/*
 * begin_choice
 *
 * Declares some variables for decoding CHOICE types.  This is meant
 * to be called in an inner block that ends with a call to
 * end_choice().
 */
#define begin_choice()                                          \
  asn1buf subbuf;                                               \
  int seqindef;                                                 \
  int indef;                                                    \
  taginfo t;                                                    \
  retval = asn1_get_tag_2(buf, &t);                             \
  if (retval) return retval;                                    \
  tagnum = t.tagnum;                                            \
  taglen = t.length;                                            \
  indef = t.indef;                                              \
  length = t.length;                                            \
  seqindef = t.indef;                                           \
  asn1class = t.asn1class;                                      \
  construction = t.construction;                                \
  retval = asn1buf_imbed(&subbuf, buf, length, seqindef);       \
  if (retval) return retval

/* skip trailing garbage */
#define end_choice()                                            \
  length -= t.length;                                           \
  retval = asn1buf_sync(buf, &subbuf, t.asn1class, t.tagnum,    \
                        length, t.indef, seqindef);             \
  if (retval) return retval

/*
 * sequence_of
 *
 * Declares some variables for decoding SEQUENCE OF types.  This is
 * meant to be called in an inner block that ends with a call to
 * end_sequence_of().
 */
#define sequence_of(buf)                        \
  unsigned int length, taglen;                  \
  asn1_class asn1class;                         \
  asn1_construction construction;               \
  asn1_tagnum tagnum;                           \
  int indef;                                    \
  sequence_of_common(buf)

/*
 * sequence_of_no_tagvars
 *
 * This is meant for use inside decoder functions that have an outer
 * sequence structure and thus declares variables of different names
 * than does sequence_of() to avoid shadowing.
 */
#define sequence_of_no_tagvars(buf)             \
  asn1_class eseqclass;                         \
  asn1_construction eseqconstr;                 \
  asn1_tagnum eseqnum;                          \
  unsigned int eseqlen;                         \
  int eseqindef;                                \
  sequence_of_common(buf)

/*
 * sequence_of_common
 *
 * Fetches the outer SEQUENCE OF length info into {length,seqofindef}
 * and imbeds an inner buffer seqbuf.  Unlike begin_structure(), it
 * does not prefetch the next tag.
 */
#define sequence_of_common(buf)                                 \
  int size = 0;                                                 \
  asn1buf seqbuf;                                               \
  int seqofindef;                                               \
  retval = asn1_get_sequence(buf, &length, &seqofindef);        \
  if (retval) return retval;                                    \
  retval = asn1buf_imbed(&seqbuf, buf, length, seqofindef);     \
  if (retval) return retval

/*
 * end_sequence_of
 *
 * Attempts to fetch an EOC tag, if any, and to sync over trailing
 * garbage, if any.
 */
#define end_sequence_of(buf)                                                    \
  {                                                                             \
      taginfo t4;                                                               \
      retval = asn1_get_tag_2(&seqbuf, &t4);                                    \
      if (retval) return retval;                                                \
      /* Copy out to match previous functionality, until better integrated.  */ \
      asn1class = t4.asn1class;                                                 \
      construction = t4.construction;                                           \
      tagnum = t4.tagnum;                                                       \
      taglen = t4.length;                                                       \
      indef = t4.indef;                                                         \
  }                                                                             \
  retval = asn1buf_sync(buf, &seqbuf, asn1class, tagnum,                        \
                        length, indef, seqofindef);                             \
  if (retval) return retval;

/*
 * end_sequence_of_no_tagvars
 *
 * Like end_sequence_of(), but uses the different (non-shadowing)
 * variable names.
 */
#define end_sequence_of_no_tagvars(buf)                                         \
  {                                                                             \
      taginfo t5;                                                               \
      retval = asn1_get_tag_2(&seqbuf, &t5);                                    \
      if (retval) return retval;                                                \
      /* Copy out to match previous functionality, until better integrated.  */ \
      eseqclass = t5.asn1class;                                                 \
      eseqconstr = t5.construction;                                             \
      eseqnum = t5.tagnum;                                                      \
      eseqlen = t5.length;                                                      \
      eseqindef = t5.indef;                                                     \
  }                                                                             \
  retval = asn1buf_sync(buf, &seqbuf, eseqclass, eseqnum,                       \
                        eseqlen, eseqindef, seqofindef);                        \
  if (retval) return retval;

#define cleanup()                               \
  return 0

/* scalars */
asn1_error_code asn1_decode_kerberos_time(asn1buf *buf, krb5_timestamp *val)
{
    time_t      t;
    asn1_error_code retval;

    retval =  asn1_decode_generaltime(buf,&t);
    if (retval)
        return retval;

    *val = t;
    return 0;
}

#define integer_convert(fname,ktype)\
asn1_error_code fname(asn1buf * buf, ktype * val)\
{\
  asn1_error_code retval;\
  long n;\
  retval = asn1_decode_integer(buf,&n);\
  if(retval) return retval;\
  *val = (ktype)n;\
  return 0;\
}
#define unsigned_integer_convert(fname,ktype)\
asn1_error_code fname(asn1buf * buf, ktype * val)\
{\
  asn1_error_code retval;\
  unsigned long n;\
  retval = asn1_decode_unsigned_integer(buf,&n);\
  if(retval) return retval;\
  *val = (ktype)n;\
  return 0;\
}
integer_convert(asn1_decode_int,int)
integer_convert(asn1_decode_int32,krb5_int32)
integer_convert(asn1_decode_kvno,krb5_kvno)
integer_convert(asn1_decode_enctype,krb5_enctype)
integer_convert(asn1_decode_cksumtype,krb5_cksumtype)
integer_convert(asn1_decode_octet,krb5_octet)
integer_convert(asn1_decode_addrtype,krb5_addrtype)
integer_convert(asn1_decode_authdatatype,krb5_authdatatype)
unsigned_integer_convert(asn1_decode_ui_2,krb5_ui_2)
unsigned_integer_convert(asn1_decode_ui_4,krb5_ui_4)

asn1_error_code asn1_decode_seqnum(asn1buf *buf, krb5_ui_4 *val)
{
  asn1_error_code retval;
  unsigned long n;

  retval = asn1_decode_maybe_unsigned(buf, &n);
  if (retval) return retval;
  *val = (krb5_ui_4)n & 0xffffffff;
  return 0;
}

asn1_error_code asn1_decode_msgtype(asn1buf *buf, krb5_msgtype *val)
{
  asn1_error_code retval;
  unsigned long n;

  retval = asn1_decode_unsigned_integer(buf,&n);
  if(retval) return retval;

  *val = (krb5_msgtype) n;
  return 0;
}


/* structures */
asn1_error_code asn1_decode_realm(asn1buf *buf, krb5_principal *val)
{
  return asn1_decode_generalstring(buf,
                                   &((*val)->realm.length),
                                   &((*val)->realm.data));
}

asn1_error_code asn1_decode_principal_name(asn1buf *buf, krb5_principal *val)
{
  setup();
  { begin_structure();
    get_field((*val)->type,0,asn1_decode_int32);

    { sequence_of_no_tagvars(&subbuf);
      while(asn1buf_remains(&seqbuf,seqofindef) > 0){
        size++;
        if ((*val)->data == NULL)
          (*val)->data = (krb5_data*)malloc(size*sizeof(krb5_data));
        else
          (*val)->data = (krb5_data*)realloc((*val)->data,
                                             size*sizeof(krb5_data));
        if((*val)->data == NULL) return ENOMEM;
        retval = asn1_decode_generalstring(&seqbuf,
                                           &((*val)->data[size-1].length),
                                           &((*val)->data[size-1].data));
        if(retval) return retval;
      }
      (*val)->length = size;
      end_sequence_of_no_tagvars(&subbuf);
    }
    if (indef) {
        get_eoc();
    }
    next_tag();
    end_structure();
    (*val)->magic = KV5M_PRINCIPAL;
  }
  cleanup();
}

asn1_error_code asn1_decode_checksum(asn1buf *buf, krb5_checksum *val)
{
  setup();
  { begin_structure();
    get_field(val->checksum_type,0,asn1_decode_cksumtype);
    get_lenfield(val->length,val->contents,1,asn1_decode_octetstring);
    end_structure();
    val->magic = KV5M_CHECKSUM;
  }
  cleanup();
}

asn1_error_code asn1_decode_encryption_key(asn1buf *buf, krb5_keyblock *val)
{
  setup();
  { begin_structure();
    get_field(val->enctype,0,asn1_decode_enctype);
    get_lenfield(val->length,val->contents,1,asn1_decode_octetstring);
    end_structure();
    val->magic = KV5M_KEYBLOCK;
  }
  cleanup();
}

asn1_error_code asn1_decode_encrypted_data(asn1buf *buf, krb5_enc_data *val)
{
  setup();
  { begin_structure();
    get_field(val->enctype,0,asn1_decode_enctype);
    opt_field(val->kvno,1,asn1_decode_kvno,0);
    get_lenfield(val->ciphertext.length,val->ciphertext.data,2,asn1_decode_charstring);
    end_structure();
    val->magic = KV5M_ENC_DATA;
  }
  cleanup();
}

asn1_error_code asn1_decode_krb5_flags(asn1buf *buf, krb5_flags *val)
{
  asn1_error_code retval;
  asn1_octet unused, o;
  taginfo t;
  int i;
  krb5_flags f=0;
  unsigned int length;

  retval = asn1_get_tag_2(buf, &t);
  if (retval) return retval;
  if (t.asn1class != UNIVERSAL || t.construction != PRIMITIVE ||
      t.tagnum != ASN1_BITSTRING)
      return ASN1_BAD_ID;
  length = t.length;

  retval = asn1buf_remove_octet(buf,&unused); /* # of padding bits */
  if(retval) return retval;

  /* Number of unused bits must be between 0 and 7. */
  if (unused > 7) return ASN1_BAD_FORMAT;
  length--;

  for(i = 0; i < length; i++) {
    retval = asn1buf_remove_octet(buf,&o);
    if(retval) return retval;
    /* ignore bits past number 31 */
    if (i < 4)
      f = (f<<8) | ((krb5_flags)o&0xFF);
  }
  if (length <= 4) {
    /* Mask out unused bits, but only if necessary. */
    f &= VALID_UINT_BITS << unused;
  }
  /* left-justify */
  if (length < 4)
    f <<= (4 - length) * 8;
  *val = f;
  return 0;
}

asn1_error_code asn1_decode_ticket_flags(asn1buf *buf, krb5_flags *val)
{ return asn1_decode_krb5_flags(buf,val); }

asn1_error_code asn1_decode_ap_options(asn1buf *buf, krb5_flags *val)
{ return asn1_decode_krb5_flags(buf,val); }

asn1_error_code asn1_decode_kdc_options(asn1buf *buf, krb5_flags *val)
{ return asn1_decode_krb5_flags(buf,val); }

asn1_error_code asn1_decode_transited_encoding(asn1buf *buf, krb5_transited *val)
{
  setup();
  { begin_structure();
    get_field(val->tr_type,0,asn1_decode_octet);
    get_lenfield(val->tr_contents.length,val->tr_contents.data,1,asn1_decode_charstring);
    end_structure();
    val->magic = KV5M_TRANSITED;
  }
  cleanup();
}

asn1_error_code asn1_decode_enc_kdc_rep_part(asn1buf *buf, krb5_enc_kdc_rep_part *val)
{
  setup();
  { begin_structure();
    alloc_field(val->session,krb5_keyblock);
    get_field(*(val->session),0,asn1_decode_encryption_key);
    get_field(val->last_req,1,asn1_decode_last_req);
    get_field(val->nonce,2,asn1_decode_int32);
    opt_field(val->key_exp,3,asn1_decode_kerberos_time,0);
    get_field(val->flags,4,asn1_decode_ticket_flags);
    get_field(val->times.authtime,5,asn1_decode_kerberos_time);
    /* Set to authtime if missing */
    opt_field(val->times.starttime,6,asn1_decode_kerberos_time,val->times.authtime);
    get_field(val->times.endtime,7,asn1_decode_kerberos_time);
    opt_field(val->times.renew_till,8,asn1_decode_kerberos_time,0);
    alloc_field(val->server,krb5_principal_data);
    get_field(val->server,9,asn1_decode_realm);
    get_field(val->server,10,asn1_decode_principal_name);
    opt_field(val->caddrs,11,asn1_decode_host_addresses,NULL);
    end_structure();
    val->magic = KV5M_ENC_KDC_REP_PART;
  }
  cleanup();
}

asn1_error_code asn1_decode_ticket(asn1buf *buf, krb5_ticket *val)
{
  setup();
  unsigned int applen;
  apptag(1);
  { begin_structure();
    { krb5_kvno vno;
      get_field(vno,0,asn1_decode_kvno);
      if(vno != KVNO) return KRB5KDC_ERR_BAD_PVNO; }
    alloc_field(val->server,krb5_principal_data);
    get_field(val->server,1,asn1_decode_realm);
    get_field(val->server,2,asn1_decode_principal_name);
    get_field(val->enc_part,3,asn1_decode_encrypted_data);
    end_structure();
    val->magic = KV5M_TICKET;
  }
  if (!applen) {
      taginfo t;
      retval = asn1_get_tag_2(buf, &t);
      if (retval) return retval;
  }
  cleanup();
}

asn1_error_code asn1_decode_kdc_req(asn1buf *buf, krb5_kdc_req *val)
{
  setup();
  { begin_structure();
    { krb5_kvno kvno;
      get_field(kvno,1,asn1_decode_kvno);
      if(kvno != KVNO) return KRB5KDC_ERR_BAD_PVNO; }
    get_field(val->msg_type,2,asn1_decode_msgtype);
    opt_field(val->padata,3,asn1_decode_sequence_of_pa_data,NULL);
    get_field(*val,4,asn1_decode_kdc_req_body);
    end_structure();
    val->magic = KV5M_KDC_REQ;
  }
  cleanup();
}

asn1_error_code asn1_decode_kdc_req_body(asn1buf *buf, krb5_kdc_req *val)
{
  setup();
  {
    krb5_principal psave;
    begin_structure();
    get_field(val->kdc_options,0,asn1_decode_kdc_options);
    if(tagnum == 1){ alloc_field(val->client,krb5_principal_data); }
    opt_field(val->client,1,asn1_decode_principal_name,NULL);
    alloc_field(val->server,krb5_principal_data);
    get_field(val->server,2,asn1_decode_realm);
    if(val->client != NULL){
      retval = asn1_krb5_realm_copy(val->client,val->server);
      if(retval) return retval; }

    /* If opt_field server is missing, memory reference to server is
       lost and results in memory leak */
    psave = val->server;
    opt_field(val->server,3,asn1_decode_principal_name,NULL);
    if(val->server == NULL){
      if(psave->realm.data) {
        free(psave->realm.data);
        psave->realm.data = NULL;
        psave->realm.length=0;
      }
      free(psave);
    }
    opt_field(val->from,4,asn1_decode_kerberos_time,0);
    get_field(val->till,5,asn1_decode_kerberos_time);
    opt_field(val->rtime,6,asn1_decode_kerberos_time,0);
    get_field(val->nonce,7,asn1_decode_int32);
    get_lenfield(val->nktypes,val->ktype,8,asn1_decode_sequence_of_enctype);
    opt_field(val->addresses,9,asn1_decode_host_addresses,0);
    if(tagnum == 10){
      get_field(val->authorization_data,10,asn1_decode_encrypted_data); }
    else{
      val->authorization_data.magic = KV5M_ENC_DATA;
      val->authorization_data.enctype = 0;
      val->authorization_data.kvno = 0;
      val->authorization_data.ciphertext.data = NULL;
      val->authorization_data.ciphertext.length = 0;
    }
    opt_field(val->second_ticket,11,asn1_decode_sequence_of_ticket,NULL);
    end_structure();
    val->magic = KV5M_KDC_REQ;
  }
  cleanup();
}

asn1_error_code asn1_decode_krb_safe_body(asn1buf *buf, krb5_safe *val)
{
  setup();
  { begin_structure();
    get_lenfield(val->user_data.length,val->user_data.data,0,asn1_decode_charstring);
    opt_field(val->timestamp,1,asn1_decode_kerberos_time,0);
    opt_field(val->usec,2,asn1_decode_int32,0);
    opt_field(val->seq_number,3,asn1_decode_seqnum,0);
    alloc_field(val->s_address,krb5_address);
    get_field(*(val->s_address),4,asn1_decode_host_address);
    if(tagnum == 5){
      alloc_field(val->r_address,krb5_address);
      get_field(*(val->r_address),5,asn1_decode_host_address);
    } else val->r_address = NULL;
    end_structure();
    val->magic = KV5M_SAFE;
  }
  cleanup();
}

asn1_error_code asn1_decode_host_address(asn1buf *buf, krb5_address *val)
{
  setup();
  { begin_structure();
    get_field(val->addrtype,0,asn1_decode_addrtype);
    get_lenfield(val->length,val->contents,1,asn1_decode_octetstring);
    end_structure();
    val->magic = KV5M_ADDRESS;
  }
  cleanup();
}

asn1_error_code asn1_decode_kdc_rep(asn1buf *buf, krb5_kdc_rep *val)
{
  setup();
  { begin_structure();
    { krb5_kvno pvno;
      get_field(pvno,0,asn1_decode_kvno);
      if(pvno != KVNO) return KRB5KDC_ERR_BAD_PVNO; }
    get_field(val->msg_type,1,asn1_decode_msgtype);
    opt_field(val->padata,2,asn1_decode_sequence_of_pa_data,NULL);
    alloc_field(val->client,krb5_principal_data);
    get_field(val->client,3,asn1_decode_realm);
    get_field(val->client,4,asn1_decode_principal_name);
    alloc_field(val->ticket,krb5_ticket);
    get_field(*(val->ticket),5,asn1_decode_ticket);
    get_field(val->enc_part,6,asn1_decode_encrypted_data);
    end_structure();
    val->magic = KV5M_KDC_REP;
  }
  cleanup();
}


/* arrays */
#define get_element(element,decoder)\
retval = decoder(&seqbuf,element);\
if(retval) return retval

#define array_append(array,size,element,type)\
size++;\
if (*(array) == NULL)\
     *(array) = (type**)malloc((size+1)*sizeof(type*));\
else\
  *(array) = (type**)realloc(*(array),\
                             (size+1)*sizeof(type*));\
if(*(array) == NULL) return ENOMEM;\
(*(array))[(size)-1] = elt

#define decode_array_body(type,decoder)\
  asn1_error_code retval;\
  type *elt;\
\
  { sequence_of(buf);\
    while(asn1buf_remains(&seqbuf,seqofindef) > 0){\
      alloc_field(elt,type);\
      get_element(elt,decoder);\
      array_append(val,size,elt,type);\
    }\
    if (*val == NULL)\
        *val = (type **)malloc(sizeof(type*));\
    (*val)[size] = NULL;\
    end_sequence_of(buf);\
  }\
  cleanup()


asn1_error_code asn1_decode_authorization_data(asn1buf *buf, krb5_authdata ***val)
{
  decode_array_body(krb5_authdata,asn1_decode_authdata_elt);
}

asn1_error_code asn1_decode_authdata_elt(asn1buf *buf, krb5_authdata *val)
{
  setup();
  { begin_structure();
    get_field(val->ad_type,0,asn1_decode_authdatatype);
    get_lenfield(val->length,val->contents,1,asn1_decode_octetstring);
    end_structure();
    val->magic = KV5M_AUTHDATA;
  }
  cleanup();
}

asn1_error_code asn1_decode_host_addresses(asn1buf *buf, krb5_address ***val)
{
  decode_array_body(krb5_address,asn1_decode_host_address);
}

asn1_error_code asn1_decode_sequence_of_ticket(asn1buf *buf, krb5_ticket ***val)
{
  decode_array_body(krb5_ticket,asn1_decode_ticket);
}

asn1_error_code asn1_decode_sequence_of_krb_cred_info(asn1buf *buf, krb5_cred_info ***val)
{
  decode_array_body(krb5_cred_info,asn1_decode_krb_cred_info);
}

asn1_error_code asn1_decode_krb_cred_info(asn1buf *buf, krb5_cred_info *val)
{
  setup();
  { begin_structure();
    alloc_field(val->session,krb5_keyblock);
    get_field(*(val->session),0,asn1_decode_encryption_key);
    if(tagnum == 1){
      alloc_field(val->client,krb5_principal_data);
      opt_field(val->client,1,asn1_decode_realm,NULL);
      opt_field(val->client,2,asn1_decode_principal_name,NULL); }
    opt_field(val->flags,3,asn1_decode_ticket_flags,0);
    opt_field(val->times.authtime,4,asn1_decode_kerberos_time,0);
    opt_field(val->times.starttime,5,asn1_decode_kerberos_time,0);
    opt_field(val->times.endtime,6,asn1_decode_kerberos_time,0);
    opt_field(val->times.renew_till,7,asn1_decode_kerberos_time,0);
    if(tagnum == 8){
      alloc_field(val->server,krb5_principal_data);
      opt_field(val->server,8,asn1_decode_realm,NULL);
      opt_field(val->server,9,asn1_decode_principal_name,NULL); }
    opt_field(val->caddrs,10,asn1_decode_host_addresses,NULL);
    end_structure();
    val->magic = KV5M_CRED_INFO;
  }
  cleanup();
}

asn1_error_code asn1_decode_sequence_of_pa_data(asn1buf *buf, krb5_pa_data ***val)
{
  decode_array_body(krb5_pa_data,asn1_decode_pa_data);
}

asn1_error_code asn1_decode_pa_data(asn1buf *buf, krb5_pa_data *val)
{
  setup();
  { begin_structure();
    get_field(val->pa_type,1,asn1_decode_int32);
    get_lenfield(val->length,val->contents,2,asn1_decode_octetstring);
    end_structure();
    val->magic = KV5M_PA_DATA;
  }
  cleanup();
}

asn1_error_code asn1_decode_last_req(asn1buf *buf, krb5_last_req_entry ***val)
{
  decode_array_body(krb5_last_req_entry,asn1_decode_last_req_entry);
}

asn1_error_code asn1_decode_last_req_entry(asn1buf *buf, krb5_last_req_entry *val)
{
  setup();
  { begin_structure();
    get_field(val->lr_type,0,asn1_decode_int32);
    get_field(val->value,1,asn1_decode_kerberos_time);
    end_structure();
    val->magic = KV5M_LAST_REQ_ENTRY;
#ifdef KRB5_GENEROUS_LR_TYPE
    /* If we are only a single byte wide and negative - fill in the
       other bits */
    if((val->lr_type & 0xffffff80U) == 0x80) val->lr_type |= 0xffffff00U;
#endif
  }
  cleanup();
}

asn1_error_code asn1_decode_sequence_of_enctype(asn1buf *buf, int *num, krb5_enctype **val)
{
  asn1_error_code retval;
  { sequence_of(buf);
    while(asn1buf_remains(&seqbuf,seqofindef) > 0){
      size++;
      if (*val == NULL)
        *val = (krb5_enctype*)malloc(size*sizeof(krb5_enctype));
      else
        *val = (krb5_enctype*)realloc(*val,size*sizeof(krb5_enctype));
      if(*val == NULL) return ENOMEM;
      retval = asn1_decode_enctype(&seqbuf,&((*val)[size-1]));
      if(retval) return retval;
    }
    *num = size;
    end_sequence_of(buf);
  }
  cleanup();
}

asn1_error_code asn1_decode_sequence_of_checksum(asn1buf *buf, krb5_checksum ***val)
{
  decode_array_body(krb5_checksum, asn1_decode_checksum);
}

static asn1_error_code asn1_decode_etype_info2_entry(asn1buf *buf, krb5_etype_info_entry *val )
{
  /*
   * Solaris Kerberos:
   * Use a temporary char* (tmpp) in place of val->salt when calling
   * get_lenfield(). val->salt cannot be cast to a char* as casting will not
   * produce an lvalue. Use the new value pointed to by tmpp as the value for
   * val->salt.
   */
  char *tmpp;
  setup();
  { begin_structure();
    get_field(val->etype,0,asn1_decode_enctype);
    if (tagnum == 1) {
      get_lenfield(val->length,tmpp,1,asn1_decode_generalstring);
      val->salt = (krb5_octet*)tmpp;    /* SUNW14resync hack */
    } else {
            val->length = KRB5_ETYPE_NO_SALT;
            val->salt = 0;
    }
    if ( tagnum ==2) {
      krb5_octet *params ;
      get_lenfield( val->s2kparams.length, params,
                      2, asn1_decode_octetstring);
      val->s2kparams.data = ( char *) params;
    } else {
        val->s2kparams.data = NULL;
        val->s2kparams.length = 0;
    }
    end_structure();
    val->magic = KV5M_ETYPE_INFO_ENTRY;
  }
  cleanup();
}

static asn1_error_code asn1_decode_etype_info2_entry_1_3(asn1buf *buf, krb5_etype_info_entry *val )
{
  setup();
  { begin_structure();
    get_field(val->etype,0,asn1_decode_enctype);
    if (tagnum == 1) {
            get_lenfield(val->length,val->salt,1,asn1_decode_octetstring);
    } else {
            val->length = KRB5_ETYPE_NO_SALT;
            val->salt = 0;
    }
    if ( tagnum ==2) {
      krb5_octet *params ;
      get_lenfield( val->s2kparams.length, params,
                      2, asn1_decode_octetstring);
      val->s2kparams.data = ( char *) params;
    } else {
        val->s2kparams.data = NULL;
        val->s2kparams.length = 0;
    }
    end_structure();
    val->magic = KV5M_ETYPE_INFO_ENTRY;
  }
  cleanup();
}


static asn1_error_code asn1_decode_etype_info_entry(asn1buf *buf, krb5_etype_info_entry *val )
{
  setup();
  { begin_structure();
    get_field(val->etype,0,asn1_decode_enctype);
    if (tagnum == 1) {
            get_lenfield(val->length,val->salt,1,asn1_decode_octetstring);
    } else {
            val->length = KRB5_ETYPE_NO_SALT;
            val->salt = 0;
    }
    val->s2kparams.data = NULL;
    val->s2kparams.length = 0;

    end_structure();
    val->magic = KV5M_ETYPE_INFO_ENTRY;
  }
  cleanup();
}

asn1_error_code asn1_decode_etype_info(asn1buf *buf, krb5_etype_info_entry ***val )
{
  decode_array_body(krb5_etype_info_entry,asn1_decode_etype_info_entry);
}

asn1_error_code asn1_decode_etype_info2(asn1buf *buf, krb5_etype_info_entry ***val ,
                                        krb5_boolean v1_3_behavior)
{
    if (v1_3_behavior) {
        decode_array_body(krb5_etype_info_entry,
                          asn1_decode_etype_info2_entry_1_3);
    } else {
        decode_array_body(krb5_etype_info_entry,
                          asn1_decode_etype_info2_entry);
    }
}

asn1_error_code asn1_decode_passwdsequence(asn1buf *buf, passwd_phrase_element *val)
{
  setup();
  { begin_structure();
    alloc_field(val->passwd,krb5_data);
    get_lenfield(val->passwd->length,val->passwd->data,
                 0,asn1_decode_charstring);
    val->passwd->magic = KV5M_DATA;
    alloc_field(val->phrase,krb5_data);
    get_lenfield(val->phrase->length,val->phrase->data,
                 1,asn1_decode_charstring);
    val->phrase->magic = KV5M_DATA;
    end_structure();
    val->magic = KV5M_PASSWD_PHRASE_ELEMENT;
  }
  cleanup();
}

asn1_error_code asn1_decode_sequence_of_passwdsequence(asn1buf *buf, passwd_phrase_element ***val)
{
  decode_array_body(passwd_phrase_element,asn1_decode_passwdsequence);
}

asn1_error_code asn1_decode_sam_flags(asn1buf *buf, krb5_flags *val)
{ return asn1_decode_krb5_flags(buf,val); }

#define opt_string(val,n,fn) opt_lenfield((val).length,(val).data,n,fn)
#define opt_cksum(var,tagexpect,decoder)\
if(tagnum == (tagexpect)){\
  get_field_body(var,decoder); }\
else var.length = 0

asn1_error_code asn1_decode_sam_challenge(asn1buf *buf, krb5_sam_challenge *val)
{
  setup();
  { begin_structure();
    get_field(val->sam_type,0,asn1_decode_int32);
    get_field(val->sam_flags,1,asn1_decode_sam_flags);
    opt_string(val->sam_type_name,2,asn1_decode_charstring);
    opt_string(val->sam_track_id,3,asn1_decode_charstring);
    opt_string(val->sam_challenge_label,4,asn1_decode_charstring);
    opt_string(val->sam_challenge,5,asn1_decode_charstring);
    opt_string(val->sam_response_prompt,6,asn1_decode_charstring);
    opt_string(val->sam_pk_for_sad,7,asn1_decode_charstring);
    opt_field(val->sam_nonce,8,asn1_decode_int32,0);
    opt_cksum(val->sam_cksum,9,asn1_decode_checksum);
    end_structure();
    val->magic = KV5M_SAM_CHALLENGE;
  }
  cleanup();
}
asn1_error_code asn1_decode_sam_challenge_2(asn1buf *buf, krb5_sam_challenge_2 *val)
{
  setup();
  { char *save, *end;
    size_t alloclen;
    begin_structure();
    if (tagnum != 0) return ASN1_MISSING_FIELD;
    if (asn1class != CONTEXT_SPECIFIC || construction != CONSTRUCTED)
      return ASN1_BAD_ID;
    save = subbuf.next;
    { sequence_of_no_tagvars(&subbuf);
      unused_var(size);
      end_sequence_of_no_tagvars(&subbuf);
    }
    end = subbuf.next;
    alloclen = end - save;
    if ((val->sam_challenge_2_body.data = (char *) malloc(alloclen)) == NULL)
      return ENOMEM;
    val->sam_challenge_2_body.length = alloclen;
    memcpy(val->sam_challenge_2_body.data, save, alloclen);
    next_tag();
    get_field(val->sam_cksum, 1, asn1_decode_sequence_of_checksum);
    end_structure();
  }
  cleanup();
}
asn1_error_code asn1_decode_sam_challenge_2_body(asn1buf *buf, krb5_sam_challenge_2_body *val)
{
  setup();
  { begin_structure();
    get_field(val->sam_type,0,asn1_decode_int32);
    get_field(val->sam_flags,1,asn1_decode_sam_flags);
    opt_string(val->sam_type_name,2,asn1_decode_charstring);
    opt_string(val->sam_track_id,3,asn1_decode_charstring);
    opt_string(val->sam_challenge_label,4,asn1_decode_charstring);
    opt_string(val->sam_challenge,5,asn1_decode_charstring);
    opt_string(val->sam_response_prompt,6,asn1_decode_charstring);
    opt_string(val->sam_pk_for_sad,7,asn1_decode_charstring);
    get_field(val->sam_nonce,8,asn1_decode_int32);
    get_field(val->sam_etype, 9, asn1_decode_int32);
    end_structure();
    val->magic = KV5M_SAM_CHALLENGE;
  }
  cleanup();
}
asn1_error_code asn1_decode_enc_sam_key(asn1buf *buf, krb5_sam_key *val)
{
  setup();
  { begin_structure();
    /* alloc_field(val->sam_key,krb5_keyblock); */
    get_field(val->sam_key,0,asn1_decode_encryption_key);
    end_structure();
    val->magic = KV5M_SAM_KEY;
  }
  cleanup();
}

asn1_error_code asn1_decode_enc_sam_response_enc(asn1buf *buf, krb5_enc_sam_response_enc *val)
{
  setup();
  { begin_structure();
    opt_field(val->sam_nonce,0,asn1_decode_int32,0);
    opt_field(val->sam_timestamp,1,asn1_decode_kerberos_time,0);
    opt_field(val->sam_usec,2,asn1_decode_int32,0);
    opt_string(val->sam_sad,3,asn1_decode_charstring);
    end_structure();
    val->magic = KV5M_ENC_SAM_RESPONSE_ENC;
  }
  cleanup();
}

asn1_error_code asn1_decode_enc_sam_response_enc_2(asn1buf *buf, krb5_enc_sam_response_enc_2 *val)
{
  setup();
  { begin_structure();
    get_field(val->sam_nonce,0,asn1_decode_int32);
    opt_string(val->sam_sad,1,asn1_decode_charstring);
    end_structure();
    val->magic = KV5M_ENC_SAM_RESPONSE_ENC_2;
  }
  cleanup();
}

#define opt_encfield(fld,tag,fn) \
    if(tagnum == tag){ \
      get_field(fld,tag,fn); } \
    else{\
      fld.magic = 0;\
      fld.enctype = 0;\
      fld.kvno = 0;\
      fld.ciphertext.data = NULL;\
      fld.ciphertext.length = 0;\
    }

asn1_error_code asn1_decode_sam_response(asn1buf *buf, krb5_sam_response *val)
{
  setup();
  { begin_structure();
    get_field(val->sam_type,0,asn1_decode_int32);
    get_field(val->sam_flags,1,asn1_decode_sam_flags);
    opt_string(val->sam_track_id,2,asn1_decode_charstring);
    opt_encfield(val->sam_enc_key,3,asn1_decode_encrypted_data);
    get_field(val->sam_enc_nonce_or_ts,4,asn1_decode_encrypted_data);
    opt_field(val->sam_nonce,5,asn1_decode_int32,0);
    opt_field(val->sam_patimestamp,6,asn1_decode_kerberos_time,0);
    end_structure();
    val->magic = KV5M_SAM_RESPONSE;
  }
  cleanup();
}

asn1_error_code asn1_decode_sam_response_2(asn1buf *buf, krb5_sam_response_2 *val)
{
  setup();
  { begin_structure();
    get_field(val->sam_type,0,asn1_decode_int32);
    get_field(val->sam_flags,1,asn1_decode_sam_flags);
    opt_string(val->sam_track_id,2,asn1_decode_charstring);
    get_field(val->sam_enc_nonce_or_sad,3,asn1_decode_encrypted_data);
    get_field(val->sam_nonce,4,asn1_decode_int32);
    end_structure();
    val->magic = KV5M_SAM_RESPONSE;
  }
  cleanup();
}


asn1_error_code asn1_decode_predicted_sam_response(asn1buf *buf, krb5_predicted_sam_response *val)
{
  setup();
  { begin_structure();
    get_field(val->sam_key,0,asn1_decode_encryption_key);
    get_field(val->sam_flags,1,asn1_decode_sam_flags);
    get_field(val->stime,2,asn1_decode_kerberos_time);
    get_field(val->susec,3,asn1_decode_int32);
    alloc_field(val->client,krb5_principal_data);
    get_field(val->client,4,asn1_decode_realm);
    get_field(val->client,5,asn1_decode_principal_name);
    opt_string(val->msd,6,asn1_decode_charstring); /* should be octet */
    end_structure();
    val->magic = KV5M_PREDICTED_SAM_RESPONSE;
  }
  cleanup();
}

/* PKINIT */

asn1_error_code asn1_decode_external_principal_identifier(asn1buf *buf, krb5_external_principal_identifier *val)
{
    setup();
    {
      begin_structure();
      opt_implicit_octet_string(val->subjectName.length, val->subjectName.data, 0);
      opt_implicit_octet_string(val->issuerAndSerialNumber.length, val->issuerAndSerialNumber.data, 1);
      opt_implicit_octet_string(val->subjectKeyIdentifier.length, val->subjectKeyIdentifier.data, 2);
      end_structure();
    }
    cleanup();
}

asn1_error_code asn1_decode_sequence_of_external_principal_identifier(asn1buf *buf, krb5_external_principal_identifier ***val)
{
    decode_array_body(krb5_external_principal_identifier,asn1_decode_external_principal_identifier);
}

asn1_error_code asn1_decode_pa_pk_as_req(asn1buf *buf, krb5_pa_pk_as_req *val)
{
  setup();
  {
    begin_structure();
    get_implicit_octet_string(val->signedAuthPack.length, val->signedAuthPack.data, 0);
    opt_field(val->trustedCertifiers, 1, asn1_decode_sequence_of_external_principal_identifier, NULL);
    opt_implicit_octet_string(val->kdcPkId.length, val->kdcPkId.data, 2);
    end_structure();
  }
  cleanup();
}

#if 0   /* XXX   This needs to be tested!!! XXX */
asn1_error_code asn1_decode_trusted_ca(asn1buf *buf, krb5_trusted_ca *val)
{
    setup();
    {
      char *start, *end;
      size_t alloclen;

      begin_explicit_choice();
      if (t.tagnum == choice_trusted_cas_principalName) {
        val->choice = choice_trusted_cas_principalName;
      } else if (t.tagnum == choice_trusted_cas_caName) {
        val->choice = choice_trusted_cas_caName;
        start = subbuf.next;
        {
          sequence_of_no_tagvars(&subbuf);
          unused_var(size);
          end_sequence_of_no_tagvars(&subbuf);
        }
        end = subbuf.next;
        alloclen = end - start;
        val->u.caName.data = malloc(alloclen);
        if (val->u.caName.data == NULL)
          return ENOMEM;
        memcpy(val->u.caName.data, start, alloclen);
        val->u.caName.length = alloclen;
        next_tag();
      } else if (t.tagnum == choice_trusted_cas_issuerAndSerial) {
        val->choice = choice_trusted_cas_issuerAndSerial;
        start = subbuf.next;
        {
          sequence_of_no_tagvars(&subbuf);
          unused_var(size);
          end_sequence_of_no_tagvars(&subbuf);
        }
        end = subbuf.next;
        alloclen = end - start;
        val->u.issuerAndSerial.data = malloc(alloclen);
        if (val->u.issuerAndSerial.data == NULL)
          return ENOMEM;
        memcpy(val->u.issuerAndSerial.data, start, alloclen);
        val->u.issuerAndSerial.length = alloclen;
        next_tag();
      } else return ASN1_BAD_ID;
      end_explicit_choice();
    }
    cleanup();
}
#else
asn1_error_code asn1_decode_trusted_ca(asn1buf *buf, krb5_trusted_ca *val)
{
    setup();
    { begin_choice();
      if (tagnum == choice_trusted_cas_principalName) {
        val->choice = choice_trusted_cas_principalName;
        asn1_decode_krb5_principal_name(&subbuf, &(val->u.principalName));
      } else if (tagnum == choice_trusted_cas_caName) {
        val->choice = choice_trusted_cas_caName;
        get_implicit_octet_string(val->u.caName.length, val->u.caName.data, choice_trusted_cas_caName);
      } else if (tagnum == choice_trusted_cas_issuerAndSerial) {
        val->choice = choice_trusted_cas_issuerAndSerial;
        get_implicit_octet_string(val->u.issuerAndSerial.length, val->u.issuerAndSerial.data,
                                  choice_trusted_cas_issuerAndSerial);
      } else return ASN1_BAD_ID;
      end_choice();
    }
    cleanup();
}
#endif

asn1_error_code asn1_decode_sequence_of_trusted_ca(asn1buf *buf, krb5_trusted_ca ***val)
{
    decode_array_body(krb5_trusted_ca, asn1_decode_trusted_ca);
}

asn1_error_code asn1_decode_pa_pk_as_req_draft9(asn1buf *buf, krb5_pa_pk_as_req_draft9 *val)
{
  setup();
  { begin_structure();
    get_implicit_octet_string(val->signedAuthPack.length, val->signedAuthPack.data, 0);
    opt_field(val->trustedCertifiers, 1, asn1_decode_sequence_of_trusted_ca, NULL);
    opt_lenfield(val->kdcCert.length, val->kdcCert.data, 2, asn1_decode_octetstring);
    opt_lenfield(val->encryptionCert.length, val->encryptionCert.data, 2, asn1_decode_octetstring);
    end_structure();
  }
  cleanup();
}

asn1_error_code asn1_decode_dh_rep_info(asn1buf *buf, krb5_dh_rep_info *val)
{
    setup();
    { begin_structure();
      get_implicit_octet_string(val->dhSignedData.length, val->dhSignedData.data, 0);

      opt_lenfield(val->serverDHNonce.length, val->serverDHNonce.data, 1, asn1_decode_octetstring);
      end_structure();
    }
    cleanup();
}

asn1_error_code asn1_decode_pk_authenticator(asn1buf *buf, krb5_pk_authenticator *val)
{
    setup();
    { begin_structure();
      get_field(val->cusec, 0, asn1_decode_int32);
      get_field(val->ctime, 1, asn1_decode_kerberos_time);
      get_field(val->nonce, 2, asn1_decode_int32);
      opt_lenfield(val->paChecksum.length, val->paChecksum.contents, 3, asn1_decode_octetstring);
      end_structure();
    }
    cleanup();
}

asn1_error_code asn1_decode_pk_authenticator_draft9(asn1buf *buf, krb5_pk_authenticator_draft9 *val)
{
    setup();
    { begin_structure();
      alloc_field(val->kdcName,krb5_principal_data);
      get_field(val->kdcName, 0, asn1_decode_principal_name);
      get_field(val->kdcName, 1, asn1_decode_realm);
      get_field(val->cusec, 2, asn1_decode_int32);
      get_field(val->ctime, 3, asn1_decode_kerberos_time);
      get_field(val->nonce, 4, asn1_decode_int32);
      end_structure();
    }
    cleanup();
}

asn1_error_code asn1_decode_algorithm_identifier(asn1buf *buf,  krb5_algorithm_identifier *val) {

  setup();
  { begin_structure_no_tag();
    /*
     * Forbid indefinite encoding because we don't read enough tag
     * information from the trailing octets ("ANY DEFINED BY") to
     * synchronize EOC tags, etc.
     */
    if (seqindef) return ASN1_BAD_FORMAT;
    /*
     * Set up tag variables because we don't actually call anything
     * that fetches tag info for us; it's all buried in the decoder
     * primitives.
     */
    tagnum = ASN1_TAGNUM_CEILING;
    asn1class = UNIVERSAL;
    construction = PRIMITIVE;
    taglen = 0;
    indef = 0;
    retval = asn1_decode_oid(&subbuf, &val->algorithm.length,
                             &val->algorithm.data);
    if(retval) return retval;
    val->parameters.length = 0;
    val->parameters.data = NULL;

    if(length > subbuf.next - subbuf.base) {
      unsigned int size = length - (subbuf.next - subbuf.base);
      retval = asn1buf_remove_octetstring(&subbuf, size,
                                          &val->parameters.data);
      if(retval) return retval;
      val->parameters.length = size;
    }

    end_structure();
  }
  cleanup();
}

asn1_error_code asn1_decode_subject_pk_info(asn1buf *buf, krb5_subject_pk_info *val)
{
    asn1_octet unused;
    setup();
    { begin_structure_no_tag();

      retval = asn1_decode_algorithm_identifier(&subbuf, &val->algorithm);
      if (retval) return retval;

      /* SubjectPublicKey encoded as a BIT STRING */
      next_tag();
      if (asn1class != UNIVERSAL || construction != PRIMITIVE ||
          tagnum != ASN1_BITSTRING)
        return ASN1_BAD_ID;

      retval = asn1buf_remove_octet(&subbuf, &unused);
      if(retval) return retval;

      /* Number of unused bits must be between 0 and 7. */
      /* What to do if unused is not zero? */
      if (unused > 7) return ASN1_BAD_FORMAT;
      taglen--;

      val->subjectPublicKey.length = 0;
      val->subjectPublicKey.data = NULL;
      retval = asn1buf_remove_octetstring(&subbuf, taglen,
                                          &val->subjectPublicKey.data);
      if(retval) return retval;
      val->subjectPublicKey.length = taglen;
      /*
       * We didn't call any macro that does next_tag(); do so now to
       * preload tag of any trailing encodings.
       */
      next_tag();
      end_structure();
    }
    cleanup();
}

asn1_error_code asn1_decode_sequence_of_algorithm_identifier(asn1buf *buf, krb5_algorithm_identifier ***val)
{
    decode_array_body(krb5_algorithm_identifier, asn1_decode_algorithm_identifier);
}

asn1_error_code asn1_decode_kdc_dh_key_info (asn1buf *buf, krb5_kdc_dh_key_info *val)
{
    setup();
    { begin_structure();
      retval = asn1buf_remove_octetstring(&subbuf, taglen, &val->subjectPublicKey.data);
      if(retval) return retval;
      val->subjectPublicKey.length = taglen;
      next_tag();
      get_field(val->nonce, 1, asn1_decode_int32);
      opt_field(val->dhKeyExpiration, 2, asn1_decode_kerberos_time, 0);
      end_structure();
    }
    cleanup();
}

asn1_error_code asn1_decode_reply_key_pack (asn1buf *buf, krb5_reply_key_pack *val)
{
    setup();
    { begin_structure();
      get_field(val->replyKey, 0, asn1_decode_encryption_key);
      get_field(val->asChecksum, 1, asn1_decode_checksum);
      end_structure();
    }
    cleanup();
}

asn1_error_code asn1_decode_reply_key_pack_draft9 (asn1buf *buf, krb5_reply_key_pack_draft9 *val)
{
    setup();
    { begin_structure();
      get_field(val->replyKey, 0, asn1_decode_encryption_key);
      get_field(val->nonce, 1, asn1_decode_int32);
      end_structure();
    }
    cleanup();
}


asn1_error_code asn1_decode_krb5_principal_name (asn1buf *buf, krb5_principal *val)
{
    setup();
    { begin_structure();
      get_field(*val, 0, asn1_decode_realm);
      get_field(*val, 1, asn1_decode_principal_name);
      end_structure();
    }
    cleanup();
}

asn1_error_code asn1_decode_auth_pack(asn1buf *buf, krb5_auth_pack *val)
{
    setup();
    { begin_structure();
      get_field(val->pkAuthenticator, 0, asn1_decode_pk_authenticator);
      if (tagnum == 1) { alloc_field(val->clientPublicValue, krb5_subject_pk_info); }
      /* can't call opt_field because it does decoder(&subbuf, &(val)); */
      if (asn1buf_remains(&subbuf, seqindef)) {
        if ((asn1class != CONTEXT_SPECIFIC || construction != CONSTRUCTED)
            && (tagnum || taglen || asn1class != UNIVERSAL))
          return ASN1_BAD_ID;
        if (tagnum == 1) {
          retval = asn1_decode_subject_pk_info(&subbuf,
                                               val->clientPublicValue);
          if (!taglen && indef) { get_eoc(); }
          next_tag();
        } else val->clientPublicValue = NULL;
      }
      /* can't call opt_field because it does decoder(&subbuf, &(val)); */
      if (asn1buf_remains(&subbuf, seqindef)) {
        if (tagnum == 2) {
          asn1_decode_sequence_of_algorithm_identifier(&subbuf, &val->supportedCMSTypes);
          if (!taglen && indef) { get_eoc(); }
          next_tag();
        } else val->supportedCMSTypes = NULL;
      }
      opt_lenfield(val->clientDHNonce.length, val->clientDHNonce.data, 3, asn1_decode_octetstring);
      end_structure();
    }
    cleanup();
}

asn1_error_code asn1_decode_auth_pack_draft9(asn1buf *buf, krb5_auth_pack_draft9 *val)
{
    setup();
    { begin_structure();
      get_field(val->pkAuthenticator, 0, asn1_decode_pk_authenticator_draft9);
      if (tagnum == 1) {
        alloc_field(val->clientPublicValue, krb5_subject_pk_info);
        /* can't call opt_field because it does decoder(&subbuf, &(val)); */
        if (asn1buf_remains(&subbuf, seqindef)) {
          if ((asn1class != CONTEXT_SPECIFIC || construction != CONSTRUCTED)
            && (tagnum || taglen || asn1class != UNIVERSAL))
            return ASN1_BAD_ID;
          if (tagnum == 1) {
            retval = asn1_decode_subject_pk_info(&subbuf,
                                                 val->clientPublicValue);
            if (!taglen && indef) { get_eoc(); }
            next_tag();
          } else val->clientPublicValue = NULL;
        }
      }
      end_structure();
    }
    cleanup();
}

asn1_error_code asn1_decode_pa_pk_as_rep(asn1buf *buf, krb5_pa_pk_as_rep *val)
{
  setup();
  { begin_choice();
    if (tagnum == choice_pa_pk_as_rep_dhInfo) {
      val->choice = choice_pa_pk_as_rep_dhInfo;
      get_field_body(val->u.dh_Info, asn1_decode_dh_rep_info);
    } else if (tagnum == choice_pa_pk_as_rep_encKeyPack) {
      val->choice = choice_pa_pk_as_rep_encKeyPack;
      get_implicit_octet_string(val->u.encKeyPack.length, val->u.encKeyPack.data,
                                choice_pa_pk_as_rep_encKeyPack);
    } else {
      val->choice = choice_pa_pk_as_rep_UNKNOWN;
    }
    end_choice();
  }
  cleanup();
}

asn1_error_code asn1_decode_pa_pk_as_rep_draft9(asn1buf *buf, krb5_pa_pk_as_rep_draft9 *val)
{
  setup();
  { begin_structure();
    if (tagnum == choice_pa_pk_as_rep_draft9_dhSignedData) {
      val->choice = choice_pa_pk_as_rep_draft9_dhSignedData;
      get_lenfield(val->u.dhSignedData.length, val->u.dhSignedData.data,
                    choice_pa_pk_as_rep_draft9_dhSignedData, asn1_decode_octetstring);
    } else if (tagnum == choice_pa_pk_as_rep_draft9_encKeyPack) {
      val->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
      get_lenfield(val->u.encKeyPack.length, val->u.encKeyPack.data,
                    choice_pa_pk_as_rep_draft9_encKeyPack, asn1_decode_octetstring);
    } else {
      val->choice = choice_pa_pk_as_rep_draft9_UNKNOWN;
    }
    end_structure();
  }
  cleanup();
}

asn1_error_code asn1_decode_sequence_of_typed_data(asn1buf *buf, krb5_typed_data ***val)
{
    decode_array_body(krb5_typed_data,asn1_decode_typed_data);
}

asn1_error_code asn1_decode_typed_data(asn1buf *buf, krb5_typed_data *val)
{
  setup();
  { begin_structure();
    get_field(val->type,0,asn1_decode_int32);
    get_lenfield(val->length,val->data,1,asn1_decode_octetstring);
    end_structure();
  }
  cleanup();
}