ksgi

Форк
0
/
httpauth.c 
451 строка · 10.9 Кб
1
/*	$Id$ */
2
/*
3
 * Copyright (c) 2015, 2017 Kristaps Dzonsons <kristaps@bsd.lv>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
#include "config.h"
18

19
#include <assert.h>
20
#include <ctype.h>
21
#include <errno.h>
22
#include <limits.h>
23
#include <stdarg.h>
24
#include <stdio.h>
25
#include <stdint.h>
26
#include <stdlib.h>
27
#include <string.h>
28

29
#include "kcgi.h"
30
#include "extern.h"
31

32
/*
33
 * A sequence of bytes parsed from the input stream.
34
 */
35
struct	pdigbuf {
36
	const char	*pos;
37
	size_t		 sz;
38
};
39

40
/*
41
 * Pointers to the components of an HTTP digest scheme in the input
42
 * stream.
43
 * These are empty strings (sz == 0) if undescribed.
44
 */
45
struct	pdigest {
46
	enum khttpalg	 alg;
47
	enum khttpqop	 qop;
48
	struct pdigbuf	 user;
49
	struct pdigbuf	 uri;
50
	struct pdigbuf	 realm;
51
	struct pdigbuf	 nonce;
52
	struct pdigbuf	 cnonce;
53
	struct pdigbuf	 response;
54
	struct pdigbuf	 opaque;
55
	uint32_t	 count;
56
};
57

58
/*
59
 * Hash algorithm identifiers.
60
 */
61
static	const char *const httpalgs[KHTTPALG__MAX] = {
62
	"MD5", /* KHTTPALG_MD5 */
63
	"MD5-sess" /* KHTTPALG_MD5_SESS */
64
};
65

66
/*
67
 * Quality-of-protection identifiers.
68
 */
69
static	const char *const httpqops[KHTTPQOP__MAX] = {
70
	NULL, /* KHTTPQOP_NONE */
71
	"auth", /* KHTTPQOP_AUTH */
72
	"auth-int" /* KHTTPQOP_AUTH_INT */
73
};
74

75
/*
76
 * Parses the next token part---a sequence of non-whitespace (and
77
 * non-"delim", with '\0' being the noop delimeter) characters---after
78
 * skipping leading whitespace, then positions "next" to be at the next
79
 * token (after any whitespace).
80
 * Returns the start of the token and its size.
81
 */
82
static const char *
83
kauth_nexttok(const char **next, char delim, size_t *sz)
84
{
85
	const char	*cp;
86

87
	/* Skip past leading white-space. */
88

89
	while (isspace((unsigned char)**next))
90
		(*next)++;
91

92
	/* Scan til whitespace or delimiter. */
93

94
	cp = *next;
95
	while ('\0' != **next && delim != **next && 
96
	       ! isspace((unsigned char)**next))
97
		(*next)++;
98
	*sz = *next - cp;
99

100
	/* Scan after delimiter, if applicable. */
101

102
	if ('\0' != delim && delim == **next) 
103
		(*next)++;
104

105
	/* Scan til next non-whitespace. */
106

107
	while (isspace((unsigned char)**next))
108
		(*next)++;
109

110
	return(cp);
111
}
112

113
/*
114
 * Parse a quoted-pair (or non-quoted pair) from the string "cp".
115
 * Puts the location of the next token back into "cp" and fills the
116
 * pointer in "val" (if non-NULL) and its size in "sz".
117
 */
118
static void
119
kauth_nextvalue(struct pdigbuf *val, const char **cp)
120
{
121
	int	 quot;
122

123
	if ('\0' == **cp)
124
		return;
125

126
	if ((quot = ('"' == **cp)))
127
		(*cp)++;
128

129
	if (NULL != val) {
130
		val->pos = *cp;
131
		val->sz = 0;
132
	}
133

134
	for ( ; '\0' != **cp; (*cp)++) {
135
		if (quot && '"' == **cp && '\\' != (*cp)[-1])
136
			break;
137
		else if ( ! quot && isspace((unsigned char)**cp))
138
			break;
139
		else if ( ! quot && ',' == **cp)
140
			break;
141
		if (NULL != val)
142
			val->sz++;
143
	}
144

145
	if (quot && '"' == **cp)
146
		(*cp)++;
147
	while (isspace((unsigned char)**cp))
148
		(*cp)++;
149
	if (',' == **cp)
150
		(*cp)++;
151
	while (isspace((unsigned char)**cp))
152
		(*cp)++;
153
}
154

