summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorrob <rob@cvs.openbsd.org>2019-05-11 17:46:03 +0000
committerrob <rob@cvs.openbsd.org>2019-05-11 17:46:03 +0000
commit4f7eb64fd01586ea3d3953ab8aaa43cf0d210eac (patch)
tree60fde55cb10de0afc2300cccdadb97ed8ead0889 /lib
parent6fc532963fda2a46e6fd21a3e4456223286cded9 (diff)
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@
Diffstat (limited to 'lib')
-rw-r--r--lib/libutil/Makefile9
-rw-r--r--lib/libutil/Symbols.map45
-rw-r--r--lib/libutil/ber.3453
-rw-r--r--lib/libutil/ber.c1328
-rw-r--r--lib/libutil/ber.h152
-rw-r--r--lib/libutil/shlib_version2
6 files changed, 1984 insertions, 5 deletions
diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile
index ad279fac12f..e643be436a5 100644
--- a/lib/libutil/Makefile
+++ b/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/lib/libutil/Symbols.map b/lib/libutil/Symbols.map
index 942ea2794fc..7bb605156a4 100644
--- a/lib/libutil/Symbols.map
+++ b/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/lib/libutil/ber.3 b/lib/libutil/ber.3
new file mode 100644
index 00000000000..32c1aa4f9a6
--- /dev/null
+++ b/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 <reyk@openbsd.org>
+.\"
+.\" 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/lib/libutil/ber.c b/lib/libutil/ber.c
new file mode 100644
index 00000000000..d6952515481
--- /dev/null
+++ b/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 <reyk@openbsd.org>
+ * Copyright (c) 2006, 2007 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2006, 2007 Marc Balmer <mbalmer@openbsd.org>
+ *
+ * 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 <sys/types.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <err.h> /* XXX for debug output */
+#include <stdio.h> /* XXX for debug output */
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+#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/lib/libutil/ber.h b/lib/libutil/ber.h
new file mode 100644
index 00000000000..a8348150ef9
--- /dev/null
+++ b/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 <reyk@openbsd.org>
+ * Copyright (c) 2006, 2007 Claudio Jeker <claudio@openbsd.org>
+ *
+ * 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/lib/libutil/shlib_version b/lib/libutil/shlib_version
index 262f3bc13b6..b91c32ce7c8 100644
--- a/lib/libutil/shlib_version
+++ b/lib/libutil/shlib_version
@@ -1,2 +1,2 @@
major=13
-minor=0
+minor=1