Mercurial > hg > nginx-quic
annotate src/http/modules/ngx_http_auth_request_module.c @ 8880:d26db4f82d7d
All known output headers can be linked lists now.
The h->next pointer properly provided as NULL in all cases where known
output headers are added.
Note that there are 3rd party modules which might not do this, and it
might be risky to rely on this for arbitrary headers.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Mon, 30 May 2022 21:25:45 +0300 |
parents | f01ab2dbcfdc |
children | 8272c823a7d0 |
rev | line source |
---|---|
5329 | 1 |
2 /* | |
3 * Copyright (C) Maxim Dounin | |
4 * Copyright (C) Nginx, Inc. | |
5 */ | |
6 | |
7 | |
8 #include <ngx_config.h> | |
9 #include <ngx_core.h> | |
10 #include <ngx_http.h> | |
11 | |
12 | |
13 typedef struct { | |
14 ngx_str_t uri; | |
15 ngx_array_t *vars; | |
16 } ngx_http_auth_request_conf_t; | |
17 | |
18 | |
19 typedef struct { | |
20 ngx_uint_t done; | |
21 ngx_uint_t status; | |
22 ngx_http_request_t *subrequest; | |
23 } ngx_http_auth_request_ctx_t; | |
24 | |
25 | |
26 typedef struct { | |
27 ngx_int_t index; | |
28 ngx_http_complex_value_t value; | |
29 ngx_http_set_variable_pt set_handler; | |
30 } ngx_http_auth_request_variable_t; | |
31 | |
32 | |
33 static ngx_int_t ngx_http_auth_request_handler(ngx_http_request_t *r); | |
34 static ngx_int_t ngx_http_auth_request_done(ngx_http_request_t *r, | |
35 void *data, ngx_int_t rc); | |
36 static ngx_int_t ngx_http_auth_request_set_variables(ngx_http_request_t *r, | |
37 ngx_http_auth_request_conf_t *arcf, ngx_http_auth_request_ctx_t *ctx); | |
38 static ngx_int_t ngx_http_auth_request_variable(ngx_http_request_t *r, | |
39 ngx_http_variable_value_t *v, uintptr_t data); | |
40 static void *ngx_http_auth_request_create_conf(ngx_conf_t *cf); | |
41 static char *ngx_http_auth_request_merge_conf(ngx_conf_t *cf, | |
42 void *parent, void *child); | |
43 static ngx_int_t ngx_http_auth_request_init(ngx_conf_t *cf); | |
44 static char *ngx_http_auth_request(ngx_conf_t *cf, ngx_command_t *cmd, | |
45 void *conf); | |
46 static char *ngx_http_auth_request_set(ngx_conf_t *cf, ngx_command_t *cmd, | |
47 void *conf); | |
48 | |
49 | |
50 static ngx_command_t ngx_http_auth_request_commands[] = { | |
51 | |
52 { ngx_string("auth_request"), | |
53 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
54 ngx_http_auth_request, | |
55 NGX_HTTP_LOC_CONF_OFFSET, | |
56 0, | |
57 NULL }, | |
58 | |
59 { ngx_string("auth_request_set"), | |
60 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, | |
61 ngx_http_auth_request_set, | |
62 NGX_HTTP_LOC_CONF_OFFSET, | |
63 0, | |
64 NULL }, | |
65 | |
66 ngx_null_command | |
67 }; | |
68 | |
69 | |
70 static ngx_http_module_t ngx_http_auth_request_module_ctx = { | |
71 NULL, /* preconfiguration */ | |
72 ngx_http_auth_request_init, /* postconfiguration */ | |
73 | |
74 NULL, /* create main configuration */ | |
75 NULL, /* init main configuration */ | |
76 | |
77 NULL, /* create server configuration */ | |
78 NULL, /* merge server configuration */ | |
79 | |
80 ngx_http_auth_request_create_conf, /* create location configuration */ | |
81 ngx_http_auth_request_merge_conf /* merge location configuration */ | |
82 }; | |
83 | |
84 | |
85 ngx_module_t ngx_http_auth_request_module = { | |
86 NGX_MODULE_V1, | |
87 &ngx_http_auth_request_module_ctx, /* module context */ | |
88 ngx_http_auth_request_commands, /* module directives */ | |
89 NGX_HTTP_MODULE, /* module type */ | |
90 NULL, /* init master */ | |
91 NULL, /* init module */ | |
92 NULL, /* init process */ | |
93 NULL, /* init thread */ | |
94 NULL, /* exit thread */ | |
95 NULL, /* exit process */ | |
96 NULL, /* exit master */ | |
97 NGX_MODULE_V1_PADDING | |
98 }; | |
99 | |
100 | |
101 static ngx_int_t | |
102 ngx_http_auth_request_handler(ngx_http_request_t *r) | |
103 { | |
104 ngx_table_elt_t *h, *ho; | |
105 ngx_http_request_t *sr; | |
106 ngx_http_post_subrequest_t *ps; | |
107 ngx_http_auth_request_ctx_t *ctx; | |
108 ngx_http_auth_request_conf_t *arcf; | |
109 | |
110 arcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_request_module); | |
111 | |
112 if (arcf->uri.len == 0) { | |
113 return NGX_DECLINED; | |
114 } | |
115 | |
116 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
117 "auth request handler"); | |
118 | |
119 ctx = ngx_http_get_module_ctx(r, ngx_http_auth_request_module); | |
120 | |
121 if (ctx != NULL) { | |
122 if (!ctx->done) { | |
123 return NGX_AGAIN; | |
124 } | |
125 | |
126 /* | |
127 * as soon as we are done - explicitly set variables to make | |
128 * sure they will be available after internal redirects | |
129 */ | |
130 | |
131 if (ngx_http_auth_request_set_variables(r, arcf, ctx) != NGX_OK) { | |
132 return NGX_ERROR; | |
133 } | |
134 | |
135 /* return appropriate status */ | |
136 | |
137 if (ctx->status == NGX_HTTP_FORBIDDEN) { | |
138 return ctx->status; | |
139 } | |
140 | |
141 if (ctx->status == NGX_HTTP_UNAUTHORIZED) { | |
142 sr = ctx->subrequest; | |
143 | |
144 h = sr->headers_out.www_authenticate; | |
145 | |
146 if (!h && sr->upstream) { | |
147 h = sr->upstream->headers_in.www_authenticate; | |
148 } | |
149 | |
150 if (h) { | |
151 ho = ngx_list_push(&r->headers_out.headers); | |
152 if (ho == NULL) { | |
153 return NGX_ERROR; | |
154 } | |
155 | |
156 *ho = *h; | |
8880
d26db4f82d7d
All known output headers can be linked lists now.
Maxim Dounin <mdounin@mdounin.ru>
parents:
6480
diff
changeset
|
157 ho->next = NULL; |
5329 | 158 |
159 r->headers_out.www_authenticate = ho; | |
160 } | |
161 | |
162 return ctx->status; | |
163 } | |
164 | |
165 if (ctx->status >= NGX_HTTP_OK | |
166 && ctx->status < NGX_HTTP_SPECIAL_RESPONSE) | |
167 { | |
168 return NGX_OK; | |
169 } | |
170 | |
171 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
6480 | 172 "auth request unexpected status: %ui", ctx->status); |
5329 | 173 |
174 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
175 } | |
176 | |
177 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_request_ctx_t)); | |
178 if (ctx == NULL) { | |
179 return NGX_ERROR; | |
180 } | |
181 | |
182 ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t)); | |
183 if (ps == NULL) { | |
184 return NGX_ERROR; | |
185 } | |
186 | |
187 ps->handler = ngx_http_auth_request_done; | |
188 ps->data = ctx; | |
189 | |
190 if (ngx_http_subrequest(r, &arcf->uri, NULL, &sr, ps, | |
191 NGX_HTTP_SUBREQUEST_WAITED) | |
192 != NGX_OK) | |
193 { | |
194 return NGX_ERROR; | |
195 } | |
196 | |
197 /* | |
198 * allocate fake request body to avoid attempts to read it and to make | |
199 * sure real body file (if already read) won't be closed by upstream | |
200 */ | |
201 | |
202 sr->request_body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); | |
203 if (sr->request_body == NULL) { | |
204 return NGX_ERROR; | |
205 } | |
206 | |
207 sr->header_only = 1; | |
208 | |
209 ctx->subrequest = sr; | |
210 | |
211 ngx_http_set_ctx(r, ctx, ngx_http_auth_request_module); | |
212 | |
213 return NGX_AGAIN; | |
214 } | |
215 | |
216 | |
217 static ngx_int_t | |
218 ngx_http_auth_request_done(ngx_http_request_t *r, void *data, ngx_int_t rc) | |
219 { | |
220 ngx_http_auth_request_ctx_t *ctx = data; | |
221 | |
222 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
6480 | 223 "auth request done s:%ui", r->headers_out.status); |
5329 | 224 |
225 ctx->done = 1; | |
226 ctx->status = r->headers_out.status; | |
227 | |
228 return rc; | |
229 } | |
230 | |
231 | |
232 static ngx_int_t | |
233 ngx_http_auth_request_set_variables(ngx_http_request_t *r, | |
234 ngx_http_auth_request_conf_t *arcf, ngx_http_auth_request_ctx_t *ctx) | |
235 { | |
236 ngx_str_t val; | |
237 ngx_http_variable_t *v; | |
238 ngx_http_variable_value_t *vv; | |
239 ngx_http_auth_request_variable_t *av, *last; | |
240 ngx_http_core_main_conf_t *cmcf; | |
241 | |
242 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
243 "auth request set variables"); | |
244 | |
245 if (arcf->vars == NULL) { | |
246 return NGX_OK; | |
247 } | |
248 | |
249 cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); | |
250 v = cmcf->variables.elts; | |
251 | |
252 av = arcf->vars->elts; | |
253 last = av + arcf->vars->nelts; | |
254 | |
255 while (av < last) { | |
256 /* | |
257 * explicitly set new value to make sure it will be available after | |
258 * internal redirects | |
259 */ | |
260 | |
261 vv = &r->variables[av->index]; | |
262 | |
263 if (ngx_http_complex_value(ctx->subrequest, &av->value, &val) | |
264 != NGX_OK) | |
265 { | |
266 return NGX_ERROR; | |
267 } | |
268 | |
269 vv->valid = 1; | |
270 vv->not_found = 0; | |
271 vv->data = val.data; | |
272 vv->len = val.len; | |
273 | |
274 if (av->set_handler) { | |
275 /* | |
276 * set_handler only available in cmcf->variables_keys, so we store | |
277 * it explicitly | |
278 */ | |
279 | |
280 av->set_handler(r, vv, v[av->index].data); | |
281 } | |
282 | |
283 av++; | |
284 } | |
285 | |
286 return NGX_OK; | |
287 } | |
288 | |
289 | |
290 static ngx_int_t | |
291 ngx_http_auth_request_variable(ngx_http_request_t *r, | |
292 ngx_http_variable_value_t *v, uintptr_t data) | |
293 { | |
294 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
295 "auth request variable"); | |
296 | |
297 v->not_found = 1; | |
298 | |
299 return NGX_OK; | |
300 } | |
301 | |
302 | |
303 static void * | |
304 ngx_http_auth_request_create_conf(ngx_conf_t *cf) | |
305 { | |
306 ngx_http_auth_request_conf_t *conf; | |
307 | |
308 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_request_conf_t)); | |
309 if (conf == NULL) { | |
310 return NULL; | |
311 } | |
312 | |
313 /* | |
314 * set by ngx_pcalloc(): | |
315 * | |
316 * conf->uri = { 0, NULL }; | |
317 */ | |
318 | |
319 conf->vars = NGX_CONF_UNSET_PTR; | |
320 | |
321 return conf; | |
322 } | |
323 | |
324 | |
325 static char * | |
326 ngx_http_auth_request_merge_conf(ngx_conf_t *cf, void *parent, void *child) | |
327 { | |
328 ngx_http_auth_request_conf_t *prev = parent; | |
329 ngx_http_auth_request_conf_t *conf = child; | |
330 | |
331 ngx_conf_merge_str_value(conf->uri, prev->uri, ""); | |
332 ngx_conf_merge_ptr_value(conf->vars, prev->vars, NULL); | |
333 | |
334 return NGX_CONF_OK; | |
335 } | |
336 | |
337 | |
338 static ngx_int_t | |
339 ngx_http_auth_request_init(ngx_conf_t *cf) | |
340 { | |
341 ngx_http_handler_pt *h; | |
342 ngx_http_core_main_conf_t *cmcf; | |
343 | |
344 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); | |
345 | |
346 h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); | |
347 if (h == NULL) { | |
348 return NGX_ERROR; | |
349 } | |
350 | |
351 *h = ngx_http_auth_request_handler; | |
352 | |
353 return NGX_OK; | |
354 } | |
355 | |
356 | |
357 static char * | |
358 ngx_http_auth_request(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
359 { | |
360 ngx_http_auth_request_conf_t *arcf = conf; | |
361 | |
362 ngx_str_t *value; | |
363 | |
364 if (arcf->uri.data != NULL) { | |
365 return "is duplicate"; | |
366 } | |
367 | |
368 value = cf->args->elts; | |
369 | |
370 if (ngx_strcmp(value[1].data, "off") == 0) { | |
371 arcf->uri.len = 0; | |
372 arcf->uri.data = (u_char *) ""; | |
373 | |
374 return NGX_CONF_OK; | |
375 } | |
376 | |
377 arcf->uri = value[1]; | |
378 | |
379 return NGX_CONF_OK; | |
380 } | |
381 | |
382 | |
383 static char * | |
384 ngx_http_auth_request_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
385 { | |
386 ngx_http_auth_request_conf_t *arcf = conf; | |
387 | |
388 ngx_str_t *value; | |
389 ngx_http_variable_t *v; | |
390 ngx_http_auth_request_variable_t *av; | |
391 ngx_http_compile_complex_value_t ccv; | |
392 | |
393 value = cf->args->elts; | |
394 | |
395 if (value[1].data[0] != '$') { | |
396 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
397 "invalid variable name \"%V\"", &value[1]); | |
398 return NGX_CONF_ERROR; | |
399 } | |
400 | |
401 value[1].len--; | |
402 value[1].data++; | |
403 | |
404 if (arcf->vars == NGX_CONF_UNSET_PTR) { | |
405 arcf->vars = ngx_array_create(cf->pool, 1, | |
406 sizeof(ngx_http_auth_request_variable_t)); | |
407 if (arcf->vars == NULL) { | |
408 return NGX_CONF_ERROR; | |
409 } | |
410 } | |
411 | |
412 av = ngx_array_push(arcf->vars); | |
413 if (av == NULL) { | |
414 return NGX_CONF_ERROR; | |
415 } | |
416 | |
417 v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE); | |
418 if (v == NULL) { | |
419 return NGX_CONF_ERROR; | |
420 } | |
421 | |
422 av->index = ngx_http_get_variable_index(cf, &value[1]); | |
423 if (av->index == NGX_ERROR) { | |
424 return NGX_CONF_ERROR; | |
425 } | |
426 | |
427 if (v->get_handler == NULL) { | |
428 v->get_handler = ngx_http_auth_request_variable; | |
429 v->data = (uintptr_t) av; | |
430 } | |
431 | |
432 av->set_handler = v->set_handler; | |
433 | |
434 ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); | |
435 | |
436 ccv.cf = cf; | |
437 ccv.value = &value[2]; | |
438 ccv.complex_value = &av->value; | |
439 | |
440 if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { | |
441 return NGX_CONF_ERROR; | |
442 } | |
443 | |
444 return NGX_CONF_OK; | |
445 } |