encode.c revision 3433:2971a4d3cf72
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/*
33 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
34 * Use is subject to license terms.
35 */
36
37#pragma ident	"%Z%%M%	%I%	%E% SMI"
38
39#include <sys/types.h>
40#include <netinet/in.h>
41#include <inttypes.h>
42
43#include <ber_der.h>
44#include "kmfber_int.h"
45
46/* the following constants are used in kmfber_calc_lenlen */
47
48#define	LENMASK1	0xFF
49#define	LENMASK2 	0xFFFF
50#define	LENMASK3	0xFFFFFF
51#define	LENMASK4	0xFFFFFFFF
52#define	_MASK		0x80
53
54int
55kmfber_calc_taglen(ber_tag_t tag)
56{
57	int		i;
58	ber_int_t	mask;
59
60	/* find the first non-all-zero byte in the tag */
61	for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
62		mask = (LENMASK3 << (i * 8));
63		/* not all zero */
64		if (tag & mask)
65			break;
66	}
67
68	return (i + 1);
69}
70
71static int
72ber_put_tag(BerElement	*ber, ber_tag_t tag, int nosos)
73{
74	ber_int_t	taglen;
75	ber_tag_t	ntag;
76
77	taglen = kmfber_calc_taglen(tag);
78
79	ntag = htonl(tag);
80
81	return (kmfber_write(ber,
82		((char *) &ntag) + sizeof (ber_int_t) - taglen,
83		taglen, nosos));
84}
85
86int
87kmfber_calc_lenlen(ber_int_t len)
88{
89	/*
90	 * short len if it's less than 128 - one byte giving the len,
91	 * with bit 8 0.
92	 */
93
94	if (len <= 0x7F)
95		return (1);
96
97	/*
98	 * long len otherwise - one byte with bit 8 set, giving the
99	 * length of the length, followed by the length itself.
100	 */
101
102	if (len <= LENMASK1)
103		return (2);
104	if (len <= LENMASK2)
105		return (3);
106	if (len <= LENMASK3)
107		return (4);
108
109	return (5);
110}
111
112int
113kmfber_put_len(BerElement *ber, ber_int_t len, int nosos)
114{
115	int		i;
116	char		lenlen;
117	ber_int_t	mask, netlen;
118
119	/*
120	 * short len if it's less than 128 - one byte giving the len,
121	 * with bit 8 0.
122	 */
123	if (len <= 127) {
124		netlen = htonl(len);
125		return (kmfber_write(ber,
126			(char *)&netlen + sizeof (ber_int_t) - 1,
127			1, nosos));
128	}
129
130	/*
131	 * long len otherwise - one byte with bit 8 set, giving the
132	 * length of the length, followed by the length itself.
133	 */
134
135	/* find the first non-all-zero byte */
136	for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
137		mask = (LENMASK1 << (i * 8));
138		/* not all zero */
139		if (len & mask)
140			break;
141	}
142	lenlen = ++i;
143	if (lenlen > 4)
144		return (-1);
145	lenlen |= 0x80;
146
147	/* write the length of the length */
148	if (kmfber_write(ber, &lenlen, 1, nosos) != 1)
149		return (-1);
150
151	/* write the length itself */
152	netlen = htonl(len);
153	if (kmfber_write(ber,
154		(char *) &netlen + (sizeof (ber_int_t) - i), i, nosos) != i)
155		return (-1);
156
157	return (i + 1);
158}
159
160static int
161ber_put_int_or_enum(BerElement *ber, ber_int_t num, ber_tag_t tag)
162{
163	int		i, sign;
164	ber_int_t	len, lenlen, taglen, netnum, mask;
165
166	sign = (num < 0);
167
168	/*
169	 * high bit is set - look for first non-all-one byte
170	 * high bit is clear - look for first non-all-zero byte
171	 */
172	for (i = sizeof (ber_int_t) - 1; i > 0; i--) {
173		mask = (LENMASK1 << (i * 8));
174
175		if (sign) {
176			/* not all ones */
177			if ((num & mask) != mask)
178				break;
179		} else {
180			/* not all zero */
181			if (num & mask)
182				break;
183		}
184	}
185
186	/*
187	 * we now have the "leading byte".  if the high bit on this
188	 * byte matches the sign bit, we need to "back up" a byte.
189	 */
190	mask = (num & (_MASK << (i * 8)));
191	if ((mask && !sign) || (sign && !mask))
192		i++;
193
194	len = i + 1;
195
196	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
197		return (-1);
198
199	if ((lenlen = kmfber_put_len(ber, len, 0)) == -1)
200		return (-1);
201	i++;
202	netnum = htonl(num);
203	if (kmfber_write(ber,
204		(char *) &netnum + (sizeof (ber_int_t) - i), i, 0) == i)
205		/* length of tag + length + contents */
206		return (taglen + lenlen + i);
207
208	return (-1);
209}
210
211static int
212kmfber_put_enum(BerElement *ber, ber_int_t num, ber_tag_t tag)
213{
214	if (tag == KMFBER_DEFAULT)
215		tag = BER_ENUMERATED;
216
217	return (ber_put_int_or_enum(ber, num, tag));
218}
219
220int
221ber_put_int(BerElement *ber, ber_int_t num, ber_tag_t tag)
222{
223	if (tag == KMFBER_DEFAULT)
224		tag = BER_INTEGER;
225
226	return (ber_put_int_or_enum(ber, num, tag));
227}
228
229int
230ber_put_oid(BerElement *ber, struct berval *oid, ber_tag_t tag)
231{
232	ber_int_t taglen, lenlen, rc, len;
233
234	if (tag == KMFBER_DEFAULT)
235		tag = 0x06; 	/* TODO: Add new OID constant to header */
236
237	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
238		return (-1);
239
240	len = (ber_int_t)oid->bv_len;
241	if ((lenlen = kmfber_put_len(ber, len, 0)) == -1 ||
242		kmfber_write(ber, oid->bv_val, oid->bv_len, 0) !=
243		(ber_int_t)oid->bv_len) {
244		rc = -1;
245	} else {
246		/* return length of tag + length + contents */
247		rc = taglen + lenlen + oid->bv_len;
248	}
249	return (rc);
250}
251
252int
253ber_put_big_int(BerElement *ber, ber_tag_t tag, char *data,
254	ber_len_t len)
255{
256	ber_int_t taglen, lenlen, ilen, rc;
257
258	if (tag == KMFBER_DEFAULT)
259		tag = BER_INTEGER;
260
261	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
262		return (-1);
263
264	ilen = (ber_int_t)len;
265	if ((lenlen = kmfber_put_len(ber, ilen, 0)) == -1 ||
266		kmfber_write(ber, data, len, 0) != (ber_int_t)len) {
267		rc = -1;
268	} else {
269		/* return length of tag + length + contents */
270		rc = taglen + lenlen + len;
271	}
272	return (rc);
273}
274
275static int
276kmfber_put_ostring(BerElement *ber, char *str, ber_len_t len,
277	ber_tag_t tag)
278{
279	ber_int_t	taglen, lenlen, ilen, rc;
280#ifdef STR_TRANSLATION
281	int	free_str;
282#endif /* STR_TRANSLATION */
283
284	if (tag == KMFBER_DEFAULT)
285		tag = BER_OCTET_STRING;
286
287	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
288		return (-1);
289
290#ifdef STR_TRANSLATION
291	if (len > 0 && (ber->ber_options & KMFBER_OPT_TRANSLATE_STRINGS) != 0 &&
292	    ber->ber_encode_translate_proc != NULL) {
293		if ((*(ber->ber_encode_translate_proc))(&str, &len, 0)
294		    != 0) {
295			return (-1);
296		}
297		free_str = 1;
298	} else {
299		free_str = 0;
300	}
301#endif /* STR_TRANSLATION */
302
303	/*
304	 *  Note:  below is a spot where we limit ber_write
305	 *	to signed long (instead of unsigned long)
306	 */
307	ilen = (ber_int_t)len;
308	if ((lenlen = kmfber_put_len(ber, ilen, 0)) == -1 ||
309		kmfber_write(ber, str, len, 0) != (ber_int_t)len) {
310		rc = -1;
311	} else {
312		/* return length of tag + length + contents */
313		rc = taglen + lenlen + len;
314	}
315
316#ifdef STR_TRANSLATION
317	if (free_str) {
318		free(str);
319	}
320#endif /* STR_TRANSLATION */
321
322	return (rc);
323}
324
325static int
326kmfber_put_string(BerElement *ber, char *str, ber_tag_t tag)
327{
328	return (kmfber_put_ostring(ber, str, (ber_len_t)strlen(str), tag));
329}
330
331static int
332kmfber_put_bitstring(BerElement *ber, char *str,
333	ber_len_t blen /* in bits */, ber_tag_t tag)
334{
335	ber_int_t	taglen, lenlen, len;
336	unsigned char	unusedbits;
337
338	if (tag == KMFBER_DEFAULT)
339		tag = BER_BIT_STRING;
340
341	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
342		return (-1);
343
344	len = (blen + 7) / 8;
345	unusedbits = (unsigned char) (len * 8 - blen);
346	if ((lenlen = kmfber_put_len(ber, len + 1, 0)) == -1)
347		return (-1);
348
349	if (kmfber_write(ber, (char *)&unusedbits, 1, 0) != 1)
350		return (-1);
351
352	if (kmfber_write(ber, str, len, 0) != len)
353		return (-1);
354
355	/* return length of tag + length + unused bit count + contents */
356	return (taglen + 1 + lenlen + len);
357}
358
359static int
360kmfber_put_null(BerElement *ber, ber_tag_t tag)
361{
362	int	taglen;
363
364	if (tag == KMFBER_DEFAULT)
365		tag = BER_NULL;
366
367	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
368		return (-1);
369
370	if (kmfber_put_len(ber, 0, 0) != 1)
371		return (-1);
372
373	return (taglen + 1);
374}
375
376static int
377kmfber_put_boolean(BerElement *ber, int boolval, ber_tag_t tag)
378{
379	int		taglen;
380	unsigned char	trueval = 0xff;
381	unsigned char	falseval = 0x00;
382
383	if (tag == KMFBER_DEFAULT)
384		tag = BER_BOOLEAN;
385
386	if ((taglen = ber_put_tag(ber, tag, 0)) == -1)
387		return (-1);
388
389	if (kmfber_put_len(ber, 1, 0) != 1)
390		return (-1);
391
392	if (kmfber_write(ber, (char *)(boolval ? &trueval : &falseval), 1, 0)
393	    != 1)
394		return (-1);
395
396	return (taglen + 2);
397}
398
399#define	FOUR_BYTE_LEN	5
400
401
402/*
403 * The idea here is roughly this: we maintain a stack of these Seqorset
404 * structures. This is pushed when we see the beginning of a new set or
405 * sequence. It is popped when we see the end of a set or sequence.
406 * Since we don't want to malloc and free these structures all the time,
407 * we pre-allocate a small set of them within the ber element structure.
408 * thus we need to spot when we've overflowed this stack and fall back to
409 * malloc'ing instead.
410 */
411static int
412ber_start_seqorset(BerElement *ber, ber_tag_t tag)
413{
414	Seqorset	*new_sos;
415
416	/* can we fit into the local stack ? */
417	if (ber->ber_sos_stack_posn < SOS_STACK_SIZE) {
418		/* yes */
419		new_sos = &ber->ber_sos_stack[ber->ber_sos_stack_posn];
420	} else {
421		/* no */
422		if ((new_sos = (Seqorset *)malloc(sizeof (Seqorset)))
423		    == NULLSEQORSET) {
424			return (-1);
425		}
426	}
427	ber->ber_sos_stack_posn++;
428
429	if (ber->ber_sos == NULLSEQORSET)
430		new_sos->sos_first = ber->ber_ptr;
431	else
432		new_sos->sos_first = ber->ber_sos->sos_ptr;
433
434	/* Set aside room for a 4 byte length field */
435	new_sos->sos_ptr = new_sos->sos_first + kmfber_calc_taglen(tag) +
436		FOUR_BYTE_LEN;
437	new_sos->sos_tag = tag;
438
439	new_sos->sos_next = ber->ber_sos;
440	new_sos->sos_clen = 0;
441
442	ber->ber_sos = new_sos;
443	if (ber->ber_sos->sos_ptr > ber->ber_end) {
444		(void) realloc(ber, ber->ber_sos->sos_ptr - ber->ber_end);
445	}
446	return (0);
447}
448
449static int
450kmfber_start_seq(BerElement *ber, ber_tag_t tag)
451{
452	if (tag == KMFBER_DEFAULT)
453		tag = BER_CONSTRUCTED_SEQUENCE;
454
455	return (ber_start_seqorset(ber, tag));
456}
457
458static int
459kmfber_start_set(BerElement *ber, ber_tag_t tag)
460{
461	if (tag == KMFBER_DEFAULT)
462		tag = BER_CONSTRUCTED_SET;
463
464	return (ber_start_seqorset(ber, tag));
465}
466
467static int
468ber_put_seqorset(BerElement *ber)
469{
470	ber_int_t	netlen, len, taglen, lenlen;
471	unsigned char	ltag = 0x80 + FOUR_BYTE_LEN - 1;
472	Seqorset	*next;
473	Seqorset	**sos = &ber->ber_sos;
474
475	/*
476	 * If this is the toplevel sequence or set, we need to actually
477	 * write the stuff out.  Otherwise, it's already been put in
478	 * the appropriate buffer and will be written when the toplevel
479	 * one is written.  In this case all we need to do is update the
480	 * length and tag.
481	 */
482
483	len = (*sos)->sos_clen;
484	netlen = (ber_len_t)htonl(len);
485
486	if (ber->ber_options & KMFBER_OPT_USE_DER) {
487		lenlen = kmfber_calc_lenlen(len);
488	} else {
489		lenlen = FOUR_BYTE_LEN;
490	}
491
492	if ((next = (*sos)->sos_next) == NULLSEQORSET) {
493		/* write the tag */
494		if ((taglen = ber_put_tag(ber, (*sos)->sos_tag, 1)) == -1)
495			return (-1);
496
497		if (ber->ber_options & KMFBER_OPT_USE_DER) {
498			/* Write the length in the minimum # of octets */
499			if (kmfber_put_len(ber, len, 1) == -1)
500				return (-1);
501
502			if (lenlen != FOUR_BYTE_LEN) {
503				/*
504				 * We set aside FOUR_BYTE_LEN bytes for
505				 * the length field.  Move the data if
506				 * we don't actually need that much
507				 */
508				(void) memmove((*sos)->sos_first + taglen +
509				    lenlen, (*sos)->sos_first + taglen +
510				    FOUR_BYTE_LEN, len);
511			}
512		} else {
513			/* Fill FOUR_BYTE_LEN bytes for length field */
514			/* one byte of length length */
515			if (kmfber_write(ber, (char *)&ltag, 1, 1) != 1)
516				return (-1);
517
518			/* the length itself */
519			if (kmfber_write(ber,
520				(char *)&netlen + sizeof (ber_int_t)
521				- (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1, 1) !=
522				FOUR_BYTE_LEN - 1)
523				return (-1);
524		}
525		/* The ber_ptr is at the set/seq start - move it to the end */
526		ber->ber_ptr += len;
527	} else {
528		ber_tag_t	ntag;
529
530		/* the tag */
531		taglen = kmfber_calc_taglen((*sos)->sos_tag);
532		ntag = htonl((*sos)->sos_tag);
533		(void) memmove((*sos)->sos_first, (char *)&ntag +
534		    sizeof (ber_int_t) - taglen, taglen);
535
536		if (ber->ber_options & KMFBER_OPT_USE_DER) {
537			ltag = (lenlen == 1) ? (unsigned char)len :
538				(unsigned char) (0x80 + (lenlen - 1));
539		}
540
541		/* one byte of length length */
542		(void) memmove((*sos)->sos_first + 1, &ltag, 1);
543
544		if (ber->ber_options & KMFBER_OPT_USE_DER) {
545			if (lenlen > 1) {
546				/* Write the length itself */
547				(void) memmove((*sos)->sos_first + 2,
548				    (char *)&netlen + sizeof (ber_uint_t) -
549				    (lenlen - 1),
550				    lenlen - 1);
551			}
552			if (lenlen != FOUR_BYTE_LEN) {
553				/*
554				 * We set aside FOUR_BYTE_LEN bytes for
555				 * the length field.  Move the data if
556				 * we don't actually need that much
557				 */
558				(void) memmove((*sos)->sos_first + taglen +
559				    lenlen, (*sos)->sos_first + taglen +
560				    FOUR_BYTE_LEN, len);
561			}
562		} else {
563			/* the length itself */
564			(void) memmove((*sos)->sos_first + taglen + 1,
565			    (char *) &netlen + sizeof (ber_int_t) -
566			    (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1);
567		}
568
569		next->sos_clen += (taglen + lenlen + len);
570		next->sos_ptr += (taglen + lenlen + len);
571	}
572
573	/* we're done with this seqorset, so free it up */
574	/* was this one from the local stack ? */
575	if (ber->ber_sos_stack_posn > SOS_STACK_SIZE) {
576		free((char *)(*sos));
577	}
578	ber->ber_sos_stack_posn--;
579	*sos = next;
580
581	return (taglen + lenlen + len);
582}
583
584/* VARARGS */
585int
586kmfber_printf(BerElement *ber, const char *fmt, ...)
587{
588	va_list		ap;
589	char		*s, **ss;
590	struct berval	**bv, *oid;
591	int		rc, i, t;
592	ber_int_t	len;
593
594	va_start(ap, fmt);
595
596#ifdef KMFBER_DEBUG
597	if (lber_debug & 64) {
598		char msg[80];
599		sprintf(msg, "kmfber_printf fmt (%s)\n", fmt);
600		ber_err_print(msg);
601	}
602#endif
603
604	for (rc = 0; *fmt && rc != -1; fmt++) {
605		switch (*fmt) {
606		case 'b':	/* boolean */
607			i = va_arg(ap, int);
608			rc = kmfber_put_boolean(ber, i, ber->ber_tag);
609			break;
610
611		case 'i':	/* int */
612			i = va_arg(ap, int);
613			rc = ber_put_int(ber, (ber_int_t)i, ber->ber_tag);
614			break;
615
616		case 'D':	/* Object ID */
617			if ((oid = va_arg(ap, struct berval *)) == NULL)
618				break;
619			rc = ber_put_oid(ber, oid, ber->ber_tag);
620			break;
621		case 'I':	/* int */
622			s = va_arg(ap, char *);
623			len = va_arg(ap, ber_int_t);
624			rc = ber_put_big_int(ber, ber->ber_tag, s, len);
625			break;
626
627		case 'e':	/* enumeration */
628			i = va_arg(ap, int);
629			rc = kmfber_put_enum(ber, (ber_int_t)i, ber->ber_tag);
630			break;
631
632		case 'l':
633			t = va_arg(ap, int);
634			rc = kmfber_put_len(ber, t, 0);
635			break;
636		case 'n':	/* null */
637			rc = kmfber_put_null(ber, ber->ber_tag);
638			break;
639
640		case 'o':	/* octet string (non-null terminated) */
641			s = va_arg(ap, char *);
642			len = va_arg(ap, int);
643			rc = kmfber_put_ostring(ber, s, len, ber->ber_tag);
644			break;
645
646		case 's':	/* string */
647			s = va_arg(ap, char *);
648			rc = kmfber_put_string(ber, s, ber->ber_tag);
649			break;
650
651		case 'B':	/* bit string */
652			s = va_arg(ap, char *);
653			len = va_arg(ap, int);	/* in bits */
654			rc = kmfber_put_bitstring(ber, s, len, ber->ber_tag);
655			break;
656
657		case 't':	/* tag for the next element */
658			ber->ber_tag = va_arg(ap, ber_tag_t);
659			ber->ber_usertag = 1;
660			break;
661
662		case 'T': /* Write an explicit tag, but don't change current */
663			t = va_arg(ap, int);
664			rc = ber_put_tag(ber, t, 0);
665			break;
666
667		case 'v':	/* vector of strings */
668			if ((ss = va_arg(ap, char **)) == NULL)
669				break;
670			for (i = 0; ss[i] != NULL; i++) {
671				if ((rc = kmfber_put_string(ber, ss[i],
672				    ber->ber_tag)) == -1)
673					break;
674			}
675			break;
676
677		case 'V':	/* sequences of strings + lengths */
678			if ((bv = va_arg(ap, struct berval **)) == NULL)
679				break;
680			for (i = 0; bv[i] != NULL; i++) {
681				if ((rc = kmfber_put_ostring(ber, bv[i]->bv_val,
682				    bv[i]->bv_len, ber->ber_tag)) == -1)
683					break;
684			}
685			break;
686
687		case '{':	/* begin sequence */
688			rc = kmfber_start_seq(ber, ber->ber_tag);
689			break;
690
691		case '}':	/* end sequence */
692			rc = ber_put_seqorset(ber);
693			break;
694
695		case '[':	/* begin set */
696			rc = kmfber_start_set(ber, ber->ber_tag);
697			break;
698
699		case ']':	/* end set */
700			rc = ber_put_seqorset(ber);
701			break;
702
703		default: {
704#ifdef KMFBER_DEBUG
705				char msg[80];
706				sprintf(msg, "unknown fmt %c\n", *fmt);
707				ber_err_print(msg);
708#endif
709				rc = -1;
710				break;
711			}
712		}
713
714		if (ber->ber_usertag == 0)
715			ber->ber_tag = KMFBER_DEFAULT;
716		else
717			ber->ber_usertag = 0;
718	}
719
720	va_end(ap);
721
722	return (rc);
723}
724