From ad393dcd5fb7d16a0075a47d142d1a1fc663b98c Mon Sep 17 00:00:00 2001 From: rob <> Date: Sat, 11 May 2019 17:46:02 +0000 Subject: [PATCH] The BER API is currently used by ldap, ldapd, ldapctl, ypldap, snmpd, and snmpctl. Separate copies of ber.[ch] have existed and been maintained in sync in ldap, ldapd, ypldap and snmpd. This commit moves the BER API into /usr/lib/libutil. All current consumers already link libutil. ldapd and snmpd regress passes, and release builds. With help from tb@ and guenther@. ok deraadt@, tb@ --- src/lib/libutil/Makefile | 9 +- src/lib/libutil/Symbols.map | 45 ++ src/lib/libutil/ber.3 | 453 +++++++++++ src/lib/libutil/ber.c | 1328 +++++++++++++++++++++++++++++++++ src/lib/libutil/ber.h | 152 ++++ src/lib/libutil/shlib_version | 2 +- 6 files changed, 1984 insertions(+), 5 deletions(-) create mode 100644 src/lib/libutil/ber.3 create mode 100644 src/lib/libutil/ber.c create mode 100644 src/lib/libutil/ber.h diff --git a/src/lib/libutil/Makefile b/src/lib/libutil/Makefile index ad279fac..e643be43 100644 --- a/src/lib/libutil/Makefile +++ b/src/lib/libutil/Makefile @@ -1,18 +1,19 @@ -# $OpenBSD: Makefile,v 1.40 2017/12/14 09:27:44 kettenis Exp $ +# $OpenBSD: Makefile,v 1.41 2019/05/11 17:46:02 rob Exp $ # $NetBSD: Makefile,v 1.8 1996/05/16 07:03:28 thorpej Exp $ LIB= util VERSION_SCRIPT= ${.CURDIR}/Symbols.map -HDRS= util.h imsg.h -SRCS= bcrypt_pbkdf.c check_expire.c duid.c getmaxpartitions.c \ +HDRS= ber.h util.h imsg.h +SRCS= bcrypt_pbkdf.c ber.c check_expire.c duid.c getmaxpartitions.c \ getrawpartition.c login.c \ login_tty.c logout.c logwtmp.c opendev.c passwd.c pty.c readlabel.c \ login_fbtab.c uucplock.c fparseln.c opendisk.c pidfile.c \ fmt_scaled.c imsg.c imsg-buffer.c pkcs5_pbkdf2.c -MAN= bcrypt_pbkdf.3 check_expire.3 getmaxpartitions.3 getrawpartition.3 \ +MAN= bcrypt_pbkdf.3 ber.3 check_expire.3 getmaxpartitions.3 \ + getrawpartition.3 \ isduid.3 login.3 \ opendev.3 openpty.3 pw_init.3 pw_lock.3 readlabelfs.3 uucplock.3 \ fparseln.3 opendisk.3 login_fbtab.3 pidfile.3 fmt_scaled.3 imsg_init.3 \ diff --git a/src/lib/libutil/Symbols.map b/src/lib/libutil/Symbols.map index 942ea279..7bb60515 100644 --- a/src/lib/libutil/Symbols.map +++ b/src/lib/libutil/Symbols.map @@ -9,6 +9,51 @@ { global: bcrypt_pbkdf; + ber_add_bitstring; + ber_add_boolean; + ber_add_enumerated; + ber_add_eoc; + ber_add_integer; + ber_add_noid; + ber_add_nstring; + ber_add_null; + ber_add_oid; + ber_add_oidstring; + ber_add_ostring; + ber_add_sequence; + ber_add_set; + ber_add_string; + ber_calc_len; + ber_free; + ber_free_element; + ber_free_elements; + ber_get_bitstring; + ber_get_boolean; + ber_get_element; + ber_get_enumerated; + ber_get_eoc; + ber_get_integer; + ber_get_nstring; + ber_get_null; + ber_get_oid; + ber_get_ostring; + ber_get_string; + ber_get_writebuf; + ber_getpos; + ber_link_elements; + ber_oid2ber; + ber_oid_cmp; + ber_printf_elements; + ber_read_elements; + ber_replace_elements; + ber_scanf_elements; + ber_set_application; + ber_set_header; + ber_set_readbuf; + ber_set_writecallback; + ber_string2oid; + ber_unlink_elements; + ber_write_elements; fdforkpty; fdopenpty; fmt_scaled; diff --git a/src/lib/libutil/ber.3 b/src/lib/libutil/ber.3 new file mode 100644 index 00000000..32c1aa4f --- /dev/null +++ b/src/lib/libutil/ber.3 @@ -0,0 +1,453 @@ +.\" $OpenBSD: ber.3,v 1.1 2019/05/11 17:46:02 rob Exp $ +.\" +.\" Copyright (c) 2007, 2012 Reyk Floeter +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: May 11 2019 $ +.Dt BER 3 +.Os +.Sh NAME +.Nm ber_get_element , +.Nm ber_set_header , +.Nm ber_link_elements , +.Nm ber_unlink_elements , +.Nm ber_replace_elements , +.Nm ber_add_sequence , +.Nm ber_add_set , +.Nm ber_add_enumerated , +.Nm ber_add_integer , +.Nm ber_get_integer , +.Nm ber_get_enumerated , +.Nm ber_add_boolean , +.Nm ber_get_boolean , +.Nm ber_add_string , +.Nm ber_add_nstring , +.Nm ber_add_ostring , +.Nm ber_add_bitstring , +.Nm ber_get_string , +.Nm ber_get_nstring , +.Nm ber_get_ostring , +.Nm ber_get_bitstring , +.Nm ber_add_null , +.Nm ber_get_null , +.Nm ber_add_eoc , +.Nm ber_get_eoc , +.Nm ber_add_oid , +.Nm ber_add_noid , +.Nm ber_add_oidstring , +.Nm ber_get_oid , +.Nm ber_oid2ber , +.Nm ber_string2oid , +.Nm ber_oid_cmp , +.Nm ber_printf_elements , +.Nm ber_scanf_elements , +.Nm ber_get_writebuf , +.Nm ber_write_elements , +.Nm ber_set_readbuf , +.Nm ber_read_elements , +.Nm ber_getpos , +.Nm ber_free_element , +.Nm ber_free_elements , +.Nm ber_calc_len , +.Nm ber_set_application , +.Nm ber_set_writecallback , +.Nm ber_free +.Nd encode and decode ASN.1 with Basic Encoding Rules +.Sh SYNOPSIS +.In ber.h +.Ft "struct ber_element *" +.Fn "ber_get_element" "unsigned int encoding" +.Ft "void" +.Fn "ber_set_header" "struct ber_element *elm" "int class" "unsigned int type" +.Ft "void" +.Fn "ber_link_elements" "struct ber_element *prev" "struct ber_element *elm" +.Ft "struct ber_element *" +.Fn "ber_unlink_elements" "struct ber_element *prev" +.Ft "void" +.Fn "ber_replace_elements" "struct ber_element *prev" "struct ber_element *elm" +.Ft "struct ber_element *" +.Fn "ber_add_sequence" "struct ber_element *prev" +.Ft "struct ber_element *" +.Fn "ber_add_set" "struct ber_element *prev" +.Ft "struct ber_element *" +.Fn "ber_add_integer" "struct ber_element *prev" "long long val" +.Ft "int" +.Fn "ber_get_integer" "struct ber_element *root" "long long *val" +.Ft "struct ber_element *" +.Fn "ber_add_enumerated" "struct ber_element *prev" "long long val" +.Ft "int" +.Fn "ber_get_enumerated" "struct ber_element *root" "long long *val" +.Ft "struct ber_element *" +.Fn "ber_add_boolean" "struct ber_element *prev" "int bool" +.Ft "int" +.Fn "ber_get_boolean" "struct ber_element *root" "int *bool" +.Ft "struct ber_element *" +.Fn "ber_add_string" "struct ber_element *prev" "const char *string" +.Ft "struct ber_element *" +.Fn "ber_add_nstring" "struct ber_element *prev" "const char *string" "size_t size" +.Ft "struct ber_element *" +.Fo "ber_add_ostring" +.Fa "struct ber_element *prev" +.Fa "struct ber_octetstring *ostring" +.Fc +.Ft "int" +.Fn "ber_get_string" "struct ber_element *root" "char **charbuf" +.Ft "int" +.Fn "ber_get_nstring" "struct ber_element *root" "void **buf" "size_t *size" +.Ft "int" +.Fn "ber_get_ostring" "struct ber_element *root" "struct ber_octetstring *ostring" +.Ft "struct ber_element *" +.Fo "ber_add_bitstring" +.Fa "struct ber_element *prev" +.Fa "const void *buf" +.Fa "size_t size" +.Fc +.Ft "int" +.Fn "ber_get_bitstring" "struct ber_element *root" "void **buf" "size_t *size" +.Ft "struct ber_element *" +.Fn "ber_add_null" "struct ber_element *prev" +.Ft "int" +.Fn "ber_get_null" "struct ber_element *root" +.Ft "struct ber_element *" +.Fn "ber_add_eoc" "struct ber_element *prev" +.Ft "int" +.Fn "ber_get_eoc" "struct ber_element *root" +.Ft "struct ber_element *" +.Fn "ber_add_oid" "struct ber_element *prev" "struct ber_oid *oid" +.Ft "struct ber_element *" +.Fn "ber_add_noid" "struct ber_element *prev" "struct ber_oid *oid" "int n" +.Ft "struct ber_element *" +.Fn "ber_add_oidstring" "struct ber_element *prev" "const char *string" +.Ft "int" +.Fn "ber_get_oid" "struct ber_element *root" "struct ber_oid *oid" +.Ft "size_t" +.Fn "ber_oid2ber" "struct ber_oid *oid" "u_int8_t *buf" "size_t size" +.Ft "int" +.Fn "ber_string2oid" "const char *string" "struct ber_oid *oid" +.Ft "int" +.Fn "ber_oid_cmp" "struct ber_oid *oid" "struct ber_oid *oid" +.Ft "struct ber_element *" +.Fn "ber_printf_elements" "struct ber_element *prev" "char *format" "..." +.Ft "int" +.Fn "ber_scanf_elements" "struct ber_element *root" "char *format" "..." +.Ft "ssize_t" +.Fn "ber_get_writebuf" "struct ber *ber" "void **buf" +.Ft "ssize_t" +.Fn "ber_write_elements" "struct ber *ber" "struct ber_element *root" +.Ft "void" +.Fn "ber_set_readbuf" "struct ber *ber" "void *buf" "size_t len" +.Ft "struct ber_element *" +.Fn "ber_read_elements" "struct ber *ber" "struct ber_element *root" +.Ft off_t +.Fn "ber_getpos" "struct ber_element *elm" +.Ft "void" +.Fn "ber_free_element" "struct ber_element *root" +.Ft "void" +.Fn "ber_free_elements" "struct ber_element *root" +.Ft "size_t" +.Fn "ber_calc_len" "struct ber_element *root" +.Ft "void" +.Fo "ber_set_application" +.Fa "struct ber *ber" +.Fa "unsigned int (*cb)(struct ber_element *)" +.Fc +.Ft "void" +.Fo "ber_set_writecallback" +.Fa "struct ber_element *elm" +.Fa "void (*cb)(void *arg, size_t offs)" +.Fa "void *arg" +.Fc +.Ft "void" +.Fn "ber_free" "struct ber *ber" +.Sh DESCRIPTION +The +.Nm ber +API provides a mechanism to read and write ASN.1 streams and buffers using the +Basic Encoding Rules. +.Pp +Encoded +.Nm ber +is stored in the following structure: +.Bd -literal +struct ber { + off_t br_offs; + u_char *br_wbuf; + u_char *br_wptr; + u_char *br_wend; + u_char *br_rbuf; + u_char *br_rptr; + u_char *br_rend; + + unsigned int (*br_application)(struct ber_element *); +}; +.Ed +.Pp +.Fa br_rbuf +and +.Fa br_wbuf +are the read and write buffers for a +.Nm ber +stream. +These buffers are used when reading an existing byte stream (e.g. received from +a TLS connection), or when writing a new byte stream in preparation for +subsequent operations performed by the calling application (e.g. network +transmission or export to a file). +.Pp +Intermediary storage of ber elements during decoding and encoding uses the +following structure: +.Bd -literal +struct ber_element { + struct ber_element *be_next; + unsigned int be_type; + unsigned int be_encoding; + size_t be_len; + off_t be_offs; + int be_free; + u_int8_t be_class; + void (*be_cb)(void *, size_t); + void *be_cbarg; + union { + struct ber_element *bv_sub; + void *bv_val; + long long bv_numeric; + } be_union; +#define be_sub be_union.bv_sub +#define be_val be_union.bv_val +#define be_numeric be_union.bv_numeric +}; +.Ed +.Pp +A linked list containing one or more +.Vt ber_element +is created during the decoding and encoding of +.Vt ber . +.Pp +Once the +.Vt ber +and +.Vt ber_element +data structures have been declared, +.Fn ber_set_readbuf +may be called to initialize +.Fa br_rbuf +in preparation for decoding. +It is assumed that a pointer to a ber byte stream is already available to the +application, commonly obtained by +.Xr read 2 , +.Xr recv 2 , +or +.Xr tls_read 3 . +.Fn ber_read_elements +may then be called to parse, validate, and store the data stream into its +consituent parts for subsequent processing. +.Fn ber_read_elements +returns a pointer to a fully populated list of one or more +.Vt ber_element , +or +.Dv NULL +on a type mismatch or read error. +.Pp +The calling application must have explicit knowledge of the expected data +types in order for correct decoding. +.Fn ber_scanf_elements +may be called to extract +.Vt ber_element +content into local variables. +The +.Fn ber_get_* +functions extract the value of a single +.Vt ber_element +instance. +.Fn ber_scanf_elements +and the +.Fn ber_get_* +functions return 0 on success and -1 on failure. +.Pp +The first step when creating new ber is to populate +.Vt ber_element +with the desired content. +This may be achieved using the +.Fn ber_add_* +and +.Fn ber_printf_elements +functions, each of which return a pointer to +.Vt ber_element +on success or +.Dv NULL +on failure. +.Pp +Once +.Vt ber_element +has been fully populated, +.Fn ber_get_writebuf +may be used to initialize +.Fa br_wbuf +for writing. +.Fn ber_write_elements +encodes +.Vt ber_element +into a compliant +.Nm ber +byte stream for subsequent use by the calling application, most commonly using +.Xr send 2 , +.Xr write 2 , +or +.Xr tls_write 3 . +.Sh I/O OPERATIONS +.Fn ber_get_writebuf , +.Fn ber_write_elements , +.Fn ber_set_readbuf , +.Fn ber_read_elements , +.Fn ber_getpos , +.Fn ber_free_element , +.Fn ber_free_elements , +.Fn ber_set_application , +.Fn ber_set_writecallback , +.Fn ber_free +.Sh BER ELEMENTS +.Fn ber_get_element , +.Fn ber_set_header , +.Fn ber_link_elements , +.Fn ber_unlink_elements , +.Fn ber_replace_elements , +.Fn ber_calc_len +.Sh BER TYPES +.Fn ber_add_sequence , +.Fn ber_add_set , +.Fn ber_add_integer , +.Fn ber_get_integer , +.Fn ber_add_enumerated , +.Fn ber_get_enumerated , +.Fn ber_add_boolean , +.Fn ber_get_boolean , +.Fn ber_add_string , +.Fn ber_add_nstring , +.Fn ber_add_ostring , +.Fn ber_add_bitstring , +.Fn ber_get_string , +.Fn ber_get_nstring , +.Fn ber_get_ostring , +.Fn ber_get_bitstring , +.Fn ber_add_null , +.Fn ber_get_null , +.Fn ber_add_eoc , +.Fn ber_get_eoc +.Sh FORMAT STRINGS +.Fn ber_printf_elements , +.Fn ber_scanf_elements +.Sh OBJECT IDS +Object Identifiers are commonly used in ASN.1-based protocols. +These functions provide an interface to parse OIDs. +For internal representation of OIDs, the following structure +.Vt struct ber_oid +is being used: +.Bd -literal +#define BER_MIN_OID_LEN 2 +#define BER_MAX_OID_LEN 32 + +struct ber_oid { + u_int32_t bo_id[BER_MAX_OID_LEN + 1]; + size_t bo_n; +}; +.Ed +.Pp +.Fn ber_add_oid , +.Fn ber_add_noid , +.Fn ber_add_oidstring , +.Fn ber_get_oid , +.Fn ber_oid2ber , +.Fn ber_string2oid +.Fn ber_oid_cmp , +.Sh RETURN VALUES +Upon successful completion +.Fn ber_get_integer , +.Fn ber_get_enumerated , +.Fn ber_get_boolean , +.Fn ber_get_string , +.Fn ber_get_nstring , +.Fn ber_get_ostring , +.Fn ber_get_bitstring , +.Fn ber_get_null , +.Fn ber_get_eoc , +.Fn ber_get_oid , +.Fn ber_string2oid +and +.Fn ber_scanf_elements +return 0, while +.Fn ber_write_elements +returns the number of bytes written. +Otherwise, \-1 is returned and the global variable errno is +set to indicate the error. +.Sh SEE ALSO +.Xr read 2 , +.Xr recv 2 , +.Xr send 2 , +.Xr write 2 , +.Xr tls_read 3 +.Sh STANDARDS +ITU-T Recommendation X.690, also known as ISO/IEC 8825-1: +Information technology - ASN.1 encoding rules. +.Sh HISTORY +The +.Nm ber +manpage first appeared in +.Ox 4.3 . +.Sh AUTHORS +.An -nosplit +The +.Nm ber +library was written by +.An Claudio Jeker Aq Mt claudio@openbsd.org , +.An Marc Balmer Aq Mt marc@openbsd.org +and +.An Reyk Floeter Aq Mt reyk@openbsd.org . +.Sh CAVEATS +The +.Nm ber +API is subject to the following restrictions which are common to the +Distinguished Encoding Rules as defined by X.690: +.Pp +.Bl -enum -compact +.It +Only the definite form of length encoding shall be used, encoded in the +minimum number of octets. +.It +For bitstring, octetstring and restricted character string types, the +constructed form of encoding shall not be used. +.It +If a boolean encoding represents the boolean value TRUE, its single contents +octet shall have all eight bits set to one. +.It +Each unused bit in the final octet of the encoding of a bit string value shall +be set to zero. +.It +If a bitstring value has no 1 bits, then an encoder shall encode the value with +a length of 1 and an initial octet set to 0. +.El +.Pp +In addition, set and sequence values are limited to a maximum of 65535 elements. +No alternative encodings are permitted. +.Pp +.Do +Whereas the basic encoding rules give the sender of an encoding various choices +as to how data values may be encoded, the canonical and distinguished encoding +rules select just one encoding from those allowed by the basic encoding rules. +.Dc +.Bq X.690 +.Pp +The restrictions placed on this API avoid the ambiguity inherent in +.Nm ber +encoded ASN.1 thereby acting as a security mitigation. +.Sh BUGS +This manpage is a stub. diff --git a/src/lib/libutil/ber.c b/src/lib/libutil/ber.c new file mode 100644 index 00000000..d6952515 --- /dev/null +++ b/src/lib/libutil/ber.c @@ -0,0 +1,1328 @@ +/* $OpenBSD: ber.c,v 1.1 2019/05/11 17:46:02 rob Exp $ */ + +/* + * Copyright (c) 2007, 2012 Reyk Floeter + * Copyright (c) 2006, 2007 Claudio Jeker + * Copyright (c) 2006, 2007 Marc Balmer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include /* XXX for debug output */ +#include /* XXX for debug output */ +#include +#include +#include + +#include "ber.h" + +#define BER_TYPE_CONSTRUCTED 0x20 /* otherwise primitive */ +#define BER_TYPE_SINGLE_MAX 30 +#define BER_TAG_MASK 0x1f +#define BER_TAG_MORE 0x80 /* more subsequent octets */ +#define BER_TAG_TYPE_MASK 0x7f +#define BER_CLASS_SHIFT 6 + +static int ber_dump_element(struct ber *ber, struct ber_element *root); +static void ber_dump_header(struct ber *ber, struct ber_element *root); +static void ber_putc(struct ber *ber, u_char c); +static void ber_write(struct ber *ber, void *buf, size_t len); +static ssize_t get_id(struct ber *b, unsigned int *tag, int *class, + int *cstruct); +static ssize_t get_len(struct ber *b, ssize_t *len); +static ssize_t ber_read_element(struct ber *ber, struct ber_element *elm); +static ssize_t ber_getc(struct ber *b, u_char *c); +static ssize_t ber_read(struct ber *ber, void *buf, size_t len); + +#ifdef DEBUG +#define DPRINTF(...) printf(__VA_ARGS__) +#else +#define DPRINTF(...) do { } while (0) +#endif + +struct ber_element * +ber_get_element(unsigned int encoding) +{ + struct ber_element *elm; + + if ((elm = calloc(1, sizeof(*elm))) == NULL) + return NULL; + + elm->be_encoding = encoding; + ber_set_header(elm, BER_CLASS_UNIVERSAL, BER_TYPE_DEFAULT); + + return elm; +} + +void +ber_set_header(struct ber_element *elm, int class, unsigned int type) +{ + elm->be_class = class & BER_CLASS_MASK; + if (type == BER_TYPE_DEFAULT) + type = elm->be_encoding; + elm->be_type = type; +} + +void +ber_link_elements(struct ber_element *prev, struct ber_element *elm) +{ + if (prev != NULL) { + if ((prev->be_encoding == BER_TYPE_SEQUENCE || + prev->be_encoding == BER_TYPE_SET) && + prev->be_sub == NULL) + prev->be_sub = elm; + else + prev->be_next = elm; + } +} + +struct ber_element * +ber_unlink_elements(struct ber_element *prev) +{ + struct ber_element *elm; + + if ((prev->be_encoding == BER_TYPE_SEQUENCE || + prev->be_encoding == BER_TYPE_SET) && + prev->be_sub != NULL) { + elm = prev->be_sub; + prev->be_sub = NULL; + } else { + elm = prev->be_next; + prev->be_next = NULL; + } + + return (elm); +} + +void +ber_replace_elements(struct ber_element *prev, struct ber_element *new) +{ + struct ber_element *ber, *next; + + ber = ber_unlink_elements(prev); + next = ber_unlink_elements(ber); + ber_link_elements(new, next); + ber_link_elements(prev, new); + + /* cleanup old element */ + ber_free_elements(ber); +} + +struct ber_element * +ber_add_sequence(struct ber_element *prev) +{ + struct ber_element *elm; + + if ((elm = ber_get_element(BER_TYPE_SEQUENCE)) == NULL) + return NULL; + + ber_link_elements(prev, elm); + + return elm; +} + +struct ber_element * +ber_add_set(struct ber_element *prev) +{ + struct ber_element *elm; + + if ((elm = ber_get_element(BER_TYPE_SET)) == NULL) + return NULL; + + ber_link_elements(prev, elm); + + return elm; +} + +struct ber_element * +ber_add_enumerated(struct ber_element *prev, long long val) +{ + struct ber_element *elm; + u_int i, len = 0; + u_char cur, last = 0; + + if ((elm = ber_get_element(BER_TYPE_ENUMERATED)) == NULL) + return NULL; + + elm->be_numeric = val; + + for (i = 0; i < sizeof(long long); i++) { + cur = val & 0xff; + if (cur != 0 && cur != 0xff) + len = i; + if ((cur == 0 && last & 0x80) || + (cur == 0xff && (last & 0x80) == 0)) + len = i; + val >>= 8; + last = cur; + } + elm->be_len = len + 1; + + ber_link_elements(prev, elm); + + return elm; +} + +struct ber_element * +ber_add_integer(struct ber_element *prev, long long val) +{ + struct ber_element *elm; + u_int i, len = 0; + u_char cur, last = 0; + + if ((elm = ber_get_element(BER_TYPE_INTEGER)) == NULL) + return NULL; + + elm->be_numeric = val; + + for (i = 0; i < sizeof(long long); i++) { + cur = val & 0xff; + if (cur != 0 && cur != 0xff) + len = i; + if ((cur == 0 && last & 0x80) || + (cur == 0xff && (last & 0x80) == 0)) + len = i; + val >>= 8; + last = cur; + } + elm->be_len = len + 1; + + ber_link_elements(prev, elm); + + return elm; +} + +int +ber_get_integer(struct ber_element *elm, long long *n) +{ + if (elm->be_encoding != BER_TYPE_INTEGER) + return -1; + + *n = elm->be_numeric; + return 0; +} + +int +ber_get_enumerated(struct ber_element *elm, long long *n) +{ + if (elm->be_encoding != BER_TYPE_ENUMERATED) + return -1; + + *n = elm->be_numeric; + return 0; +} + +struct ber_element * +ber_add_boolean(struct ber_element *prev, int bool) +{ + struct ber_element *elm; + + if ((elm = ber_get_element(BER_TYPE_BOOLEAN)) == NULL) + return NULL; + + elm->be_numeric = bool ? 0xff : 0; + elm->be_len = 1; + + ber_link_elements(prev, elm); + + return elm; +} + +int +ber_get_boolean(struct ber_element *elm, int *b) +{ + if (elm->be_encoding != BER_TYPE_BOOLEAN) + return -1; + + *b = !(elm->be_numeric == 0); + return 0; +} + +struct ber_element * +ber_add_string(struct ber_element *prev, const char *string) +{ + return ber_add_nstring(prev, string, strlen(string)); +} + +struct ber_element * +ber_add_nstring(struct ber_element *prev, const char *string0, size_t len) +{ + struct ber_element *elm; + char *string; + + if ((string = calloc(1, len + 1)) == NULL) + return NULL; + if ((elm = ber_get_element(BER_TYPE_OCTETSTRING)) == NULL) { + free(string); + return NULL; + } + + bcopy(string0, string, len); + elm->be_val = string; + elm->be_len = len; + elm->be_free = 1; /* free string on cleanup */ + + ber_link_elements(prev, elm); + + return elm; +} + +struct ber_element * +ber_add_ostring(struct ber_element *prev, struct ber_octetstring *s) +{ + return ber_add_nstring(prev, s->ostr_val, s->ostr_len); +} + +int +ber_get_string(struct ber_element *elm, char **s) +{ + if (elm->be_encoding != BER_TYPE_OCTETSTRING) + return -1; + /* Some components use getstring on binary data containing \0 */ +#if 0 + if (memchr(elm->be_val, '\0', elm->be_len) != NULL) + return -1; +#endif + + *s = elm->be_val; + return 0; +} + +int +ber_get_nstring(struct ber_element *elm, void **p, size_t *len) +{ + if (elm->be_encoding != BER_TYPE_OCTETSTRING) + return -1; + + *p = elm->be_val; + *len = elm->be_len; + return 0; +} + +int +ber_get_ostring(struct ber_element *elm, struct ber_octetstring *s) +{ + if (elm->be_encoding != BER_TYPE_OCTETSTRING) + return -1; + + s->ostr_val = elm->be_val; + s->ostr_len = elm->be_len; + return 0; +} + +struct ber_element * +ber_add_bitstring(struct ber_element *prev, const void *v0, size_t len) +{ + struct ber_element *elm; + void *v; + + if ((v = calloc(1, len)) == NULL) + return NULL; + if ((elm = ber_get_element(BER_TYPE_BITSTRING)) == NULL) { + free(v); + return NULL; + } + + bcopy(v0, v, len); + elm->be_val = v; + elm->be_len = len; + elm->be_free = 1; /* free string on cleanup */ + + ber_link_elements(prev, elm); + + return elm; +} + +int +ber_get_bitstring(struct ber_element *elm, void **v, size_t *len) +{ + if (elm->be_encoding != BER_TYPE_BITSTRING) + return -1; + + *v = elm->be_val; + *len = elm->be_len; + return 0; +} + +struct ber_element * +ber_add_null(struct ber_element *prev) +{ + struct ber_element *elm; + + if ((elm = ber_get_element(BER_TYPE_NULL)) == NULL) + return NULL; + + ber_link_elements(prev, elm); + + return elm; +} + +int +ber_get_null(struct ber_element *elm) +{ + if (elm->be_encoding != BER_TYPE_NULL) + return -1; + + return 0; +} + +struct ber_element * +ber_add_eoc(struct ber_element *prev) +{ + struct ber_element *elm; + + if ((elm = ber_get_element(BER_TYPE_EOC)) == NULL) + return NULL; + + ber_link_elements(prev, elm); + + return elm; +} + +int +ber_get_eoc(struct ber_element *elm) +{ + if (elm->be_encoding != BER_TYPE_EOC) + return -1; + + return 0; +} + +size_t +ber_oid2ber(struct ber_oid *o, u_int8_t *buf, size_t len) +{ + u_int32_t v; + u_int i, j = 0, k; + + if (o->bo_n < BER_MIN_OID_LEN || o->bo_n > BER_MAX_OID_LEN || + o->bo_id[0] > 2 || o->bo_id[1] > 40) + return (0); + + v = (o->bo_id[0] * 40) + o->bo_id[1]; + for (i = 2, j = 0; i <= o->bo_n; v = o->bo_id[i], i++) { + for (k = 28; k >= 7; k -= 7) { + if (v >= (u_int)(1 << k)) { + if (len) + buf[j] = v >> k | BER_TAG_MORE; + j++; + } + } + if (len) + buf[j] = v & BER_TAG_TYPE_MASK; + j++; + } + + return (j); +} + +int +ber_string2oid(const char *oidstr, struct ber_oid *o) +{ + char *sp, *p, str[BUFSIZ]; + const char *errstr; + + if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str)) + return (-1); + memset(o, 0, sizeof(*o)); + + /* Parse OID strings in the common forms n.n.n, n_n_n_n, or n-n-n */ + for (p = sp = str; p != NULL; sp = p) { + if ((p = strpbrk(p, "._-")) != NULL) + *p++ = '\0'; + o->bo_id[o->bo_n++] = strtonum(sp, 0, UINT_MAX, &errstr); + if (errstr || o->bo_n > BER_MAX_OID_LEN) + return (-1); + } + + return (0); +} + +int +ber_oid_cmp(struct ber_oid *a, struct ber_oid *b) +{ + size_t i; + for (i = 0; i < BER_MAX_OID_LEN; i++) { + if (a->bo_id[i] != 0) { + if (a->bo_id[i] == b->bo_id[i]) + continue; + else if (a->bo_id[i] < b->bo_id[i]) { + /* b is a successor of a */ + return (1); + } else { + /* b is a predecessor of a */ + return (-1); + } + } else if (b->bo_id[i] != 0) { + /* b is larger, but a child of a */ + return (2); + } else + break; + } + + /* b and a are identical */ + return (0); +} + +struct ber_element * +ber_add_oid(struct ber_element *prev, struct ber_oid *o) +{ + struct ber_element *elm; + u_int8_t *buf; + size_t len; + + if ((elm = ber_get_element(BER_TYPE_OBJECT)) == NULL) + return (NULL); + + if ((len = ber_oid2ber(o, NULL, 0)) == 0) + goto fail; + + if ((buf = calloc(1, len)) == NULL) + goto fail; + + elm->be_val = buf; + elm->be_len = len; + elm->be_free = 1; + + if (ber_oid2ber(o, buf, len) != len) + goto fail; + + ber_link_elements(prev, elm); + + return (elm); + + fail: + ber_free_elements(elm); + return (NULL); +} + +struct ber_element * +ber_add_noid(struct ber_element *prev, struct ber_oid *o, int n) +{ + struct ber_oid no; + + if (n > BER_MAX_OID_LEN) + return (NULL); + no.bo_n = n; + bcopy(&o->bo_id, &no.bo_id, sizeof(no.bo_id)); + + return (ber_add_oid(prev, &no)); +} + +struct ber_element * +ber_add_oidstring(struct ber_element *prev, const char *oidstr) +{ + struct ber_oid o; + + if (ber_string2oid(oidstr, &o) == -1) + return (NULL); + + return (ber_add_oid(prev, &o)); +} + +int +ber_get_oid(struct ber_element *elm, struct ber_oid *o) +{ + u_int8_t *buf; + size_t len, i = 0, j = 0; + + if (elm->be_encoding != BER_TYPE_OBJECT) + return (-1); + + buf = elm->be_val; + len = elm->be_len; + + if (!buf[i]) + return (-1); + + memset(o, 0, sizeof(*o)); + o->bo_id[j++] = buf[i] / 40; + o->bo_id[j++] = buf[i++] % 40; + for (; i < len && j < BER_MAX_OID_LEN; i++) { + o->bo_id[j] = (o->bo_id[j] << 7) + (buf[i] & ~0x80); + if (buf[i] & 0x80) + continue; + j++; + } + o->bo_n = j; + + return (0); +} + +struct ber_element * +ber_printf_elements(struct ber_element *ber, char *fmt, ...) +{ + va_list ap; + int d, class; + size_t len; + unsigned int type; + long long i; + char *s; + void *p; + struct ber_oid *o; + struct ber_element *sub = ber, *e; + + va_start(ap, fmt); + while (*fmt) { + switch (*fmt++) { + case 'B': + p = va_arg(ap, void *); + len = va_arg(ap, size_t); + if ((ber = ber_add_bitstring(ber, p, len)) == NULL) + goto fail; + break; + case 'b': + d = va_arg(ap, int); + if ((ber = ber_add_boolean(ber, d)) == NULL) + goto fail; + break; + case 'd': + d = va_arg(ap, int); + if ((ber = ber_add_integer(ber, d)) == NULL) + goto fail; + break; + case 'e': + e = va_arg(ap, struct ber_element *); + ber_link_elements(ber, e); + break; + case 'E': + i = va_arg(ap, long long); + if ((ber = ber_add_enumerated(ber, i)) == NULL) + goto fail; + break; + case 'i': + i = va_arg(ap, long long); + if ((ber = ber_add_integer(ber, i)) == NULL) + goto fail; + break; + case 'O': + o = va_arg(ap, struct ber_oid *); + if ((ber = ber_add_oid(ber, o)) == NULL) + goto fail; + break; + case 'o': + s = va_arg(ap, char *); + if ((ber = ber_add_oidstring(ber, s)) == NULL) + goto fail; + break; + case 's': + s = va_arg(ap, char *); + if ((ber = ber_add_string(ber, s)) == NULL) + goto fail; + break; + case 't': + class = va_arg(ap, int); + type = va_arg(ap, unsigned int); + ber_set_header(ber, class, type); + break; + case 'x': + s = va_arg(ap, char *); + len = va_arg(ap, size_t); + if ((ber = ber_add_nstring(ber, s, len)) == NULL) + goto fail; + break; + case '0': + if ((ber = ber_add_null(ber)) == NULL) + goto fail; + break; + case '{': + if ((ber = sub = ber_add_sequence(ber)) == NULL) + goto fail; + break; + case '(': + if ((ber = sub = ber_add_set(ber)) == NULL) + goto fail; + break; + case '}': + case ')': + ber = sub; + break; + case '.': + if ((e = ber_add_eoc(ber)) == NULL) + goto fail; + ber = e; + break; + default: + break; + } + } + va_end(ap); + + return (ber); + fail: + ber_free_elements(ber); + return (NULL); +} + +int +ber_scanf_elements(struct ber_element *ber, char *fmt, ...) +{ +#define _MAX_SEQ 128 + va_list ap; + int *d, level = -1; + unsigned int *t; + long long *i, l; + void **ptr; + size_t *len, ret = 0, n = strlen(fmt); + char **s; + off_t *pos; + struct ber_oid *o; + struct ber_element *parent[_MAX_SEQ], **e; + + memset(parent, 0, sizeof(struct ber_element *) * _MAX_SEQ); + + va_start(ap, fmt); + while (*fmt) { + switch (*fmt++) { + case 'B': + ptr = va_arg(ap, void **); + len = va_arg(ap, size_t *); + if (ber_get_bitstring(ber, ptr, len) == -1) + goto fail; + ret++; + break; + case 'b': + d = va_arg(ap, int *); + if (ber_get_boolean(ber, d) == -1) + goto fail; + ret++; + break; + case 'd': + d = va_arg(ap, int *); + if (ber_get_integer(ber, &l) == -1) + goto fail; + *d = l; + ret++; + break; + case 'e': + e = va_arg(ap, struct ber_element **); + *e = ber; + ret++; + continue; + case 'E': + i = va_arg(ap, long long *); + if (ber_get_enumerated(ber, i) == -1) + goto fail; + ret++; + break; + case 'i': + i = va_arg(ap, long long *); + if (ber_get_integer(ber, i) == -1) + goto fail; + ret++; + break; + case 'o': + o = va_arg(ap, struct ber_oid *); + if (ber_get_oid(ber, o) == -1) + goto fail; + ret++; + break; + case 'S': + ret++; + break; + case 's': + s = va_arg(ap, char **); + if (ber_get_string(ber, s) == -1) + goto fail; + ret++; + break; + case 't': + d = va_arg(ap, int *); + t = va_arg(ap, unsigned int *); + *d = ber->be_class; + *t = ber->be_type; + ret++; + continue; + case 'x': + ptr = va_arg(ap, void **); + len = va_arg(ap, size_t *); + if (ber_get_nstring(ber, ptr, len) == -1) + goto fail; + ret++; + break; + case '0': + if (ber->be_encoding != BER_TYPE_NULL) + goto fail; + ret++; + break; + case '.': + if (ber->be_encoding != BER_TYPE_EOC) + goto fail; + ret++; + break; + case 'p': + pos = va_arg(ap, off_t *); + *pos = ber_getpos(ber); + ret++; + continue; + case '{': + case '(': + if (ber->be_encoding != BER_TYPE_SEQUENCE && + ber->be_encoding != BER_TYPE_SET) + goto fail; + if (ber->be_sub == NULL || level >= _MAX_SEQ-1) + goto fail; + parent[++level] = ber; + ber = ber->be_sub; + ret++; + continue; + case '}': + case ')': + if (parent[level] == NULL) + goto fail; + ber = parent[level--]; + ret++; + break; + default: + goto fail; + } + + if (ber->be_next == NULL) + continue; + ber = ber->be_next; + } + va_end(ap); + return (ret == n ? 0 : -1); + + fail: + va_end(ap); + return (-1); + +} + +ssize_t +ber_get_writebuf(struct ber *b, void **buf) +{ + if (b->br_wbuf == NULL) + return -1; + *buf = b->br_wbuf; + return (b->br_wend - b->br_wbuf); +} + +/* + * write ber elements to the write buffer + * + * params: + * ber holds the destination write buffer byte stream + * root fully populated element tree + * + * returns: + * >=0 number of bytes written + * -1 on failure and sets errno + */ +ssize_t +ber_write_elements(struct ber *ber, struct ber_element *root) +{ + size_t len; + + /* calculate length because only the definite form is required */ + len = ber_calc_len(root); + DPRINTF("write ber element of %zd bytes length\n", len); + + if (ber->br_wbuf != NULL && ber->br_wbuf + len > ber->br_wend) { + free(ber->br_wbuf); + ber->br_wbuf = NULL; + } + if (ber->br_wbuf == NULL) { + if ((ber->br_wbuf = malloc(len)) == NULL) + return -1; + ber->br_wend = ber->br_wbuf + len; + } + + /* reset write pointer */ + ber->br_wptr = ber->br_wbuf; + + if (ber_dump_element(ber, root) == -1) + return -1; + + return (len); +} + +void +ber_set_readbuf(struct ber *b, void *buf, size_t len) +{ + b->br_rbuf = b->br_rptr = buf; + b->br_rend = (u_int8_t *)buf + len; +} + +/* + * read ber elements from the read buffer + * + * params: + * ber holds a fully populated read buffer byte stream + * root if NULL, build up an element tree from what we receive on + * the wire. If not null, use the specified encoding for the + * elements received. + * + * returns: + * !=NULL, elements read and store in the ber_element tree + * NULL, type mismatch or read error + */ +struct ber_element * +ber_read_elements(struct ber *ber, struct ber_element *elm) +{ + struct ber_element *root = elm; + + if (root == NULL) { + if ((root = ber_get_element(0)) == NULL) + return NULL; + } + + DPRINTF("read ber elements, root %p\n", root); + + if (ber_read_element(ber, root) == -1) { + /* Cleanup if root was allocated by us */ + if (elm == NULL) + ber_free_elements(root); + return NULL; + } + + return root; +} + +off_t +ber_getpos(struct ber_element *elm) +{ + return elm->be_offs; +} + +void +ber_free_element(struct ber_element *root) +{ + if (root->be_sub && (root->be_encoding == BER_TYPE_SEQUENCE || + root->be_encoding == BER_TYPE_SET)) + ber_free_elements(root->be_sub); + if (root->be_free && (root->be_encoding == BER_TYPE_OCTETSTRING || + root->be_encoding == BER_TYPE_BITSTRING || + root->be_encoding == BER_TYPE_OBJECT)) + free(root->be_val); + free(root); +} + +void +ber_free_elements(struct ber_element *root) +{ + if (root == NULL) + return; + if (root->be_sub && (root->be_encoding == BER_TYPE_SEQUENCE || + root->be_encoding == BER_TYPE_SET)) + ber_free_elements(root->be_sub); + if (root->be_next) + ber_free_elements(root->be_next); + if (root->be_free && (root->be_encoding == BER_TYPE_OCTETSTRING || + root->be_encoding == BER_TYPE_BITSTRING || + root->be_encoding == BER_TYPE_OBJECT)) + free(root->be_val); + free(root); +} + +size_t +ber_calc_len(struct ber_element *root) +{ + unsigned int t; + size_t s; + size_t size = 2; /* minimum 1 byte head and 1 byte size */ + + /* calculate the real length of a sequence or set */ + if (root->be_sub && (root->be_encoding == BER_TYPE_SEQUENCE || + root->be_encoding == BER_TYPE_SET)) + root->be_len = ber_calc_len(root->be_sub); + + /* fix header length for extended types */ + if (root->be_type > BER_TYPE_SINGLE_MAX) + for (t = root->be_type; t > 0; t >>= 7) + size++; + if (root->be_len >= BER_TAG_MORE) + for (s = root->be_len; s > 0; s >>= 8) + size++; + + /* calculate the length of the following elements */ + if (root->be_next) + size += ber_calc_len(root->be_next); + + /* This is an empty element, do not use a minimal size */ + if (root->be_class == BER_CLASS_UNIVERSAL && + root->be_type == BER_TYPE_EOC && root->be_len == 0) + return (0); + + return (root->be_len + size); +} + +void +ber_set_application(struct ber *b, unsigned int (*cb)(struct ber_element *)) +{ + b->br_application = cb; +} + +void +ber_set_writecallback(struct ber_element *elm, void (*cb)(void *, size_t), + void *arg) +{ + elm->be_cb = cb; + elm->be_cbarg = arg; +} + +void +ber_free(struct ber *b) +{ + free(b->br_wbuf); +} + +/* + * internal functions + */ + +static int +ber_dump_element(struct ber *ber, struct ber_element *root) +{ + unsigned long long l; + int i; + uint8_t u; + + ber_dump_header(ber, root); + if (root->be_cb) + root->be_cb(root->be_cbarg, ber->br_wptr - ber->br_wbuf); + + switch (root->be_encoding) { + case BER_TYPE_BOOLEAN: + case BER_TYPE_INTEGER: + case BER_TYPE_ENUMERATED: + l = (unsigned long long)root->be_numeric; + for (i = root->be_len; i > 0; i--) { + u = (l >> ((i - 1) * 8)) & 0xff; + ber_putc(ber, u); + } + break; + case BER_TYPE_BITSTRING: + return -1; + case BER_TYPE_OCTETSTRING: + case BER_TYPE_OBJECT: + ber_write(ber, root->be_val, root->be_len); + break; + case BER_TYPE_NULL: /* no payload */ + case BER_TYPE_EOC: + break; + case BER_TYPE_SEQUENCE: + case BER_TYPE_SET: + if (root->be_sub && ber_dump_element(ber, root->be_sub) == -1) + return -1; + break; + } + + if (root->be_next == NULL) + return 0; + return ber_dump_element(ber, root->be_next); +} + +static void +ber_dump_header(struct ber *ber, struct ber_element *root) +{ + u_char id = 0, t, buf[5]; + unsigned int type; + size_t size; + + /* class universal, type encoding depending on type value */ + /* length encoding */ + if (root->be_type <= BER_TYPE_SINGLE_MAX) { + id = root->be_type | (root->be_class << BER_CLASS_SHIFT); + if (root->be_encoding == BER_TYPE_SEQUENCE || + root->be_encoding == BER_TYPE_SET) + id |= BER_TYPE_CONSTRUCTED; + + ber_putc(ber, id); + } else { + id = BER_TAG_MASK | (root->be_class << BER_CLASS_SHIFT); + if (root->be_encoding == BER_TYPE_SEQUENCE || + root->be_encoding == BER_TYPE_SET) + id |= BER_TYPE_CONSTRUCTED; + + ber_putc(ber, id); + + for (t = 0, type = root->be_type; type > 0; type >>= 7) + buf[t++] = type & ~BER_TAG_MORE; + + while (t-- > 0) { + if (t > 0) + buf[t] |= BER_TAG_MORE; + ber_putc(ber, buf[t]); + } + } + + if (root->be_len < BER_TAG_MORE) { + /* short form */ + ber_putc(ber, root->be_len); + } else { + for (t = 0, size = root->be_len; size > 0; size >>= 8) + buf[t++] = size & 0xff; + + ber_putc(ber, t | BER_TAG_MORE); + + while (t > 0) + ber_putc(ber, buf[--t]); + } +} + +static void +ber_putc(struct ber *ber, u_char c) +{ + if (ber->br_wptr + 1 <= ber->br_wend) + *ber->br_wptr = c; + ber->br_wptr++; +} + +static void +ber_write(struct ber *ber, void *buf, size_t len) +{ + if (ber->br_wptr + len <= ber->br_wend) + bcopy(buf, ber->br_wptr, len); + ber->br_wptr += len; +} + +/* + * extract a BER encoded tag. There are two types, a short and long form. + */ +static ssize_t +get_id(struct ber *b, unsigned int *tag, int *class, int *cstruct) +{ + u_char u; + size_t i = 0; + unsigned int t = 0; + + if (ber_getc(b, &u) == -1) + return -1; + + *class = (u >> BER_CLASS_SHIFT) & BER_CLASS_MASK; + *cstruct = (u & BER_TYPE_CONSTRUCTED) == BER_TYPE_CONSTRUCTED; + + if ((u & BER_TAG_MASK) != BER_TAG_MASK) { + *tag = u & BER_TAG_MASK; + return 1; + } + + do { + if (ber_getc(b, &u) == -1) + return -1; + t = (t << 7) | (u & ~BER_TAG_MORE); + i++; + if (i > sizeof(unsigned int)) { + errno = ERANGE; + return -1; + } + } while (u & BER_TAG_MORE); + + *tag = t; + return i + 1; +} + +/* + * extract length of a ber object -- if length is unknown an error is returned. + */ +static ssize_t +get_len(struct ber *b, ssize_t *len) +{ + u_char u, n; + ssize_t s, r; + + if (ber_getc(b, &u) == -1) + return -1; + if ((u & BER_TAG_MORE) == 0) { + /* short form */ + *len = u; + return 1; + } + + if (u == 0x80) { + /* Indefinite length not supported. */ + errno = EINVAL; + return -1; + } + + n = u & ~BER_TAG_MORE; + if (sizeof(ssize_t) < n) { + errno = ERANGE; + return -1; + } + r = n + 1; + + for (s = 0; n > 0; n--) { + if (ber_getc(b, &u) == -1) + return -1; + s = (s << 8) | u; + } + + if (s < 0) { + /* overflow */ + errno = ERANGE; + return -1; + } + + *len = s; + return r; +} + +static ssize_t +ber_read_element(struct ber *ber, struct ber_element *elm) +{ + long long val = 0; + struct ber_element *next; + unsigned int type; + int i, class, cstruct, elements = 0; + ssize_t len, r, totlen = 0; + u_char c; + + if ((r = get_id(ber, &type, &class, &cstruct)) == -1) + return -1; + DPRINTF("ber read got class %d type %u, %s\n", + class, type, cstruct ? "constructed" : "primitive"); + totlen += r; + if ((r = get_len(ber, &len)) == -1) + return -1; + DPRINTF("ber read element size %zd\n", len); + totlen += r + len; + + /* If the total size of the element is larger than the buffer + * don't bother to continue. */ + if (len > ber->br_rend - ber->br_rptr) { + errno = ECANCELED; + return -1; + } + + elm->be_type = type; + elm->be_len = len; + elm->be_offs = ber->br_offs; /* element position within stream */ + elm->be_class = class; + + if (elm->be_encoding == 0) { + /* try to figure out the encoding via class, type and cstruct */ + if (cstruct) + elm->be_encoding = BER_TYPE_SEQUENCE; + else if (class == BER_CLASS_UNIVERSAL) + elm->be_encoding = type; + else if (ber->br_application != NULL) { + /* + * Ask the application to map the encoding to a + * universal type. For example, a SMI IpAddress + * type is defined as 4 byte OCTET STRING. + */ + elm->be_encoding = (*ber->br_application)(elm); + } else + /* last resort option */ + elm->be_encoding = BER_TYPE_NULL; + } + + switch (elm->be_encoding) { + case BER_TYPE_EOC: /* End-Of-Content */ + break; + case BER_TYPE_BOOLEAN: + case BER_TYPE_INTEGER: + case BER_TYPE_ENUMERATED: + if (len > (ssize_t)sizeof(long long)) + return -1; + for (i = 0; i < len; i++) { + if (ber_getc(ber, &c) != 1) + return -1; + val <<= 8; + val |= c; + } + + /* sign extend if MSB is set */ + if (len < (ssize_t)sizeof(long long) && + (val >> ((len - 1) * 8) & 0x80)) + val |= ULLONG_MAX << (len * 8); + elm->be_numeric = val; + break; + case BER_TYPE_BITSTRING: + elm->be_val = malloc(len); + if (elm->be_val == NULL) + return -1; + elm->be_free = 1; + elm->be_len = len; + ber_read(ber, elm->be_val, len); + break; + case BER_TYPE_OCTETSTRING: + case BER_TYPE_OBJECT: + elm->be_val = malloc(len + 1); + if (elm->be_val == NULL) + return -1; + elm->be_free = 1; + elm->be_len = len; + ber_read(ber, elm->be_val, len); + ((u_char *)elm->be_val)[len] = '\0'; + break; + case BER_TYPE_NULL: /* no payload */ + if (len != 0) + return -1; + break; + case BER_TYPE_SEQUENCE: + case BER_TYPE_SET: + if (elm->be_sub == NULL) { + if ((elm->be_sub = ber_get_element(0)) == NULL) + return -1; + } + next = elm->be_sub; + while (len > 0) { + /* + * Prevent stack overflow from excessive recursion + * depth in ber_free_elements(). + */ + if (elements >= BER_MAX_SEQ_ELEMENTS) { + errno = ERANGE; + return -1; + } + r = ber_read_element(ber, next); + if (r == -1) + return -1; + elements++; + len -= r; + if (len > 0 && next->be_next == NULL) { + if ((next->be_next = ber_get_element(0)) == + NULL) + return -1; + } + next = next->be_next; + } + break; + } + return totlen; +} + +static ssize_t +ber_getc(struct ber *b, u_char *c) +{ + return ber_read(b, c, 1); +} + +static ssize_t +ber_read(struct ber *ber, void *buf, size_t len) +{ + size_t sz; + + if (ber->br_rbuf == NULL) + return -1; + + sz = ber->br_rend - ber->br_rptr; + if (len > sz) { + errno = ECANCELED; + return -1; /* parser wants more data than available */ + } + + bcopy(ber->br_rptr, buf, len); + ber->br_rptr += len; + ber->br_offs += len; + + return len; +} diff --git a/src/lib/libutil/ber.h b/src/lib/libutil/ber.h new file mode 100644 index 00000000..a8348150 --- /dev/null +++ b/src/lib/libutil/ber.h @@ -0,0 +1,152 @@ +/* $OpenBSD: ber.h,v 1.1 2019/05/11 17:46:02 rob Exp $ */ + +/* + * Copyright (c) 2007, 2012 Reyk Floeter + * Copyright (c) 2006, 2007 Claudio Jeker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _BER_H +#define _BER_H + +struct ber_octetstring { + size_t ostr_len; + const void *ostr_val; +}; + +struct ber_element { + struct ber_element *be_next; + unsigned int be_type; + unsigned int be_encoding; + size_t be_len; + off_t be_offs; + int be_free; + u_int8_t be_class; + void (*be_cb)(void *, size_t); + void *be_cbarg; + union { + struct ber_element *bv_sub; + void *bv_val; + long long bv_numeric; + } be_union; +#define be_sub be_union.bv_sub +#define be_val be_union.bv_val +#define be_numeric be_union.bv_numeric +}; + +struct ber { + off_t br_offs; + u_char *br_wbuf; + u_char *br_wptr; + u_char *br_wend; + u_char *br_rbuf; + u_char *br_rptr; + u_char *br_rend; + + unsigned int (*br_application)(struct ber_element *); +}; + +/* well-known ber_element types */ +#define BER_TYPE_DEFAULT ((unsigned int)-1) +#define BER_TYPE_EOC 0 +#define BER_TYPE_BOOLEAN 1 +#define BER_TYPE_INTEGER 2 +#define BER_TYPE_BITSTRING 3 +#define BER_TYPE_OCTETSTRING 4 +#define BER_TYPE_NULL 5 +#define BER_TYPE_OBJECT 6 +#define BER_TYPE_ENUMERATED 10 +#define BER_TYPE_SEQUENCE 16 +#define BER_TYPE_SET 17 + +/* ber classes */ +#define BER_CLASS_UNIVERSAL 0x0 +#define BER_CLASS_UNIV BER_CLASS_UNIVERSAL +#define BER_CLASS_APPLICATION 0x1 +#define BER_CLASS_APP BER_CLASS_APPLICATION +#define BER_CLASS_CONTEXT 0x2 +#define BER_CLASS_PRIVATE 0x3 +#define BER_CLASS_MASK 0x3 + +/* common definitions */ +#define BER_MIN_OID_LEN 2 /* OBJECT */ +#define BER_MAX_OID_LEN 32 /* OBJECT */ +#define BER_MAX_SEQ_ELEMENTS USHRT_MAX /* 65535 */ + +struct ber_oid { + u_int32_t bo_id[BER_MAX_OID_LEN + 1]; + size_t bo_n; +}; + +__BEGIN_DECLS +struct ber_element *ber_get_element(unsigned int); +void ber_set_header(struct ber_element *, int, + unsigned int); +void ber_link_elements(struct ber_element *, + struct ber_element *); +struct ber_element *ber_unlink_elements(struct ber_element *); +void ber_replace_elements(struct ber_element *, + struct ber_element *); +struct ber_element *ber_add_sequence(struct ber_element *); +struct ber_element *ber_add_set(struct ber_element *); +struct ber_element *ber_add_integer(struct ber_element *, long long); +int ber_get_integer(struct ber_element *, long long *); +struct ber_element *ber_add_enumerated(struct ber_element *, long long); +int ber_get_enumerated(struct ber_element *, long long *); +struct ber_element *ber_add_boolean(struct ber_element *, int); +int ber_get_boolean(struct ber_element *, int *); +struct ber_element *ber_add_string(struct ber_element *, const char *); +struct ber_element *ber_add_nstring(struct ber_element *, const char *, + size_t); +struct ber_element *ber_add_ostring(struct ber_element *, + struct ber_octetstring *); +int ber_get_string(struct ber_element *, char **); +int ber_get_nstring(struct ber_element *, void **, + size_t *); +int ber_get_ostring(struct ber_element *, + struct ber_octetstring *); +struct ber_element *ber_add_bitstring(struct ber_element *, const void *, + size_t); +int ber_get_bitstring(struct ber_element *, void **, + size_t *); +struct ber_element *ber_add_null(struct ber_element *); +int ber_get_null(struct ber_element *); +struct ber_element *ber_add_eoc(struct ber_element *); +int ber_get_eoc(struct ber_element *); +struct ber_element *ber_add_oid(struct ber_element *, struct ber_oid *); +struct ber_element *ber_add_noid(struct ber_element *, struct ber_oid *, int); +struct ber_element *ber_add_oidstring(struct ber_element *, const char *); +int ber_get_oid(struct ber_element *, struct ber_oid *); +size_t ber_oid2ber(struct ber_oid *, u_int8_t *, size_t); +int ber_string2oid(const char *, struct ber_oid *); +struct ber_element *ber_printf_elements(struct ber_element *, char *, ...); +int ber_scanf_elements(struct ber_element *, char *, ...); +ssize_t ber_get_writebuf(struct ber *, void **); +ssize_t ber_write_elements(struct ber *, struct ber_element *); +void ber_set_readbuf(struct ber *, void *, size_t); +struct ber_element *ber_read_elements(struct ber *, struct ber_element *); +off_t ber_getpos(struct ber_element *); +void ber_free_element(struct ber_element *); +void ber_free_elements(struct ber_element *); +size_t ber_calc_len(struct ber_element *); +void ber_set_application(struct ber *, + unsigned int (*)(struct ber_element *)); +void ber_set_writecallback(struct ber_element *, + void (*)(void *, size_t), void *); +void ber_free(struct ber *); +int ber_oid_cmp(struct ber_oid *, struct ber_oid *); + +__END_DECLS + +#endif /* _BER_H */ diff --git a/src/lib/libutil/shlib_version b/src/lib/libutil/shlib_version index 262f3bc1..b91c32ce 100644 --- a/src/lib/libutil/shlib_version +++ b/src/lib/libutil/shlib_version @@ -1,2 +1,2 @@ major=13 -minor=0 +minor=1