Commit | Line | Data |
---|---|---|
f4d091fb JH |
1 | /************************************************* |
2 | * Exim - an Internet mail transport agent * | |
3 | *************************************************/ | |
4 | ||
5 | /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004, 2015 */ | |
6 | /* License: GPL */ | |
7 | ||
f9ba5e22 | 8 | /* Copyright (c) University of Cambridge 1995 - 2018 */ |
f4d091fb JH |
9 | /* See the file NOTICE for conditions of use and distribution. */ |
10 | ||
11 | ||
12 | #include "exim.h" | |
13 | #ifdef WITH_CONTENT_SCAN /* file-IO specific decode function */ | |
14 | # include "mime.h" | |
15 | ||
16 | /* BASE64 decoder matrix */ | |
17 | static unsigned char mime_b64[256]={ | |
18 | /* 0 */ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, | |
19 | /* 16 */ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, | |
20 | /* 32 */ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 62, 128, 128, 128, 63, | |
21 | /* 48 */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 128, 128, 128, 255, 128, 128, | |
22 | /* 64 */ 128, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, | |
23 | /* 80 */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 128, 128, 128, 128, 128, | |
24 | /* 96 */ 128, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, | |
25 | /* 112 */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 128, 128, 128, 128, 128, | |
26 | /* 128 */ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, | |
27 | /* 144 */ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, | |
28 | /* 160 */ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, | |
29 | /* 176 */ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, | |
30 | /* 192 */ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, | |
31 | /* 208 */ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, | |
32 | /* 224 */ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, | |
33 | /* 240 */ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 | |
34 | }; | |
35 | ||
36 | /* decode base64 MIME part */ | |
37 | ssize_t | |
38 | mime_decode_base64(FILE * in, FILE * out, uschar * boundary) | |
39 | { | |
40 | uschar ibuf[MIME_MAX_LINE_LENGTH], obuf[MIME_MAX_LINE_LENGTH]; | |
d7978c0f | 41 | uschar *opos; |
f4d091fb JH |
42 | ssize_t len, size = 0; |
43 | int bytestate = 0; | |
44 | ||
45 | opos = obuf; | |
46 | ||
47 | while (Ufgets(ibuf, MIME_MAX_LINE_LENGTH, in) != NULL) | |
48 | { | |
49 | if (boundary != NULL | |
50 | && Ustrncmp(ibuf, "--", 2) == 0 | |
51 | && Ustrncmp((ibuf+2), boundary, Ustrlen(boundary)) == 0 | |
52 | ) | |
53 | break; | |
54 | ||
d7978c0f | 55 | for (uschar * ipos = ibuf ; *ipos != '\r' && *ipos != '\n' && *ipos; ++ipos) |
f4d091fb JH |
56 | if (*ipos == '=') /* skip padding */ |
57 | ++bytestate; | |
58 | ||
59 | else if (mime_b64[*ipos] == 128) /* skip bad characters */ | |
60 | mime_set_anomaly(MIME_ANOMALY_BROKEN_BASE64); | |
61 | ||
62 | /* simple state-machine */ | |
63 | else switch((bytestate++) & 3) | |
64 | { | |
65 | case 0: | |
66 | *opos = mime_b64[*ipos] << 2; break; | |
67 | case 1: | |
68 | *opos++ |= mime_b64[*ipos] >> 4; | |
69 | *opos = mime_b64[*ipos] << 4; break; | |
70 | case 2: | |
71 | *opos++ |= mime_b64[*ipos] >> 2; | |
72 | *opos = mime_b64[*ipos] << 6; break; | |
73 | case 3: | |
74 | *opos++ |= mime_b64[*ipos]; break; | |
75 | } | |
76 | ||
77 | /* something to write? */ | |
78 | len = opos - obuf; | |
79 | if (len > 0) | |
80 | { | |
81 | if (fwrite(obuf, 1, len, out) != len) return -1; /* error */ | |
82 | size += len; | |
83 | /* copy incomplete last byte to start of obuf, where we continue */ | |
84 | if ((bytestate & 3) != 0) | |
85 | *obuf = *opos; | |
86 | opos = obuf; | |
87 | } | |
88 | } /* while */ | |
89 | ||
90 | /* write out last byte if it was incomplete */ | |
91 | if (bytestate & 3) | |
92 | { | |
93 | if (fwrite(obuf, 1, 1, out) != 1) return -1; | |
94 | ++size; | |
95 | } | |
96 | ||
97 | return size; | |
98 | } | |
99 | ||
100 | #endif /*WITH_CONTENT_SCAN*/ | |
101 | ||
102 | /************************************************* | |
3c51463e JH |
103 | ************************************************* |
104 | ************************************************* | |
105 | ************************************************* | |
106 | ************************************************* | |
107 | ************************************************* | |
108 | ************************************************* | |
109 | ************************************************* | |
110 | ************************************************* | |
111 | ************************************************* | |
112 | ************************************************* | |
113 | ************************************************* | |
114 | ************************************************* | |
115 | ************************************************* | |
116 | ************************************************* | |
117 | *************************************************/ | |
f4d091fb JH |
118 | |
119 | ||
120 | /************************************************* | |
121 | * Decode byte-string in base 64 * | |
122 | *************************************************/ | |
123 | ||
124 | /* This function decodes a string in base 64 format as defined in RFC 2045 | |
125 | (MIME) and required by the SMTP AUTH extension (RFC 2554). The decoding | |
126 | algorithm is written out in a straightforward way. Turning it into some kind of | |
127 | compact loop is messy and would probably run more slowly. | |
128 | ||
129 | Arguments: | |
130 | code points to the coded string, zero-terminated | |
131 | ptr where to put the pointer to the result, which is in | |
df3def24 | 132 | allocated store, and zero-terminated |
f4d091fb JH |
133 | |
134 | Returns: the number of bytes in the result, | |
135 | or -1 if the input was malformed | |
136 | ||
69a70afa | 137 | Whitespace in the input is ignored. |
f4d091fb JH |
138 | A zero is added on to the end to make it easy in cases where the result is to |
139 | be interpreted as text. This is not included in the count. */ | |
140 | ||
141 | static uschar dec64table[] = { | |
142 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, /* 0-15 */ | |
143 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, /* 16-31 */ | |
144 | 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63, /* 32-47 */ | |
145 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,255,255,255, /* 48-63 */ | |
146 | 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 64-79 */ | |
147 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255, /* 80-95 */ | |
148 | 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 96-111 */ | |
149 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255 /* 112-127*/ | |
150 | }; | |
151 | ||
152 | int | |
35cf75e9 | 153 | b64decode(const uschar *code, uschar **ptr) |
f4d091fb | 154 | { |
cf3cd306 | 155 | |
f4d091fb | 156 | int x, y; |
cf3cd306 | 157 | uschar *result; |
f4d091fb | 158 | |
cf3cd306 HSHR |
159 | { |
160 | int l = Ustrlen(code); | |
f3ebb786 | 161 | *ptr = result = store_get(1 + l/4 * 3 + l%4, is_tainted(code)); |
cf3cd306 | 162 | } |
f4d091fb JH |
163 | |
164 | /* Each cycle of the loop handles a quantum of 4 input bytes. For the last | |
165 | quantum this may decode to 1, 2, or 3 output bytes. */ | |
166 | ||
167 | while ((x = *code++) != 0) | |
168 | { | |
69a70afa | 169 | if (isspace(x)) continue; |
df3def24 | 170 | /* debug_printf("b64d: '%c'\n", x); */ |
69a70afa | 171 | |
f4d091fb | 172 | if (x > 127 || (x = dec64table[x]) == 255) return -1; |
69a70afa JH |
173 | |
174 | while (isspace(y = *code++)) ; | |
df3def24 | 175 | /* debug_printf("b64d: '%c'\n", y); */ |
889d293b | 176 | if (y > 127 || (y = dec64table[y]) == 255) |
f4d091fb JH |
177 | return -1; |
178 | ||
179 | *result++ = (x << 2) | (y >> 4); | |
df3def24 | 180 | /* debug_printf("b64d: -> %02x\n", result[-1]); */ |
f4d091fb | 181 | |
69a70afa | 182 | while (isspace(x = *code++)) ; |
df3def24 JH |
183 | /* debug_printf("b64d: '%c'\n", x); */ |
184 | if (x == '=') /* endmarker, but there should be another */ | |
f4d091fb | 185 | { |
69a70afa | 186 | while (isspace(x = *code++)) ; |
df3def24 JH |
187 | /* debug_printf("b64d: '%c'\n", x); */ |
188 | if (x != '=') return -1; | |
189 | while (isspace(y = *code++)) ; | |
190 | if (y != 0) return -1; | |
191 | /* debug_printf("b64d: DONE\n"); */ | |
192 | break; | |
f4d091fb JH |
193 | } |
194 | else | |
195 | { | |
196 | if (x > 127 || (x = dec64table[x]) == 255) return -1; | |
197 | *result++ = (y << 4) | (x >> 2); | |
df3def24 | 198 | /* debug_printf("b64d: -> %02x\n", result[-1]); */ |
69a70afa JH |
199 | |
200 | while (isspace(y = *code++)) ; | |
df3def24 | 201 | /* debug_printf("b64d: '%c'\n", y); */ |
69a70afa | 202 | if (y == '=') |
f4d091fb | 203 | { |
df3def24 JH |
204 | while (isspace(y = *code++)) ; |
205 | if (y != 0) return -1; | |
206 | /* debug_printf("b64d: DONE\n"); */ | |
207 | break; | |
f4d091fb JH |
208 | } |
209 | else | |
210 | { | |
211 | if (y > 127 || (y = dec64table[y]) == 255) return -1; | |
212 | *result++ = (x << 6) | y; | |
df3def24 | 213 | /* debug_printf("b64d: -> %02x\n", result[-1]); */ |
f4d091fb JH |
214 | } |
215 | } | |
216 | } | |
217 | ||
218 | *result = 0; | |
219 | return result - *ptr; | |
220 | } | |
221 | ||
222 | ||
f4d091fb JH |
223 | /************************************************* |
224 | * Encode byte-string in base 64 * | |
225 | *************************************************/ | |
226 | ||
227 | /* This function encodes a string of bytes, containing any values whatsoever, | |
228 | in base 64 as defined in RFC 2045 (MIME) and required by the SMTP AUTH | |
229 | extension (RFC 2554). The encoding algorithm is written out in a | |
230 | straightforward way. Turning it into some kind of compact loop is messy and | |
231 | would probably run more slowly. | |
232 | ||
233 | Arguments: | |
234 | clear points to the clear text bytes | |
235 | len the number of bytes to encode | |
236 | ||
237 | Returns: a pointer to the zero-terminated base 64 string, which | |
238 | is in working store | |
239 | */ | |
240 | ||
241 | static uschar *enc64table = | |
242 | US"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |
243 | ||
244 | uschar * | |
b1a32a3c | 245 | b64encode_taint(const uschar * clear, int len, BOOL tainted) |
f4d091fb | 246 | { |
b1a32a3c | 247 | uschar *code = store_get(4*((len+2)/3) + 1, tainted); |
f4d091fb JH |
248 | uschar *p = code; |
249 | ||
250 | while (len-- >0) | |
251 | { | |
286b9d5f | 252 | int x, y; |
f4d091fb JH |
253 | |
254 | x = *clear++; | |
255 | *p++ = enc64table[(x >> 2) & 63]; | |
256 | ||
257 | if (len-- <= 0) | |
258 | { | |
259 | *p++ = enc64table[(x << 4) & 63]; | |
260 | *p++ = '='; | |
261 | *p++ = '='; | |
262 | break; | |
263 | } | |
264 | ||
265 | y = *clear++; | |
266 | *p++ = enc64table[((x << 4) | ((y >> 4) & 15)) & 63]; | |
267 | ||
268 | if (len-- <= 0) | |
269 | { | |
270 | *p++ = enc64table[(y << 2) & 63]; | |
271 | *p++ = '='; | |
272 | break; | |
273 | } | |
274 | ||
275 | x = *clear++; | |
276 | *p++ = enc64table[((y << 2) | ((x >> 6) & 3)) & 63]; | |
277 | ||
278 | *p++ = enc64table[x & 63]; | |
279 | } | |
280 | ||
281 | *p = 0; | |
282 | ||
283 | return code; | |
284 | } | |
285 | ||
b1a32a3c JH |
286 | uschar * |
287 | b64encode(const uschar * clear, int len) | |
288 | { | |
289 | return b64encode_taint(clear, len, is_tainted(clear)); | |
290 | } | |
291 | ||
f4d091fb JH |
292 | |
293 | /* End of base64.c */ | |
294 | /* vi: sw ai sw=2 | |
295 | */ |