1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "str.h"
5 #include "base64.h"
6 #include "array.h"
7 #include "http-parser.h"
8
9 #include "http-auth.h"
10
11 /* RFC 7235, Section 2.1:
12
13 challenge = auth-scheme [ 1*SP ( token68 / #auth-param ) ]
14 credentials = auth-scheme [ 1*SP ( token68 / #auth-param ) ]
15
16 auth-scheme = token
17 auth-param = token BWS "=" BWS ( token / quoted-string )
18 token68 = 1*( ALPHA / DIGIT /
19 "-" / "." / "_" / "~" / "+" / "/" ) *"="
20
21 OWS = *( SP / HTAB )
22 ; optional whitespace
23 BWS = OWS
24 ; "bad" whitespace
25 */
26
27 /*
28 * Parsing
29 */
30
31 static int
http_parse_token68(struct http_parser * parser,const char ** token68_r)32 http_parse_token68(struct http_parser *parser, const char **token68_r)
33 {
34 const unsigned char *first;
35
36 /* token68 = 1*( ALPHA / DIGIT /
37 "-" / "." / "_" / "~" / "+" / "/" ) *"="
38 */
39
40 /* 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) */
41 if (parser->cur >= parser->end || !http_char_is_token68(*parser->cur))
42 return 0;
43 first = parser->cur++;
44 while (parser->cur < parser->end && http_char_is_token68(*parser->cur))
45 parser->cur++;
46
47 /* *"=" */
48 while (parser->cur < parser->end && *parser->cur == '=')
49 parser->cur++;
50
51 *token68_r = t_strndup(first, parser->cur - first);
52 return 1;
53 }
54
55 static int
http_parse_auth_param(struct http_parser * parser,const char ** param_r,const char ** value_r)56 http_parse_auth_param(struct http_parser *parser,
57 const char **param_r, const char **value_r)
58 {
59 const unsigned char *first = parser->cur, *end_token;
60 int ret;
61
62 /* auth-param = token BWS "=" BWS ( token / quoted-string ) */
63
64 /* token */
65 if ((ret=http_parser_skip_token(parser)) <= 0) {
66 parser->cur = first;
67 return ret;
68 }
69 end_token = parser->cur;
70
71 /* BWS "=" BWS */
72 http_parse_ows(parser);
73 if (parser->cur >= parser->end || *parser->cur != '=') {
74 parser->cur = first;
75 return 0;
76 }
77 parser->cur++;
78 http_parse_ows(parser);
79
80 /* ( token / quoted-string ) */
81 if ((ret=http_parse_token_or_qstring(parser, value_r)) <= 0) {
82 parser->cur = first;
83 return ret;
84 }
85
86 *param_r = t_strndup(first, end_token - first);
87 return 1;
88 }
89
90 static int
http_parse_auth_params(struct http_parser * parser,ARRAY_TYPE (http_auth_param)* params)91 http_parse_auth_params(struct http_parser *parser,
92 ARRAY_TYPE(http_auth_param) *params)
93 {
94 const unsigned char *last = parser->cur;
95 struct http_auth_param param;
96 unsigned int count = 0;
97 int ret;
98
99 i_zero(¶m);
100 while ((ret=http_parse_auth_param
101 (parser, ¶m.name, ¶m.value)) > 0) {
102 if (!array_is_created(params))
103 t_array_init(params, 4);
104 array_push_back(params, ¶m);
105 count++;
106
107 last = parser->cur;
108
109 /* OWS "," OWS
110 --> also allow empty elements
111 */
112 for (;;) {
113 http_parse_ows(parser);
114 if (parser->cur >= parser->end || *parser->cur != ',')
115 break;
116 parser->cur++;
117 }
118 }
119
120 parser->cur = last;
121 if (ret < 0)
122 return -1;
123 return (count > 0 ? 1 : 0);
124 }
125
http_auth_parse_challenges(const unsigned char * data,size_t size,ARRAY_TYPE (http_auth_challenge)* chlngs)126 int http_auth_parse_challenges(const unsigned char *data, size_t size,
127 ARRAY_TYPE(http_auth_challenge) *chlngs)
128 {
129 struct http_parser parser;
130 int ret;
131
132 http_parser_init(&parser, data, size);
133
134 /* WWW-Authenticate = 1#challenge
135 Proxy-Authenticate = 1#challenge
136
137 challenge = auth-scheme [ 1*SP ( token68 / #auth-param ) ]
138 auth-scheme = token
139 */
140
141 /* 1#element => *( "," OWS ) ... ; RFC 7230, Section 7 */
142 for (;;) {
143 if (parser.cur >= parser.end || *parser.cur != ',')
144 break;
145 parser.cur++;
146 http_parse_ows(&parser);
147 }
148
149 for (;;) {
150 struct http_auth_challenge chlng;
151
152 i_zero(&chlng);
153
154 /* auth-scheme */
155 if ((ret=http_parse_token(&parser, &chlng.scheme)) <= 0) {
156 if (ret < 0)
157 return -1;
158 break;
159 }
160
161 /* [ 1*SP ... ] */
162 if (parser.cur >= parser.end || *parser.cur != ' ')
163 return 1;
164 parser.cur++;
165 while (parser.cur < parser.end && *parser.cur == ' ')
166 parser.cur++;
167
168 /* ( token68 / #auth-param ) */
169 if ((ret=http_parse_auth_params(&parser, &chlng.params)) <= 0) {
170 if (ret < 0)
171 return -1;
172 if (http_parse_token68(&parser, &chlng.data) < 0)
173 return -1;
174 }
175
176 if (!array_is_created(chlngs))
177 t_array_init(chlngs, 4);
178 array_push_back(chlngs, &chlng);
179
180 /* OWS "," OWS
181 --> also allow empty elements
182 */
183 for (;;) {
184 http_parse_ows(&parser);
185 if (parser.cur >= parser.end || *parser.cur != ',')
186 break;
187 parser.cur++;
188 }
189 }
190
191 if (parser.cur != parser.end)
192 return -1;
193 return 1;
194 }
195
http_auth_parse_credentials(const unsigned char * data,size_t size,struct http_auth_credentials * crdts)196 int http_auth_parse_credentials(const unsigned char *data, size_t size,
197 struct http_auth_credentials *crdts)
198 {
199 struct http_parser parser;
200 int ret;
201
202 http_parser_init(&parser, data, size);
203
204 /* Authorization = credentials
205 Proxy-Authorization = credentials
206
207 credentials = auth-scheme [ 1*SP ( token68 / #auth-param ) ]
208 auth-scheme = token
209 */
210
211 i_zero(crdts);
212
213 /* auth-scheme */
214 if (http_parse_token(&parser, &crdts->scheme) <= 0)
215 return -1;
216
217 /* [ 1*SP ... ] */
218 if (parser.cur >= parser.end || *parser.cur != ' ')
219 return 1;
220 parser.cur++;
221 while (parser.cur < parser.end && *parser.cur == ' ')
222 parser.cur++;
223
224 /* ( token68 / #auth-param ) */
225 if ((ret=http_parse_auth_params(&parser, &crdts->params)) <= 0) {
226 if (ret < 0)
227 return -1;
228 if (http_parse_token68(&parser, &crdts->data) < 0)
229 return -1;
230 }
231
232 if (parser.cur != parser.end)
233 return -1;
234 return 1;
235 }
236
237 /*
238 * Construction
239 */
240
241 static void
http_auth_create_param(string_t * out,const struct http_auth_param * param)242 http_auth_create_param(string_t *out, const struct http_auth_param *param)
243 {
244 const char *p, *first;
245
246 /* auth-param = token BWS "=" BWS ( token / quoted-string ) */
247
248 str_append(out, param->name);
249 str_append_c(out, '=');
250
251 for (p = param->value; *p != '\0' && http_char_is_token(*p); p++);
252
253 if ( *p != '\0' ) {
254 str_append_c(out, '"');
255 p = first = param->value;
256 while (*p != '\0') {
257 if (*p == '\\' || *p == '"') {
258 str_append_data(out, first, p-first);
259 str_append_c(out, '\\');
260 first = p;
261 }
262 p++;
263 }
264 str_append_data(out, first, p-first);
265 str_append_c(out, '"');
266 } else {
267 str_append(out, param->value);
268 }
269 }
270
271 static void
http_auth_create_params(string_t * out,const ARRAY_TYPE (http_auth_param)* params)272 http_auth_create_params(string_t *out,
273 const ARRAY_TYPE(http_auth_param) *params)
274 {
275 const struct http_auth_param *prms;
276 unsigned int count, i;
277
278 if (!array_is_created(params))
279 return;
280
281 prms = array_get(params, &count);
282 for (i = 0; i < count; i++) {
283 if (i > 0)
284 str_append(out, ", ");
285 http_auth_create_param(out, &prms[i]);
286 }
287 }
288
http_auth_check_token68(const char * data)289 static void http_auth_check_token68(const char *data)
290 {
291 const char *p = data;
292
293 /* Make sure we're not working with nonsense. */
294 i_assert(http_char_is_token68(*p));
295 for (p++; *p != '\0' && *p != '='; p++)
296 i_assert(http_char_is_token68(*p));
297 for (; *p != '\0'; p++)
298 i_assert(*p == '=');
299 }
300
http_auth_create_challenge(string_t * out,const struct http_auth_challenge * chlng)301 void http_auth_create_challenge(string_t *out,
302 const struct http_auth_challenge *chlng)
303 {
304 /* challenge = auth-scheme [ 1*SP ( token68 / #auth-param ) ]
305 auth-scheme = token
306 */
307
308 /* auth-scheme */
309 str_append(out, chlng->scheme);
310
311 if (chlng->data != NULL) {
312 /* SP token68 */
313 http_auth_check_token68(chlng->data);
314 str_append_c(out, ' ');
315 str_append(out, chlng->data);
316
317 } else {
318 /* SP #auth-param */
319 str_append_c(out, ' ');
320 http_auth_create_params(out, &chlng->params);
321 }
322 }
323
http_auth_create_challenges(string_t * out,const ARRAY_TYPE (http_auth_challenge)* chlngs)324 void http_auth_create_challenges(string_t *out,
325 const ARRAY_TYPE(http_auth_challenge) *chlngs)
326 {
327 const struct http_auth_challenge *chlgs;
328 unsigned int count, i;
329
330 /* WWW-Authenticate = 1#challenge
331 Proxy-Authenticate = 1#challenge
332 */
333 chlgs = array_get(chlngs, &count);
334 for (i = 0; i < count; i++) {
335 if (i > 0)
336 str_append(out, ", ");
337 http_auth_create_challenge(out, &chlgs[i]);
338 }
339 }
340
http_auth_create_credentials(string_t * out,const struct http_auth_credentials * crdts)341 void http_auth_create_credentials(string_t *out,
342 const struct http_auth_credentials *crdts)
343 {
344 /* Authorization = credentials
345 Proxy-Authorization = credentials
346
347 credentials = auth-scheme [ 1*SP ( token68 / #auth-param ) ]
348 auth-scheme = token
349 */
350
351 /* auth-scheme */
352 str_append(out, crdts->scheme);
353
354 if (crdts->data != NULL) {
355 /* SP token68 */
356 http_auth_check_token68(crdts->data);
357 str_append_c(out, ' ');
358 str_append(out, crdts->data);
359
360 } else {
361 /* SP #auth-param */
362 str_append_c(out, ' ');
363 http_auth_create_params(out, &crdts->params);
364 }
365 }
366
367 /*
368 * Manipulation
369 */
370
371 static void
http_auth_params_clone(pool_t pool,ARRAY_TYPE (http_auth_param)* dst,const ARRAY_TYPE (http_auth_param)* src)372 http_auth_params_clone(pool_t pool,
373 ARRAY_TYPE(http_auth_param) *dst,
374 const ARRAY_TYPE(http_auth_param) *src)
375 {
376 const struct http_auth_param *sparam;
377
378 if (!array_is_created(src))
379 return;
380
381 p_array_init(dst, pool, 4);
382 array_foreach(src, sparam) {
383 struct http_auth_param nparam;
384
385 i_zero(&nparam);
386 nparam.name = p_strdup(pool, sparam->name);
387 nparam.value = p_strdup(pool, sparam->value);
388
389 array_push_back(dst, &nparam);
390 }
391 }
392
http_auth_challenge_copy(pool_t pool,struct http_auth_challenge * dst,const struct http_auth_challenge * src)393 void http_auth_challenge_copy(pool_t pool,
394 struct http_auth_challenge *dst,
395 const struct http_auth_challenge *src)
396 {
397 dst->scheme = p_strdup(pool, src->scheme);
398 if (src->data != NULL)
399 dst->data = p_strdup(pool, src->data);
400 else
401 http_auth_params_clone(pool, &dst->params, &src->params);
402 }
403
404 struct http_auth_challenge *
http_auth_challenge_clone(pool_t pool,const struct http_auth_challenge * src)405 http_auth_challenge_clone(pool_t pool,
406 const struct http_auth_challenge *src)
407 {
408 struct http_auth_challenge *new;
409
410 new = p_new(pool, struct http_auth_challenge, 1);
411 http_auth_challenge_copy(pool, new, src);
412
413 return new;
414 }
415
http_auth_credentials_copy(pool_t pool,struct http_auth_credentials * dst,const struct http_auth_credentials * src)416 void http_auth_credentials_copy(pool_t pool,
417 struct http_auth_credentials *dst,
418 const struct http_auth_credentials *src)
419 {
420 dst->scheme = p_strdup(pool, src->scheme);
421 if (src->data != NULL)
422 dst->data = p_strdup(pool, src->data);
423 else
424 http_auth_params_clone(pool, &dst->params, &src->params);
425 }
426
427 struct http_auth_credentials *
http_auth_credentials_clone(pool_t pool,const struct http_auth_credentials * src)428 http_auth_credentials_clone(pool_t pool,
429 const struct http_auth_credentials *src)
430 {
431 struct http_auth_credentials *new;
432
433 new = p_new(pool, struct http_auth_credentials, 1);
434 http_auth_credentials_copy(pool, new, src);
435
436 return new;
437 }
438
439 /*
440 * Simple schemes
441 */
442
http_auth_basic_challenge_init(struct http_auth_challenge * chlng,const char * realm)443 void http_auth_basic_challenge_init(struct http_auth_challenge *chlng,
444 const char *realm)
445 {
446 i_zero(chlng);
447 chlng->scheme = "Basic";
448 if (realm != NULL) {
449 struct http_auth_param param;
450
451 i_zero(¶m);
452 param.name = "realm";
453 param.value = t_strdup(realm);
454
455 t_array_init(&chlng->params, 1);
456 array_push_back(&chlng->params, ¶m);
457 }
458 }
459
http_auth_basic_credentials_init(struct http_auth_credentials * crdts,const char * username,const char * password)460 void http_auth_basic_credentials_init(struct http_auth_credentials *crdts,
461 const char *username, const char *password)
462 {
463 const char *auth;
464 string_t *data;
465
466 i_assert(username != NULL && *username != '\0');
467 i_assert(strchr(username, ':') == NULL);
468
469 data = t_str_new(64);
470 auth = t_strconcat(username, ":", password, NULL);
471 base64_encode(auth, strlen(auth), data);
472
473 i_zero(crdts);
474 crdts->scheme = "Basic";
475 crdts->data = str_c(data);
476 }
477