155
/*
156
 * Parse a token.
157
 * We don't strictly follow RFC 2615's TOKEN specification, which says
158
 * that tokens can be followed by any separator.
159
 * We only use commas as separators.
160
 */
161
static void
162
kauth_nexttoken(size_t *val, const char **cp,
163
	const char *const *vals, size_t valsz)
164
{
165
	struct pdigbuf	 buf;
166

167
	memset(&buf, 0, sizeof(struct pdigbuf));
168
	kauth_nextvalue(&buf, cp);
169

170
	for (*val = 0; *val < valsz; (*val)++) {
171
		if (NULL == vals[*val])
172
			continue;
173
		if (buf.sz != strlen(vals[*val]))
174
			continue;
175
		if (0 == strncasecmp(buf.pos, vals[*val], buf.sz)) 
176
			return;
177
	}
178
}
179

180
static void
181
kauth_alg(enum khttpalg *val, const char **cp)
182
{
183
	size_t	 i;
184

185
	kauth_nexttoken(&i, cp, httpalgs, KHTTPALG__MAX);
186
	*val = i;
187
}
188

189
static void
190
kauth_qop(enum khttpqop *val, const char **cp)
191
{
192
	size_t	 i;
193

194
	kauth_nexttoken(&i, cp, httpqops, KHTTPQOP__MAX);
195
	*val = i;
196
}
197

198
/*
199
 * Parse the 8-byte nonce count ("nc") value.
200
 * See RFC 7616 section 3.4.
201
 */
202
static void
203
kauth_count(uint32_t *count, const char **cp)
204
{
205
	struct pdigbuf	 buf;
206
	char		 numbuf[9];
207
	char		*ep;
208
	unsigned long long ulval;
209

210
	*count = 0;
211

212
	memset(&buf, 0, sizeof(struct pdigbuf));
213

214
	/* According to the RFC, this is 8 bytes long. */
215

216
	kauth_nextvalue(&buf, cp);
217
	if (buf.sz != 8)
218
		return;
219

220
	/* Copy into a NUL-terminated buffer. */
221

222
	memcpy(numbuf, buf.pos, buf.sz);
223
	numbuf[buf.sz] = '\0';
224

225
	/* 
226
	 * Convert from the hex string into a number.
227
	 * There are a maximum of 8 possible digits in this hex value,
228
	 * so we'll have no more than 0xffffffff.
229
	 * Default to zero if there are errors.
230
	 * Note: UINT32_MAX < long long int maximum.
231
	 */
232

233
	errno = 0;
234
	ulval = strtoull(numbuf, &ep, 16);
235
	if (numbuf[0] == '\0' || *ep != '\0')
236
		*count = 0;
237
	else if (errno == ERANGE && ulval == ULLONG_MAX)
238
		*count = 0;
239
	else if (ulval > UINT32_MAX)
240
		*count = 0;
241
	else
242
		*count = ulval;
243
}
244

245
static int
246
kauth_eq(const char *p, const char *start, size_t sz, size_t want)
247
{
248

249
	return(want == sz && 0 == strncasecmp(start, p, want));
250
}
251

252
/*
253
 * The "bearer" or "basic" authentication is just that word followed by
254
 * opaque text.  HTTP "basic" authentication has a well-defined username
255
 * and password structure, but "bearer" is opaque.  These are both
256
 * handled by the calling context: we don't do any validation here.
257
 */
258
static void
259
khttpbasic_input(int fd, const char *cp, enum kauth auth)
260
{
261
	int		 authorised;
262

263
	fullwrite(fd, &auth, sizeof(enum kauth));
264
	while (isspace((unsigned char)*cp))
265
		cp++;
266

267
	if ('\0' == *cp) {
268
		authorised = 0;
269
		fullwrite(fd, &authorised, sizeof(int));
270
		return;
271
	}
272

273
	authorised = 1;
274
	fullwrite(fd, &authorised, sizeof(int));
275
	fullwriteword(fd, cp);
276
}
277

