1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7  *
8  * See the COPYRIGHT file distributed with this work for additional
9  * information regarding copyright ownership.
10  */
11 
12 /*! \file */
13 
14 #include <stdbool.h>
15 
16 #include <isc/base64.h>
17 #include <isc/buffer.h>
18 #include <isc/lex.h>
19 #include <isc/string.h>
20 #include <isc/util.h>
21 
22 #define RETERR(x)                        \
23 	do {                             \
24 		isc_result_t _r = (x);   \
25 		if (_r != ISC_R_SUCCESS) \
26 			return ((_r));   \
27 	} while (0)
28 
29 /*@{*/
30 /*!
31  * These static functions are also present in lib/dns/rdata.c.  I'm not
32  * sure where they should go. -- bwelling
33  */
34 static isc_result_t
35 str_totext(const char *source, isc_buffer_t *target);
36 
37 static isc_result_t
38 mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length);
39 
40 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw"
41 			     "xyz0123456789+/=";
42 /*@}*/
43 
44 isc_result_t
isc_base64_totext(isc_region_t * source,int wordlength,const char * wordbreak,isc_buffer_t * target)45 isc_base64_totext(isc_region_t *source, int wordlength, const char *wordbreak,
46 		  isc_buffer_t *target) {
47 	char buf[5];
48 	unsigned int loops = 0;
49 
50 	if (wordlength < 4) {
51 		wordlength = 4;
52 	}
53 
54 	memset(buf, 0, sizeof(buf));
55 	while (source->length > 2) {
56 		buf[0] = base64[(source->base[0] >> 2) & 0x3f];
57 		buf[1] = base64[((source->base[0] << 4) & 0x30) |
58 				((source->base[1] >> 4) & 0x0f)];
59 		buf[2] = base64[((source->base[1] << 2) & 0x3c) |
60 				((source->base[2] >> 6) & 0x03)];
61 		buf[3] = base64[source->base[2] & 0x3f];
62 		RETERR(str_totext(buf, target));
63 		isc_region_consume(source, 3);
64 
65 		loops++;
66 		if (source->length != 0 && (int)((loops + 1) * 4) >= wordlength)
67 		{
68 			loops = 0;
69 			RETERR(str_totext(wordbreak, target));
70 		}
71 	}
72 	if (source->length == 2) {
73 		buf[0] = base64[(source->base[0] >> 2) & 0x3f];
74 		buf[1] = base64[((source->base[0] << 4) & 0x30) |
75 				((source->base[1] >> 4) & 0x0f)];
76 		buf[2] = base64[((source->base[1] << 2) & 0x3c)];
77 		buf[3] = '=';
78 		RETERR(str_totext(buf, target));
79 		isc_region_consume(source, 2);
80 	} else if (source->length == 1) {
81 		buf[0] = base64[(source->base[0] >> 2) & 0x3f];
82 		buf[1] = base64[((source->base[0] << 4) & 0x30)];
83 		buf[2] = buf[3] = '=';
84 		RETERR(str_totext(buf, target));
85 		isc_region_consume(source, 1);
86 	}
87 	return (ISC_R_SUCCESS);
88 }
89 
90 /*%
91  * State of a base64 decoding process in progress.
92  */
93 typedef struct {
94 	int length;	      /*%< Desired length of binary data or -1 */
95 	isc_buffer_t *target; /*%< Buffer for resulting binary data */
96 	int digits;	      /*%< Number of buffered base64 digits */
97 	bool seen_end;	      /*%< True if "=" end marker seen */
98 	int val[4];
99 } base64_decode_ctx_t;
100 
101 static inline void
base64_decode_init(base64_decode_ctx_t * ctx,int length,isc_buffer_t * target)102 base64_decode_init(base64_decode_ctx_t *ctx, int length, isc_buffer_t *target) {
103 	ctx->digits = 0;
104 	ctx->seen_end = false;
105 	ctx->length = length;
106 	ctx->target = target;
107 }
108 
109 static inline isc_result_t
base64_decode_char(base64_decode_ctx_t * ctx,int c)110 base64_decode_char(base64_decode_ctx_t *ctx, int c) {
111 	const char *s;
112 
113 	if (ctx->seen_end) {
114 		return (ISC_R_BADBASE64);
115 	}
116 	if ((s = strchr(base64, c)) == NULL) {
117 		return (ISC_R_BADBASE64);
118 	}
119 	ctx->val[ctx->digits++] = (int)(s - base64);
120 	if (ctx->digits == 4) {
121 		int n;
122 		unsigned char buf[3];
123 		if (ctx->val[0] == 64 || ctx->val[1] == 64) {
124 			return (ISC_R_BADBASE64);
125 		}
126 		if (ctx->val[2] == 64 && ctx->val[3] != 64) {
127 			return (ISC_R_BADBASE64);
128 		}
129 		/*
130 		 * Check that bits that should be zero are.
131 		 */
132 		if (ctx->val[2] == 64 && (ctx->val[1] & 0xf) != 0) {
133 			return (ISC_R_BADBASE64);
134 		}
135 		/*
136 		 * We don't need to test for ctx->val[2] != 64 as
137 		 * the bottom two bits of 64 are zero.
138 		 */
139 		if (ctx->val[3] == 64 && (ctx->val[2] & 0x3) != 0) {
140 			return (ISC_R_BADBASE64);
141 		}
142 		n = (ctx->val[2] == 64) ? 1 : (ctx->val[3] == 64) ? 2 : 3;
143 		if (n != 3) {
144 			ctx->seen_end = true;
145 			if (ctx->val[2] == 64) {
146 				ctx->val[2] = 0;
147 			}
148 			if (ctx->val[3] == 64) {
149 				ctx->val[3] = 0;
150 			}
151 		}
152 		buf[0] = (ctx->val[0] << 2) | (ctx->val[1] >> 4);
153 		buf[1] = (ctx->val[1] << 4) | (ctx->val[2] >> 2);
154 		buf[2] = (ctx->val[2] << 6) | (ctx->val[3]);
155 		RETERR(mem_tobuffer(ctx->target, buf, n));
156 		if (ctx->length >= 0) {
157 			if (n > ctx->length) {
158 				return (ISC_R_BADBASE64);
159 			} else {
160 				ctx->length -= n;
161 			}
162 		}
163 		ctx->digits = 0;
164 	}
165 	return (ISC_R_SUCCESS);
166 }
167 
168 static inline isc_result_t
base64_decode_finish(base64_decode_ctx_t * ctx)169 base64_decode_finish(base64_decode_ctx_t *ctx) {
170 	if (ctx->length > 0) {
171 		return (ISC_R_UNEXPECTEDEND);
172 	}
173 	if (ctx->digits != 0) {
174 		return (ISC_R_BADBASE64);
175 	}
176 	return (ISC_R_SUCCESS);
177 }
178 
179 isc_result_t
isc_base64_tobuffer(isc_lex_t * lexer,isc_buffer_t * target,int length)180 isc_base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) {
181 	unsigned int before, after;
182 	base64_decode_ctx_t ctx;
183 	isc_textregion_t *tr;
184 	isc_token_t token;
185 	bool eol;
186 
187 	REQUIRE(length >= -2);
188 
189 	base64_decode_init(&ctx, length, target);
190 
191 	before = isc_buffer_usedlength(target);
192 	while (!ctx.seen_end && (ctx.length != 0)) {
193 		unsigned int i;
194 
195 		if (length > 0) {
196 			eol = false;
197 		} else {
198 			eol = true;
199 		}
200 		RETERR(isc_lex_getmastertoken(lexer, &token,
201 					      isc_tokentype_string, eol));
202 		if (token.type != isc_tokentype_string) {
203 			break;
204 		}
205 		tr = &token.value.as_textregion;
206 		for (i = 0; i < tr->length; i++) {
207 			RETERR(base64_decode_char(&ctx, tr->base[i]));
208 		}
209 	}
210 	after = isc_buffer_usedlength(target);
211 	if (ctx.length < 0 && !ctx.seen_end) {
212 		isc_lex_ungettoken(lexer, &token);
213 	}
214 	RETERR(base64_decode_finish(&ctx));
215 	if (length == -2 && before == after) {
216 		return (ISC_R_UNEXPECTEDEND);
217 	}
218 	return (ISC_R_SUCCESS);
219 }
220 
221 isc_result_t
isc_base64_decodestring(const char * cstr,isc_buffer_t * target)222 isc_base64_decodestring(const char *cstr, isc_buffer_t *target) {
223 	base64_decode_ctx_t ctx;
224 
225 	base64_decode_init(&ctx, -1, target);
226 	for (;;) {
227 		int c = *cstr++;
228 		if (c == '\0') {
229 			break;
230 		}
231 		if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
232 			continue;
233 		}
234 		RETERR(base64_decode_char(&ctx, c));
235 	}
236 	RETERR(base64_decode_finish(&ctx));
237 	return (ISC_R_SUCCESS);
238 }
239 
240 static isc_result_t
str_totext(const char * source,isc_buffer_t * target)241 str_totext(const char *source, isc_buffer_t *target) {
242 	unsigned int l;
243 	isc_region_t region;
244 
245 	isc_buffer_availableregion(target, &region);
246 	l = strlen(source);
247 
248 	if (l > region.length) {
249 		return (ISC_R_NOSPACE);
250 	}
251 
252 	memmove(region.base, source, l);
253 	isc_buffer_add(target, l);
254 	return (ISC_R_SUCCESS);
255 }
256 
257 static isc_result_t
mem_tobuffer(isc_buffer_t * target,void * base,unsigned int length)258 mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) {
259 	isc_region_t tr;
260 
261 	isc_buffer_availableregion(target, &tr);
262 	if (length > tr.length) {
263 		return (ISC_R_NOSPACE);
264 	}
265 	memmove(tr.base, base, length);
266 	isc_buffer_add(target, length);
267 	return (ISC_R_SUCCESS);
268 }
269