xref: /freebsd/usr.sbin/ypldap/ber.c (revision 2a63c3be158216222d89a073dcbd6a72ee4aab5a)
19e7c127fSCraig Rodrigues /*	$OpenBSD: ber.c,v 1.9 2015/02/12 00:30:38 pelikan Exp $ */
29e7c127fSCraig Rodrigues 
39e7c127fSCraig Rodrigues /*
49e7c127fSCraig Rodrigues  * Copyright (c) 2007 Reyk Floeter <reyk@vantronix.net>
59e7c127fSCraig Rodrigues  * Copyright (c) 2006, 2007 Claudio Jeker <claudio@openbsd.org>
69e7c127fSCraig Rodrigues  * Copyright (c) 2006, 2007 Marc Balmer <mbalmer@openbsd.org>
79e7c127fSCraig Rodrigues  *
89e7c127fSCraig Rodrigues  * Permission to use, copy, modify, and distribute this software for any
99e7c127fSCraig Rodrigues  * purpose with or without fee is hereby granted, provided that the above
109e7c127fSCraig Rodrigues  * copyright notice and this permission notice appear in all copies.
119e7c127fSCraig Rodrigues  *
129e7c127fSCraig Rodrigues  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
139e7c127fSCraig Rodrigues  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
149e7c127fSCraig Rodrigues  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
159e7c127fSCraig Rodrigues  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
169e7c127fSCraig Rodrigues  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
179e7c127fSCraig Rodrigues  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
189e7c127fSCraig Rodrigues  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
199e7c127fSCraig Rodrigues  */
209e7c127fSCraig Rodrigues 
219e7c127fSCraig Rodrigues #include <sys/types.h>
229e7c127fSCraig Rodrigues 
239e7c127fSCraig Rodrigues #include <errno.h>
249e7c127fSCraig Rodrigues #include <limits.h>
259e7c127fSCraig Rodrigues #include <stdlib.h>
269e7c127fSCraig Rodrigues #include <err.h>	/* XXX for debug output */
279e7c127fSCraig Rodrigues #include <stdio.h>	/* XXX for debug output */
2828b524fbSCraig Rodrigues #include <string.h>
299e7c127fSCraig Rodrigues #include <unistd.h>
309e7c127fSCraig Rodrigues #include <stdarg.h>
319e7c127fSCraig Rodrigues 
329e7c127fSCraig Rodrigues #include "ber.h"
339e7c127fSCraig Rodrigues 
349e7c127fSCraig Rodrigues #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
359e7c127fSCraig Rodrigues 
369e7c127fSCraig Rodrigues #define BER_TYPE_CONSTRUCTED	0x20	/* otherwise primitive */
379e7c127fSCraig Rodrigues #define BER_TYPE_SINGLE_MAX	30
389e7c127fSCraig Rodrigues #define BER_TAG_MASK		0x1f
399e7c127fSCraig Rodrigues #define BER_TAG_MORE		0x80	/* more subsequent octets */
409e7c127fSCraig Rodrigues #define BER_TAG_TYPE_MASK	0x7f
419e7c127fSCraig Rodrigues #define BER_CLASS_SHIFT		6
429e7c127fSCraig Rodrigues 
439e7c127fSCraig Rodrigues static int	ber_dump_element(struct ber *ber, struct ber_element *root);
449e7c127fSCraig Rodrigues static void	ber_dump_header(struct ber *ber, struct ber_element *root);
459e7c127fSCraig Rodrigues static void	ber_putc(struct ber *ber, u_char c);
469e7c127fSCraig Rodrigues static void	ber_write(struct ber *ber, void *buf, size_t len);
479e7c127fSCraig Rodrigues static ssize_t	get_id(struct ber *b, unsigned long *tag, int *class,
489e7c127fSCraig Rodrigues     int *cstruct);
499e7c127fSCraig Rodrigues static ssize_t	get_len(struct ber *b, ssize_t *len);
509e7c127fSCraig Rodrigues static ssize_t	ber_read_element(struct ber *ber, struct ber_element *elm);
519e7c127fSCraig Rodrigues static ssize_t	ber_readbuf(struct ber *b, void *buf, size_t nbytes);
529e7c127fSCraig Rodrigues static ssize_t	ber_getc(struct ber *b, u_char *c);
539e7c127fSCraig Rodrigues static ssize_t	ber_read(struct ber *ber, void *buf, size_t len);
549e7c127fSCraig Rodrigues 
559e7c127fSCraig Rodrigues #ifdef DEBUG
569e7c127fSCraig Rodrigues #define DPRINTF(...)	printf(__VA_ARGS__)
579e7c127fSCraig Rodrigues #else
589e7c127fSCraig Rodrigues #define DPRINTF(...)	do { } while (0)
599e7c127fSCraig Rodrigues #endif
609e7c127fSCraig Rodrigues 
619e7c127fSCraig Rodrigues struct ber_element *
ber_get_element(unsigned long encoding)629e7c127fSCraig Rodrigues ber_get_element(unsigned long encoding)
639e7c127fSCraig Rodrigues {
649e7c127fSCraig Rodrigues 	struct ber_element *elm;
659e7c127fSCraig Rodrigues 
669e7c127fSCraig Rodrigues 	if ((elm = calloc(1, sizeof(*elm))) == NULL)
679e7c127fSCraig Rodrigues 		return NULL;
689e7c127fSCraig Rodrigues 
699e7c127fSCraig Rodrigues 	elm->be_encoding = encoding;
709e7c127fSCraig Rodrigues 	ber_set_header(elm, BER_CLASS_UNIVERSAL, BER_TYPE_DEFAULT);
719e7c127fSCraig Rodrigues 
729e7c127fSCraig Rodrigues 	return elm;
739e7c127fSCraig Rodrigues }
749e7c127fSCraig Rodrigues 
759e7c127fSCraig Rodrigues void
ber_set_header(struct ber_element * elm,int class,unsigned long type)769e7c127fSCraig Rodrigues ber_set_header(struct ber_element *elm, int class, unsigned long type)
779e7c127fSCraig Rodrigues {
789e7c127fSCraig Rodrigues 	elm->be_class = class & BER_CLASS_MASK;
799e7c127fSCraig Rodrigues 	if (type == BER_TYPE_DEFAULT)
809e7c127fSCraig Rodrigues 		type = elm->be_encoding;
819e7c127fSCraig Rodrigues 	elm->be_type = type;
829e7c127fSCraig Rodrigues }
839e7c127fSCraig Rodrigues 
849e7c127fSCraig Rodrigues void
ber_link_elements(struct ber_element * prev,struct ber_element * elm)859e7c127fSCraig Rodrigues ber_link_elements(struct ber_element *prev, struct ber_element *elm)
869e7c127fSCraig Rodrigues {
879e7c127fSCraig Rodrigues 	if (prev != NULL) {
889e7c127fSCraig Rodrigues 		if ((prev->be_encoding == BER_TYPE_SEQUENCE ||
899e7c127fSCraig Rodrigues 		    prev->be_encoding == BER_TYPE_SET) &&
909e7c127fSCraig Rodrigues 		    prev->be_sub == NULL)
919e7c127fSCraig Rodrigues 			prev->be_sub = elm;
929e7c127fSCraig Rodrigues 		else
939e7c127fSCraig Rodrigues 			prev->be_next = elm;
949e7c127fSCraig Rodrigues 	}
959e7c127fSCraig Rodrigues }
969e7c127fSCraig Rodrigues 
979e7c127fSCraig Rodrigues struct ber_element *
ber_unlink_elements(struct ber_element * prev)989e7c127fSCraig Rodrigues ber_unlink_elements(struct ber_element *prev)
999e7c127fSCraig Rodrigues {
1009e7c127fSCraig Rodrigues 	struct ber_element *elm;
1019e7c127fSCraig Rodrigues 
1029e7c127fSCraig Rodrigues 	if ((prev->be_encoding == BER_TYPE_SEQUENCE ||
1039e7c127fSCraig Rodrigues 	    prev->be_encoding == BER_TYPE_SET) &&
1049e7c127fSCraig Rodrigues 	    prev->be_sub != NULL) {
1059e7c127fSCraig Rodrigues 		elm = prev->be_sub;
1069e7c127fSCraig Rodrigues 		prev->be_sub = NULL;
1079e7c127fSCraig Rodrigues 	} else {
1089e7c127fSCraig Rodrigues 		elm = prev->be_next;
1099e7c127fSCraig Rodrigues 		prev->be_next = NULL;
1109e7c127fSCraig Rodrigues 	}
1119e7c127fSCraig Rodrigues 
1129e7c127fSCraig Rodrigues 	return (elm);
1139e7c127fSCraig Rodrigues }
1149e7c127fSCraig Rodrigues 
1159e7c127fSCraig Rodrigues void
ber_replace_elements(struct ber_element * prev,struct ber_element * new)1169e7c127fSCraig Rodrigues ber_replace_elements(struct ber_element *prev, struct ber_element *new)
1179e7c127fSCraig Rodrigues {
1189e7c127fSCraig Rodrigues 	struct ber_element *ber, *next;
1199e7c127fSCraig Rodrigues 
1209e7c127fSCraig Rodrigues 	ber = ber_unlink_elements(prev);
1219e7c127fSCraig Rodrigues 	next = ber_unlink_elements(ber);
1229e7c127fSCraig Rodrigues 	ber_link_elements(new, next);
1239e7c127fSCraig Rodrigues 	ber_link_elements(prev, new);
1249e7c127fSCraig Rodrigues 
1259e7c127fSCraig Rodrigues 	/* cleanup old element */
1269e7c127fSCraig Rodrigues 	ber_free_elements(ber);
1279e7c127fSCraig Rodrigues }
1289e7c127fSCraig Rodrigues 
1299e7c127fSCraig Rodrigues struct ber_element *
ber_add_sequence(struct ber_element * prev)1309e7c127fSCraig Rodrigues ber_add_sequence(struct ber_element *prev)
1319e7c127fSCraig Rodrigues {
1329e7c127fSCraig Rodrigues 	struct ber_element *elm;
1339e7c127fSCraig Rodrigues 
1349e7c127fSCraig Rodrigues 	if ((elm = ber_get_element(BER_TYPE_SEQUENCE)) == NULL)
1359e7c127fSCraig Rodrigues 		return NULL;
1369e7c127fSCraig Rodrigues 
1379e7c127fSCraig Rodrigues 	ber_link_elements(prev, elm);
1389e7c127fSCraig Rodrigues 
1399e7c127fSCraig Rodrigues 	return elm;
1409e7c127fSCraig Rodrigues }
1419e7c127fSCraig Rodrigues 
1429e7c127fSCraig Rodrigues struct ber_element *
ber_add_set(struct ber_element * prev)1439e7c127fSCraig Rodrigues ber_add_set(struct ber_element *prev)
1449e7c127fSCraig Rodrigues {
1459e7c127fSCraig Rodrigues 	struct ber_element *elm;
1469e7c127fSCraig Rodrigues 
1479e7c127fSCraig Rodrigues 	if ((elm = ber_get_element(BER_TYPE_SET)) == NULL)
1489e7c127fSCraig Rodrigues 		return NULL;
1499e7c127fSCraig Rodrigues 
1509e7c127fSCraig Rodrigues 	ber_link_elements(prev, elm);
1519e7c127fSCraig Rodrigues 
1529e7c127fSCraig Rodrigues 	return elm;
1539e7c127fSCraig Rodrigues }
1549e7c127fSCraig Rodrigues 
1559e7c127fSCraig Rodrigues struct ber_element *
ber_add_enumerated(struct ber_element * prev,long long val)1569e7c127fSCraig Rodrigues ber_add_enumerated(struct ber_element *prev, long long val)
1579e7c127fSCraig Rodrigues {
1589e7c127fSCraig Rodrigues 	struct ber_element *elm;
1599e7c127fSCraig Rodrigues 	u_int i, len = 0;
1609e7c127fSCraig Rodrigues 	u_char cur, last = 0;
1619e7c127fSCraig Rodrigues 
1629e7c127fSCraig Rodrigues 	if ((elm = ber_get_element(BER_TYPE_ENUMERATED)) == NULL)
1639e7c127fSCraig Rodrigues 		return NULL;
1649e7c127fSCraig Rodrigues 
1659e7c127fSCraig Rodrigues 	elm->be_numeric = val;
1669e7c127fSCraig Rodrigues 
1679e7c127fSCraig Rodrigues 	for (i = 0; i < sizeof(long long); i++) {
1689e7c127fSCraig Rodrigues 		cur = val & 0xff;
1699e7c127fSCraig Rodrigues 		if (cur != 0 && cur != 0xff)
1709e7c127fSCraig Rodrigues 			len = i;
1719e7c127fSCraig Rodrigues 		if ((cur == 0 && last & 0x80) ||
1729e7c127fSCraig Rodrigues 		    (cur == 0xff && (last & 0x80) == 0))
1739e7c127fSCraig Rodrigues 			len = i;
1749e7c127fSCraig Rodrigues 		val >>= 8;
1759e7c127fSCraig Rodrigues 		last = cur;
1769e7c127fSCraig Rodrigues 	}
1779e7c127fSCraig Rodrigues 	elm->be_len = len + 1;
1789e7c127fSCraig Rodrigues 
1799e7c127fSCraig Rodrigues 	ber_link_elements(prev, elm);
1809e7c127fSCraig Rodrigues 
1819e7c127fSCraig Rodrigues 	return elm;
1829e7c127fSCraig Rodrigues }
1839e7c127fSCraig Rodrigues 
1849e7c127fSCraig Rodrigues struct ber_element *
ber_add_integer(struct ber_element * prev,long long val)1859e7c127fSCraig Rodrigues ber_add_integer(struct ber_element *prev, long long val)
1869e7c127fSCraig Rodrigues {
1879e7c127fSCraig Rodrigues 	struct ber_element *elm;
1889e7c127fSCraig Rodrigues 	u_int i, len = 0;
1899e7c127fSCraig Rodrigues 	u_char cur, last = 0;
1909e7c127fSCraig Rodrigues 
1919e7c127fSCraig Rodrigues 	if ((elm = ber_get_element(BER_TYPE_INTEGER)) == NULL)
1929e7c127fSCraig Rodrigues 		return NULL;
1939e7c127fSCraig Rodrigues 
1949e7c127fSCraig Rodrigues 	elm->be_numeric = val;
1959e7c127fSCraig Rodrigues 
1969e7c127fSCraig Rodrigues 	for (i = 0; i < sizeof(long long); i++) {
1979e7c127fSCraig Rodrigues 		cur = val & 0xff;
1989e7c127fSCraig Rodrigues 		if (cur != 0 && cur != 0xff)
1999e7c127fSCraig Rodrigues 			len = i;
2009e7c127fSCraig Rodrigues 		if ((cur == 0 && last & 0x80) ||
2019e7c127fSCraig Rodrigues 		    (cur == 0xff && (last & 0x80) == 0))
2029e7c127fSCraig Rodrigues 			len = i;
2039e7c127fSCraig Rodrigues 		val >>= 8;
2049e7c127fSCraig Rodrigues 		last = cur;
2059e7c127fSCraig Rodrigues 	}
2069e7c127fSCraig Rodrigues 	elm->be_len = len + 1;
2079e7c127fSCraig Rodrigues 
2089e7c127fSCraig Rodrigues 	ber_link_elements(prev, elm);
2099e7c127fSCraig Rodrigues 
2109e7c127fSCraig Rodrigues 	return elm;
2119e7c127fSCraig Rodrigues }
2129e7c127fSCraig Rodrigues 
2139e7c127fSCraig Rodrigues int
ber_get_integer(struct ber_element * elm,long long * n)2149e7c127fSCraig Rodrigues ber_get_integer(struct ber_element *elm, long long *n)
2159e7c127fSCraig Rodrigues {
2169e7c127fSCraig Rodrigues 	if (elm->be_encoding != BER_TYPE_INTEGER)
2179e7c127fSCraig Rodrigues 		return -1;
2189e7c127fSCraig Rodrigues 
2199e7c127fSCraig Rodrigues 	*n = elm->be_numeric;
2209e7c127fSCraig Rodrigues 	return 0;
2219e7c127fSCraig Rodrigues }
2229e7c127fSCraig Rodrigues 
2239e7c127fSCraig Rodrigues int
ber_get_enumerated(struct ber_element * elm,long long * n)2249e7c127fSCraig Rodrigues ber_get_enumerated(struct ber_element *elm, long long *n)
2259e7c127fSCraig Rodrigues {
2269e7c127fSCraig Rodrigues 	if (elm->be_encoding != BER_TYPE_ENUMERATED)
2279e7c127fSCraig Rodrigues 		return -1;
2289e7c127fSCraig Rodrigues 
2299e7c127fSCraig Rodrigues 	*n = elm->be_numeric;
2309e7c127fSCraig Rodrigues 	return 0;
2319e7c127fSCraig Rodrigues }
2329e7c127fSCraig Rodrigues 
2339e7c127fSCraig Rodrigues 
2349e7c127fSCraig Rodrigues struct ber_element *
ber_add_boolean(struct ber_element * prev,int bool)2359e7c127fSCraig Rodrigues ber_add_boolean(struct ber_element *prev, int bool)
2369e7c127fSCraig Rodrigues {
2379e7c127fSCraig Rodrigues 	struct ber_element *elm;
2389e7c127fSCraig Rodrigues 
2399e7c127fSCraig Rodrigues 	if ((elm = ber_get_element(BER_TYPE_BOOLEAN)) == NULL)
2409e7c127fSCraig Rodrigues 		return NULL;
2419e7c127fSCraig Rodrigues 
2429e7c127fSCraig Rodrigues 	elm->be_numeric = bool ? 0xff : 0;
2439e7c127fSCraig Rodrigues 	elm->be_len = 1;
2449e7c127fSCraig Rodrigues 
2459e7c127fSCraig Rodrigues 	ber_link_elements(prev, elm);
2469e7c127fSCraig Rodrigues 
2479e7c127fSCraig Rodrigues 	return elm;
2489e7c127fSCraig Rodrigues }
2499e7c127fSCraig Rodrigues 
2509e7c127fSCraig Rodrigues int
ber_get_boolean(struct ber_element * elm,int * b)2519e7c127fSCraig Rodrigues ber_get_boolean(struct ber_element *elm, int *b)
2529e7c127fSCraig Rodrigues {
2539e7c127fSCraig Rodrigues 	if (elm->be_encoding != BER_TYPE_BOOLEAN)
2549e7c127fSCraig Rodrigues 		return -1;
2559e7c127fSCraig Rodrigues 
2569e7c127fSCraig Rodrigues 	*b = !(elm->be_numeric == 0);
2579e7c127fSCraig Rodrigues 	return 0;
2589e7c127fSCraig Rodrigues }
2599e7c127fSCraig Rodrigues 
2609e7c127fSCraig Rodrigues struct ber_element *
ber_add_string(struct ber_element * prev,const char * string)2619e7c127fSCraig Rodrigues ber_add_string(struct ber_element *prev, const char *string)
2629e7c127fSCraig Rodrigues {
2639e7c127fSCraig Rodrigues 	return ber_add_nstring(prev, string, strlen(string));
2649e7c127fSCraig Rodrigues }
2659e7c127fSCraig Rodrigues 
2669e7c127fSCraig Rodrigues struct ber_element *
ber_add_nstring(struct ber_element * prev,const char * string0,size_t len)2679e7c127fSCraig Rodrigues ber_add_nstring(struct ber_element *prev, const char *string0, size_t len)
2689e7c127fSCraig Rodrigues {
2699e7c127fSCraig Rodrigues 	struct ber_element *elm;
2709e7c127fSCraig Rodrigues 	char *string;
2719e7c127fSCraig Rodrigues 
2729e7c127fSCraig Rodrigues 	if ((string = calloc(1, len)) == NULL)
2739e7c127fSCraig Rodrigues 		return NULL;
2749e7c127fSCraig Rodrigues 	if ((elm = ber_get_element(BER_TYPE_OCTETSTRING)) == NULL) {
2759e7c127fSCraig Rodrigues 		free(string);
2769e7c127fSCraig Rodrigues 		return NULL;
2779e7c127fSCraig Rodrigues 	}
2789e7c127fSCraig Rodrigues 
2799e7c127fSCraig Rodrigues 	bcopy(string0, string, len);
2809e7c127fSCraig Rodrigues 	elm->be_val = string;
2819e7c127fSCraig Rodrigues 	elm->be_len = len;
2829e7c127fSCraig Rodrigues 	elm->be_free = 1;		/* free string on cleanup */
2839e7c127fSCraig Rodrigues 
2849e7c127fSCraig Rodrigues 	ber_link_elements(prev, elm);
2859e7c127fSCraig Rodrigues 
2869e7c127fSCraig Rodrigues 	return elm;
2879e7c127fSCraig Rodrigues }
2889e7c127fSCraig Rodrigues 
2899e7c127fSCraig Rodrigues int
ber_get_string(struct ber_element * elm,char ** s)2909e7c127fSCraig Rodrigues ber_get_string(struct ber_element *elm, char **s)
2919e7c127fSCraig Rodrigues {
2929e7c127fSCraig Rodrigues 	if (elm->be_encoding != BER_TYPE_OCTETSTRING)
2939e7c127fSCraig Rodrigues 		return -1;
2949e7c127fSCraig Rodrigues 
2959e7c127fSCraig Rodrigues 	*s = elm->be_val;
2969e7c127fSCraig Rodrigues 	return 0;
2979e7c127fSCraig Rodrigues }
2989e7c127fSCraig Rodrigues 
2999e7c127fSCraig Rodrigues int
ber_get_nstring(struct ber_element * elm,void ** p,size_t * len)3009e7c127fSCraig Rodrigues ber_get_nstring(struct ber_element *elm, void **p, size_t *len)
3019e7c127fSCraig Rodrigues {
3029e7c127fSCraig Rodrigues 	if (elm->be_encoding != BER_TYPE_OCTETSTRING)
3039e7c127fSCraig Rodrigues 		return -1;
3049e7c127fSCraig Rodrigues 
3059e7c127fSCraig Rodrigues 	*p = elm->be_val;
3069e7c127fSCraig Rodrigues 	*len = elm->be_len;
3079e7c127fSCraig Rodrigues 	return 0;
3089e7c127fSCraig Rodrigues }
3099e7c127fSCraig Rodrigues 
3109e7c127fSCraig Rodrigues struct ber_element *
ber_add_bitstring(struct ber_element * prev,const void * v0,size_t len)3119e7c127fSCraig Rodrigues ber_add_bitstring(struct ber_element *prev, const void *v0, size_t len)
3129e7c127fSCraig Rodrigues {
3139e7c127fSCraig Rodrigues 	struct ber_element *elm;
3149e7c127fSCraig Rodrigues 	void *v;
3159e7c127fSCraig Rodrigues 
3169e7c127fSCraig Rodrigues 	if ((v = calloc(1, len)) == NULL)
3179e7c127fSCraig Rodrigues 		return NULL;
3189e7c127fSCraig Rodrigues 	if ((elm = ber_get_element(BER_TYPE_BITSTRING)) == NULL) {
3199e7c127fSCraig Rodrigues 		free(v);
3209e7c127fSCraig Rodrigues 		return NULL;
3219e7c127fSCraig Rodrigues 	}
3229e7c127fSCraig Rodrigues 
3239e7c127fSCraig Rodrigues 	bcopy(v0, v, len);
3249e7c127fSCraig Rodrigues 	elm->be_val = v;
3259e7c127fSCraig Rodrigues 	elm->be_len = len;
3269e7c127fSCraig Rodrigues 	elm->be_free = 1;		/* free string on cleanup */
3279e7c127fSCraig Rodrigues 
3289e7c127fSCraig Rodrigues 	ber_link_elements(prev, elm);
3299e7c127fSCraig Rodrigues 
3309e7c127fSCraig Rodrigues 	return elm;
3319e7c127fSCraig Rodrigues }
3329e7c127fSCraig Rodrigues 
3339e7c127fSCraig Rodrigues int
ber_get_bitstring(struct ber_element * elm,void ** v,size_t * len)3349e7c127fSCraig Rodrigues ber_get_bitstring(struct ber_element *elm, void **v, size_t *len)
3359e7c127fSCraig Rodrigues {
3369e7c127fSCraig Rodrigues 	if (elm->be_encoding != BER_TYPE_BITSTRING)
3379e7c127fSCraig Rodrigues 		return -1;
3389e7c127fSCraig Rodrigues 
3399e7c127fSCraig Rodrigues 	*v = elm->be_val;
3409e7c127fSCraig Rodrigues 	*len = elm->be_len;
3419e7c127fSCraig Rodrigues 	return 0;
3429e7c127fSCraig Rodrigues }
3439e7c127fSCraig Rodrigues 
3449e7c127fSCraig Rodrigues struct ber_element *
ber_add_null(struct ber_element * prev)3459e7c127fSCraig Rodrigues ber_add_null(struct ber_element *prev)
3469e7c127fSCraig Rodrigues {
3479e7c127fSCraig Rodrigues 	struct ber_element *elm;
3489e7c127fSCraig Rodrigues 
3499e7c127fSCraig Rodrigues 	if ((elm = ber_get_element(BER_TYPE_NULL)) == NULL)
3509e7c127fSCraig Rodrigues 		return NULL;
3519e7c127fSCraig Rodrigues 
3529e7c127fSCraig Rodrigues 	ber_link_elements(prev, elm);
3539e7c127fSCraig Rodrigues 
3549e7c127fSCraig Rodrigues 	return elm;
3559e7c127fSCraig Rodrigues }
3569e7c127fSCraig Rodrigues 
3579e7c127fSCraig Rodrigues int
ber_get_null(struct ber_element * elm)3589e7c127fSCraig Rodrigues ber_get_null(struct ber_element *elm)
3599e7c127fSCraig Rodrigues {
3609e7c127fSCraig Rodrigues 	if (elm->be_encoding != BER_TYPE_NULL)
3619e7c127fSCraig Rodrigues 		return -1;
3629e7c127fSCraig Rodrigues 
3639e7c127fSCraig Rodrigues 	return 0;
3649e7c127fSCraig Rodrigues }
3659e7c127fSCraig Rodrigues 
3669e7c127fSCraig Rodrigues struct ber_element *
ber_add_eoc(struct ber_element * prev)3679e7c127fSCraig Rodrigues ber_add_eoc(struct ber_element *prev)
3689e7c127fSCraig Rodrigues {
3699e7c127fSCraig Rodrigues 	struct ber_element *elm;
3709e7c127fSCraig Rodrigues 
3719e7c127fSCraig Rodrigues 	if ((elm = ber_get_element(BER_TYPE_EOC)) == NULL)
3729e7c127fSCraig Rodrigues 		return NULL;
3739e7c127fSCraig Rodrigues 
3749e7c127fSCraig Rodrigues 	ber_link_elements(prev, elm);
3759e7c127fSCraig Rodrigues 
3769e7c127fSCraig Rodrigues 	return elm;
3779e7c127fSCraig Rodrigues }
3789e7c127fSCraig Rodrigues 
3799e7c127fSCraig Rodrigues int
ber_get_eoc(struct ber_element * elm)3809e7c127fSCraig Rodrigues ber_get_eoc(struct ber_element *elm)
3819e7c127fSCraig Rodrigues {
3829e7c127fSCraig Rodrigues 	if (elm->be_encoding != BER_TYPE_EOC)
3839e7c127fSCraig Rodrigues 		return -1;
3849e7c127fSCraig Rodrigues 
3859e7c127fSCraig Rodrigues 	return 0;
3869e7c127fSCraig Rodrigues }
3879e7c127fSCraig Rodrigues 
3889e7c127fSCraig Rodrigues size_t
ber_oid2ber(struct ber_oid * o,u_int8_t * buf,size_t len)3899e7c127fSCraig Rodrigues ber_oid2ber(struct ber_oid *o, u_int8_t *buf, size_t len)
3909e7c127fSCraig Rodrigues {
3919e7c127fSCraig Rodrigues 	u_int32_t	 v;
3929e7c127fSCraig Rodrigues 	u_int		 i, j = 0, k;
3939e7c127fSCraig Rodrigues 
3949e7c127fSCraig Rodrigues 	if (o->bo_n < BER_MIN_OID_LEN || o->bo_n > BER_MAX_OID_LEN ||
3959e7c127fSCraig Rodrigues 	    o->bo_id[0] > 2 || o->bo_id[1] > 40)
3969e7c127fSCraig Rodrigues 		return (0);
3979e7c127fSCraig Rodrigues 
3989e7c127fSCraig Rodrigues 	v = (o->bo_id[0] * 40) + o->bo_id[1];
3999e7c127fSCraig Rodrigues 	for (i = 2, j = 0; i <= o->bo_n; v = o->bo_id[i], i++) {
4009e7c127fSCraig Rodrigues 		for (k = 28; k >= 7; k -= 7) {
4019e7c127fSCraig Rodrigues 			if (v >= (u_int)(1 << k)) {
4029e7c127fSCraig Rodrigues 				if (len)
4039e7c127fSCraig Rodrigues 					buf[j] = v >> k | BER_TAG_MORE;
4049e7c127fSCraig Rodrigues 				j++;
4059e7c127fSCraig Rodrigues 			}
4069e7c127fSCraig Rodrigues 		}
4079e7c127fSCraig Rodrigues 		if (len)
4089e7c127fSCraig Rodrigues 			buf[j] = v & BER_TAG_TYPE_MASK;
4099e7c127fSCraig Rodrigues 		j++;
4109e7c127fSCraig Rodrigues 	}
4119e7c127fSCraig Rodrigues 
4129e7c127fSCraig Rodrigues 	return (j);
4139e7c127fSCraig Rodrigues }
4149e7c127fSCraig Rodrigues 
4159e7c127fSCraig Rodrigues int
ber_string2oid(const char * oidstr,struct ber_oid * o)4169e7c127fSCraig Rodrigues ber_string2oid(const char *oidstr, struct ber_oid *o)
4179e7c127fSCraig Rodrigues {
4189e7c127fSCraig Rodrigues 	char			*sp, *p, str[BUFSIZ];
4199e7c127fSCraig Rodrigues 	const char		*errstr;
4209e7c127fSCraig Rodrigues 
4219e7c127fSCraig Rodrigues 	if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str))
4229e7c127fSCraig Rodrigues 		return (-1);
4239e7c127fSCraig Rodrigues 	bzero(o, sizeof(*o));
4249e7c127fSCraig Rodrigues 
4259e7c127fSCraig Rodrigues 	/* Parse OID strings in the common forms n.n.n, n_n_n_n, or n-n-n */
4269e7c127fSCraig Rodrigues 	for (p = sp = str; p != NULL; sp = p) {
4279e7c127fSCraig Rodrigues 		if ((p = strpbrk(p, "._-")) != NULL)
4289e7c127fSCraig Rodrigues 			*p++ = '\0';
4299e7c127fSCraig Rodrigues 		o->bo_id[o->bo_n++] = strtonum(sp, 0, UINT_MAX, &errstr);
4309e7c127fSCraig Rodrigues 		if (errstr || o->bo_n > BER_MAX_OID_LEN)
4319e7c127fSCraig Rodrigues 			return (-1);
4329e7c127fSCraig Rodrigues 	}
4339e7c127fSCraig Rodrigues 
4349e7c127fSCraig Rodrigues 	return (0);
4359e7c127fSCraig Rodrigues }
4369e7c127fSCraig Rodrigues 
4379e7c127fSCraig Rodrigues struct ber_element *
ber_add_oid(struct ber_element * prev,struct ber_oid * o)4389e7c127fSCraig Rodrigues ber_add_oid(struct ber_element *prev, struct ber_oid *o)
4399e7c127fSCraig Rodrigues {
4409e7c127fSCraig Rodrigues 	struct ber_element	*elm;
4419e7c127fSCraig Rodrigues 	u_int8_t		*buf;
4429e7c127fSCraig Rodrigues 	size_t			 len;
4439e7c127fSCraig Rodrigues 
4449e7c127fSCraig Rodrigues 	if ((elm = ber_get_element(BER_TYPE_OBJECT)) == NULL)
4459e7c127fSCraig Rodrigues 		return (NULL);
4469e7c127fSCraig Rodrigues 
4479e7c127fSCraig Rodrigues 	if ((len = ber_oid2ber(o, NULL, 0)) == 0)
4489e7c127fSCraig Rodrigues 		goto fail;
4499e7c127fSCraig Rodrigues 
4509e7c127fSCraig Rodrigues 	if ((buf = calloc(1, len)) == NULL)
4519e7c127fSCraig Rodrigues 		goto fail;
4529e7c127fSCraig Rodrigues 
4539e7c127fSCraig Rodrigues 	elm->be_val = buf;
4549e7c127fSCraig Rodrigues 	elm->be_len = len;
4559e7c127fSCraig Rodrigues 	elm->be_free = 1;
4569e7c127fSCraig Rodrigues 
4579e7c127fSCraig Rodrigues 	if (ber_oid2ber(o, buf, len) != len)
4589e7c127fSCraig Rodrigues 		goto fail;
4599e7c127fSCraig Rodrigues 
4609e7c127fSCraig Rodrigues 	ber_link_elements(prev, elm);
4619e7c127fSCraig Rodrigues 
4629e7c127fSCraig Rodrigues 	return (elm);
4639e7c127fSCraig Rodrigues 
4649e7c127fSCraig Rodrigues  fail:
4659e7c127fSCraig Rodrigues 	ber_free_elements(elm);
4669e7c127fSCraig Rodrigues 	return (NULL);
4679e7c127fSCraig Rodrigues }
4689e7c127fSCraig Rodrigues 
4699e7c127fSCraig Rodrigues struct ber_element *
ber_add_noid(struct ber_element * prev,struct ber_oid * o,int n)4709e7c127fSCraig Rodrigues ber_add_noid(struct ber_element *prev, struct ber_oid *o, int n)
4719e7c127fSCraig Rodrigues {
4729e7c127fSCraig Rodrigues 	struct ber_oid		 no;
4739e7c127fSCraig Rodrigues 
4749e7c127fSCraig Rodrigues 	if (n > BER_MAX_OID_LEN)
4759e7c127fSCraig Rodrigues 		return (NULL);
4769e7c127fSCraig Rodrigues 	no.bo_n = n;
4779e7c127fSCraig Rodrigues 	bcopy(&o->bo_id, &no.bo_id, sizeof(no.bo_id));
4789e7c127fSCraig Rodrigues 
4799e7c127fSCraig Rodrigues 	return (ber_add_oid(prev, &no));
4809e7c127fSCraig Rodrigues }
4819e7c127fSCraig Rodrigues 
4829e7c127fSCraig Rodrigues struct ber_element *
ber_add_oidstring(struct ber_element * prev,const char * oidstr)4839e7c127fSCraig Rodrigues ber_add_oidstring(struct ber_element *prev, const char *oidstr)
4849e7c127fSCraig Rodrigues {
4859e7c127fSCraig Rodrigues 	struct ber_oid		 o;
4869e7c127fSCraig Rodrigues 
4879e7c127fSCraig Rodrigues 	if (ber_string2oid(oidstr, &o) == -1)
4889e7c127fSCraig Rodrigues 		return (NULL);
4899e7c127fSCraig Rodrigues 
4909e7c127fSCraig Rodrigues 	return (ber_add_oid(prev, &o));
4919e7c127fSCraig Rodrigues }
4929e7c127fSCraig Rodrigues 
4939e7c127fSCraig Rodrigues int
ber_get_oid(struct ber_element * elm,struct ber_oid * o)4949e7c127fSCraig Rodrigues ber_get_oid(struct ber_element *elm, struct ber_oid *o)
4959e7c127fSCraig Rodrigues {
4969e7c127fSCraig Rodrigues 	u_int8_t	*buf;
4979e7c127fSCraig Rodrigues 	size_t		 len, i = 0, j = 0;
4989e7c127fSCraig Rodrigues 
4999e7c127fSCraig Rodrigues 	if (elm->be_encoding != BER_TYPE_OBJECT)
5009e7c127fSCraig Rodrigues 		return (-1);
5019e7c127fSCraig Rodrigues 
5029e7c127fSCraig Rodrigues 	buf = elm->be_val;
5039e7c127fSCraig Rodrigues 	len = elm->be_len;
5049e7c127fSCraig Rodrigues 
5059e7c127fSCraig Rodrigues 	if (!buf[i])
5069e7c127fSCraig Rodrigues 		return (-1);
5079e7c127fSCraig Rodrigues 
5089e7c127fSCraig Rodrigues 	bzero(o, sizeof(*o));
5099e7c127fSCraig Rodrigues 	o->bo_id[j++] = buf[i] / 40;
5109e7c127fSCraig Rodrigues 	o->bo_id[j++] = buf[i++] % 40;
5119e7c127fSCraig Rodrigues 	for (; i < len && j < BER_MAX_OID_LEN; i++) {
5129e7c127fSCraig Rodrigues 		o->bo_id[j] = (o->bo_id[j] << 7) + (buf[i] & ~0x80);
5139e7c127fSCraig Rodrigues 		if (buf[i] & 0x80)
5149e7c127fSCraig Rodrigues 			continue;
5159e7c127fSCraig Rodrigues 		j++;
5169e7c127fSCraig Rodrigues 	}
5179e7c127fSCraig Rodrigues 	o->bo_n = j;
5189e7c127fSCraig Rodrigues 
5199e7c127fSCraig Rodrigues 	return (0);
5209e7c127fSCraig Rodrigues }
5219e7c127fSCraig Rodrigues 
5229e7c127fSCraig Rodrigues struct ber_element *
ber_printf_elements(struct ber_element * ber,char * fmt,...)5239e7c127fSCraig Rodrigues ber_printf_elements(struct ber_element *ber, char *fmt, ...)
5249e7c127fSCraig Rodrigues {
5259e7c127fSCraig Rodrigues 	va_list			 ap;
5269e7c127fSCraig Rodrigues 	int			 d, class;
5279e7c127fSCraig Rodrigues 	size_t			 len;
5289e7c127fSCraig Rodrigues 	unsigned long		 type;
5299e7c127fSCraig Rodrigues 	long long		 i;
5309e7c127fSCraig Rodrigues 	char			*s;
5319e7c127fSCraig Rodrigues 	void			*p;
5329e7c127fSCraig Rodrigues 	struct ber_oid		*o;
5339e7c127fSCraig Rodrigues 	struct ber_element	*sub = ber, *e;
5349e7c127fSCraig Rodrigues 
5359e7c127fSCraig Rodrigues 	va_start(ap, fmt);
5369e7c127fSCraig Rodrigues 	while (*fmt) {
5379e7c127fSCraig Rodrigues 		switch (*fmt++) {
5389e7c127fSCraig Rodrigues 		case 'B':
5399e7c127fSCraig Rodrigues 			p = va_arg(ap, void *);
5409e7c127fSCraig Rodrigues 			len = va_arg(ap, size_t);
5419e7c127fSCraig Rodrigues 			if ((ber = ber_add_bitstring(ber, p, len)) == NULL)
5429e7c127fSCraig Rodrigues 				goto fail;
5439e7c127fSCraig Rodrigues 			break;
5449e7c127fSCraig Rodrigues 		case 'b':
5459e7c127fSCraig Rodrigues 			d = va_arg(ap, int);
5469e7c127fSCraig Rodrigues 			if ((ber = ber_add_boolean(ber, d)) == NULL)
5479e7c127fSCraig Rodrigues 				goto fail;
5489e7c127fSCraig Rodrigues 			break;
5499e7c127fSCraig Rodrigues 		case 'd':
5509e7c127fSCraig Rodrigues 			d = va_arg(ap, int);
5519e7c127fSCraig Rodrigues 			if ((ber = ber_add_integer(ber, d)) == NULL)
5529e7c127fSCraig Rodrigues 				goto fail;
5539e7c127fSCraig Rodrigues 			break;
5549e7c127fSCraig Rodrigues 		case 'e':
5559e7c127fSCraig Rodrigues 			e = va_arg(ap, struct ber_element *);
5569e7c127fSCraig Rodrigues 			ber_link_elements(ber, e);
5579e7c127fSCraig Rodrigues 			break;
5589e7c127fSCraig Rodrigues 		case 'E':
5599e7c127fSCraig Rodrigues 			i = va_arg(ap, long long);
5609e7c127fSCraig Rodrigues 			if ((ber = ber_add_enumerated(ber, i)) == NULL)
5619e7c127fSCraig Rodrigues 				goto fail;
5629e7c127fSCraig Rodrigues 			break;
5639e7c127fSCraig Rodrigues 		case 'i':
5649e7c127fSCraig Rodrigues 			i = va_arg(ap, long long);
5659e7c127fSCraig Rodrigues 			if ((ber = ber_add_integer(ber, i)) == NULL)
5669e7c127fSCraig Rodrigues 				goto fail;
5679e7c127fSCraig Rodrigues 			break;
5689e7c127fSCraig Rodrigues 		case 'O':
5699e7c127fSCraig Rodrigues 			o = va_arg(ap, struct ber_oid *);
5709e7c127fSCraig Rodrigues 			if ((ber = ber_add_oid(ber, o)) == NULL)
5719e7c127fSCraig Rodrigues 				goto fail;
5729e7c127fSCraig Rodrigues 			break;
5739e7c127fSCraig Rodrigues 		case 'o':
5749e7c127fSCraig Rodrigues 			s = va_arg(ap, char *);
5759e7c127fSCraig Rodrigues 			if ((ber = ber_add_oidstring(ber, s)) == NULL)
5769e7c127fSCraig Rodrigues 				goto fail;
5779e7c127fSCraig Rodrigues 			break;
5789e7c127fSCraig Rodrigues 		case 's':
5799e7c127fSCraig Rodrigues 			s = va_arg(ap, char *);
5809e7c127fSCraig Rodrigues 			if ((ber = ber_add_string(ber, s)) == NULL)
5819e7c127fSCraig Rodrigues 				goto fail;
5829e7c127fSCraig Rodrigues 			break;
5839e7c127fSCraig Rodrigues 		case 't':
5849e7c127fSCraig Rodrigues 			class = va_arg(ap, int);
5859e7c127fSCraig Rodrigues 			type = va_arg(ap, unsigned long);
5869e7c127fSCraig Rodrigues 			ber_set_header(ber, class, type);
5879e7c127fSCraig Rodrigues 			break;
5889e7c127fSCraig Rodrigues 		case 'x':
5899e7c127fSCraig Rodrigues 			s = va_arg(ap, char *);
5909e7c127fSCraig Rodrigues 			len = va_arg(ap, size_t);
5919e7c127fSCraig Rodrigues 			if ((ber = ber_add_nstring(ber, s, len)) == NULL)
5929e7c127fSCraig Rodrigues 				goto fail;
5939e7c127fSCraig Rodrigues 			break;
5949e7c127fSCraig Rodrigues 		case '0':
5959e7c127fSCraig Rodrigues 			if ((ber = ber_add_null(ber)) == NULL)
5969e7c127fSCraig Rodrigues 				goto fail;
5979e7c127fSCraig Rodrigues 			break;
5989e7c127fSCraig Rodrigues 		case '{':
5999e7c127fSCraig Rodrigues 			if ((ber = sub = ber_add_sequence(ber)) == NULL)
6009e7c127fSCraig Rodrigues 				goto fail;
6019e7c127fSCraig Rodrigues 			break;
6029e7c127fSCraig Rodrigues 		case '(':
6039e7c127fSCraig Rodrigues 			if ((ber = sub = ber_add_set(ber)) == NULL)
6049e7c127fSCraig Rodrigues 				goto fail;
6059e7c127fSCraig Rodrigues 			break;
6069e7c127fSCraig Rodrigues 		case '}':
6079e7c127fSCraig Rodrigues 		case ')':
6089e7c127fSCraig Rodrigues 			ber = sub;
6099e7c127fSCraig Rodrigues 			break;
6109e7c127fSCraig Rodrigues 		case '.':
6119e7c127fSCraig Rodrigues 			if ((e = ber_add_eoc(ber)) == NULL)
6129e7c127fSCraig Rodrigues 				goto fail;
6139e7c127fSCraig Rodrigues 			ber = e;
6149e7c127fSCraig Rodrigues 			break;
6159e7c127fSCraig Rodrigues 		default:
6169e7c127fSCraig Rodrigues 			break;
6179e7c127fSCraig Rodrigues 		}
6189e7c127fSCraig Rodrigues 	}
6199e7c127fSCraig Rodrigues 	va_end(ap);
6209e7c127fSCraig Rodrigues 
6219e7c127fSCraig Rodrigues 	return (ber);
6229e7c127fSCraig Rodrigues  fail:
623*446bb222SConrad Meyer 	va_end(ap);
6249e7c127fSCraig Rodrigues 	return (NULL);
6259e7c127fSCraig Rodrigues }
6269e7c127fSCraig Rodrigues 
6279e7c127fSCraig Rodrigues int
ber_scanf_elements(struct ber_element * ber,char * fmt,...)6289e7c127fSCraig Rodrigues ber_scanf_elements(struct ber_element *ber, char *fmt, ...)
6299e7c127fSCraig Rodrigues {
6309e7c127fSCraig Rodrigues #define _MAX_SEQ		 128
6319e7c127fSCraig Rodrigues 	va_list			 ap;
6329e7c127fSCraig Rodrigues 	int			*d, level = -1;
6339e7c127fSCraig Rodrigues 	unsigned long		*t;
6349e7c127fSCraig Rodrigues 	long long		*i;
6359e7c127fSCraig Rodrigues 	void			**ptr;
6369e7c127fSCraig Rodrigues 	size_t			*len, ret = 0, n = strlen(fmt);
6379e7c127fSCraig Rodrigues 	char			**s;
6389e7c127fSCraig Rodrigues 	struct ber_oid		*o;
6399e7c127fSCraig Rodrigues 	struct ber_element	*parent[_MAX_SEQ], **e;
6409e7c127fSCraig Rodrigues 
6419e7c127fSCraig Rodrigues 	bzero(parent, sizeof(struct ber_element *) * _MAX_SEQ);
6429e7c127fSCraig Rodrigues 
6439e7c127fSCraig Rodrigues 	va_start(ap, fmt);
6449e7c127fSCraig Rodrigues 	while (*fmt) {
6459e7c127fSCraig Rodrigues 		switch (*fmt++) {
6469e7c127fSCraig Rodrigues 		case 'B':
6479e7c127fSCraig Rodrigues 			ptr = va_arg(ap, void **);
6489e7c127fSCraig Rodrigues 			len = va_arg(ap, size_t *);
6499e7c127fSCraig Rodrigues 			if (ber_get_bitstring(ber, ptr, len) == -1)
6509e7c127fSCraig Rodrigues 				goto fail;
6519e7c127fSCraig Rodrigues 			ret++;
6529e7c127fSCraig Rodrigues 			break;
6539e7c127fSCraig Rodrigues 		case 'b':
6549e7c127fSCraig Rodrigues 			d = va_arg(ap, int *);
6559e7c127fSCraig Rodrigues 			if (ber_get_boolean(ber, d) == -1)
6569e7c127fSCraig Rodrigues 				goto fail;
6579e7c127fSCraig Rodrigues 			ret++;
6589e7c127fSCraig Rodrigues 			break;
6599e7c127fSCraig Rodrigues 		case 'e':
6609e7c127fSCraig Rodrigues 			e = va_arg(ap, struct ber_element **);
6619e7c127fSCraig Rodrigues 			*e = ber;
6629e7c127fSCraig Rodrigues 			ret++;
6639e7c127fSCraig Rodrigues 			continue;
6649e7c127fSCraig Rodrigues 		case 'E':
6659e7c127fSCraig Rodrigues 			i = va_arg(ap, long long *);
6669e7c127fSCraig Rodrigues 			if (ber_get_enumerated(ber, i) == -1)
6679e7c127fSCraig Rodrigues 				goto fail;
6689e7c127fSCraig Rodrigues 			ret++;
6699e7c127fSCraig Rodrigues 			break;
6709e7c127fSCraig Rodrigues 		case 'i':
6719e7c127fSCraig Rodrigues 			i = va_arg(ap, long long *);
6729e7c127fSCraig Rodrigues 			if (ber_get_integer(ber, i) == -1)
6739e7c127fSCraig Rodrigues 				goto fail;
6749e7c127fSCraig Rodrigues 			ret++;
6759e7c127fSCraig Rodrigues 			break;
6769e7c127fSCraig Rodrigues 		case 'o':
6779e7c127fSCraig Rodrigues 			o = va_arg(ap, struct ber_oid *);
6789e7c127fSCraig Rodrigues 			if (ber_get_oid(ber, o) == -1)
6799e7c127fSCraig Rodrigues 				goto fail;
6809e7c127fSCraig Rodrigues 			ret++;
6819e7c127fSCraig Rodrigues 			break;
6829e7c127fSCraig Rodrigues 		case 'S':
6839e7c127fSCraig Rodrigues 			ret++;
6849e7c127fSCraig Rodrigues 			break;
6859e7c127fSCraig Rodrigues 		case 's':
6869e7c127fSCraig Rodrigues 			s = va_arg(ap, char **);
6879e7c127fSCraig Rodrigues 			if (ber_get_string(ber, s) == -1)
6889e7c127fSCraig Rodrigues 				goto fail;
6899e7c127fSCraig Rodrigues 			ret++;
6909e7c127fSCraig Rodrigues 			break;
6919e7c127fSCraig Rodrigues 		case 't':
6929e7c127fSCraig Rodrigues 			d = va_arg(ap, int *);
6939e7c127fSCraig Rodrigues 			t = va_arg(ap, unsigned long *);
6949e7c127fSCraig Rodrigues 			*d = ber->be_class;
6959e7c127fSCraig Rodrigues 			*t = ber->be_type;
6969e7c127fSCraig Rodrigues 			ret++;
6979e7c127fSCraig Rodrigues 			continue;
6989e7c127fSCraig Rodrigues 		case 'x':
6999e7c127fSCraig Rodrigues 			ptr = va_arg(ap, void **);
7009e7c127fSCraig Rodrigues 			len = va_arg(ap, size_t *);
7019e7c127fSCraig Rodrigues 			if (ber_get_nstring(ber, ptr, len) == -1)
7029e7c127fSCraig Rodrigues 				goto fail;
7039e7c127fSCraig Rodrigues 			ret++;
7049e7c127fSCraig Rodrigues 			break;
7059e7c127fSCraig Rodrigues 		case '0':
7069e7c127fSCraig Rodrigues 			if (ber->be_encoding != BER_TYPE_NULL)
7079e7c127fSCraig Rodrigues 				goto fail;
7089e7c127fSCraig Rodrigues 			ret++;
7099e7c127fSCraig Rodrigues 			break;
7109e7c127fSCraig Rodrigues 		case '.':
7119e7c127fSCraig Rodrigues 			if (ber->be_encoding != BER_TYPE_EOC)
7129e7c127fSCraig Rodrigues 				goto fail;
7139e7c127fSCraig Rodrigues 			ret++;
7149e7c127fSCraig Rodrigues 			break;
7159e7c127fSCraig Rodrigues 		case '{':
7169e7c127fSCraig Rodrigues 		case '(':
7179e7c127fSCraig Rodrigues 			if (ber->be_encoding != BER_TYPE_SEQUENCE &&
7189e7c127fSCraig Rodrigues 			    ber->be_encoding != BER_TYPE_SET)
7199e7c127fSCraig Rodrigues 				goto fail;
7209e7c127fSCraig Rodrigues 			if (ber->be_sub == NULL || level >= _MAX_SEQ-1)
7219e7c127fSCraig Rodrigues 				goto fail;
7229e7c127fSCraig Rodrigues 			parent[++level] = ber;
7239e7c127fSCraig Rodrigues 			ber = ber->be_sub;
7249e7c127fSCraig Rodrigues 			ret++;
7259e7c127fSCraig Rodrigues 			continue;
7269e7c127fSCraig Rodrigues 		case '}':
7279e7c127fSCraig Rodrigues 		case ')':
728a5f63746SDon Lewis 			if (level < 0 || parent[level] == NULL)
7299e7c127fSCraig Rodrigues 				goto fail;
7309e7c127fSCraig Rodrigues 			ber = parent[level--];
7319e7c127fSCraig Rodrigues 			ret++;
7329e7c127fSCraig Rodrigues 			continue;
7339e7c127fSCraig Rodrigues 		default:
7349e7c127fSCraig Rodrigues 			goto fail;
7359e7c127fSCraig Rodrigues 		}
7369e7c127fSCraig Rodrigues 
7379e7c127fSCraig Rodrigues 		if (ber->be_next == NULL)
7389e7c127fSCraig Rodrigues 			continue;
7399e7c127fSCraig Rodrigues 		ber = ber->be_next;
7409e7c127fSCraig Rodrigues 	}
7419e7c127fSCraig Rodrigues 	va_end(ap);
7429e7c127fSCraig Rodrigues 	return (ret == n ? 0 : -1);
7439e7c127fSCraig Rodrigues 
7449e7c127fSCraig Rodrigues  fail:
7459e7c127fSCraig Rodrigues 	va_end(ap);
7469e7c127fSCraig Rodrigues 	return (-1);
7479e7c127fSCraig Rodrigues 
7489e7c127fSCraig Rodrigues }
7499e7c127fSCraig Rodrigues 
7509e7c127fSCraig Rodrigues /*
7519e7c127fSCraig Rodrigues  * write ber elements to the socket
7529e7c127fSCraig Rodrigues  *
7539e7c127fSCraig Rodrigues  * params:
7549e7c127fSCraig Rodrigues  *	ber	holds the socket
7559e7c127fSCraig Rodrigues  *	root	fully populated element tree
7569e7c127fSCraig Rodrigues  *
7579e7c127fSCraig Rodrigues  * returns:
7589e7c127fSCraig Rodrigues  *      >=0     number of bytes written
7599e7c127fSCraig Rodrigues  *	-1	on failure and sets errno
7609e7c127fSCraig Rodrigues  */
7619e7c127fSCraig Rodrigues int
ber_write_elements(struct ber * ber,struct ber_element * root)7629e7c127fSCraig Rodrigues ber_write_elements(struct ber *ber, struct ber_element *root)
7639e7c127fSCraig Rodrigues {
7649e7c127fSCraig Rodrigues 	size_t len;
7659e7c127fSCraig Rodrigues 
7669e7c127fSCraig Rodrigues 	/* calculate length because only the definite form is required */
7679e7c127fSCraig Rodrigues 	len = ber_calc_len(root);
7689e7c127fSCraig Rodrigues 	DPRINTF("write ber element of %zd bytes length\n", len);
7699e7c127fSCraig Rodrigues 
7709e7c127fSCraig Rodrigues 	if (ber->br_wbuf != NULL && ber->br_wbuf + len > ber->br_wend) {
7719e7c127fSCraig Rodrigues 		free(ber->br_wbuf);
7729e7c127fSCraig Rodrigues 		ber->br_wbuf = NULL;
7739e7c127fSCraig Rodrigues 	}
7749e7c127fSCraig Rodrigues 	if (ber->br_wbuf == NULL) {
7759e7c127fSCraig Rodrigues 		if ((ber->br_wbuf = malloc(len)) == NULL)
7769e7c127fSCraig Rodrigues 			return -1;
7779e7c127fSCraig Rodrigues 		ber->br_wend = ber->br_wbuf + len;
7789e7c127fSCraig Rodrigues 	}
7799e7c127fSCraig Rodrigues 
7809e7c127fSCraig Rodrigues 	/* reset write pointer */
7819e7c127fSCraig Rodrigues 	ber->br_wptr = ber->br_wbuf;
7829e7c127fSCraig Rodrigues 
7839e7c127fSCraig Rodrigues 	if (ber_dump_element(ber, root) == -1)
7849e7c127fSCraig Rodrigues 		return -1;
7859e7c127fSCraig Rodrigues 
7869e7c127fSCraig Rodrigues 	/* XXX this should be moved to a different function */
7879e7c127fSCraig Rodrigues 	if (ber->fd != -1)
7889e7c127fSCraig Rodrigues 		return write(ber->fd, ber->br_wbuf, len);
7899e7c127fSCraig Rodrigues 
7909e7c127fSCraig Rodrigues 	return (len);
7919e7c127fSCraig Rodrigues }
7929e7c127fSCraig Rodrigues 
7939e7c127fSCraig Rodrigues /*
7949e7c127fSCraig Rodrigues  * read ber elements from the socket
7959e7c127fSCraig Rodrigues  *
7969e7c127fSCraig Rodrigues  * params:
7979e7c127fSCraig Rodrigues  *	ber	holds the socket and lot more
7989e7c127fSCraig Rodrigues  *	root	if NULL, build up an element tree from what we receive on
7999e7c127fSCraig Rodrigues  *		the wire. If not null, use the specified encoding for the
8009e7c127fSCraig Rodrigues  *		elements received.
8019e7c127fSCraig Rodrigues  *
8029e7c127fSCraig Rodrigues  * returns:
8039e7c127fSCraig Rodrigues  *	!=NULL, elements read and store in the ber_element tree
8049e7c127fSCraig Rodrigues  *	NULL, type mismatch or read error
8059e7c127fSCraig Rodrigues  */
8069e7c127fSCraig Rodrigues struct ber_element *
ber_read_elements(struct ber * ber,struct ber_element * elm)8079e7c127fSCraig Rodrigues ber_read_elements(struct ber *ber, struct ber_element *elm)
8089e7c127fSCraig Rodrigues {
8099e7c127fSCraig Rodrigues 	struct ber_element *root = elm;
8109e7c127fSCraig Rodrigues 
8119e7c127fSCraig Rodrigues 	if (root == NULL) {
8129e7c127fSCraig Rodrigues 		if ((root = ber_get_element(0)) == NULL)
8139e7c127fSCraig Rodrigues 			return NULL;
8149e7c127fSCraig Rodrigues 	}
8159e7c127fSCraig Rodrigues 
8169e7c127fSCraig Rodrigues 	DPRINTF("read ber elements, root %p\n", root);
8179e7c127fSCraig Rodrigues 
8189e7c127fSCraig Rodrigues 	if (ber_read_element(ber, root) == -1) {
8199e7c127fSCraig Rodrigues 		/* Cleanup if root was allocated by us */
8209e7c127fSCraig Rodrigues 		if (elm == NULL)
8219e7c127fSCraig Rodrigues 			ber_free_elements(root);
8229e7c127fSCraig Rodrigues 		return NULL;
8239e7c127fSCraig Rodrigues 	}
8249e7c127fSCraig Rodrigues 
8259e7c127fSCraig Rodrigues 	return root;
8269e7c127fSCraig Rodrigues }
8279e7c127fSCraig Rodrigues 
8289e7c127fSCraig Rodrigues void
ber_free_elements(struct ber_element * root)8299e7c127fSCraig Rodrigues ber_free_elements(struct ber_element *root)
8309e7c127fSCraig Rodrigues {
8319e7c127fSCraig Rodrigues 	if (root->be_sub && (root->be_encoding == BER_TYPE_SEQUENCE ||
8329e7c127fSCraig Rodrigues 	    root->be_encoding == BER_TYPE_SET))
8339e7c127fSCraig Rodrigues 		ber_free_elements(root->be_sub);
8349e7c127fSCraig Rodrigues 	if (root->be_next)
8359e7c127fSCraig Rodrigues 		ber_free_elements(root->be_next);
8369e7c127fSCraig Rodrigues 	if (root->be_free && (root->be_encoding == BER_TYPE_OCTETSTRING ||
8379e7c127fSCraig Rodrigues 	    root->be_encoding == BER_TYPE_BITSTRING ||
8389e7c127fSCraig Rodrigues 	    root->be_encoding == BER_TYPE_OBJECT))
8399e7c127fSCraig Rodrigues 		free(root->be_val);
8409e7c127fSCraig Rodrigues 	free(root);
8419e7c127fSCraig Rodrigues }
8429e7c127fSCraig Rodrigues 
8439e7c127fSCraig Rodrigues size_t
ber_calc_len(struct ber_element * root)8449e7c127fSCraig Rodrigues ber_calc_len(struct ber_element *root)
8459e7c127fSCraig Rodrigues {
8469e7c127fSCraig Rodrigues 	unsigned long t;
8479e7c127fSCraig Rodrigues 	size_t s;
8489e7c127fSCraig Rodrigues 	size_t size = 2;	/* minimum 1 byte head and 1 byte size */
8499e7c127fSCraig Rodrigues 
8509e7c127fSCraig Rodrigues 	/* calculate the real length of a sequence or set */
8519e7c127fSCraig Rodrigues 	if (root->be_sub && (root->be_encoding == BER_TYPE_SEQUENCE ||
8529e7c127fSCraig Rodrigues 	    root->be_encoding == BER_TYPE_SET))
8539e7c127fSCraig Rodrigues 		root->be_len = ber_calc_len(root->be_sub);
8549e7c127fSCraig Rodrigues 
8559e7c127fSCraig Rodrigues 	/* fix header length for extended types */
8569e7c127fSCraig Rodrigues 	if (root->be_type > BER_TYPE_SINGLE_MAX)
8579e7c127fSCraig Rodrigues 		for (t = root->be_type; t > 0; t >>= 7)
8589e7c127fSCraig Rodrigues 			size++;
8599e7c127fSCraig Rodrigues 	if (root->be_len >= BER_TAG_MORE)
8609e7c127fSCraig Rodrigues 		for (s = root->be_len; s > 0; s >>= 8)
8619e7c127fSCraig Rodrigues 			size++;
8629e7c127fSCraig Rodrigues 
8639e7c127fSCraig Rodrigues 	/* calculate the length of the following elements */
8649e7c127fSCraig Rodrigues 	if (root->be_next)
8659e7c127fSCraig Rodrigues 		size += ber_calc_len(root->be_next);
8669e7c127fSCraig Rodrigues 
8679e7c127fSCraig Rodrigues 	/* This is an empty element, do not use a minimal size */
8689e7c127fSCraig Rodrigues 	if (root->be_type == BER_TYPE_EOC && root->be_len == 0)
8699e7c127fSCraig Rodrigues 		return (0);
8709e7c127fSCraig Rodrigues 
8719e7c127fSCraig Rodrigues 	return (root->be_len + size);
8729e7c127fSCraig Rodrigues }
8739e7c127fSCraig Rodrigues 
8749e7c127fSCraig Rodrigues /*
8759e7c127fSCraig Rodrigues  * internal functions
8769e7c127fSCraig Rodrigues  */
8779e7c127fSCraig Rodrigues 
8789e7c127fSCraig Rodrigues static int
ber_dump_element(struct ber * ber,struct ber_element * root)8799e7c127fSCraig Rodrigues ber_dump_element(struct ber *ber, struct ber_element *root)
8809e7c127fSCraig Rodrigues {
8819e7c127fSCraig Rodrigues 	unsigned long long l;
8829e7c127fSCraig Rodrigues 	int i;
8839e7c127fSCraig Rodrigues 	uint8_t u;
8849e7c127fSCraig Rodrigues 
8859e7c127fSCraig Rodrigues 	ber_dump_header(ber, root);
8869e7c127fSCraig Rodrigues 
8879e7c127fSCraig Rodrigues 	switch (root->be_encoding) {
8889e7c127fSCraig Rodrigues 	case BER_TYPE_BOOLEAN:
8899e7c127fSCraig Rodrigues 	case BER_TYPE_INTEGER:
8909e7c127fSCraig Rodrigues 	case BER_TYPE_ENUMERATED:
8919e7c127fSCraig Rodrigues 		l = (unsigned long long)root->be_numeric;
8929e7c127fSCraig Rodrigues 		for (i = root->be_len; i > 0; i--) {
8939e7c127fSCraig Rodrigues 			u = (l >> ((i - 1) * 8)) & 0xff;
8949e7c127fSCraig Rodrigues 			ber_putc(ber, u);
8959e7c127fSCraig Rodrigues 		}
8969e7c127fSCraig Rodrigues 		break;
8979e7c127fSCraig Rodrigues 	case BER_TYPE_BITSTRING:
8989e7c127fSCraig Rodrigues 		return -1;
8999e7c127fSCraig Rodrigues 	case BER_TYPE_OCTETSTRING:
9009e7c127fSCraig Rodrigues 	case BER_TYPE_OBJECT:
9019e7c127fSCraig Rodrigues 		ber_write(ber, root->be_val, root->be_len);
9029e7c127fSCraig Rodrigues 		break;
9039e7c127fSCraig Rodrigues 	case BER_TYPE_NULL:	/* no payload */
9049e7c127fSCraig Rodrigues 	case BER_TYPE_EOC:
9059e7c127fSCraig Rodrigues 		break;
9069e7c127fSCraig Rodrigues 	case BER_TYPE_SEQUENCE:
9079e7c127fSCraig Rodrigues 	case BER_TYPE_SET:
9089e7c127fSCraig Rodrigues 		if (root->be_sub && ber_dump_element(ber, root->be_sub) == -1)
9099e7c127fSCraig Rodrigues 			return -1;
9109e7c127fSCraig Rodrigues 		break;
9119e7c127fSCraig Rodrigues 	}
9129e7c127fSCraig Rodrigues 
9139e7c127fSCraig Rodrigues 	if (root->be_next == NULL)
9149e7c127fSCraig Rodrigues 		return 0;
9159e7c127fSCraig Rodrigues 	return ber_dump_element(ber, root->be_next);
9169e7c127fSCraig Rodrigues }
9179e7c127fSCraig Rodrigues 
9189e7c127fSCraig Rodrigues static void
ber_dump_header(struct ber * ber,struct ber_element * root)9199e7c127fSCraig Rodrigues ber_dump_header(struct ber *ber, struct ber_element *root)
9209e7c127fSCraig Rodrigues {
9219e7c127fSCraig Rodrigues 	u_char	id = 0, t, buf[8];
9229e7c127fSCraig Rodrigues 	unsigned long type;
9239e7c127fSCraig Rodrigues 	size_t size;
9249e7c127fSCraig Rodrigues 
9259e7c127fSCraig Rodrigues 	/* class universal, type encoding depending on type value */
9269e7c127fSCraig Rodrigues 	/* length encoding */
9279e7c127fSCraig Rodrigues 	if (root->be_type <= BER_TYPE_SINGLE_MAX) {
9289e7c127fSCraig Rodrigues 		id = root->be_type | (root->be_class << BER_CLASS_SHIFT);
9299e7c127fSCraig Rodrigues 		if (root->be_encoding == BER_TYPE_SEQUENCE ||
9309e7c127fSCraig Rodrigues 		    root->be_encoding == BER_TYPE_SET)
9319e7c127fSCraig Rodrigues 			id |= BER_TYPE_CONSTRUCTED;
9329e7c127fSCraig Rodrigues 
9339e7c127fSCraig Rodrigues 		ber_putc(ber, id);
9349e7c127fSCraig Rodrigues 	} else {
9359e7c127fSCraig Rodrigues 		id = BER_TAG_MASK | (root->be_class << BER_CLASS_SHIFT);
9369e7c127fSCraig Rodrigues 		if (root->be_encoding == BER_TYPE_SEQUENCE ||
9379e7c127fSCraig Rodrigues 		    root->be_encoding == BER_TYPE_SET)
9389e7c127fSCraig Rodrigues 			id |= BER_TYPE_CONSTRUCTED;
9399e7c127fSCraig Rodrigues 
9409e7c127fSCraig Rodrigues 		ber_putc(ber, id);
9419e7c127fSCraig Rodrigues 
9429e7c127fSCraig Rodrigues 		for (t = 0, type = root->be_type; type > 0; type >>= 7)
9439e7c127fSCraig Rodrigues 			buf[t++] = type & ~BER_TAG_MORE;
9449e7c127fSCraig Rodrigues 
9459e7c127fSCraig Rodrigues 		while (t-- > 0) {
9469e7c127fSCraig Rodrigues 			if (t > 0)
9479e7c127fSCraig Rodrigues 				buf[t] |= BER_TAG_MORE;
9489e7c127fSCraig Rodrigues 			ber_putc(ber, buf[t]);
9499e7c127fSCraig Rodrigues 		}
9509e7c127fSCraig Rodrigues 	}
9519e7c127fSCraig Rodrigues 
9529e7c127fSCraig Rodrigues 	if (root->be_len < BER_TAG_MORE) {
9539e7c127fSCraig Rodrigues 		/* short form */
9549e7c127fSCraig Rodrigues 		ber_putc(ber, root->be_len);
9559e7c127fSCraig Rodrigues 	} else {
9569e7c127fSCraig Rodrigues 		for (t = 0, size = root->be_len; size > 0; size >>= 8)
9579e7c127fSCraig Rodrigues 			buf[t++] = size & 0xff;
9589e7c127fSCraig Rodrigues 
9599e7c127fSCraig Rodrigues 		ber_putc(ber, t | BER_TAG_MORE);
9609e7c127fSCraig Rodrigues 
9619e7c127fSCraig Rodrigues 		while (t > 0)
9629e7c127fSCraig Rodrigues 			ber_putc(ber, buf[--t]);
9639e7c127fSCraig Rodrigues 	}
9649e7c127fSCraig Rodrigues }
9659e7c127fSCraig Rodrigues 
9669e7c127fSCraig Rodrigues static void
ber_putc(struct ber * ber,u_char c)9679e7c127fSCraig Rodrigues ber_putc(struct ber *ber, u_char c)
9689e7c127fSCraig Rodrigues {
9699e7c127fSCraig Rodrigues 	if (ber->br_wptr + 1 <= ber->br_wend)
9709e7c127fSCraig Rodrigues 		*ber->br_wptr = c;
9719e7c127fSCraig Rodrigues 	ber->br_wptr++;
9729e7c127fSCraig Rodrigues }
9739e7c127fSCraig Rodrigues 
9749e7c127fSCraig Rodrigues static void
ber_write(struct ber * ber,void * buf,size_t len)9759e7c127fSCraig Rodrigues ber_write(struct ber *ber, void *buf, size_t len)
9769e7c127fSCraig Rodrigues {
9779e7c127fSCraig Rodrigues 	if (ber->br_wptr + len <= ber->br_wend)
9789e7c127fSCraig Rodrigues 		bcopy(buf, ber->br_wptr, len);
9799e7c127fSCraig Rodrigues 	ber->br_wptr += len;
9809e7c127fSCraig Rodrigues }
9819e7c127fSCraig Rodrigues 
9829e7c127fSCraig Rodrigues /*
9839e7c127fSCraig Rodrigues  * extract a BER encoded tag. There are two types, a short and long form.
9849e7c127fSCraig Rodrigues  */
9859e7c127fSCraig Rodrigues static ssize_t
get_id(struct ber * b,unsigned long * tag,int * class,int * cstruct)9869e7c127fSCraig Rodrigues get_id(struct ber *b, unsigned long *tag, int *class, int *cstruct)
9879e7c127fSCraig Rodrigues {
9889e7c127fSCraig Rodrigues 	u_char u;
9899e7c127fSCraig Rodrigues 	size_t i = 0;
9909e7c127fSCraig Rodrigues 	unsigned long t = 0;
9919e7c127fSCraig Rodrigues 
9929e7c127fSCraig Rodrigues 	if (ber_getc(b, &u) == -1)
9939e7c127fSCraig Rodrigues 		return -1;
9949e7c127fSCraig Rodrigues 
9959e7c127fSCraig Rodrigues 	*class = (u >> BER_CLASS_SHIFT) & BER_CLASS_MASK;
9969e7c127fSCraig Rodrigues 	*cstruct = (u & BER_TYPE_CONSTRUCTED) == BER_TYPE_CONSTRUCTED;
9979e7c127fSCraig Rodrigues 
9989e7c127fSCraig Rodrigues 	if ((u & BER_TAG_MASK) != BER_TAG_MASK) {
9999e7c127fSCraig Rodrigues 		*tag = u & BER_TAG_MASK;
10009e7c127fSCraig Rodrigues 		return 1;
10019e7c127fSCraig Rodrigues 	}
10029e7c127fSCraig Rodrigues 
10039e7c127fSCraig Rodrigues 	do {
10049e7c127fSCraig Rodrigues 		if (ber_getc(b, &u) == -1)
10059e7c127fSCraig Rodrigues 			return -1;
10069e7c127fSCraig Rodrigues 		t = (t << 7) | (u & ~BER_TAG_MORE);
10079e7c127fSCraig Rodrigues 		i++;
10089e7c127fSCraig Rodrigues 	} while (u & BER_TAG_MORE);
10099e7c127fSCraig Rodrigues 
10109e7c127fSCraig Rodrigues 	if (i > sizeof(unsigned long)) {
10119e7c127fSCraig Rodrigues 		errno = ERANGE;
10129e7c127fSCraig Rodrigues 		return -1;
10139e7c127fSCraig Rodrigues 	}
10149e7c127fSCraig Rodrigues 
10159e7c127fSCraig Rodrigues 	*tag = t;
10169e7c127fSCraig Rodrigues 	return i + 1;
10179e7c127fSCraig Rodrigues }
10189e7c127fSCraig Rodrigues 
10199e7c127fSCraig Rodrigues /*
10209e7c127fSCraig Rodrigues  * extract length of a ber object -- if length is unknown an error is returned.
10219e7c127fSCraig Rodrigues  */
10229e7c127fSCraig Rodrigues static ssize_t
get_len(struct ber * b,ssize_t * len)10239e7c127fSCraig Rodrigues get_len(struct ber *b, ssize_t *len)
10249e7c127fSCraig Rodrigues {
10259e7c127fSCraig Rodrigues 	u_char	u, n;
10269e7c127fSCraig Rodrigues 	ssize_t	s, r;
10279e7c127fSCraig Rodrigues 
10289e7c127fSCraig Rodrigues 	if (ber_getc(b, &u) == -1)
10299e7c127fSCraig Rodrigues 		return -1;
10309e7c127fSCraig Rodrigues 	if ((u & BER_TAG_MORE) == 0) {
10319e7c127fSCraig Rodrigues 		/* short form */
10329e7c127fSCraig Rodrigues 		*len = u;
10339e7c127fSCraig Rodrigues 		return 1;
10349e7c127fSCraig Rodrigues 	}
10359e7c127fSCraig Rodrigues 
10369e7c127fSCraig Rodrigues 	n = u & ~BER_TAG_MORE;
10379e7c127fSCraig Rodrigues 	if (sizeof(ssize_t) < n) {
10389e7c127fSCraig Rodrigues 		errno = ERANGE;
10399e7c127fSCraig Rodrigues 		return -1;
10409e7c127fSCraig Rodrigues 	}
10419e7c127fSCraig Rodrigues 	r = n + 1;
10429e7c127fSCraig Rodrigues 
10439e7c127fSCraig Rodrigues 	for (s = 0; n > 0; n--) {
10449e7c127fSCraig Rodrigues 		if (ber_getc(b, &u) == -1)
10459e7c127fSCraig Rodrigues 			return -1;
10469e7c127fSCraig Rodrigues 		s = (s << 8) | u;
10479e7c127fSCraig Rodrigues 	}
10489e7c127fSCraig Rodrigues 
10499e7c127fSCraig Rodrigues 	if (s < 0) {
10509e7c127fSCraig Rodrigues 		/* overflow */
10519e7c127fSCraig Rodrigues 		errno = ERANGE;
10529e7c127fSCraig Rodrigues 		return -1;
10539e7c127fSCraig Rodrigues 	}
10549e7c127fSCraig Rodrigues 
10559e7c127fSCraig Rodrigues 	if (s == 0) {
10569e7c127fSCraig Rodrigues 		/* invalid encoding */
10579e7c127fSCraig Rodrigues 		errno = EINVAL;
10589e7c127fSCraig Rodrigues 		return -1;
10599e7c127fSCraig Rodrigues 	}
10609e7c127fSCraig Rodrigues 
10619e7c127fSCraig Rodrigues 	*len = s;
10629e7c127fSCraig Rodrigues 	return r;
10639e7c127fSCraig Rodrigues }
10649e7c127fSCraig Rodrigues 
10659e7c127fSCraig Rodrigues static ssize_t
ber_read_element(struct ber * ber,struct ber_element * elm)10669e7c127fSCraig Rodrigues ber_read_element(struct ber *ber, struct ber_element *elm)
10679e7c127fSCraig Rodrigues {
10689e7c127fSCraig Rodrigues 	long long val = 0;
10699e7c127fSCraig Rodrigues 	struct ber_element *next;
10709e7c127fSCraig Rodrigues 	unsigned long type;
10719e7c127fSCraig Rodrigues 	int i, class, cstruct;
10729e7c127fSCraig Rodrigues 	ssize_t len, r, totlen = 0;
10739e7c127fSCraig Rodrigues 	u_char c;
10749e7c127fSCraig Rodrigues 
10759e7c127fSCraig Rodrigues 	if ((r = get_id(ber, &type, &class, &cstruct)) == -1)
10769e7c127fSCraig Rodrigues 		return -1;
10779e7c127fSCraig Rodrigues 	DPRINTF("ber read got class %d type %lu, %s\n",
10789e7c127fSCraig Rodrigues 	    class, type, cstruct ? "constructive" : "primitive");
10799e7c127fSCraig Rodrigues 	totlen += r;
10809e7c127fSCraig Rodrigues 	if ((r = get_len(ber, &len)) == -1)
10819e7c127fSCraig Rodrigues 		return -1;
10829e7c127fSCraig Rodrigues 	DPRINTF("ber read element size %zd\n", len);
10839e7c127fSCraig Rodrigues 	totlen += r + len;
10849e7c127fSCraig Rodrigues 
10850e356242SMarcelo Araujo 	/*
10860e356242SMarcelo Araujo 	 * If using an external buffer and the total size of the element
10870e356242SMarcelo Araujo 	 * is larger, then the external buffer don't bother to continue.
10880e356242SMarcelo Araujo 	 */
10890e356242SMarcelo Araujo 	if (ber->fd == -1 && len > ber->br_rend - ber->br_rptr) {
10900e356242SMarcelo Araujo 		errno = ECANCELED;
10910e356242SMarcelo Araujo 		return -1;
10920e356242SMarcelo Araujo 	}
10930e356242SMarcelo Araujo 
10949e7c127fSCraig Rodrigues 	elm->be_type = type;
10959e7c127fSCraig Rodrigues 	elm->be_len = len;
10969e7c127fSCraig Rodrigues 	elm->be_class = class;
10979e7c127fSCraig Rodrigues 
10989e7c127fSCraig Rodrigues 	if (elm->be_encoding == 0) {
10999e7c127fSCraig Rodrigues 		/* try to figure out the encoding via class, type and cstruct */
11009e7c127fSCraig Rodrigues 		if (cstruct)
11019e7c127fSCraig Rodrigues 			elm->be_encoding = BER_TYPE_SEQUENCE;
11029e7c127fSCraig Rodrigues 		else if (class == BER_CLASS_UNIVERSAL)
11039e7c127fSCraig Rodrigues 			elm->be_encoding = type;
11049e7c127fSCraig Rodrigues 		else if (ber->br_application != NULL) {
11059e7c127fSCraig Rodrigues 			/*
11069e7c127fSCraig Rodrigues 			 * Ask the application to map the encoding to a
11079e7c127fSCraig Rodrigues 			 * universal type. For example, a SMI IpAddress
11089e7c127fSCraig Rodrigues 			 * type is defined as 4 byte OCTET STRING.
11099e7c127fSCraig Rodrigues 			 */
11109e7c127fSCraig Rodrigues 			elm->be_encoding = (*ber->br_application)(elm);
11119e7c127fSCraig Rodrigues 		} else
11129e7c127fSCraig Rodrigues 			/* last resort option */
11139e7c127fSCraig Rodrigues 			elm->be_encoding = BER_TYPE_NULL;
11149e7c127fSCraig Rodrigues 	}
11159e7c127fSCraig Rodrigues 
11169e7c127fSCraig Rodrigues 	switch (elm->be_encoding) {
11179e7c127fSCraig Rodrigues 	case BER_TYPE_EOC:	/* End-Of-Content */
11189e7c127fSCraig Rodrigues 		break;
11199e7c127fSCraig Rodrigues 	case BER_TYPE_BOOLEAN:
11209e7c127fSCraig Rodrigues 	case BER_TYPE_INTEGER:
11219e7c127fSCraig Rodrigues 	case BER_TYPE_ENUMERATED:
11229e7c127fSCraig Rodrigues 		if (len > (ssize_t)sizeof(long long))
11239e7c127fSCraig Rodrigues 			return -1;
11249e7c127fSCraig Rodrigues 		for (i = 0; i < len; i++) {
11259e7c127fSCraig Rodrigues 			if (ber_getc(ber, &c) != 1)
11269e7c127fSCraig Rodrigues 				return -1;
11279e7c127fSCraig Rodrigues 			val <<= 8;
11289e7c127fSCraig Rodrigues 			val |= c;
11299e7c127fSCraig Rodrigues 		}
11309e7c127fSCraig Rodrigues 
11319e7c127fSCraig Rodrigues 		/* sign extend if MSB is set */
11329e7c127fSCraig Rodrigues 		if (val >> ((i - 1) * 8) & 0x80)
11339e7c127fSCraig Rodrigues 			val |= ULLONG_MAX << (i * 8);
11349e7c127fSCraig Rodrigues 		elm->be_numeric = val;
11359e7c127fSCraig Rodrigues 		break;
11369e7c127fSCraig Rodrigues 	case BER_TYPE_BITSTRING:
11379e7c127fSCraig Rodrigues 		elm->be_val = malloc(len);
11389e7c127fSCraig Rodrigues 		if (elm->be_val == NULL)
11399e7c127fSCraig Rodrigues 			return -1;
11409e7c127fSCraig Rodrigues 		elm->be_free = 1;
11419e7c127fSCraig Rodrigues 		elm->be_len = len;
11429e7c127fSCraig Rodrigues 		ber_read(ber, elm->be_val, len);
11439e7c127fSCraig Rodrigues 		break;
11449e7c127fSCraig Rodrigues 	case BER_TYPE_OCTETSTRING:
11459e7c127fSCraig Rodrigues 	case BER_TYPE_OBJECT:
11469e7c127fSCraig Rodrigues 		elm->be_val = malloc(len + 1);
11479e7c127fSCraig Rodrigues 		if (elm->be_val == NULL)
11489e7c127fSCraig Rodrigues 			return -1;
11499e7c127fSCraig Rodrigues 		elm->be_free = 1;
11509e7c127fSCraig Rodrigues 		elm->be_len = len;
11519e7c127fSCraig Rodrigues 		ber_read(ber, elm->be_val, len);
11529e7c127fSCraig Rodrigues 		((u_char *)elm->be_val)[len] = '\0';
11539e7c127fSCraig Rodrigues 		break;
11549e7c127fSCraig Rodrigues 	case BER_TYPE_NULL:	/* no payload */
11559e7c127fSCraig Rodrigues 		if (len != 0)
11569e7c127fSCraig Rodrigues 			return -1;
11579e7c127fSCraig Rodrigues 		break;
11589e7c127fSCraig Rodrigues 	case BER_TYPE_SEQUENCE:
11599e7c127fSCraig Rodrigues 	case BER_TYPE_SET:
11609e7c127fSCraig Rodrigues 		if (elm->be_sub == NULL) {
11619e7c127fSCraig Rodrigues 			if ((elm->be_sub = ber_get_element(0)) == NULL)
11629e7c127fSCraig Rodrigues 				return -1;
11639e7c127fSCraig Rodrigues 		}
11649e7c127fSCraig Rodrigues 		next = elm->be_sub;
11659e7c127fSCraig Rodrigues 		while (len > 0) {
11669e7c127fSCraig Rodrigues 			r = ber_read_element(ber, next);
11679e7c127fSCraig Rodrigues 			if (r == -1)
11689e7c127fSCraig Rodrigues 				return -1;
11699e7c127fSCraig Rodrigues 			len -= r;
11709e7c127fSCraig Rodrigues 			if (len > 0 && next->be_next == NULL) {
11719e7c127fSCraig Rodrigues 				if ((next->be_next = ber_get_element(0)) ==
11729e7c127fSCraig Rodrigues 				    NULL)
11739e7c127fSCraig Rodrigues 					return -1;
11749e7c127fSCraig Rodrigues 			}
11759e7c127fSCraig Rodrigues 			next = next->be_next;
11769e7c127fSCraig Rodrigues 		}
11779e7c127fSCraig Rodrigues 		break;
11789e7c127fSCraig Rodrigues 	}
11799e7c127fSCraig Rodrigues 	return totlen;
11809e7c127fSCraig Rodrigues }
11819e7c127fSCraig Rodrigues 
11829e7c127fSCraig Rodrigues static ssize_t
ber_readbuf(struct ber * b,void * buf,size_t nbytes)11839e7c127fSCraig Rodrigues ber_readbuf(struct ber *b, void *buf, size_t nbytes)
11849e7c127fSCraig Rodrigues {
11859e7c127fSCraig Rodrigues 	size_t	 sz;
11869e7c127fSCraig Rodrigues 	size_t	 len;
11879e7c127fSCraig Rodrigues 
11889e7c127fSCraig Rodrigues 	if (b->br_rbuf == NULL)
11899e7c127fSCraig Rodrigues 		return -1;
11909e7c127fSCraig Rodrigues 
11919e7c127fSCraig Rodrigues 	sz = b->br_rend - b->br_rptr;
11929e7c127fSCraig Rodrigues 	len = MINIMUM(nbytes, sz);
11939e7c127fSCraig Rodrigues 	if (len == 0) {
11949e7c127fSCraig Rodrigues 		errno = ECANCELED;
11959e7c127fSCraig Rodrigues 		return (-1);	/* end of buffer and parser wants more data */
11969e7c127fSCraig Rodrigues 	}
11979e7c127fSCraig Rodrigues 
11989e7c127fSCraig Rodrigues 	bcopy(b->br_rptr, buf, len);
11999e7c127fSCraig Rodrigues 	b->br_rptr += len;
12009e7c127fSCraig Rodrigues 
12019e7c127fSCraig Rodrigues 	return (len);
12029e7c127fSCraig Rodrigues }
12039e7c127fSCraig Rodrigues 
12049e7c127fSCraig Rodrigues void
ber_set_readbuf(struct ber * b,void * buf,size_t len)12059e7c127fSCraig Rodrigues ber_set_readbuf(struct ber *b, void *buf, size_t len)
12069e7c127fSCraig Rodrigues {
12079e7c127fSCraig Rodrigues 	b->br_rbuf = b->br_rptr = buf;
12089e7c127fSCraig Rodrigues 	b->br_rend = (u_int8_t *)buf + len;
12099e7c127fSCraig Rodrigues }
12109e7c127fSCraig Rodrigues 
12119e7c127fSCraig Rodrigues ssize_t
ber_get_writebuf(struct ber * b,void ** buf)12129e7c127fSCraig Rodrigues ber_get_writebuf(struct ber *b, void **buf)
12139e7c127fSCraig Rodrigues {
12149e7c127fSCraig Rodrigues 	if (b->br_wbuf == NULL)
12159e7c127fSCraig Rodrigues 		return -1;
12169e7c127fSCraig Rodrigues 	*buf = b->br_wbuf;
12179e7c127fSCraig Rodrigues 	return (b->br_wend - b->br_wbuf);
12189e7c127fSCraig Rodrigues }
12199e7c127fSCraig Rodrigues 
12209e7c127fSCraig Rodrigues void
ber_set_application(struct ber * b,unsigned long (* cb)(struct ber_element *))12219e7c127fSCraig Rodrigues ber_set_application(struct ber *b, unsigned long (*cb)(struct ber_element *))
12229e7c127fSCraig Rodrigues {
12239e7c127fSCraig Rodrigues 	b->br_application = cb;
12249e7c127fSCraig Rodrigues }
12259e7c127fSCraig Rodrigues 
12269e7c127fSCraig Rodrigues void
ber_free(struct ber * b)12279e7c127fSCraig Rodrigues ber_free(struct ber *b)
12289e7c127fSCraig Rodrigues {
12299e7c127fSCraig Rodrigues 	free(b->br_wbuf);
12309e7c127fSCraig Rodrigues }
12319e7c127fSCraig Rodrigues 
12329e7c127fSCraig Rodrigues static ssize_t
ber_getc(struct ber * b,u_char * c)12339e7c127fSCraig Rodrigues ber_getc(struct ber *b, u_char *c)
12349e7c127fSCraig Rodrigues {
12359e7c127fSCraig Rodrigues 	ssize_t r;
12369e7c127fSCraig Rodrigues 	/*
12379e7c127fSCraig Rodrigues 	 * XXX calling read here is wrong in many ways. The most obvious one
12389e7c127fSCraig Rodrigues 	 * being that we will block till data arrives.
12399e7c127fSCraig Rodrigues 	 * But for now it is _good enough_ *gulp*
12409e7c127fSCraig Rodrigues 	 */
12419e7c127fSCraig Rodrigues 	if (b->fd == -1)
12429e7c127fSCraig Rodrigues 		r = ber_readbuf(b, c, 1);
12439e7c127fSCraig Rodrigues 	else
12449e7c127fSCraig Rodrigues 		r = read(b->fd, c, 1);
12459e7c127fSCraig Rodrigues 	return r;
12469e7c127fSCraig Rodrigues }
12479e7c127fSCraig Rodrigues 
12489e7c127fSCraig Rodrigues static ssize_t
ber_read(struct ber * ber,void * buf,size_t len)12499e7c127fSCraig Rodrigues ber_read(struct ber *ber, void *buf, size_t len)
12509e7c127fSCraig Rodrigues {
12519e7c127fSCraig Rodrigues 	u_char *b = buf;
12529e7c127fSCraig Rodrigues 	ssize_t	r, remain = len;
12539e7c127fSCraig Rodrigues 
12549e7c127fSCraig Rodrigues 	/*
12559e7c127fSCraig Rodrigues 	 * XXX calling read here is wrong in many ways. The most obvious one
12569e7c127fSCraig Rodrigues 	 * being that we will block till data arrives.
12579e7c127fSCraig Rodrigues 	 * But for now it is _good enough_ *gulp*
12589e7c127fSCraig Rodrigues 	 */
12599e7c127fSCraig Rodrigues 
12609e7c127fSCraig Rodrigues 	while (remain > 0) {
12619e7c127fSCraig Rodrigues 		if (ber->fd == -1)
12629e7c127fSCraig Rodrigues 			r = ber_readbuf(ber, b, remain);
12639e7c127fSCraig Rodrigues 		else
12649e7c127fSCraig Rodrigues 			r = read(ber->fd, b, remain);
12659e7c127fSCraig Rodrigues 		if (r == -1) {
12669e7c127fSCraig Rodrigues 			if (errno == EINTR || errno == EAGAIN)
12679e7c127fSCraig Rodrigues 				continue;
12689e7c127fSCraig Rodrigues 			return -1;
12699e7c127fSCraig Rodrigues 		}
12709e7c127fSCraig Rodrigues 		if (r == 0)
12719e7c127fSCraig Rodrigues 			return (b - (u_char *)buf);
12729e7c127fSCraig Rodrigues 		b += r;
12739e7c127fSCraig Rodrigues 		remain -= r;
12749e7c127fSCraig Rodrigues 	}
12759e7c127fSCraig Rodrigues 	return (b - (u_char *)buf);
12769e7c127fSCraig Rodrigues }
1277