278
/*
279
 * Parse HTTP ``Digest'' authentication tokens from the NUL-terminated
280
 * string, which can be NULL or malformed.
281
 */
282
static int
283
khttpdigest_input(int fd, const char *cp)
284
{
285
	enum kauth	 auth;
286
	const char	*start;
287
	int		 rc, authorised;
288
	size_t		 sz;
289
	struct pdigest	 d;
290

291
	auth = KAUTH_DIGEST;
292
	fullwrite(fd, &auth, sizeof(enum kauth));
293
	memset(&d, 0, sizeof(struct pdigest));
294

295
	for (rc = 1; 1 == rc && '\0' != *cp; ) {
296
		start = kauth_nexttok(&cp,  '=', &sz);
297
		if (kauth_eq("username", start, sz, 8))
298
			kauth_nextvalue(&d.user, &cp);
299
		else if (kauth_eq("realm", start, sz, 5))
300
			kauth_nextvalue(&d.realm, &cp);
301
		else if (kauth_eq("nonce", start, sz, 5))
302
			kauth_nextvalue(&d.nonce, &cp);
303
		else if (kauth_eq("cnonce", start, sz, 6))
304
			kauth_nextvalue(&d.cnonce, &cp);
305
		else if (kauth_eq("response", start, sz, 8))
306
			kauth_nextvalue(&d.response, &cp);
307
		else if (kauth_eq("uri", start, sz, 3))
308
			kauth_nextvalue(&d.uri, &cp);
309
		else if (kauth_eq("algorithm", start, sz, 9))
310
			kauth_alg(&d.alg, &cp);
311
		else if (kauth_eq("qop", start, sz, 3))
312
			kauth_qop(&d.qop, &cp);
313
		else if (kauth_eq("nc", start, sz, 2))
314
			kauth_count(&d.count, &cp);
315
		else if (kauth_eq("opaque", start, sz, 6))
316
			kauth_nextvalue(&d.opaque, &cp);
317
		else
318
			kauth_nextvalue(NULL, &cp);
319
	}
320

321
	/* Minimum requirements. */
322
	authorised = 
323
		0 != d.user.sz &&
324
		0 != d.realm.sz &&
325
		0 != d.nonce.sz &&
326
		0 != d.response.sz &&
327
		0 != d.uri.sz;
328

329
	/* Additional requirements: MD5-sess. */
330
	if (authorised && KHTTPALG_MD5_SESS == d.alg) 
331
		authorised = 0 != d.cnonce.sz;
332

333
	/* Additional requirements: qop. */
334
	if (authorised && 
335
		(KHTTPQOP_AUTH == d.qop ||
336
		 KHTTPQOP_AUTH_INT == d.qop))
337
		authorised = 
338
			0 != d.count &&
339
			0 != d.cnonce.sz;
340

341
	fullwrite(fd, &authorised, sizeof(int));
342

343
	if ( ! authorised)
344
		return(0);
345

346
	fullwrite(fd, &d.alg, sizeof(enum khttpalg));
347
	fullwrite(fd, &d.qop, sizeof(enum khttpqop));
348
	fullwrite(fd, &d.user.sz, sizeof(size_t));
349
	fullwrite(fd, d.user.pos, d.user.sz);
350
	fullwrite(fd, &d.uri.sz, sizeof(size_t));
351
	fullwrite(fd, d.uri.pos, d.uri.sz);
352
	fullwrite(fd, &d.realm.sz, sizeof(size_t));
353
	fullwrite(fd, d.realm.pos, d.realm.sz);
354
	fullwrite(fd, &d.nonce.sz, sizeof(size_t));
355
	fullwrite(fd, d.nonce.pos, d.nonce.sz);
356
	fullwrite(fd, &d.cnonce.sz, sizeof(size_t));
357
	fullwrite(fd, d.cnonce.pos, d.cnonce.sz);
358
	fullwrite(fd, &d.response.sz, sizeof(size_t));
359
	fullwrite(fd, d.response.pos, d.response.sz);
360
	fullwrite(fd, &d.count, sizeof(uint32_t));
361
	fullwrite(fd, &d.opaque.sz, sizeof(size_t));
362
	fullwrite(fd, d.opaque.pos, d.opaque.sz);
363

364
	/* Do we need to MD5-hash our contents? */
365
	return(KHTTPQOP_AUTH_INT == d.qop);
366
}
367

