upstream nginx-0.7.43
[nginx.git] / nginx / src / http / modules / ngx_http_auth_basic_module.c
1
2 /*
3  * Copyright (C) Igor Sysoev
4  */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
10
11
12 #define NGX_HTTP_AUTH_BUF_SIZE  2048
13
14
15 typedef struct {
16     ngx_str_t     passwd;
17 } ngx_http_auth_basic_ctx_t;
18
19
20 typedef struct {
21     ngx_str_t     realm;
22     ngx_str_t     user_file;
23     ngx_array_t  *user_file_lengths;
24     ngx_array_t  *user_file_values;
25 } ngx_http_auth_basic_loc_conf_t;
26
27
28 static ngx_int_t ngx_http_auth_basic_handler(ngx_http_request_t *r);
29 static ngx_int_t ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r,
30     ngx_http_auth_basic_ctx_t *ctx, ngx_str_t *passwd, ngx_str_t *realm);
31 static ngx_int_t ngx_http_auth_basic_set_realm(ngx_http_request_t *r,
32     ngx_str_t *realm);
33 static void ngx_http_auth_basic_close(ngx_file_t *file);
34 static void *ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf);
35 static char *ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf,
36     void *parent, void *child);
37 static ngx_int_t ngx_http_auth_basic_init(ngx_conf_t *cf);
38 static char *ngx_http_auth_basic(ngx_conf_t *cf, void *post, void *data);
39 static char *ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd,
40     void *conf);
41
42
43 static ngx_conf_post_handler_pt  ngx_http_auth_basic_p = ngx_http_auth_basic;
44
45 static ngx_command_t  ngx_http_auth_basic_commands[] = {
46
47     { ngx_string("auth_basic"),
48       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
49                         |NGX_CONF_TAKE1,
50       ngx_conf_set_str_slot,
51       NGX_HTTP_LOC_CONF_OFFSET,
52       offsetof(ngx_http_auth_basic_loc_conf_t, realm),
53       &ngx_http_auth_basic_p },
54
55     { ngx_string("auth_basic_user_file"),
56       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
57                         |NGX_CONF_TAKE1,
58       ngx_http_auth_basic_user_file,
59       NGX_HTTP_LOC_CONF_OFFSET,
60       offsetof(ngx_http_auth_basic_loc_conf_t, user_file),
61       NULL },
62
63       ngx_null_command
64 };
65
66
67 static ngx_http_module_t  ngx_http_auth_basic_module_ctx = {
68     NULL,                                  /* preconfiguration */
69     ngx_http_auth_basic_init,              /* postconfiguration */
70
71     NULL,                                  /* create main configuration */
72     NULL,                                  /* init main configuration */
73
74     NULL,                                  /* create server configuration */
75     NULL,                                  /* merge server configuration */
76
77     ngx_http_auth_basic_create_loc_conf,   /* create location configuration */
78     ngx_http_auth_basic_merge_loc_conf     /* merge location configuration */
79 };
80
81
82 ngx_module_t  ngx_http_auth_basic_module = {
83     NGX_MODULE_V1,
84     &ngx_http_auth_basic_module_ctx,       /* module context */
85     ngx_http_auth_basic_commands,          /* module directives */
86     NGX_HTTP_MODULE,                       /* module type */
87     NULL,                                  /* init master */
88     NULL,                                  /* init module */
89     NULL,                                  /* init process */
90     NULL,                                  /* init thread */
91     NULL,                                  /* exit thread */
92     NULL,                                  /* exit process */
93     NULL,                                  /* exit master */
94     NGX_MODULE_V1_PADDING
95 };
96
97
98 static ngx_int_t
99 ngx_http_auth_basic_handler(ngx_http_request_t *r)
100 {
101     off_t                            offset;
102     ssize_t                          n;
103     ngx_fd_t                         fd;
104     ngx_int_t                        rc;
105     ngx_err_t                        err;
106     ngx_str_t                        pwd, user_file;
107     ngx_uint_t                       i, level, login, left, passwd;
108     ngx_file_t                       file;
109     ngx_http_auth_basic_ctx_t       *ctx;
110     ngx_http_auth_basic_loc_conf_t  *alcf;
111     u_char                           buf[NGX_HTTP_AUTH_BUF_SIZE];
112     enum {
113         sw_login,
114         sw_passwd,
115         sw_skip
116     } state;
117
118     alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_basic_module);
119
120     if (alcf->realm.len == 0 || alcf->user_file.len == 0) {
121         return NGX_DECLINED;
122     }
123
124     ctx = ngx_http_get_module_ctx(r, ngx_http_auth_basic_module);
125
126     if (ctx) {
127         return ngx_http_auth_basic_crypt_handler(r, ctx, &ctx->passwd,
128                                                  &alcf->realm);
129     }
130
131     rc = ngx_http_auth_basic_user(r);
132
133     if (rc == NGX_DECLINED) {
134
135         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
136                       "no user/password was provided for basic authentication");
137
138         return ngx_http_auth_basic_set_realm(r, &alcf->realm);
139     }
140
141     if (rc == NGX_ERROR) {
142         return NGX_HTTP_INTERNAL_SERVER_ERROR;
143     }
144
145     if (alcf->user_file_lengths) {
146         if (ngx_http_script_run(r, &user_file, alcf->user_file_lengths->elts, 1,
147                                 alcf->user_file_values->elts)
148             == NULL)
149         {
150             return NGX_ERROR;
151         }
152
153         user_file.data[--user_file.len] = '\0';
154
155     } else {
156         user_file = alcf->user_file;
157     }
158
159     fd = ngx_open_file(user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
160
161     if (fd == NGX_INVALID_FILE) {
162         err = ngx_errno;
163
164         if (err == NGX_ENOENT) {
165             level = NGX_LOG_ERR;
166             rc = NGX_HTTP_FORBIDDEN;
167
168         } else {
169             level = NGX_LOG_CRIT;
170             rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
171         }
172
173         ngx_log_error(level, r->connection->log, err,
174                       ngx_open_file_n " \"%s\" failed", user_file.data);
175
176         return rc;
177     }
178
179     ngx_memzero(&file, sizeof(ngx_file_t));
180
181     file.fd = fd;
182     file.name = user_file;
183     file.log = r->connection->log;
184
185     state = sw_login;
186     passwd = 0;
187     login = 0;
188     left = 0;
189     offset = 0;
190
191     for ( ;; ) {
192         i = left;
193
194         n = ngx_read_file(&file, buf + left, NGX_HTTP_AUTH_BUF_SIZE - left,
195                           offset);
196
197         if (n == NGX_ERROR) {
198             ngx_http_auth_basic_close(&file);
199             return NGX_HTTP_INTERNAL_SERVER_ERROR;
200         }
201
202         if (n == 0) {
203             break;
204         }
205
206         for (i = left; i < left + n; i++) {
207             switch (state) {
208
209             case sw_login:
210                 if (login == 0) {
211
212                     if (buf[i] == '#' || buf[i] == CR) {
213                         state = sw_skip;
214                         break;
215                     }
216
217                     if (buf[i] == LF) {
218                         break;
219                     }
220                 }
221
222                 if (buf[i] != r->headers_in.user.data[login]) {
223                     state = sw_skip;
224                     break;
225                 }
226
227                 if (login == r->headers_in.user.len) {
228                     state = sw_passwd;
229                     passwd = i + 1;
230                 }
231
232                 login++;
233
234                 break;
235
236             case sw_passwd:
237                 if (buf[i] == LF || buf[i] == CR || buf[i] == ':') {
238                     buf[i] = '\0';
239
240                     ngx_http_auth_basic_close(&file);
241
242                     pwd.len = i - passwd;
243                     pwd.data = &buf[passwd];
244
245                     return ngx_http_auth_basic_crypt_handler(r, NULL, &pwd,
246                                                              &alcf->realm);
247                 }
248
249                 break;
250
251             case sw_skip:
252                 if (buf[i] == LF) {
253                     state = sw_login;
254                     login = 0;
255                 }
256
257                 break;
258             }
259         }
260
261         if (state == sw_passwd) {
262             left = left + n - passwd;
263             ngx_memcpy(buf, &buf[passwd], left);
264             passwd = 0;
265
266         } else {
267             left = 0;
268         }
269
270         offset += n;
271     }
272
273     ngx_http_auth_basic_close(&file);
274
275     if (state == sw_passwd) {
276         pwd.len = i - passwd;
277         pwd.data = ngx_pnalloc(r->pool, pwd.len + 1);
278         if (pwd.data == NULL) {
279             return NGX_HTTP_INTERNAL_SERVER_ERROR;
280         }
281
282         ngx_cpystrn(pwd.data, &buf[passwd], pwd.len + 1);
283
284         return ngx_http_auth_basic_crypt_handler(r, NULL, &pwd, &alcf->realm);
285     }
286
287     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
288                   "user \"%V\" was not found in \"%V\"",
289                   &r->headers_in.user, &user_file);
290
291     return ngx_http_auth_basic_set_realm(r, &alcf->realm);
292 }
293
294
295 static ngx_int_t
296 ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r,
297     ngx_http_auth_basic_ctx_t *ctx, ngx_str_t *passwd, ngx_str_t *realm)
298 {
299     ngx_int_t   rc;
300     u_char     *encrypted;
301
302     rc = ngx_crypt(r->pool, r->headers_in.passwd.data, passwd->data,
303                    &encrypted);
304
305     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
306                    "rc: %d user: \"%V\" salt: \"%s\"",
307                    rc, &r->headers_in.user, passwd->data);
308
309     if (rc == NGX_OK) {
310         if (ngx_strcmp(encrypted, passwd->data) == 0) {
311             return NGX_OK;
312         }
313
314         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
315                        "encrypted: \"%s\"", encrypted);
316
317         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
318                       "user \"%V\": password mismatch",
319                       &r->headers_in.user);
320
321         return ngx_http_auth_basic_set_realm(r, realm);
322     }
323
324     if (rc == NGX_ERROR) {
325         return NGX_HTTP_INTERNAL_SERVER_ERROR;
326     }
327
328     /* rc == NGX_AGAIN */
329
330     if (ctx == NULL) {
331         ctx = ngx_palloc(r->pool, sizeof(ngx_http_auth_basic_ctx_t));
332         if (ctx == NULL) {
333             return NGX_HTTP_INTERNAL_SERVER_ERROR;
334         }
335
336         ngx_http_set_ctx(r, ctx, ngx_http_auth_basic_module);
337
338         ctx->passwd.len = passwd->len;
339         passwd->len++;
340
341         ctx->passwd.data = ngx_pstrdup(r->pool, passwd);
342         if (ctx->passwd.data == NULL) {
343             return NGX_HTTP_INTERNAL_SERVER_ERROR;
344         }
345
346     }
347
348     /* TODO: add mutex event */
349
350     return rc;
351 }
352
353
354 static ngx_int_t
355 ngx_http_auth_basic_set_realm(ngx_http_request_t *r, ngx_str_t *realm)
356 {
357     r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers);
358     if (r->headers_out.www_authenticate == NULL) {
359         return NGX_HTTP_INTERNAL_SERVER_ERROR;
360     }
361
362     r->headers_out.www_authenticate->hash = 1;
363     r->headers_out.www_authenticate->key.len = sizeof("WWW-Authenticate") - 1;
364     r->headers_out.www_authenticate->key.data = (u_char *) "WWW-Authenticate";
365     r->headers_out.www_authenticate->value = *realm;
366
367     return NGX_HTTP_UNAUTHORIZED;
368 }
369
370 static void
371 ngx_http_auth_basic_close(ngx_file_t *file)
372 {
373     if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
374         ngx_log_error(NGX_LOG_ALERT, file->log, ngx_errno,
375                       ngx_close_file_n " \"%s\" failed", file->name.data);
376     }
377 }
378
379
380 static void *
381 ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf)
382 {
383     ngx_http_auth_basic_loc_conf_t  *conf;
384
385     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_basic_loc_conf_t));
386     if (conf == NULL) {
387         return NGX_CONF_ERROR;
388     }
389
390     return conf;
391 }
392
393
394 static char *
395 ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
396 {
397     ngx_http_auth_basic_loc_conf_t  *prev = parent;
398     ngx_http_auth_basic_loc_conf_t  *conf = child;
399
400     if (conf->realm.data == NULL) {
401         conf->realm = prev->realm;
402     }
403
404     if (conf->user_file.data == NULL) {
405         conf->user_file = prev->user_file;
406         conf->user_file_lengths = prev->user_file_lengths;
407         conf->user_file_values = prev->user_file_values;
408     }
409
410     return NGX_CONF_OK;
411 }
412
413
414 static ngx_int_t
415 ngx_http_auth_basic_init(ngx_conf_t *cf)
416 {
417     ngx_http_handler_pt        *h;
418     ngx_http_core_main_conf_t  *cmcf;
419
420     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
421
422     h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
423     if (h == NULL) {
424         return NGX_ERROR;
425     }
426
427     *h = ngx_http_auth_basic_handler;
428
429     return NGX_OK;
430 }
431
432
433 static char *
434 ngx_http_auth_basic(ngx_conf_t *cf, void *post, void *data)
435 {
436     ngx_str_t  *realm = data;
437
438     size_t   len;
439     u_char  *basic, *p;
440
441     if (ngx_strcmp(realm->data, "off") == 0) {
442         realm->len = 0;
443         realm->data = (u_char *) "";
444
445         return NGX_CONF_OK;
446     }
447
448     len = sizeof("Basic realm=\"") - 1 + realm->len + 1;
449
450     basic = ngx_pnalloc(cf->pool, len);
451     if (basic == NULL) {
452         return NGX_CONF_ERROR;
453     }
454
455     p = ngx_cpymem(basic, "Basic realm=\"", sizeof("Basic realm=\"") - 1);
456     p = ngx_cpymem(p, realm->data, realm->len);
457     *p = '"';
458
459     realm->len = len;
460     realm->data = basic;
461
462     return NGX_CONF_OK;
463 }
464
465
466 static char *
467 ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
468 {
469     ngx_http_auth_basic_loc_conf_t *alcf = conf;
470
471     ngx_str_t                  *value;
472     ngx_uint_t                  n;
473     ngx_http_script_compile_t   sc;
474
475     if (alcf->user_file.data) {
476         return "is duplicate";
477     }
478
479     value = cf->args->elts;
480
481     alcf->user_file = value[1];
482
483     if (alcf->user_file.len == 0) {
484         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
485                            "invalid parameter \"%V\"", &alcf->user_file);
486         return NGX_CONF_ERROR;
487     }
488
489     if (alcf->user_file.data[0] != '$') {
490         if (ngx_conf_full_name(cf->cycle, &alcf->user_file, 1) != NGX_OK) {
491             return NGX_CONF_ERROR;
492         }
493     }
494
495     n = ngx_http_script_variables_count(&alcf->user_file);
496
497     if (n == 0) {
498         return NGX_CONF_OK;
499     }
500
501     ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
502
503     sc.cf = cf;
504     sc.source = &alcf->user_file;
505     sc.lengths = &alcf->user_file_lengths;
506     sc.values = &alcf->user_file_values;
507     sc.variables = n;
508     sc.complete_lengths = 1;
509     sc.complete_values = 1;
510
511     if (ngx_http_script_compile(&sc) != NGX_OK) {
512         return NGX_CONF_ERROR;
513     }
514
515     return NGX_CONF_OK;
516 }