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, ®ion);
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