b09359d2a6b4a5499373f70518f0f7be52ce11eb
[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         return ngx_http_auth_basic_set_realm(r, &alcf->realm);
130     }
131
132     if (rc == NGX_ERROR) {
133         return NGX_HTTP_INTERNAL_SERVER_ERROR;
134     }
135
136     fd = ngx_open_file(alcf->user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
137
138     if (fd == NGX_INVALID_FILE) {
139         ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
140                       ngx_open_file_n " \"%s\" failed", alcf->user_file.data);
141         return NGX_HTTP_INTERNAL_SERVER_ERROR;
142     }
143
144     ngx_memzero(&file, sizeof(ngx_file_t));
145
146     file.fd = fd;
147     file.name = alcf->user_file;
148     file.log = r->connection->log;
149
150     state = sw_login;
151     passwd = 0;
152     login = 0;
153     left = 0;
154     offset = 0;
155
156     for ( ;; ) {
157         i = left;
158
159         n = ngx_read_file(&file, buf + left, NGX_HTTP_AUTH_BUF_SIZE - left,
160                           offset);
161
162         if (n == NGX_ERROR) {
163             ngx_http_auth_basic_close(&file);
164             return NGX_HTTP_INTERNAL_SERVER_ERROR;
165         }
166
167         if (n == 0) {
168             break;
169         }
170
171         for (i = left; i < left + n; i++) {
172             switch (state) {
173
174             case sw_login:
175                 if (login == 0 && buf[i] == '#') {
176                     state = sw_skip;
177                     break;
178                 }
179
180                 if (buf[i] != r->headers_in.user.data[login]) {
181                     state = sw_skip;
182                     break;
183                 }
184
185                 if (login == r->headers_in.user.len) {
186                     state = sw_passwd;
187                     passwd = i + 1;
188                 }
189
190                 login++;
191
192                 break;
193
194             case sw_passwd:
195                 if (buf[i] == LF || buf[i] == CR || buf[i] == ':') {
196                     buf[i] = '\0';
197
198                     ngx_http_auth_basic_close(&file);
199
200                     pwd.len = i - passwd;
201                     pwd.data = &buf[passwd];
202
203                     return ngx_http_auth_basic_crypt_handler(r, NULL, &pwd,
204                                                              &alcf->realm);
205                 }
206
207                 break;
208
209             case sw_skip:
210                 if (buf[i] == LF) {
211                     state = sw_login;
212                     login = 0;
213                 }
214
215                 break;
216             }
217         }
218
219         if (state == sw_passwd) {
220             left = left + n - passwd;
221             ngx_memcpy(buf, &buf[passwd], left);
222             passwd = 0;
223
224         } else {
225             left = 0;
226         }
227
228         offset += n;
229     }
230
231     ngx_http_auth_basic_close(&file);
232
233     if (state == sw_passwd) {
234         pwd.len = i - passwd;
235         pwd.data = ngx_pnalloc(r->pool, pwd.len + 1);
236         if (pwd.data == NULL) {
237             return NGX_HTTP_INTERNAL_SERVER_ERROR;
238         }
239
240         ngx_cpystrn(pwd.data, &buf[passwd], pwd.len + 1);
241
242         return ngx_http_auth_basic_crypt_handler(r, NULL, &pwd, &alcf->realm);
243     }
244
245     return ngx_http_auth_basic_set_realm(r, &alcf->realm);
246 }
247
248
249 static ngx_int_t
250 ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r,
251     ngx_http_auth_basic_ctx_t *ctx, ngx_str_t *passwd, ngx_str_t *realm)
252 {
253     ngx_int_t   rc;
254     u_char     *encrypted;
255
256     rc = ngx_crypt(r->pool, r->headers_in.passwd.data, passwd->data,
257                    &encrypted);
258
259     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
260                   "rc: %d user: \"%V\" salt: \"%s\"",
261                   rc, &r->headers_in.user, passwd->data);
262
263     if (rc == NGX_OK) {
264         if (ngx_strcmp(encrypted, passwd->data) == 0) {
265             return NGX_OK;
266         }
267
268         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
269                        "encrypted: \"%s\"", encrypted);
270
271         return ngx_http_auth_basic_set_realm(r, realm);
272     }
273
274     if (rc == NGX_ERROR) {
275         return NGX_HTTP_INTERNAL_SERVER_ERROR;
276     }
277
278     /* rc == NGX_AGAIN */
279
280     if (ctx == NULL) {
281         ctx = ngx_palloc(r->pool, sizeof(ngx_http_auth_basic_ctx_t));
282         if (ctx == NULL) {
283             return NGX_HTTP_INTERNAL_SERVER_ERROR;
284         }
285
286         ngx_http_set_ctx(r, ctx, ngx_http_auth_basic_module);
287
288         ctx->passwd.len = passwd->len;
289         passwd->len++;
290
291         ctx->passwd.data = ngx_pstrdup(r->pool, passwd);
292         if (ctx->passwd.data == NULL) {
293             return NGX_HTTP_INTERNAL_SERVER_ERROR;
294         }
295
296     }
297
298     /* TODO: add mutex event */
299
300     return rc;
301 }
302
303
304 static ngx_int_t
305 ngx_http_auth_basic_set_realm(ngx_http_request_t *r, ngx_str_t *realm)
306 {
307     r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers);
308     if (r->headers_out.www_authenticate == NULL) {
309         return NGX_HTTP_INTERNAL_SERVER_ERROR;
310     }
311
312     r->headers_out.www_authenticate->hash = 1;
313     r->headers_out.www_authenticate->key.len = sizeof("WWW-Authenticate") - 1;
314     r->headers_out.www_authenticate->key.data = (u_char *) "WWW-Authenticate";
315     r->headers_out.www_authenticate->value = *realm;
316
317     return NGX_HTTP_UNAUTHORIZED;
318 }
319
320 static void
321 ngx_http_auth_basic_close(ngx_file_t *file)
322 {
323     if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
324         ngx_log_error(NGX_LOG_ALERT, file->log, ngx_errno,
325                       ngx_close_file_n " \"%s\" failed", file->name.data);
326     }
327 }
328
329
330 static void *
331 ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf)
332 {
333     ngx_http_auth_basic_loc_conf_t  *conf;
334
335     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_basic_loc_conf_t));
336     if (conf == NULL) {
337         return NGX_CONF_ERROR;
338     }
339
340     return conf;
341 }
342
343
344 static char *
345 ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
346 {
347     ngx_http_auth_basic_loc_conf_t  *prev = parent;
348     ngx_http_auth_basic_loc_conf_t  *conf = child;
349
350     if (conf->realm.data == NULL) {
351         conf->realm = prev->realm;
352     }
353
354     if (conf->user_file.data) {
355         if (ngx_conf_full_name(cf->cycle, &conf->user_file, 1) != NGX_OK) {
356             return NGX_CONF_ERROR;
357         }
358
359     } else {
360         conf->user_file = prev->user_file;
361     }
362
363     return NGX_CONF_OK;
364 }
365
366
367 static ngx_int_t
368 ngx_http_auth_basic_init(ngx_conf_t *cf)
369 {
370     ngx_http_handler_pt        *h;
371     ngx_http_core_main_conf_t  *cmcf;
372
373     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
374
375     h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
376     if (h == NULL) {
377         return NGX_ERROR;
378     }
379
380     *h = ngx_http_auth_basic_handler;
381
382     return NGX_OK;
383 }
384
385
386 static char *
387 ngx_http_auth_basic(ngx_conf_t *cf, void *post, void *data)
388 {
389     ngx_str_t  *realm = data;
390
391     size_t   len;
392     u_char  *basic, *p;
393
394     if (ngx_strcmp(realm->data, "off") == 0) {
395         realm->len = 0;
396         realm->data = (u_char *) "";
397
398         return NGX_CONF_OK;
399     }
400
401     len = sizeof("Basic realm=\"") - 1 + realm->len + 1;
402
403     basic = ngx_pnalloc(cf->pool, len);
404     if (basic == NULL) {
405         return NGX_CONF_ERROR;
406     }
407
408     p = ngx_cpymem(basic, "Basic realm=\"", sizeof("Basic realm=\"") - 1);
409     p = ngx_cpymem(p, realm->data, realm->len);
410     *p = '"';
411
412     realm->len = len;
413     realm->data = basic;
414
415     return NGX_CONF_OK;
416 }