2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A
2N/A/*
2N/A * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/* Coding Buffer Implementation */
2N/A
2N/A/*
2N/A * Implementation
2N/A *
2N/A * Encoding mode
2N/A *
2N/A * The encoding buffer is filled from bottom (lowest address) to top
2N/A * (highest address). This makes it easier to expand the buffer,
2N/A * since realloc preserves the existing portion of the buffer.
2N/A *
2N/A * Note: Since ASN.1 encoding must be done in reverse, this means
2N/A * that you can't simply memcpy out the buffer data, since it will be
2N/A * backwards. You need to reverse-iterate through it, instead.
2N/A *
2N/A * ***This decision may have been a mistake. In practice, the
2N/A * implementation will probably be tuned such that reallocation is
2N/A * rarely necessary. Also, the realloc probably has recopy the
2N/A * buffer itself, so we don't really gain that much by avoiding an
2N/A * explicit copy of the buffer. --Keep this in mind for future reference.
2N/A *
2N/A *
2N/A * Decoding mode
2N/A *
2N/A * The decoding buffer is in normal order and is created by wrapping
2N/A * an asn1buf around a krb5_data structure.
2N/A */
2N/A
2N/A/*
2N/A * Abstraction Function
2N/A *
2N/A * Programs should use just pointers to asn1buf's (e.g. asn1buf *mybuf).
2N/A * These pointers must always point to a valid, allocated asn1buf
2N/A * structure or be NULL.
2N/A *
2N/A * The contents of the asn1buf represent an octet string. This string
2N/A * begins at base and continues to the octet immediately preceding next.
2N/A * If next == base or mybuf == NULL, then the asn1buf represents an empty
2N/A * octet string.
2N/A */
2N/A
2N/A/*
2N/A * Representation Invariant
2N/A *
2N/A * Pointers to asn1buf's must always point to a valid, allocated
2N/A * asn1buf structure or be NULL.
2N/A *
2N/A * base points to a valid, allocated octet array or is NULL
2N/A * bound, if non-NULL, points to the last valid octet
2N/A * next >= base
2N/A * next <= bound+2 (i.e. next should be able to step just past the bound,
2N/A * but no further. (The bound should move out in response
2N/A * to being crossed by next.))
2N/A */
2N/A
2N/A#define ASN1BUF_OMIT_INLINE_FUNCS
2N/A#include "asn1buf.h"
2N/A#include <stdio.h>
2N/A#include "asn1_get.h"
2N/A
2N/A#ifdef USE_VALGRIND
2N/A#include <valgrind/memcheck.h>
2N/A#else
2N/A#define VALGRIND_CHECK_READABLE(PTR,SIZE) ((void)0)
2N/A#endif
2N/A
2N/A#if !defined(__GNUC__) || defined(CONFIG_SMALL)
2N/A/*
2N/A * Declare private procedures as static if they're not used for inline
2N/A * expansion of other stuff elsewhere.
2N/A */
2N/Astatic unsigned int asn1buf_free(const asn1buf *);
2N/Astatic asn1_error_code asn1buf_ensure_space(asn1buf *, unsigned int);
2N/Astatic asn1_error_code asn1buf_expand(asn1buf *, unsigned int);
2N/A#endif
2N/A
2N/A#define asn1_is_eoc(class, num, indef) \
2N/A ((class) == UNIVERSAL && !(num) && !(indef))
2N/A
2N/Aasn1_error_code
2N/Aasn1buf_create(asn1buf **buf)
2N/A{
2N/A *buf = (asn1buf*)malloc(sizeof(asn1buf));
2N/A if (*buf == NULL) return ENOMEM;
2N/A (*buf)->base = NULL;
2N/A (*buf)->bound = NULL;
2N/A (*buf)->next = NULL;
2N/A return 0;
2N/A}
2N/A
2N/Aasn1_error_code
2N/Aasn1buf_wrap_data(asn1buf *buf, const krb5_data *code)
2N/A{
2N/A if (code == NULL || code->data == NULL) return ASN1_MISSING_FIELD;
2N/A buf->next = buf->base = code->data;
2N/A buf->bound = code->data + code->length - 1;
2N/A return 0;
2N/A}
2N/A
2N/Aasn1_error_code
2N/Aasn1buf_imbed(asn1buf *subbuf, const asn1buf *buf, const unsigned int length, const int indef)
2N/A{
2N/A if (buf->next > buf->bound + 1) return ASN1_OVERRUN;
2N/A subbuf->base = subbuf->next = buf->next;
2N/A if (!indef) {
2N/A if (length > (size_t)(buf->bound + 1 - buf->next)) return ASN1_OVERRUN;
2N/A subbuf->bound = subbuf->base + length - 1;
2N/A } else /* constructed indefinite */
2N/A subbuf->bound = buf->bound;
2N/A return 0;
2N/A}
2N/A
2N/Aasn1_error_code
2N/Aasn1buf_sync(asn1buf *buf, asn1buf *subbuf,
2N/A asn1_class asn1class, asn1_tagnum lasttag,
2N/A unsigned int length, int indef, int seqindef)
2N/A{
2N/A asn1_error_code retval;
2N/A
2N/A if (!seqindef) {
2N/A /* sequence was encoded as definite length */
2N/A buf->next = subbuf->bound + 1;
2N/A } else if (!asn1_is_eoc(asn1class, lasttag, indef)) {
2N/A retval = asn1buf_skiptail(subbuf, length, indef);
2N/A if (retval)
2N/A return retval;
2N/A } else {
2N/A /* We have just read the EOC octets. */
2N/A buf->next = subbuf->next;
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/Aasn1_error_code
2N/Aasn1buf_skiptail(asn1buf *buf, const unsigned int length, const int indef)
2N/A{
2N/A asn1_error_code retval;
2N/A taginfo t;
2N/A int nestlevel;
2N/A
2N/A nestlevel = 1 + indef;
2N/A if (!indef) {
2N/A if (length <= (size_t)(buf->bound - buf->next + 1))
2N/A buf->next += length;
2N/A else
2N/A return ASN1_OVERRUN;
2N/A }
2N/A while (nestlevel > 0) {
2N/A if (buf->bound - buf->next + 1 <= 0)
2N/A return ASN1_OVERRUN;
2N/A retval = asn1_get_tag_2(buf, &t);
2N/A if (retval) return retval;
2N/A if (!t.indef) {
2N/A if (t.length <= (size_t)(buf->bound - buf->next + 1))
2N/A buf->next += t.length;
2N/A else
2N/A return ASN1_OVERRUN;
2N/A }
2N/A if (t.indef)
2N/A nestlevel++;
2N/A if (asn1_is_eoc(t.asn1class, t.tagnum, t.indef))
2N/A nestlevel--; /* got an EOC encoding */
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/Avoid
2N/Aasn1buf_destroy(asn1buf **buf)
2N/A{
2N/A if (*buf != NULL) {
2N/A free((*buf)->base);
2N/A free(*buf);
2N/A *buf = NULL;
2N/A }
2N/A}
2N/A
2N/A#ifdef asn1buf_insert_octet
2N/A#undef asn1buf_insert_octet
2N/A#endif
2N/Aasn1_error_code
2N/Aasn1buf_insert_octet(asn1buf *buf, const int o)
2N/A{
2N/A asn1_error_code retval;
2N/A
2N/A retval = asn1buf_ensure_space(buf,1U);
2N/A if (retval) return retval;
2N/A *(buf->next) = (char)o;
2N/A (buf->next)++;
2N/A return 0;
2N/A}
2N/A
2N/Aasn1_error_code
2N/Aasn1buf_insert_bytestring(asn1buf *buf, const unsigned int len, const void *sv)
2N/A{
2N/A asn1_error_code retval;
2N/A unsigned int length;
2N/A const char *s = sv;
2N/A
2N/A retval = asn1buf_ensure_space(buf,len);
2N/A if (retval) return retval;
2N/A VALGRIND_CHECK_READABLE(sv, len);
2N/A for (length=1; length<=len; length++,(buf->next)++)
2N/A *(buf->next) = (s[len-length]);
2N/A return 0;
2N/A}
2N/A
2N/A
2N/A#undef asn1buf_remove_octet
2N/Aasn1_error_code asn1buf_remove_octet(asn1buf *buf, asn1_octet *o)
2N/A{
2N/A if (buf->next > buf->bound) return ASN1_OVERRUN;
2N/A *o = (asn1_octet)(*((buf->next)++));
2N/A return 0;
2N/A}
2N/A
2N/Aasn1_error_code
2N/Aasn1buf_remove_octetstring(asn1buf *buf, const unsigned int len, asn1_octet **s)
2N/A{
2N/A unsigned int i;
2N/A
2N/A if (buf->next > buf->bound + 1) return ASN1_OVERRUN;
2N/A if (len > (size_t)(buf->bound + 1 - buf->next)) return ASN1_OVERRUN;
2N/A if (len == 0) {
2N/A *s = 0;
2N/A return 0;
2N/A }
2N/A *s = (asn1_octet*)malloc(len*sizeof(asn1_octet));
2N/A if (*s == NULL)
2N/A return ENOMEM;
2N/A for (i=0; i<len; i++)
2N/A (*s)[i] = (asn1_octet)(buf->next)[i];
2N/A buf->next += len;
2N/A return 0;
2N/A}
2N/A
2N/Aasn1_error_code
2N/Aasn1buf_remove_charstring(asn1buf *buf, const unsigned int len, char **s)
2N/A{
2N/A unsigned int i;
2N/A
2N/A if (buf->next > buf->bound + 1) return ASN1_OVERRUN;
2N/A if (len > (size_t)(buf->bound + 1 - buf->next)) return ASN1_OVERRUN;
2N/A if (len == 0) {
2N/A *s = 0;
2N/A return 0;
2N/A }
2N/A *s = (char*)malloc(len*sizeof(char));
2N/A if (*s == NULL) return ENOMEM;
2N/A for (i=0; i<len; i++)
2N/A (*s)[i] = (char)(buf->next)[i];
2N/A buf->next += len;
2N/A return 0;
2N/A}
2N/A
2N/Aint
2N/Aasn1buf_remains(asn1buf *buf, int indef)
2N/A{
2N/A int remain;
2N/A if (buf == NULL || buf->base == NULL) return 0;
2N/A remain = buf->bound - buf->next +1;
2N/A if (remain <= 0) return remain;
2N/A /*
2N/A * Two 0 octets means the end of an indefinite encoding.
2N/A */
2N/A if (indef && remain >= 2 && !*(buf->next) && !*(buf->next + 1))
2N/A return 0;
2N/A else return remain;
2N/A}
2N/A
2N/Aasn1_error_code
2N/Aasn12krb5_buf(const asn1buf *buf, krb5_data **code)
2N/A{
2N/A unsigned int i;
2N/A krb5_data *d;
2N/A
2N/A *code = NULL;
2N/A
2N/A d = calloc(1, sizeof(krb5_data));
2N/A if (d == NULL)
2N/A return ENOMEM;
2N/A d->length = asn1buf_len(buf);
2N/A d->data = malloc(d->length + 1);
2N/A if (d->data == NULL) {
2N/A free(d);
2N/A return ENOMEM;
2N/A }
2N/A for (i=0; i < d->length; i++)
2N/A d->data[i] = buf->base[d->length - i - 1];
2N/A d->data[d->length] = '\0';
2N/A d->magic = KV5M_DATA;
2N/A *code = d;
2N/A return 0;
2N/A}
2N/A
2N/A
2N/A
2N/A/*
2N/A * These parse and unparse procedures should be moved out. They're
2N/A * useful only for debugging and superfluous in the production
2N/A * version.
2N/A */
2N/A
2N/Aasn1_error_code
2N/Aasn1buf_unparse(const asn1buf *buf, char **s)
2N/A{
2N/A free(*s);
2N/A if (buf == NULL) {
2N/A *s = strdup("<NULL>");
2N/A if (*s == NULL) return ENOMEM;
2N/A } else if (buf->base == NULL) {
2N/A *s = strdup("<EMPTY>");
2N/A if (*s == NULL) return ENOMEM;
2N/A } else {
2N/A unsigned int length = asn1buf_len(buf);
2N/A unsigned int i;
2N/A
2N/A *s = calloc(length+1, sizeof(char));
2N/A if (*s == NULL) return ENOMEM;
2N/A (*s)[length] = '\0';
2N/A for (i=0; i<length; i++) ;
2N/A/* OLDDECLARG( (*s)[i] = , (buf->base)[length-i-1]) */
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/Aasn1_error_code
2N/Aasn1buf_hex_unparse(const asn1buf *buf, char **s)
2N/A{
2N/A#define hexchar(d) ((d)<=9 ? ('0'+(d)) : \
2N/A ((d)<=15 ? ('A'+(d)-10) : \
2N/A 'X'))
2N/A
2N/A free(*s);
2N/A
2N/A if (buf == NULL) {
2N/A *s = strdup("<NULL>");
2N/A if (*s == NULL) return ENOMEM;
2N/A } else if (buf->base == NULL) {
2N/A *s = strdup("<EMPTY>");
2N/A if (*s == NULL) return ENOMEM;
2N/A } else {
2N/A unsigned int length = asn1buf_len(buf);
2N/A int i;
2N/A
2N/A *s = malloc(3*length);
2N/A if (*s == NULL) return ENOMEM;
2N/A for (i = length-1; i >= 0; i--) {
2N/A (*s)[3*(length-i-1)] = hexchar(((buf->base)[i]&0xF0)>>4);
2N/A (*s)[3*(length-i-1)+1] = hexchar((buf->base)[i]&0x0F);
2N/A (*s)[3*(length-i-1)+2] = ' ';
2N/A }
2N/A (*s)[3*length-1] = '\0';
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/A/****************************************************************/
2N/A/* Private Procedures */
2N/A
2N/Astatic int
2N/Aasn1buf_size(const asn1buf *buf)
2N/A{
2N/A if (buf == NULL || buf->base == NULL) return 0;
2N/A return buf->bound - buf->base + 1;
2N/A}
2N/A
2N/A#undef asn1buf_free
2N/Aunsigned int
2N/Aasn1buf_free(const asn1buf *buf)
2N/A{
2N/A if (buf == NULL || buf->base == NULL) return 0;
2N/A else return buf->bound - buf->next + 1;
2N/A}
2N/A
2N/A#undef asn1buf_ensure_space
2N/Aasn1_error_code
2N/Aasn1buf_ensure_space(asn1buf *buf, const unsigned int amount)
2N/A{
2N/A unsigned int avail = asn1buf_free(buf);
2N/A if (avail >= amount)
2N/A return 0;
2N/A return asn1buf_expand(buf, amount-avail);
2N/A}
2N/A
2N/Aasn1_error_code
2N/Aasn1buf_expand(asn1buf *buf, unsigned int inc)
2N/A{
2N/A#define STANDARD_INCREMENT 200
2N/A /* Solaris Kerberos */
2N/A int next_offset;
2N/A int bound_offset;
2N/A
2N/A if (buf != NULL)
2N/A next_offset = buf->next - buf->base;
2N/A else
2N/A return EINVAL;
2N/A
2N/A if (buf->base == NULL) bound_offset = -1;
2N/A else bound_offset = buf->bound - buf->base;
2N/A
2N/A if (inc < STANDARD_INCREMENT)
2N/A inc = STANDARD_INCREMENT;
2N/A
2N/A buf->base = realloc(buf->base,
2N/A (asn1buf_size(buf)+inc) * sizeof(asn1_octet));
2N/A if (buf->base == NULL) return ENOMEM; /* XXX leak */
2N/A buf->bound = (buf->base) + bound_offset + inc;
2N/A buf->next = (buf->base) + next_offset;
2N/A return 0;
2N/A}
2N/A
2N/A#undef asn1buf_len
2N/Aint
2N/Aasn1buf_len(const asn1buf *buf)
2N/A{
2N/A return buf->next - buf->base;
2N/A}