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