xref: /illumos-gate/usr/src/lib/libresolv2/common/isc/base64.c (revision 6e6545bfaed3bab9ce836ee82d1abd8f2edba89a)
1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 
7 /*
8  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
9  * Copyright (c) 1996-1999 by Internet Software Consortium.
10  *
11  * Permission to use, copy, modify, and distribute this software for any
12  * purpose with or without fee is hereby granted, provided that the above
13  * copyright notice and this permission notice appear in all copies.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
16  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
18  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  */
23 
24 /*
25  * Portions Copyright (c) 1995 by International Business Machines, Inc.
26  *
27  * International Business Machines, Inc. (hereinafter called IBM) grants
28  * permission under its copyrights to use, copy, modify, and distribute this
29  * Software with or without fee, provided that the above copyright notice and
30  * all paragraphs of this notice appear in all copies, and that the name of IBM
31  * not be used in connection with the marketing of any product incorporating
32  * the Software or modifications thereof, without specific, written prior
33  * permission.
34  *
35  * To the extent it has a right to do so, IBM grants an immunity from suit
36  * under its patents, if any, for the use, sale or manufacture of products to
37  * the extent that such products are used for performing Domain Name System
38  * dynamic updates in TCP/IP networks by means of the Software.  No immunity is
39  * granted for any product per se or for any other function of any product.
40  *
41  * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
42  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
43  * PARTICULAR PURPOSE.  IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
44  * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
45  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
46  * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
47  */
48 
49 #include "port_before.h"
50 
51 #include <sys/types.h>
52 #include <sys/param.h>
53 #include <sys/socket.h>
54 
55 #include <netinet/in.h>
56 #include <arpa/inet.h>
57 #include <arpa/nameser.h>
58 
59 #include <ctype.h>
60 #include <resolv.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 
65 #include "port_after.h"
66 
67 #ifndef	ORIGINAL_ISC_CODE
68 #pragma weak	__b64_ntop	=	b64_ntop
69 #pragma weak	__b64_pton	=	b64_pton
70 #endif	/* ORIGINAL_ISC_CODE */
71 
72 #define Assert(Cond) if (!(Cond)) abort()
73 
74 static const char Base64[] =
75 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
76 static const char Pad64 = '=';
77 
78 /* (From RFC1521 and draft-ietf-dnssec-secext-03.txt)
79    The following encoding technique is taken from RFC1521 by Borenstein
80    and Freed.  It is reproduced here in a slightly edited form for
81    convenience.
82 
83    A 65-character subset of US-ASCII is used, enabling 6 bits to be
84    represented per printable character. (The extra 65th character, "=",
85    is used to signify a special processing function.)
86 
87    The encoding process represents 24-bit groups of input bits as output
88    strings of 4 encoded characters. Proceeding from left to right, a
89    24-bit input group is formed by concatenating 3 8-bit input groups.
90    These 24 bits are then treated as 4 concatenated 6-bit groups, each
91    of which is translated into a single digit in the base64 alphabet.
92 
93    Each 6-bit group is used as an index into an array of 64 printable
94    characters. The character referenced by the index is placed in the
95    output string.
96 
97                          Table 1: The Base64 Alphabet
98 
99       Value Encoding  Value Encoding  Value Encoding  Value Encoding
100           0 A            17 R            34 i            51 z
101           1 B            18 S            35 j            52 0
102           2 C            19 T            36 k            53 1
103           3 D            20 U            37 l            54 2
104           4 E            21 V            38 m            55 3
105           5 F            22 W            39 n            56 4
106           6 G            23 X            40 o            57 5
107           7 H            24 Y            41 p            58 6
108           8 I            25 Z            42 q            59 7
109           9 J            26 a            43 r            60 8
110          10 K            27 b            44 s            61 9
111          11 L            28 c            45 t            62 +
112          12 M            29 d            46 u            63 /
113          13 N            30 e            47 v
114          14 O            31 f            48 w         (pad) =
115          15 P            32 g            49 x
116          16 Q            33 h            50 y
117 
118    Special processing is performed if fewer than 24 bits are available
119    at the end of the data being encoded.  A full encoding quantum is
120    always completed at the end of a quantity.  When fewer than 24 input
121    bits are available in an input group, zero bits are added (on the
122    right) to form an integral number of 6-bit groups.  Padding at the
123    end of the data is performed using the '=' character.
124 
125    Since all base64 input is an integral number of octets, only the
126          -------------------------------------------------
127    following cases can arise:
128 
129        (1) the final quantum of encoding input is an integral
130            multiple of 24 bits; here, the final unit of encoded
131 	   output will be an integral multiple of 4 characters
132 	   with no "=" padding,
133        (2) the final quantum of encoding input is exactly 8 bits;
134            here, the final unit of encoded output will be two
135 	   characters followed by two "=" padding characters, or
136        (3) the final quantum of encoding input is exactly 16 bits;
137            here, the final unit of encoded output will be three
138 	   characters followed by one "=" padding character.
139    */
140 
141 int
142 b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) {
143 	size_t datalength = 0;
144 	u_char input[3];
145 	u_char output[4];
146 	size_t i;
147 
148 	while (2U < srclength) {
149 		input[0] = *src++;
150 		input[1] = *src++;
151 		input[2] = *src++;
152 		srclength -= 3;
153 
154 		output[0] = input[0] >> 2;
155 		output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
156 		output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
157 		output[3] = input[2] & 0x3f;
158 		Assert(output[0] < 64);
159 		Assert(output[1] < 64);
160 		Assert(output[2] < 64);
161 		Assert(output[3] < 64);
162 
163 		if (datalength + 4 > targsize)
164 			return (-1);
165 		target[datalength++] = Base64[output[0]];
166 		target[datalength++] = Base64[output[1]];
167 		target[datalength++] = Base64[output[2]];
168 		target[datalength++] = Base64[output[3]];
169 	}
170 
171 	/* Now we worry about padding. */
172 	if (0U != srclength) {
173 		/* Get what's left. */
174 		input[0] = input[1] = input[2] = '\0';
175 		for (i = 0; i < srclength; i++)
176 			input[i] = *src++;
177 
178 		output[0] = input[0] >> 2;
179 		output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
180 		output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
181 		Assert(output[0] < 64);
182 		Assert(output[1] < 64);
183 		Assert(output[2] < 64);
184 
185 		if (datalength + 4 > targsize)
186 			return (-1);
187 		target[datalength++] = Base64[output[0]];
188 		target[datalength++] = Base64[output[1]];
189 		if (srclength == 1U)
190 			target[datalength++] = Pad64;
191 		else
192 			target[datalength++] = Base64[output[2]];
193 		target[datalength++] = Pad64;
194 	}
195 	if (datalength >= targsize)
196 		return (-1);
197 	target[datalength] = '\0';	/*%< Returned value doesn't count \\0. */
198 	return (datalength);
199 }
200 
201 /* skips all whitespace anywhere.
202    converts characters, four at a time, starting at (or after)
203    src from base - 64 numbers into three 8 bit bytes in the target area.
204    it returns the number of data bytes stored at the target, or -1 on error.
205  */
206 
207 int
208 b64_pton(src, target, targsize)
209 	char const *src;
210 	u_char *target;
211 	size_t targsize;
212 {
213 	int tarindex, state, ch;
214 	char *pos;
215 
216 	state = 0;
217 	tarindex = 0;
218 
219 	while ((ch = *src++) != '\0') {
220 		if (isspace(ch))	/*%< Skip whitespace anywhere. */
221 			continue;
222 
223 		if (ch == Pad64)
224 			break;
225 
226 		pos = strchr(Base64, ch);
227 		if (pos == 0) 		/*%< A non-base64 character. */
228 			return (-1);
229 
230 		switch (state) {
231 		case 0:
232 			if (target) {
233 				if ((size_t)tarindex >= targsize)
234 					return (-1);
235 				target[tarindex] = (pos - Base64) << 2;
236 			}
237 			state = 1;
238 			break;
239 		case 1:
240 			if (target) {
241 				if ((size_t)tarindex + 1 >= targsize)
242 					return (-1);
243 				target[tarindex]   |=  (pos - Base64) >> 4;
244 				target[tarindex+1]  = ((pos - Base64) & 0x0f)
245 							<< 4 ;
246 			}
247 			tarindex++;
248 			state = 2;
249 			break;
250 		case 2:
251 			if (target) {
252 				if ((size_t)tarindex + 1 >= targsize)
253 					return (-1);
254 				target[tarindex]   |=  (pos - Base64) >> 2;
255 				target[tarindex+1]  = ((pos - Base64) & 0x03)
256 							<< 6;
257 			}
258 			tarindex++;
259 			state = 3;
260 			break;
261 		case 3:
262 			if (target) {
263 				if ((size_t)tarindex >= targsize)
264 					return (-1);
265 				target[tarindex] |= (pos - Base64);
266 			}
267 			tarindex++;
268 			state = 0;
269 			break;
270 		default:
271 			abort();
272 		}
273 	}
274 
275 	/*
276 	 * We are done decoding Base-64 chars.  Let's see if we ended
277 	 * on a byte boundary, and/or with erroneous trailing characters.
278 	 */
279 
280 	if (ch == Pad64) {		/*%< We got a pad char. */
281 		ch = *src++;		/*%< Skip it, get next. */
282 		switch (state) {
283 		case 0:		/*%< Invalid = in first position */
284 		case 1:		/*%< Invalid = in second position */
285 			return (-1);
286 
287 		case 2:		/*%< Valid, means one byte of info */
288 			/* Skip any number of spaces. */
289 			for ((void)NULL; ch != '\0'; ch = *src++)
290 				if (!isspace(ch))
291 					break;
292 			/* Make sure there is another trailing = sign. */
293 			if (ch != Pad64)
294 				return (-1);
295 			ch = *src++;		/*%< Skip the = */
296 			/* Fall through to "single trailing =" case. */
297 			/* FALLTHROUGH */
298 
299 		case 3:		/*%< Valid, means two bytes of info */
300 			/*
301 			 * We know this char is an =.  Is there anything but
302 			 * whitespace after it?
303 			 */
304 			for ((void)NULL; ch != '\0'; ch = *src++)
305 				if (!isspace(ch))
306 					return (-1);
307 
308 			/*
309 			 * Now make sure for cases 2 and 3 that the "extra"
310 			 * bits that slopped past the last full byte were
311 			 * zeros.  If we don't check them, they become a
312 			 * subliminal channel.
313 			 */
314 			if (target && target[tarindex] != 0)
315 				return (-1);
316 		}
317 	} else {
318 		/*
319 		 * We ended by seeing the end of the string.  Make sure we
320 		 * have no partial bytes lying around.
321 		 */
322 		if (state != 0)
323 			return (-1);
324 	}
325 
326 	return (tarindex);
327 }
328 
329 /*! \file */
330