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

Global variables defined

Data types defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Maxim Dounin
  3. * Copyright (C) Nginx, Inc.
  4. */


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


  8. typedef struct {
  9.     ngx_str_t                 uri;
  10.     ngx_array_t              *vars;
  11. } ngx_http_auth_request_conf_t;


  12. typedef struct {
  13.     ngx_uint_t                done;
  14.     ngx_uint_t                status;
  15.     ngx_http_request_t       *subrequest;
  16. } ngx_http_auth_request_ctx_t;


  17. typedef struct {
  18.     ngx_int_t                 index;
  19.     ngx_http_complex_value_t  value;
  20.     ngx_http_set_variable_pt  set_handler;
  21. } ngx_http_auth_request_variable_t;


  22. static ngx_int_t ngx_http_auth_request_handler(ngx_http_request_t *r);
  23. static ngx_int_t ngx_http_auth_request_done(ngx_http_request_t *r,
  24.     void *data, ngx_int_t rc);
  25. static ngx_int_t ngx_http_auth_request_set_variables(ngx_http_request_t *r,
  26.     ngx_http_auth_request_conf_t *arcf, ngx_http_auth_request_ctx_t *ctx);
  27. static ngx_int_t ngx_http_auth_request_variable(ngx_http_request_t *r,
  28.     ngx_http_variable_value_t *v, uintptr_t data);
  29. static void *ngx_http_auth_request_create_conf(ngx_conf_t *cf);
  30. static char *ngx_http_auth_request_merge_conf(ngx_conf_t *cf,
  31.     void *parent, void *child);
  32. static ngx_int_t ngx_http_auth_request_init(ngx_conf_t *cf);
  33. static char *ngx_http_auth_request(ngx_conf_t *cf, ngx_command_t *cmd,
  34.     void *conf);
  35. static char *ngx_http_auth_request_set(ngx_conf_t *cf, ngx_command_t *cmd,
  36.     void *conf);


  37. static ngx_command_t  ngx_http_auth_request_commands[] = {

  38.     { ngx_string("auth_request"),
  39.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  40.       ngx_http_auth_request,
  41.       NGX_HTTP_LOC_CONF_OFFSET,
  42.       0,
  43.       NULL },

  44.     { ngx_string("auth_request_set"),
  45.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
  46.       ngx_http_auth_request_set,
  47.       NGX_HTTP_LOC_CONF_OFFSET,
  48.       0,
  49.       NULL },

  50.       ngx_null_command
  51. };


  52. static ngx_http_module_t  ngx_http_auth_request_module_ctx = {
  53.     NULL,                                  /* preconfiguration */
  54.     ngx_http_auth_request_init,            /* postconfiguration */

  55.     NULL,                                  /* create main configuration */
  56.     NULL,                                  /* init main configuration */

  57.     NULL,                                  /* create server configuration */
  58.     NULL,                                  /* merge server configuration */

  59.     ngx_http_auth_request_create_conf,     /* create location configuration */
  60.     ngx_http_auth_request_merge_conf       /* merge location configuration */
  61. };


  62. ngx_module_t  ngx_http_auth_request_module = {
  63.     NGX_MODULE_V1,
  64.     &ngx_http_auth_request_module_ctx,     /* module context */
  65.     ngx_http_auth_request_commands,        /* module directives */
  66.     NGX_HTTP_MODULE,                       /* module type */
  67.     NULL,                                  /* init master */
  68.     NULL,                                  /* init module */
  69.     NULL,                                  /* init process */
  70.     NULL,                                  /* init thread */
  71.     NULL,                                  /* exit thread */
  72.     NULL,                                  /* exit process */
  73.     NULL,                                  /* exit master */
  74.     NGX_MODULE_V1_PADDING
  75. };


  76. static ngx_int_t
  77. ngx_http_auth_request_handler(ngx_http_request_t *r)
  78. {
  79.     ngx_table_elt_t               *h, *ho;
  80.     ngx_http_request_t            *sr;
  81.     ngx_http_post_subrequest_t    *ps;
  82.     ngx_http_auth_request_ctx_t   *ctx;
  83.     ngx_http_auth_request_conf_t  *arcf;

  84.     arcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_request_module);

  85.     if (arcf->uri.len == 0) {
  86.         return NGX_DECLINED;
  87.     }

  88.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  89.                    "auth request handler");

  90.     ctx = ngx_http_get_module_ctx(r, ngx_http_auth_request_module);

  91.     if (ctx != NULL) {
  92.         if (!ctx->done) {
  93.             return NGX_AGAIN;
  94.         }

  95.         /*
  96.          * as soon as we are done - explicitly set variables to make
  97.          * sure they will be available after internal redirects
  98.          */

  99.         if (ngx_http_auth_request_set_variables(r, arcf, ctx) != NGX_OK) {
  100.             return NGX_ERROR;
  101.         }

  102.         /* return appropriate status */

  103.         if (ctx->status == NGX_HTTP_FORBIDDEN) {
  104.             return ctx->status;
  105.         }

  106.         if (ctx->status == NGX_HTTP_UNAUTHORIZED) {
  107.             sr = ctx->subrequest;

  108.             h = sr->headers_out.www_authenticate;

  109.             if (!h && sr->upstream) {
  110.                 h = sr->upstream->headers_in.www_authenticate;
  111.             }

  112.             if (h) {
  113.                 ho = ngx_list_push(&r->headers_out.headers);
  114.                 if (ho == NULL) {
  115.                     return NGX_ERROR;
  116.                 }

  117.                 *ho = *h;

  118.                 r->headers_out.www_authenticate = ho;
  119.             }

  120.             return ctx->status;
  121.         }

  122.         if (ctx->status >= NGX_HTTP_OK
  123.             && ctx->status < NGX_HTTP_SPECIAL_RESPONSE)
  124.         {
  125.             return NGX_OK;
  126.         }

  127.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  128.                       "auth request unexpected status: %d", ctx->status);

  129.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  130.     }

  131.     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_request_ctx_t));
  132.     if (ctx == NULL) {
  133.         return NGX_ERROR;
  134.     }

  135.     ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
  136.     if (ps == NULL) {
  137.         return NGX_ERROR;
  138.     }

  139.     ps->handler = ngx_http_auth_request_done;
  140.     ps->data = ctx;

  141.     if (ngx_http_subrequest(r, &arcf->uri, NULL, &sr, ps,
  142.                             NGX_HTTP_SUBREQUEST_WAITED)
  143.         != NGX_OK)
  144.     {
  145.         return NGX_ERROR;
  146.     }

  147.     /*
  148.      * allocate fake request body to avoid attempts to read it and to make
  149.      * sure real body file (if already read) won't be closed by upstream
  150.      */

  151.     sr->request_body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
  152.     if (sr->request_body == NULL) {
  153.         return NGX_ERROR;
  154.     }

  155.     sr->header_only = 1;

  156.     ctx->subrequest = sr;

  157.     ngx_http_set_ctx(r, ctx, ngx_http_auth_request_module);

  158.     return NGX_AGAIN;
  159. }


  160. static ngx_int_t
  161. ngx_http_auth_request_done(ngx_http_request_t *r, void *data, ngx_int_t rc)
  162. {
  163.     ngx_http_auth_request_ctx_t   *ctx = data;

  164.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  165.                    "auth request done s:%d", r->headers_out.status);

  166.     ctx->done = 1;
  167.     ctx->status = r->headers_out.status;

  168.     return rc;
  169. }


  170. static ngx_int_t
  171. ngx_http_auth_request_set_variables(ngx_http_request_t *r,
  172.     ngx_http_auth_request_conf_t *arcf, ngx_http_auth_request_ctx_t *ctx)
  173. {
  174.     ngx_str_t                          val;
  175.     ngx_http_variable_t               *v;
  176.     ngx_http_variable_value_t         *vv;
  177.     ngx_http_auth_request_variable_t  *av, *last;
  178.     ngx_http_core_main_conf_t         *cmcf;

  179.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  180.                    "auth request set variables");

  181.     if (arcf->vars == NULL) {
  182.         return NGX_OK;
  183.     }

  184.     cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
  185.     v = cmcf->variables.elts;

  186.     av = arcf->vars->elts;
  187.     last = av + arcf->vars->nelts;

  188.     while (av < last) {
  189.         /*
  190.          * explicitly set new value to make sure it will be available after
  191.          * internal redirects
  192.          */

  193.         vv = &r->variables[av->index];

  194.         if (ngx_http_complex_value(ctx->subrequest, &av->value, &val)
  195.             != NGX_OK)
  196.         {
  197.             return NGX_ERROR;
  198.         }

  199.         vv->valid = 1;
  200.         vv->not_found = 0;
  201.         vv->data = val.data;
  202.         vv->len = val.len;

  203.         if (av->set_handler) {
  204.             /*
  205.              * set_handler only available in cmcf->variables_keys, so we store
  206.              * it explicitly
  207.              */

  208.             av->set_handler(r, vv, v[av->index].data);
  209.         }

  210.         av++;
  211.     }

  212.     return NGX_OK;
  213. }


  214. static ngx_int_t
  215. ngx_http_auth_request_variable(ngx_http_request_t *r,
  216.     ngx_http_variable_value_t *v, uintptr_t data)
  217. {
  218.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  219.                    "auth request variable");

  220.     v->not_found = 1;

  221.     return NGX_OK;
  222. }


  223. static void *
  224. ngx_http_auth_request_create_conf(ngx_conf_t *cf)
  225. {
  226.     ngx_http_auth_request_conf_t  *conf;

  227.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_request_conf_t));
  228.     if (conf == NULL) {
  229.         return NULL;
  230.     }

  231.     /*
  232.      * set by ngx_pcalloc():
  233.      *
  234.      *     conf->uri = { 0, NULL };
  235.      */

  236.     conf->vars = NGX_CONF_UNSET_PTR;

  237.     return conf;
  238. }


  239. static char *
  240. ngx_http_auth_request_merge_conf(ngx_conf_t *cf, void *parent, void *child)
  241. {
  242.     ngx_http_auth_request_conf_t *prev = parent;
  243.     ngx_http_auth_request_conf_t *conf = child;

  244.     ngx_conf_merge_str_value(conf->uri, prev->uri, "");
  245.     ngx_conf_merge_ptr_value(conf->vars, prev->vars, NULL);

  246.     return NGX_CONF_OK;
  247. }


  248. static ngx_int_t
  249. ngx_http_auth_request_init(ngx_conf_t *cf)
  250. {
  251.     ngx_http_handler_pt        *h;
  252.     ngx_http_core_main_conf_t  *cmcf;

  253.     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

  254.     h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
  255.     if (h == NULL) {
  256.         return NGX_ERROR;
  257.     }

  258.     *h = ngx_http_auth_request_handler;

  259.     return NGX_OK;
  260. }


  261. static char *
  262. ngx_http_auth_request(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  263. {
  264.     ngx_http_auth_request_conf_t *arcf = conf;

  265.     ngx_str_t        *value;

  266.     if (arcf->uri.data != NULL) {
  267.         return "is duplicate";
  268.     }

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

  270.     if (ngx_strcmp(value[1].data, "off") == 0) {
  271.         arcf->uri.len = 0;
  272.         arcf->uri.data = (u_char *) "";

  273.         return NGX_CONF_OK;
  274.     }

  275.     arcf->uri = value[1];

  276.     return NGX_CONF_OK;
  277. }


  278. static char *
  279. ngx_http_auth_request_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  280. {
  281.     ngx_http_auth_request_conf_t *arcf = conf;

  282.     ngx_str_t                         *value;
  283.     ngx_http_variable_t               *v;
  284.     ngx_http_auth_request_variable_t  *av;
  285.     ngx_http_compile_complex_value_t   ccv;

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

  287.     if (value[1].data[0] != '$') {
  288.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  289.                            "invalid variable name \"%V\"", &value[1]);
  290.         return NGX_CONF_ERROR;
  291.     }

  292.     value[1].len--;
  293.     value[1].data++;

  294.     if (arcf->vars == NGX_CONF_UNSET_PTR) {
  295.         arcf->vars = ngx_array_create(cf->pool, 1,
  296.                                       sizeof(ngx_http_auth_request_variable_t));
  297.         if (arcf->vars == NULL) {
  298.             return NGX_CONF_ERROR;
  299.         }
  300.     }

  301.     av = ngx_array_push(arcf->vars);
  302.     if (av == NULL) {
  303.         return NGX_CONF_ERROR;
  304.     }

  305.     v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);
  306.     if (v == NULL) {
  307.         return NGX_CONF_ERROR;
  308.     }

  309.     av->index = ngx_http_get_variable_index(cf, &value[1]);
  310.     if (av->index == NGX_ERROR) {
  311.         return NGX_CONF_ERROR;
  312.     }

  313.     if (v->get_handler == NULL) {
  314.         v->get_handler = ngx_http_auth_request_variable;
  315.         v->data = (uintptr_t) av;
  316.     }

  317.     av->set_handler = v->set_handler;

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

  319.     ccv.cf = cf;
  320.     ccv.value = &value[2];
  321.     ccv.complex_value = &av->value;

  322.     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
  323.         return NGX_CONF_ERROR;
  324.     }

  325.     return NGX_CONF_OK;
  326. }