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(&param);
100 	while ((ret=http_parse_auth_param
101 		(parser, &param.name, &param.value)) > 0) {
102 		if (!array_is_created(params))
103 			t_array_init(params, 4);
104 		array_push_back(params, &param);
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(&param);
452 		param.name = "realm";
453 		param.value = t_strdup(realm);
454 
455 		t_array_init(&chlng->params, 1);
456 		array_push_back(&chlng->params, &param);
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