Ticket #559: ngx_http_authn_request_module.c

File ngx_http_authn_request_module.c, 11.5 KB (added by David Coutadeur, 12 years ago)

new nginx authentication request module


Content-Disposition: form-data; name="replace"

on

Line 
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
13typedef struct {
14 ngx_str_t uri;
15 ngx_array_t *vars;
16} ngx_http_authn_request_conf_t;
17
18
19typedef struct {
20 ngx_uint_t done;
21 ngx_uint_t status;
22 ngx_http_request_t *subrequest;
23} ngx_http_authn_request_ctx_t;
24
25
26typedef struct {
27 ngx_int_t index;
28 ngx_http_complex_value_t value;
29 ngx_http_set_variable_pt set_handler;
30} ngx_http_authn_request_variable_t;
31
32
33static ngx_int_t ngx_http_authn_request_handler(ngx_http_request_t *r);
34static ngx_int_t ngx_http_authn_request_done(ngx_http_request_t *r,
35 void *data, ngx_int_t rc);
36static ngx_int_t ngx_http_authn_request_set_variables(ngx_http_request_t *r,
37 ngx_http_authn_request_conf_t *arcf, ngx_http_authn_request_ctx_t *ctx);
38static ngx_int_t ngx_http_authn_request_variable(ngx_http_request_t *r,
39 ngx_http_variable_value_t *v, uintptr_t data);
40static void *ngx_http_authn_request_create_conf(ngx_conf_t *cf);
41static char *ngx_http_authn_request_merge_conf(ngx_conf_t *cf,
42 void *parent, void *child);
43static ngx_int_t ngx_http_authn_request_init(ngx_conf_t *cf);
44static char *ngx_http_authn_request(ngx_conf_t *cf, ngx_command_t *cmd,
45 void *conf);
46static char *ngx_http_authn_request_set(ngx_conf_t *cf, ngx_command_t *cmd,
47 void *conf);
48
49
50static ngx_command_t ngx_http_authn_request_commands[] = {
51
52 { ngx_string("authn_request"),
53 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
54 ngx_http_authn_request,
55 NGX_HTTP_LOC_CONF_OFFSET,
56 0,
57 NULL },
58
59 { ngx_string("authn_request_set"),
60 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
61 ngx_http_authn_request_set,
62 NGX_HTTP_LOC_CONF_OFFSET,
63 0,
64 NULL },
65
66 ngx_null_command
67};
68
69
70static ngx_http_module_t ngx_http_authn_request_module_ctx = {
71 NULL, /* preconfiguration */
72 ngx_http_authn_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_authn_request_create_conf, /* create location configuration */
81 ngx_http_authn_request_merge_conf /* merge location configuration */
82};
83
84
85ngx_module_t ngx_http_authn_request_module = {
86 NGX_MODULE_V1,
87 &ngx_http_authn_request_module_ctx, /* module context */
88 ngx_http_authn_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
101static ngx_int_t
102ngx_http_authn_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_authn_request_ctx_t *ctx;
108 ngx_http_authn_request_conf_t *arcf;
109
110 arcf = ngx_http_get_module_loc_conf(r, ngx_http_authn_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_authn_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_authn_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 /* case redirect */
142 if (ctx->status == NGX_HTTP_MOVED_TEMPORARILY) {
143 sr = ctx->subrequest;
144 r->headers_out = sr->headers_out;
145 return ctx->status;
146 }
147
148 if (ctx->status == NGX_HTTP_UNAUTHORIZED) {
149 sr = ctx->subrequest;
150
151 h = sr->headers_out.www_authenticate;
152
153 if (!h && sr->upstream) {
154 h = sr->upstream->headers_in.www_authenticate;
155 }
156
157 if (h) {
158 ho = ngx_list_push(&r->headers_out.headers);
159 if (ho == NULL) {
160 return NGX_ERROR;
161 }
162
163 *ho = *h;
164
165 r->headers_out.www_authenticate = ho;
166 }
167
168 return ctx->status;
169 }
170
171 if (ctx->status >= NGX_HTTP_OK
172 && ctx->status < NGX_HTTP_SPECIAL_RESPONSE)
173 {
174 return NGX_OK;
175 }
176
177 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
178 "auth request unexpected status: %d", ctx->status);
179
180 return NGX_HTTP_INTERNAL_SERVER_ERROR;
181 }
182
183 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_authn_request_ctx_t));
184 if (ctx == NULL) {
185 return NGX_ERROR;
186 }
187
188 ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
189 if (ps == NULL) {
190 return NGX_ERROR;
191 }
192
193 ps->handler = ngx_http_authn_request_done;
194 ps->data = ctx;
195
196 if (ngx_http_subrequest(r, &arcf->uri, NULL, &sr, ps,
197 NGX_HTTP_SUBREQUEST_WAITED)
198 != NGX_OK)
199 {
200 return NGX_ERROR;
201 }
202
203 /*
204 * allocate fake request body to avoid attempts to read it and to make
205 * sure real body file (if already read) won't be closed by upstream
206 */
207
208 sr->request_body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
209 if (sr->request_body == NULL) {
210 return NGX_ERROR;
211 }
212
213 sr->header_only = 1;
214
215 ctx->subrequest = sr;
216
217 ngx_http_set_ctx(r, ctx, ngx_http_authn_request_module);
218
219 return NGX_AGAIN;
220}
221
222
223static ngx_int_t
224ngx_http_authn_request_done(ngx_http_request_t *r, void *data, ngx_int_t rc)
225{
226 ngx_http_authn_request_ctx_t *ctx = data;
227
228 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
229 "auth request done s:%d", r->headers_out.status);
230
231 ctx->done = 1;
232 ctx->status = r->headers_out.status;
233
234 return rc;
235}
236
237
238static ngx_int_t
239ngx_http_authn_request_set_variables(ngx_http_request_t *r,
240 ngx_http_authn_request_conf_t *arcf, ngx_http_authn_request_ctx_t *ctx)
241{
242 ngx_str_t val;
243 ngx_http_variable_t *v;
244 ngx_http_variable_value_t *vv;
245 ngx_http_authn_request_variable_t *av, *last;
246 ngx_http_core_main_conf_t *cmcf;
247
248 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
249 "auth request set variables");
250
251 if (arcf->vars == NULL) {
252 return NGX_OK;
253 }
254
255 cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
256 v = cmcf->variables.elts;
257
258 av = arcf->vars->elts;
259 last = av + arcf->vars->nelts;
260
261 while (av < last) {
262 /*
263 * explicitly set new value to make sure it will be available after
264 * internal redirects
265 */
266
267 vv = &r->variables[av->index];
268
269 if (ngx_http_complex_value(ctx->subrequest, &av->value, &val)
270 != NGX_OK)
271 {
272 return NGX_ERROR;
273 }
274
275 vv->valid = 1;
276 vv->not_found = 0;
277 vv->data = val.data;
278 vv->len = val.len;
279
280 if (av->set_handler) {
281 /*
282 * set_handler only available in cmcf->variables_keys, so we store
283 * it explicitly
284 */
285
286 av->set_handler(r, vv, v[av->index].data);
287 }
288
289 av++;
290 }
291
292 return NGX_OK;
293}
294
295
296static ngx_int_t
297ngx_http_authn_request_variable(ngx_http_request_t *r,
298 ngx_http_variable_value_t *v, uintptr_t data)
299{
300 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
301 "auth request variable");
302
303 v->not_found = 1;
304
305 return NGX_OK;
306}
307
308
309static void *
310ngx_http_authn_request_create_conf(ngx_conf_t *cf)
311{
312 ngx_http_authn_request_conf_t *conf;
313
314 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_authn_request_conf_t));
315 if (conf == NULL) {
316 return NULL;
317 }
318
319 /*
320 * set by ngx_pcalloc():
321 *
322 * conf->uri = { 0, NULL };
323 */
324
325 conf->vars = NGX_CONF_UNSET_PTR;
326
327 return conf;
328}
329
330
331static char *
332ngx_http_authn_request_merge_conf(ngx_conf_t *cf, void *parent, void *child)
333{
334 ngx_http_authn_request_conf_t *prev = parent;
335 ngx_http_authn_request_conf_t *conf = child;
336
337 ngx_conf_merge_str_value(conf->uri, prev->uri, "");
338 ngx_conf_merge_ptr_value(conf->vars, prev->vars, NULL);
339
340 return NGX_CONF_OK;
341}
342
343
344static ngx_int_t
345ngx_http_authn_request_init(ngx_conf_t *cf)
346{
347 ngx_http_handler_pt *h;
348 ngx_http_core_main_conf_t *cmcf;
349
350 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
351
352 h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
353 if (h == NULL) {
354 return NGX_ERROR;
355 }
356
357 *h = ngx_http_authn_request_handler;
358
359 return NGX_OK;
360}
361
362
363static char *
364ngx_http_authn_request(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
365{
366 ngx_http_authn_request_conf_t *arcf = conf;
367
368 ngx_str_t *value;
369
370 if (arcf->uri.data != NULL) {
371 return "is duplicate";
372 }
373
374 value = cf->args->elts;
375
376 if (ngx_strcmp(value[1].data, "off") == 0) {
377 arcf->uri.len = 0;
378 arcf->uri.data = (u_char *) "";
379
380 return NGX_CONF_OK;
381 }
382
383 arcf->uri = value[1];
384
385 return NGX_CONF_OK;
386}
387
388
389static char *
390ngx_http_authn_request_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
391{
392 ngx_http_authn_request_conf_t *arcf = conf;
393
394 ngx_str_t *value;
395 ngx_http_variable_t *v;
396 ngx_http_authn_request_variable_t *av;
397 ngx_http_compile_complex_value_t ccv;
398
399 value = cf->args->elts;
400
401 if (value[1].data[0] != '$') {
402 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
403 "invalid variable name \"%V\"", &value[1]);
404 return NGX_CONF_ERROR;
405 }
406
407 value[1].len--;
408 value[1].data++;
409
410 if (arcf->vars == NGX_CONF_UNSET_PTR) {
411 arcf->vars = ngx_array_create(cf->pool, 1,
412 sizeof(ngx_http_authn_request_variable_t));
413 if (arcf->vars == NULL) {
414 return NGX_CONF_ERROR;
415 }
416 }
417
418 av = ngx_array_push(arcf->vars);
419 if (av == NULL) {
420 return NGX_CONF_ERROR;
421 }
422
423 v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);
424 if (v == NULL) {
425 return NGX_CONF_ERROR;
426 }
427
428 av->index = ngx_http_get_variable_index(cf, &value[1]);
429 if (av->index == NGX_ERROR) {
430 return NGX_CONF_ERROR;
431 }
432
433 if (v->get_handler == NULL) {
434 v->get_handler = ngx_http_authn_request_variable;
435 v->data = (uintptr_t) av;
436 }
437
438 av->set_handler = v->set_handler;
439
440 ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
441
442 ccv.cf = cf;
443 ccv.value = &value[2];
444 ccv.complex_value = &av->value;
445
446 if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
447 return NGX_CONF_ERROR;
448 }
449
450 return NGX_CONF_OK;
451}