src/http/modules/ngx_http_auth_basic_module.c - nginx source code

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_http_complex_value_t  *realm;
  12.     ngx_http_complex_value_t  *user_file;
  13. } ngx_http_auth_basic_loc_conf_t;


  14. static ngx_int_t ngx_http_auth_basic_handler(ngx_http_request_t *r);
  15. static ngx_int_t ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r,
  16.     ngx_str_t *passwd, ngx_str_t *realm);
  17. static ngx_int_t ngx_http_auth_basic_set_realm(ngx_http_request_t *r,
  18.     ngx_str_t *realm);
  19. static void *ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf);
  20. static char *ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf,
  21.     void *parent, void *child);
  22. static ngx_int_t ngx_http_auth_basic_init(ngx_conf_t *cf);
  23. static char *ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd,
  24.     void *conf);


  25. static ngx_command_t  ngx_http_auth_basic_commands[] = {

  26.     { ngx_string("auth_basic"),
  27.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
  28.                         |NGX_CONF_TAKE1,
  29.       ngx_http_set_complex_value_slot,
  30.       NGX_HTTP_LOC_CONF_OFFSET,
  31.       offsetof(ngx_http_auth_basic_loc_conf_t, realm),
  32.       NULL },

  33.     { ngx_string("auth_basic_user_file"),
  34.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
  35.                         |NGX_CONF_TAKE1,
  36.       ngx_http_auth_basic_user_file,
  37.       NGX_HTTP_LOC_CONF_OFFSET,
  38.       offsetof(ngx_http_auth_basic_loc_conf_t, user_file),
  39.       NULL },

  40.       ngx_null_command
  41. };


  42. static ngx_http_module_t  ngx_http_auth_basic_module_ctx = {
  43.     NULL,                                  /* preconfiguration */
  44.     ngx_http_auth_basic_init,              /* postconfiguration */

  45.     NULL,                                  /* create main configuration */
  46.     NULL,                                  /* init main configuration */

  47.     NULL,                                  /* create server configuration */
  48.     NULL,                                  /* merge server configuration */

  49.     ngx_http_auth_basic_create_loc_conf,   /* create location configuration */
  50.     ngx_http_auth_basic_merge_loc_conf     /* merge location configuration */
  51. };


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


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

  84.     alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_basic_module);

  85.     if (alcf->realm == NULL || alcf->user_file == NULL) {
  86.         return NGX_DECLINED;
  87.     }

  88.     if (ngx_http_complex_value(r, alcf->realm, &realm) != NGX_OK) {
  89.         return NGX_ERROR;
  90.     }

  91.     if (realm.len == 3 && ngx_strncmp(realm.data, "off", 3) == 0) {
  92.         return NGX_DECLINED;
  93.     }

  94.     rc = ngx_http_auth_basic_user(r);

  95.     if (rc == NGX_DECLINED) {

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

  98.         return ngx_http_auth_basic_set_realm(r, &realm);
  99.     }

  100.     if (rc == NGX_ERROR) {
  101.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  102.     }

  103.     if (ngx_http_complex_value(r, alcf->user_file, &user_file) != NGX_OK) {
  104.         return NGX_ERROR;
  105.     }

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

  107.     if (fd == NGX_INVALID_FILE) {
  108.         err = ngx_errno;

  109.         if (err == NGX_ENOENT) {
  110.             level = NGX_LOG_ERR;
  111.             rc = NGX_HTTP_FORBIDDEN;

  112.         } else {
  113.             level = NGX_LOG_CRIT;
  114.             rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
  115.         }

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

  118.         return rc;
  119.     }

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

  121.     file.fd = fd;
  122.     file.name = user_file;
  123.     file.log = r->connection->log;

  124.     state = sw_login;
  125.     passwd = 0;
  126.     login = 0;
  127.     left = 0;
  128.     offset = 0;

  129.     for ( ;; ) {
  130.         i = left;

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

  133.         if (n == NGX_ERROR) {
  134.             rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
  135.             goto cleanup;
  136.         }

  137.         if (n == 0) {
  138.             break;
  139.         }

  140.         for (i = left; i < left + n; i++) {
  141.             switch (state) {

  142.             case sw_login:
  143.                 if (login == 0) {

  144.                     if (buf[i] == '#' || buf[i] == CR) {
  145.                         state = sw_skip;
  146.                         break;
  147.                     }

  148.                     if (buf[i] == LF) {
  149.                         break;
  150.                     }
  151.                 }

  152.                 if (buf[i] != r->headers_in.user.data[login]) {
  153.                     state = sw_skip;
  154.                     break;
  155.                 }

  156.                 if (login == r->headers_in.user.len) {
  157.                     state = sw_passwd;
  158.                     passwd = i + 1;
  159.                 }

  160.                 login++;

  161.                 break;

  162.             case sw_passwd:
  163.                 if (buf[i] == LF || buf[i] == CR || buf[i] == ':') {
  164.                     buf[i] = '\0';

  165.                     pwd.len = i - passwd;
  166.                     pwd.data = &buf[passwd];

  167.                     rc = ngx_http_auth_basic_crypt_handler(r, &pwd, &realm);
  168.                     goto cleanup;
  169.                 }

  170.                 break;

  171.             case sw_skip:
  172.                 if (buf[i] == LF) {
  173.                     state = sw_login;
  174.                     login = 0;
  175.                 }

  176.                 break;
  177.             }
  178.         }

  179.         if (state == sw_passwd) {
  180.             left = left + n - passwd;
  181.             ngx_memmove(buf, &buf[passwd], left);
  182.             passwd = 0;

  183.         } else {
  184.             left = 0;
  185.         }

  186.         offset += n;
  187.     }

  188.     if (state == sw_passwd) {
  189.         pwd.len = i - passwd;
  190.         pwd.data = ngx_pnalloc(r->pool, pwd.len + 1);
  191.         if (pwd.data == NULL) {
  192.             return NGX_HTTP_INTERNAL_SERVER_ERROR;
  193.         }

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

  195.         rc = ngx_http_auth_basic_crypt_handler(r, &pwd, &realm);
  196.         goto cleanup;
  197.     }

  198.     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  199.                   "user \"%V\" was not found in \"%s\"",
  200.                   &r->headers_in.user, user_file.data);

  201.     rc = ngx_http_auth_basic_set_realm(r, &realm);

  202. cleanup:

  203.     if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
  204.         ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
  205.                       ngx_close_file_n " \"%s\" failed", user_file.data);
  206.     }

  207.     ngx_explicit_memzero(buf, NGX_HTTP_AUTH_BUF_SIZE);

  208.     return rc;
  209. }


  210. static ngx_int_t
  211. ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r, ngx_str_t *passwd,
  212.     ngx_str_t *realm)
  213. {
  214.     ngx_int_t   rc;
  215.     u_char     *encrypted;

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

  218.     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  219.                    "rc: %i user: \"%V\" salt: \"%s\"",
  220.                    rc, &r->headers_in.user, passwd->data);

  221.     if (rc != NGX_OK) {
  222.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  223.     }

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

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

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

  232.     return ngx_http_auth_basic_set_realm(r, realm);
  233. }


  234. static ngx_int_t
  235. ngx_http_auth_basic_set_realm(ngx_http_request_t *r, ngx_str_t *realm)
  236. {
  237.     size_t   len;
  238.     u_char  *basic, *p;

  239.     r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers);
  240.     if (r->headers_out.www_authenticate == NULL) {
  241.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  242.     }

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

  244.     basic = ngx_pnalloc(r->pool, len);
  245.     if (basic == NULL) {
  246.         r->headers_out.www_authenticate->hash = 0;
  247.         r->headers_out.www_authenticate = NULL;
  248.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  249.     }

  250.     p = ngx_cpymem(basic, "Basic realm=\"", sizeof("Basic realm=\"") - 1);
  251.     p = ngx_cpymem(p, realm->data, realm->len);
  252.     *p = '"';

  253.     r->headers_out.www_authenticate->hash = 1;
  254.     r->headers_out.www_authenticate->next = NULL;
  255.     ngx_str_set(&r->headers_out.www_authenticate->key, "WWW-Authenticate");
  256.     r->headers_out.www_authenticate->value.data = basic;
  257.     r->headers_out.www_authenticate->value.len = len;

  258.     return NGX_HTTP_UNAUTHORIZED;
  259. }


  260. static void *
  261. ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf)
  262. {
  263.     ngx_http_auth_basic_loc_conf_t  *conf;

  264.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_basic_loc_conf_t));
  265.     if (conf == NULL) {
  266.         return NULL;
  267.     }

  268.     conf->realm = NGX_CONF_UNSET_PTR;
  269.     conf->user_file = NGX_CONF_UNSET_PTR;

  270.     return conf;
  271. }


  272. static char *
  273. ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
  274. {
  275.     ngx_http_auth_basic_loc_conf_t  *prev = parent;
  276.     ngx_http_auth_basic_loc_conf_t  *conf = child;

  277.     ngx_conf_merge_ptr_value(conf->realm, prev->realm, NULL);
  278.     ngx_conf_merge_ptr_value(conf->user_file, prev->user_file, NULL);

  279.     return NGX_CONF_OK;
  280. }


  281. static ngx_int_t
  282. ngx_http_auth_basic_init(ngx_conf_t *cf)
  283. {
  284.     ngx_http_handler_pt        *h;
  285.     ngx_http_core_main_conf_t  *cmcf;

  286.     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

  287.     h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
  288.     if (h == NULL) {
  289.         return NGX_ERROR;
  290.     }

  291.     *h = ngx_http_auth_basic_handler;

  292.     return NGX_OK;
  293. }


  294. static char *
  295. ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  296. {
  297.     ngx_http_auth_basic_loc_conf_t *alcf = conf;

  298.     ngx_str_t                         *value;
  299.     ngx_http_compile_complex_value_t   ccv;

  300.     if (alcf->user_file != NGX_CONF_UNSET_PTR) {
  301.         return "is duplicate";
  302.     }

  303.     alcf->user_file = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
  304.     if (alcf->user_file == NULL) {
  305.         return NGX_CONF_ERROR;
  306.     }

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

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

  309.     ccv.cf = cf;
  310.     ccv.value = &value[1];
  311.     ccv.complex_value = alcf->user_file;
  312.     ccv.zero = 1;
  313.     ccv.conf_prefix = 1;

  314.     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
  315.         return NGX_CONF_ERROR;
  316.     }

  317.     return NGX_CONF_OK;
  318. }