368
enum kcgi_err
369
kworker_auth_parent(int fd, struct khttpauth *auth)
370
{
371
	enum kcgi_err	 ke;
372

373
	if (fullread(fd, &auth->type, sizeof(enum kauth), 0, &ke) < 0)
374
		return ke;
375

376
	switch (auth->type) {
377
	case KAUTH_DIGEST:
378
		if (fullread(fd, &auth->authorised, sizeof(int), 0, &ke) < 0)
379
			return ke;
380
		if (!auth->authorised)
381
			break;
382
		if (fullread(fd, &auth->d.digest.alg, sizeof(enum khttpalg), 0, &ke) < 0)
383
			return ke;
384
		if (fullread(fd, &auth->d.digest.qop, sizeof(enum khttpqop), 0, &ke) < 0)
385
			return ke;
386
		if ((ke = fullreadword(fd, &auth->d.digest.user)) != KCGI_OK)
387
			return ke;
388
		if ((ke = fullreadword(fd, &auth->d.digest.uri)) != KCGI_OK)
389
			return ke;
390
		if ((ke = fullreadword(fd, &auth->d.digest.realm)) != KCGI_OK)
391
			return ke;
392
		if ((ke = fullreadword(fd, &auth->d.digest.nonce)) != KCGI_OK)
393
			return ke;
394
		if ((ke = fullreadword(fd, &auth->d.digest.cnonce)) != KCGI_OK)
395
			return ke;
396
		if ((ke = fullreadword(fd, &auth->d.digest.response)) != KCGI_OK)
397
			return ke;
398
		if (fullread(fd, &auth->d.digest.count, sizeof(uint32_t), 0, &ke) < 0)
399
			return ke;
400
		if ((ke = fullreadword(fd, &auth->d.digest.opaque)) != KCGI_OK)
401
			return ke;
402
		break;
403
	case KAUTH_BASIC:
404
	case KAUTH_BEARER:
405
		if (fullread(fd, &auth->authorised, sizeof(int), 0, &ke) < 0)
406
			return ke;
407
		if (!auth->authorised)
408
			break;
409
		if ((ke = fullreadword(fd, &auth->d.basic.response)) != KCGI_OK)
410
			return ke;
411
		break;
412
	default:
413
		break;
414
	}
415

416
	return KCGI_OK;
417
}
418

419
/*
420
 * Parse the "basic", "digest", or "bearer" authorisation from the request.
421
 * We return non-zero if the body of the request needs to be MD5-hashed,
422
 * i.e., if we have auth-int digest QOP.
423
 */
424
int
425
kworker_auth_child(int fd, const char *cp)
426
{
427
	const char	*start;
428
	size_t	 	 sz;
429
	enum kauth	 auth;
430

431
	if (cp == NULL || *cp == '\0') {
432
		auth = KAUTH_NONE;
433
		fullwrite(fd, &auth, sizeof(enum kauth));
434
		return 0;
435
	}
436

437
	start = kauth_nexttok(&cp, '\0', &sz);
438

439
	if (sz == 6 && strncasecmp(start, "bearer", sz) == 0) {
440
		khttpbasic_input(fd, cp, KAUTH_BEARER);
441
		return 0;
442
	} else if (sz == 5 && strncasecmp(start, "basic", sz) == 0) {
443
		khttpbasic_input(fd, cp, KAUTH_BASIC);
444
		return 0;
445
	} else if (sz == 6 && strncasecmp(start, "digest", sz) == 0)
446
		return khttpdigest_input(fd, cp);
447

448
	auth = KAUTH_UNKNOWN;
449
	fullwrite(fd, &auth, sizeof(enum kauth));
450
	return 0;
451
}
452

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.