1/*
2 * -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 *
4 * The contents of this file are subject to the Netscape Public License
5 * Version 1.0 (the "NPL"); you may not use this file except in
6 * compliance with the NPL.  You may obtain a copy of the NPL at
7 * http://www.mozilla.org/NPL/
8 *
9 * Software distributed under the NPL is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
11 * for the specific language governing rights and limitations under the
12 * NPL.
13 *
14 * The Initial Developer of this code under the NPL is Netscape
15 * Communications Corporation.  Portions created by Netscape are
16 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
17 * Reserved.
18 */
19
20/*
21 * Copyright (c) 1990 Regents of the University of Michigan.
22 * All rights reserved.
23 *
24 * Redistribution and use in source and binary forms are permitted
25 * provided that this notice is preserved and that due credit is given
26 * to the University of Michigan at Ann Arbor. The name of the University
27 * may not be used to endorse or promote products derived from this
28 * software without specific prior written permission. This software
29 * is provided ``as is'' without express or implied warranty.
30 */
31/*
32 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
33 * Use is subject to license terms.
34 */
35
36#include <stdlib.h>
37#include <ber_der.h>
38#include "kmfber_int.h"
39
40#define	EXBUFSIZ	1024
41
42/*
43 * Note: kmfber_read() only uses the ber_end and ber_ptr elements of ber.
44 * Functions like kmfber_get_tag(), kmfber_skip_tag, and kmfber_peek_tag()
45 * rely on that fact, so if this code is changed to use any additional
46 * elements of the ber structure, those functions will need to be changed
47 * as well.
48 */
49ber_int_t
50kmfber_read(BerElement *ber, char *buf, ber_len_t len)
51{
52	size_t	actuallen;
53	size_t	nleft;
54
55	nleft = ber->ber_end - ber->ber_ptr;
56	actuallen = nleft < len ? nleft : len;
57
58	(void) memmove(buf, ber->ber_ptr, (size_t)actuallen);
59
60	ber->ber_ptr += actuallen;
61
62	return ((ber_int_t)actuallen);
63}
64
65/*
66 * enlarge the ber buffer.
67 * return 0 on success, -1 on error.
68 */
69int
70kmfber_realloc(BerElement *ber, ber_len_t len)
71{
72	ber_uint_t	need, have, total;
73	size_t		have_bytes;
74	Seqorset	*s;
75	size_t		off;
76	char		*oldbuf;
77
78	have_bytes = ber->ber_end - ber->ber_buf;
79	have = have_bytes / EXBUFSIZ;
80	need = (len < EXBUFSIZ ? 1 : (len + (EXBUFSIZ - 1)) / EXBUFSIZ);
81	total = have * EXBUFSIZ + need * EXBUFSIZ;
82
83	oldbuf = ber->ber_buf;
84
85	if (ber->ber_buf == NULL) {
86		if ((ber->ber_buf = (char *)malloc((size_t)total))
87		    == NULL) {
88			return (-1);
89		}
90		ber->ber_flags &= ~KMFBER_FLAG_NO_FREE_BUFFER;
91	} else {
92		if (ber->ber_flags & KMFBER_FLAG_NO_FREE_BUFFER) {
93			/* transition to malloc'd buffer */
94			if ((ber->ber_buf = (char *)malloc(
95			    (size_t)total)) == NULL) {
96				return (-1);
97			}
98			ber->ber_flags &= ~KMFBER_FLAG_NO_FREE_BUFFER;
99
100			/* copy existing data into new malloc'd buffer */
101			(void) memmove(ber->ber_buf, oldbuf, have_bytes);
102		} else {
103			if ((ber->ber_buf = (char *)realloc(
104			    oldbuf, (size_t)total)) == NULL) {
105				free(oldbuf);
106				return (-1);
107			}
108		}
109	}
110
111	ber->ber_end = ber->ber_buf + total;
112
113	/*
114	 * If the stinking thing was moved, we need to go through and
115	 * reset all the sos and ber pointers.  Offsets would've been
116	 * a better idea... oh well.
117	 */
118	if (ber->ber_buf != oldbuf) {
119		ber->ber_ptr = ber->ber_buf + (ber->ber_ptr - oldbuf);
120
121		for (s = ber->ber_sos; s != NULLSEQORSET; s = s->sos_next) {
122			off = s->sos_first - oldbuf;
123			s->sos_first = ber->ber_buf + off;
124
125			off = s->sos_ptr - oldbuf;
126			s->sos_ptr = ber->ber_buf + off;
127		}
128	}
129
130	return (0);
131}
132
133/*
134 * returns "len" on success and -1 on failure.
135 */
136ber_int_t
137kmfber_write(BerElement *ber, char *buf, ber_len_t len, int nosos)
138{
139	if (nosos || ber->ber_sos == NULL) {
140		if (ber->ber_ptr + len > ber->ber_end) {
141			if (kmfber_realloc(ber, len) != 0)
142				return (-1);
143		}
144		(void) memmove(ber->ber_ptr, buf, (size_t)len);
145		ber->ber_ptr += len;
146		return (len);
147	} else {
148		if (ber->ber_sos->sos_ptr + len > ber->ber_end) {
149			if (kmfber_realloc(ber, len) != 0)
150				return (-1);
151		}
152		(void) memmove(ber->ber_sos->sos_ptr, buf, (size_t)len);
153		ber->ber_sos->sos_ptr += len;
154		ber->ber_sos->sos_clen += len;
155		return (len);
156	}
157}
158
159void
160kmfber_free(BerElement *ber, int freebuf)
161{
162	if (ber != NULL) {
163		if (freebuf &&
164		    !(ber->ber_flags & KMFBER_FLAG_NO_FREE_BUFFER))
165			free(ber->ber_buf);
166		free((char *)ber);
167	}
168}
169
170/* we pre-allocate a buffer to save the extra malloc later */
171BerElement *
172kmfber_alloc_t(int options)
173{
174	BerElement	*ber;
175
176	if ((ber = (BerElement*)calloc(1,
177	    sizeof (struct berelement) + EXBUFSIZ)) == NULL) {
178		return (NULL);
179	}
180
181	ber->ber_tag = KMFBER_DEFAULT;
182	ber->ber_options = options;
183	ber->ber_buf = (char *)ber + sizeof (struct berelement);
184	ber->ber_ptr = ber->ber_buf;
185	ber->ber_end = ber->ber_buf + EXBUFSIZ;
186	ber->ber_flags = KMFBER_FLAG_NO_FREE_BUFFER;
187
188	return (ber);
189}
190
191
192BerElement *
193kmfber_alloc()
194{
195	return (kmfber_alloc_t(0));
196}
197
198BerElement *
199kmfder_alloc()
200{
201	return (kmfber_alloc_t(KMFBER_OPT_USE_DER));
202}
203
204BerElement *
205kmfber_dup(BerElement *ber)
206{
207	BerElement	*new;
208
209	if ((new = kmfber_alloc()) == NULL)
210		return (NULL);
211
212	*new = *ber;
213
214	return (new);
215}
216
217
218void
219ber_init_w_nullchar(BerElement *ber, int options)
220{
221	(void) memset((char *)ber, '\0', sizeof (struct berelement));
222	ber->ber_tag = KMFBER_DEFAULT;
223
224	ber->ber_options = options;
225}
226
227
228void
229kmfber_reset(BerElement *ber, int was_writing)
230{
231	if (was_writing) {
232		ber->ber_end = ber->ber_ptr;
233		ber->ber_ptr = ber->ber_buf;
234	} else {
235		ber->ber_ptr = ber->ber_end;
236	}
237
238	ber->ber_rwptr = NULL;
239}
240
241
242#ifdef KMFBER_DEBUG
243
244void
245ber_dump(BerElement *ber, int inout)
246{
247	char msg[128];
248	sprintf(msg, "ber_dump: buf 0x%lx, ptr 0x%lx, rwptr 0x%lx, end 0x%lx\n",
249	    ber->ber_buf, ber->ber_ptr, ber->ber_rwptr, ber->ber_end);
250	ber_err_print(msg);
251	if (inout == 1) {
252		sprintf(msg, "          current len %ld, contents:\n",
253		    ber->ber_end - ber->ber_ptr);
254		ber_err_print(msg);
255		lber_bprint(ber->ber_ptr, ber->ber_end - ber->ber_ptr);
256	} else {
257		sprintf(msg, "          current len %ld, contents:\n",
258		    ber->ber_ptr - ber->ber_buf);
259		ber_err_print(msg);
260		lber_bprint(ber->ber_buf, ber->ber_ptr - ber->ber_buf);
261	}
262}
263
264void
265ber_sos_dump(Seqorset *sos)
266{
267	char msg[80];
268	ber_err_print("*** sos dump ***\n");
269	while (sos != NULLSEQORSET) {
270		sprintf(msg, "ber_sos_dump: clen %ld first 0x%lx ptr 0x%lx\n",
271		    sos->sos_clen, sos->sos_first, sos->sos_ptr);
272		ber_err_print(msg);
273		sprintf(msg, "              current len %ld contents:\n",
274		    sos->sos_ptr - sos->sos_first);
275		ber_err_print(msg);
276		lber_bprint(sos->sos_first, sos->sos_ptr - sos->sos_first);
277
278		sos = sos->sos_next;
279	}
280	ber_err_print("*** end dump ***\n");
281}
282
283#endif
284
285/* new dboreham code below: */
286struct byte_buffer  {
287	unsigned char *p;
288	int offset;
289	int length;
290};
291typedef struct byte_buffer byte_buffer;
292
293/*
294 * The kmfber_flatten routine allocates a struct berval whose contents
295 * are a BER encoding taken from the ber argument. The bvPtr pointer
296 * points to the returned berval, which must be freed using
297 * kmfber_bvfree().  This routine returns 0 on success and -1 on error.
298 * The use of kmfber_flatten on a BerElement in which all '{' and '}'
299 * format modifiers have not been properly matched can result in a
300 * berval whose contents are not a valid BER encoding.
301 * Note that the ber_ptr is not modified.
302 */
303int
304kmfber_flatten(BerElement *ber, struct berval **bvPtr)
305{
306	struct berval *new;
307	ber_len_t len;
308
309	/* allocate a struct berval */
310	new = (struct berval *)malloc((size_t)(sizeof (struct berval)));
311	if (new == NULL) {
312		return (-1);
313	}
314	(void) memset(new, 0, sizeof (struct berval));
315
316	/*
317	 * Copy everything from the BerElement's ber_buf to ber_ptr
318	 * into the berval structure.
319	 */
320	if (ber == NULL) {
321		new->bv_val = NULL;
322		new->bv_len = 0;
323	} else {
324		len = ber->ber_ptr - ber->ber_buf;
325		new->bv_val = (char *)malloc((size_t)(len + 1));
326		if (new->bv_val == NULL) {
327			kmfber_bvfree(new);
328			return (-1);
329		}
330		(void) memmove(new->bv_val, ber->ber_buf, (size_t)len);
331		new->bv_val[len] = '\0';
332		new->bv_len = len;
333	}
334
335	/* set bvPtr pointer to point to the returned berval */
336	*bvPtr = new;
337
338	return (0);
339}
340
341BerElement *
342kmfder_init(const struct berval *bv)
343{
344	BerElement *ber;
345
346	/* construct BerElement */
347	if ((ber = kmfber_alloc_t(KMFBER_OPT_USE_DER)) != NULL) {
348		/* copy data from the bv argument into BerElement */
349		/* XXXmcs: had to cast unsigned long bv_len to long */
350		if ((kmfber_write(ber, bv->bv_val, bv->bv_len, 0)) !=
351		    (ber_slen_t)bv->bv_len) {
352			kmfber_free(ber, 1);
353			return (NULL);
354		}
355	}
356	/*
357	 * reset ber_ptr back to the beginning of buffer so that this new
358	 * and initialized ber element can be READ
359	 */
360	kmfber_reset(ber, 1);
361
362	/*
363	 * return a ptr to a new BerElement containing a copy of the data
364	 * in the bv argument or a null pointer on error
365	 */
366	return (ber);
367}
368
369BerElement *
370kmfber_init(const struct berval *bv)
371{
372	BerElement *ber;
373
374	/* construct BerElement */
375	if ((ber = kmfber_alloc_t(0)) != NULL) {
376		/* copy data from the bv argument into BerElement */
377		/* XXXmcs: had to cast unsigned long bv_len to long */
378		if ((kmfber_write(ber, bv->bv_val, bv->bv_len, 0)) !=
379		    (ber_slen_t)bv->bv_len) {
380			kmfber_free(ber, 1);
381			return (NULL);
382		}
383	}
384	/*
385	 * reset ber_ptr back to the beginning of buffer so that this new
386	 * and initialized ber element can be READ
387	 */
388	kmfber_reset(ber, 1);
389
390	/*
391	 * return a ptr to a new BerElement containing a copy of the data
392	 * in the bv argument or a null pointer on error
393	 */
394	return (ber);
395}
396