src/http/modules/ngx_http_auth_basic_module.c - nginx-1.7.10

Global variables defined

Data types defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>
  8. #include <ngx_crypt.h>


  9. #define NGX_HTTP_AUTH_BUF_SIZE  2048


  10. typedef struct {
  11.     ngx_str_t                 passwd;
  12. } ngx_http_auth_basic_ctx_t;


  13. typedef struct {
  14.     ngx_http_complex_value_t  *realm;
  15.     ngx_http_complex_value_t   user_file;
  16. } ngx_http_auth_basic_loc_conf_t;


  17. static ngx_int_t ngx_http_auth_basic_handler(ngx_http_request_t *r);
  18. static ngx_int_t ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r,
  19.     ngx_http_auth_basic_ctx_t *ctx, ngx_str_t *passwd, ngx_str_t *realm);
  20. static ngx_int_t ngx_http_auth_basic_set_realm(ngx_http_request_t *r,
  21.     ngx_str_t *realm);
  22. static void ngx_http_auth_basic_close(ngx_file_t *file);
  23. static void *ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf);
  24. static char *ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf,
  25.     void *parent, void *child);
  26. static ngx_int_t ngx_http_auth_basic_init(ngx_conf_t *cf);
  27. static char *ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd,
  28.     void *conf);


  29. static ngx_command_t  ngx_http_auth_basic_commands[] = {

  30.     { ngx_string("auth_basic"),
  31.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
  32.                         |NGX_CONF_TAKE1,
  33.       ngx_http_set_complex_value_slot,
  34.       NGX_HTTP_LOC_CONF_OFFSET,
  35.       offsetof(ngx_http_auth_basic_loc_conf_t, realm),
  36.       NULL },

  37.     { ngx_string("auth_basic_user_file"),
  38.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
  39.                         |NGX_CONF_TAKE1,
  40.       ngx_http_auth_basic_user_file,
  41.       NGX_HTTP_LOC_CONF_OFFSET,
  42.       offsetof(ngx_http_auth_basic_loc_conf_t, user_file),
  43.       NULL },

  44.       ngx_null_command
  45. };


  46. static ngx_http_module_t  ngx_http_auth_basic_module_ctx = {
  47.     NULL,                                  /* preconfiguration */
  48.     ngx_http_auth_basic_init,              /* postconfiguration */

  49.     NULL,                                  /* create main configuration */
  50.     NULL,                                  /* init main configuration */

  51.     NULL,                                  /* create server configuration */
  52.     NULL,                                  /* merge server configuration */

  53.     ngx_http_auth_basic_create_loc_conf,   /* create location configuration */
  54.     ngx_http_auth_basic_merge_loc_conf     /* merge location configuration */
  55. };


  56. ngx_module_t  ngx_http_auth_basic_module = {
  57.     NGX_MODULE_V1,
  58.     &ngx_http_auth_basic_module_ctx,       /* module context */
  59.     ngx_http_auth_basic_commands,          /* module directives */
  60.     NGX_HTTP_MODULE,                       /* module type */
  61.     NULL,                                  /* init master */
  62.     NULL,                                  /* init module */
  63.     NULL,                                  /* init process */
  64.     NULL,                                  /* init thread */
  65.     NULL,                                  /* exit thread */
  66.     NULL,                                  /* exit process */
  67.     NULL,                                  /* exit master */
  68.     NGX_MODULE_V1_PADDING
  69. };


  70. static ngx_int_t
  71. ngx_http_auth_basic_handler(ngx_http_request_t *r)
  72. {
  73.     off_t                            offset;
  74.     ssize_t                          n;
  75.     ngx_fd_t                         fd;
  76.     ngx_int_t                        rc;
  77.     ngx_err_t                        err;
  78.     ngx_str_t                        pwd, realm, user_file;
  79.     ngx_uint_t                       i, level, login, left, passwd;
  80.     ngx_file_t                       file;
  81.     ngx_http_auth_basic_ctx_t       *ctx;
  82.     ngx_http_auth_basic_loc_conf_t  *alcf;
  83.     u_char                           buf[NGX_HTTP_AUTH_BUF_SIZE];
  84.     enum {
  85.         sw_login,
  86.         sw_passwd,
  87.         sw_skip
  88.     } state;

  89.     alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_basic_module);

  90.     if (alcf->realm == NULL || alcf->user_file.value.data == NULL) {
  91.         return NGX_DECLINED;
  92.     }

  93.     if (ngx_http_complex_value(r, alcf->realm, &realm) != NGX_OK) {
  94.         return NGX_ERROR;
  95.     }

  96.     if (realm.len == 3 && ngx_strncmp(realm.data, "off", 3) == 0) {
  97.         return NGX_DECLINED;
  98.     }

  99.     ctx = ngx_http_get_module_ctx(r, ngx_http_auth_basic_module);

  100.     if (ctx) {
  101.         return ngx_http_auth_basic_crypt_handler(r, ctx, &ctx->passwd,
  102.                                                  &realm);
  103.     }

  104.     rc = ngx_http_auth_basic_user(r);

  105.     if (rc == NGX_DECLINED) {

  106.         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
  107.                       "no user/password was provided for basic authentication");

  108.         return ngx_http_auth_basic_set_realm(r, &realm);
  109.     }

  110.     if (rc == NGX_ERROR) {
  111.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  112.     }

  113.     if (ngx_http_complex_value(r, &alcf->user_file, &user_file) != NGX_OK) {
  114.         return NGX_ERROR;
  115.     }

  116.     fd = ngx_open_file(user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);

  117.     if (fd == NGX_INVALID_FILE) {
  118.         err = ngx_errno;

  119.         if (err == NGX_ENOENT) {
  120.             level = NGX_LOG_ERR;
  121.             rc = NGX_HTTP_FORBIDDEN;

  122.         } else {
  123.             level = NGX_LOG_CRIT;
  124.             rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
  125.         }

  126.         ngx_log_error(level, r->connection->log, err,
  127.                       ngx_open_file_n " \"%s\" failed", user_file.data);

  128.         return rc;
  129.     }

  130.     ngx_memzero(&file, sizeof(ngx_file_t));

  131.     file.fd = fd;
  132.     file.name = user_file;
  133.     file.log = r->connection->log;

  134.     state = sw_login;
  135.     passwd = 0;
  136.     login = 0;
  137.     left = 0;
  138.     offset = 0;

  139.     for ( ;; ) {
  140.         i = left;

  141.         n = ngx_read_file(&file, buf + left, NGX_HTTP_AUTH_BUF_SIZE - left,
  142.                           offset);

  143.         if (n == NGX_ERROR) {
  144.             ngx_http_auth_basic_close(&file);
  145.             return NGX_HTTP_INTERNAL_SERVER_ERROR;
  146.         }

  147.         if (n == 0) {
  148.             break;
  149.         }

  150.         for (i = left; i < left + n; i++) {
  151.             switch (state) {

  152.             case sw_login:
  153.                 if (login == 0) {

  154.                     if (buf[i] == '#' || buf[i] == CR) {
  155.                         state = sw_skip;
  156.                         break;
  157.                     }

  158.                     if (buf[i] == LF) {
  159.                         break;
  160.                     }
  161.                 }

  162.                 if (buf[i] != r->headers_in.user.data[login]) {
  163.                     state = sw_skip;
  164.                     break;
  165.                 }

  166.                 if (login == r->headers_in.user.len) {
  167.                     state = sw_passwd;
  168.                     passwd = i + 1;
  169.                 }

  170.                 login++;

  171.                 break;

  172.             case sw_passwd:
  173.                 if (buf[i] == LF || buf[i] == CR || buf[i] == ':') {
  174.                     buf[i] = '\0';

  175.                     ngx_http_auth_basic_close(&file);

  176.                     pwd.len = i - passwd;
  177.                     pwd.data = &buf[passwd];

  178.                     return ngx_http_auth_basic_crypt_handler(r, NULL, &pwd,
  179.                                                              &realm);
  180.                 }

  181.                 break;

  182.             case sw_skip:
  183.                 if (buf[i] == LF) {
  184.                     state = sw_login;
  185.                     login = 0;
  186.                 }

  187.                 break;
  188.             }
  189.         }

  190.         if (state == sw_passwd) {
  191.             left = left + n - passwd;
  192.             ngx_memmove(buf, &buf[passwd], left);
  193.             passwd = 0;

  194.         } else {
  195.             left = 0;
  196.         }

  197.         offset += n;
  198.     }

  199.     ngx_http_auth_basic_close(&file);

  200.     if (state == sw_passwd) {
  201.         pwd.len = i - passwd;
  202.         pwd.data = ngx_pnalloc(r->pool, pwd.len + 1);
  203.         if (pwd.data == NULL) {
  204.             return NGX_HTTP_INTERNAL_SERVER_ERROR;
  205.         }

  206.         ngx_cpystrn(pwd.data, &buf[passwd], pwd.len + 1);

  207.         return ngx_http_auth_basic_crypt_handler(r, NULL, &pwd, &realm);
  208.     }

  209.     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  210.                   "user \"%V\" was not found in \"%V\"",
  211.                   &r->headers_in.user, &user_file);

  212.     return ngx_http_auth_basic_set_realm(r, &realm);
  213. }


  214. static ngx_int_t
  215. ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r,
  216.     ngx_http_auth_basic_ctx_t *ctx, ngx_str_t *passwd, ngx_str_t *realm)
  217. {
  218.     ngx_int_t   rc;
  219.     u_char     *encrypted;

  220.     rc = ngx_crypt(r->pool, r->headers_in.passwd.data, passwd->data,
  221.                    &encrypted);

  222.     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  223.                    "rc: %d user: \"%V\" salt: \"%s\"",
  224.                    rc, &r->headers_in.user, passwd->data);

  225.     if (rc == NGX_OK) {
  226.         if (ngx_strcmp(encrypted, passwd->data) == 0) {
  227.             return NGX_OK;
  228.         }

  229.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  230.                        "encrypted: \"%s\"", encrypted);

  231.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  232.                       "user \"%V\": password mismatch",
  233.                       &r->headers_in.user);

  234.         return ngx_http_auth_basic_set_realm(r, realm);
  235.     }

  236.     if (rc == NGX_ERROR) {
  237.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  238.     }

  239.     /* rc == NGX_AGAIN */

  240.     if (ctx == NULL) {
  241.         ctx = ngx_palloc(r->pool, sizeof(ngx_http_auth_basic_ctx_t));
  242.         if (ctx == NULL) {
  243.             return NGX_HTTP_INTERNAL_SERVER_ERROR;
  244.         }

  245.         ngx_http_set_ctx(r, ctx, ngx_http_auth_basic_module);

  246.         ctx->passwd.len = passwd->len;
  247.         passwd->len++;

  248.         ctx->passwd.data = ngx_pstrdup(r->pool, passwd);
  249.         if (ctx->passwd.data == NULL) {
  250.             return NGX_HTTP_INTERNAL_SERVER_ERROR;
  251.         }

  252.     }

  253.     /* TODO: add mutex event */

  254.     return rc;
  255. }


  256. static ngx_int_t
  257. ngx_http_auth_basic_set_realm(ngx_http_request_t *r, ngx_str_t *realm)
  258. {
  259.     size_t   len;
  260.     u_char  *basic, *p;

  261.     r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers);
  262.     if (r->headers_out.www_authenticate == NULL) {
  263.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  264.     }

  265.     len = sizeof("Basic realm=\"\"") - 1 + realm->len;

  266.     basic = ngx_pnalloc(r->pool, len);
  267.     if (basic == NULL) {
  268.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  269.     }

  270.     p = ngx_cpymem(basic, "Basic realm=\"", sizeof("Basic realm=\"") - 1);
  271.     p = ngx_cpymem(p, realm->data, realm->len);
  272.     *p = '"';

  273.     r->headers_out.www_authenticate->hash = 1;
  274.     ngx_str_set(&r->headers_out.www_authenticate->key, "WWW-Authenticate");
  275.     r->headers_out.www_authenticate->value.data = basic;
  276.     r->headers_out.www_authenticate->value.len = len;

  277.     return NGX_HTTP_UNAUTHORIZED;
  278. }

  279. static void
  280. ngx_http_auth_basic_close(ngx_file_t *file)
  281. {
  282.     if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
  283.         ngx_log_error(NGX_LOG_ALERT, file->log, ngx_errno,
  284.                       ngx_close_file_n " \"%s\" failed", file->name.data);
  285.     }
  286. }


  287. static void *
  288. ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf)
  289. {
  290.     ngx_http_auth_basic_loc_conf_t  *conf;

  291.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_basic_loc_conf_t));
  292.     if (conf == NULL) {
  293.         return NULL;
  294.     }

  295.     return conf;
  296. }


  297. static char *
  298. ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
  299. {
  300.     ngx_http_auth_basic_loc_conf_t  *prev = parent;
  301.     ngx_http_auth_basic_loc_conf_t  *conf = child;

  302.     if (conf->realm == NULL) {
  303.         conf->realm = prev->realm;
  304.     }

  305.     if (conf->user_file.value.data == NULL) {
  306.         conf->user_file = prev->user_file;
  307.     }

  308.     return NGX_CONF_OK;
  309. }


  310. static ngx_int_t
  311. ngx_http_auth_basic_init(ngx_conf_t *cf)
  312. {
  313.     ngx_http_handler_pt        *h;
  314.     ngx_http_core_main_conf_t  *cmcf;

  315.     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

  316.     h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
  317.     if (h == NULL) {
  318.         return NGX_ERROR;
  319.     }

  320.     *h = ngx_http_auth_basic_handler;

  321.     return NGX_OK;
  322. }


  323. static char *
  324. ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  325. {
  326.     ngx_http_auth_basic_loc_conf_t *alcf = conf;

  327.     ngx_str_t                         *value;
  328.     ngx_http_compile_complex_value_t   ccv;

  329.     if (alcf->user_file.value.data) {
  330.         return "is duplicate";
  331.     }

  332.     value = cf->args->elts;

  333.     ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

  334.     ccv.cf = cf;
  335.     ccv.value = &value[1];
  336.     ccv.complex_value = &alcf->user_file;
  337.     ccv.zero = 1;
  338.     ccv.conf_prefix = 1;

  339.     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
  340.         return NGX_CONF_ERROR;
  341.     }

  342.     return NGX_CONF_OK;
  343. }