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

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, **ph;
  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.             ph = &r->headers_out.www_authenticate;

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

  118.                 *ho = *h;
  119.                 ho->next = NULL;

  120.                 *ph = ho;
  121.                 ph = &ho->next;

  122.                 h = h->next;
  123.             }

  124.             return ctx->status;
  125.         }

  126.         if (ctx->status >= NGX_HTTP_OK
  127.             && ctx->status < NGX_HTTP_SPECIAL_RESPONSE)
  128.         {
  129.             return NGX_OK;
  130.         }

  131.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  132.                       "auth request unexpected status: %ui", ctx->status);

  133.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  134.     }

  135.     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_request_ctx_t));
  136.     if (ctx == NULL) {
  137.         return NGX_ERROR;
  138.     }

  139.     ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
  140.     if (ps == NULL) {
  141.         return NGX_ERROR;
  142.     }

  143.     ps->handler = ngx_http_auth_request_done;
  144.     ps->data = ctx;

  145.     if (ngx_http_subrequest(r, &arcf->uri, NULL, &sr, ps,
  146.                             NGX_HTTP_SUBREQUEST_WAITED)
  147.         != NGX_OK)
  148.     {
  149.         return NGX_ERROR;
  150.     }

  151.     /*
  152.      * allocate fake request body to avoid attempts to read it and to make
  153.      * sure real body file (if already read) won't be closed by upstream
  154.      */

  155.     sr->request_body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
  156.     if (sr->request_body == NULL) {
  157.         return NGX_ERROR;
  158.     }

  159.     sr->header_only = 1;

  160.     ctx->subrequest = sr;

  161.     ngx_http_set_ctx(r, ctx, ngx_http_auth_request_module);

  162.     return NGX_AGAIN;
  163. }


  164. static ngx_int_t
  165. ngx_http_auth_request_done(ngx_http_request_t *r, void *data, ngx_int_t rc)
  166. {
  167.     ngx_http_auth_request_ctx_t   *ctx = data;

  168.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  169.                    "auth request done s:%ui", r->headers_out.status);

  170.     ctx->done = 1;
  171.     ctx->status = r->headers_out.status;

  172.     return rc;
  173. }


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

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

  185.     if (arcf->vars == NULL) {
  186.         return NGX_OK;
  187.     }

  188.     cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
  189.     v = cmcf->variables.elts;

  190.     av = arcf->vars->elts;
  191.     last = av + arcf->vars->nelts;

  192.     while (av < last) {
  193.         /*
  194.          * explicitly set new value to make sure it will be available after
  195.          * internal redirects
  196.          */

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

  198.         if (ngx_http_complex_value(ctx->subrequest, &av->value, &val)
  199.             != NGX_OK)
  200.         {
  201.             return NGX_ERROR;
  202.         }

  203.         vv->valid = 1;
  204.         vv->not_found = 0;
  205.         vv->data = val.data;
  206.         vv->len = val.len;

  207.         if (av->set_handler) {
  208.             /*
  209.              * set_handler only available in cmcf->variables_keys, so we store
  210.              * it explicitly
  211.              */

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

  214.         av++;
  215.     }

  216.     return NGX_OK;
  217. }


  218. static ngx_int_t
  219. ngx_http_auth_request_variable(ngx_http_request_t *r,
  220.     ngx_http_variable_value_t *v, uintptr_t data)
  221. {
  222.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  223.                    "auth request variable");

  224.     v->not_found = 1;

  225.     return NGX_OK;
  226. }


  227. static void *
  228. ngx_http_auth_request_create_conf(ngx_conf_t *cf)
  229. {
  230.     ngx_http_auth_request_conf_t  *conf;

  231.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_request_conf_t));
  232.     if (conf == NULL) {
  233.         return NULL;
  234.     }

  235.     /*
  236.      * set by ngx_pcalloc():
  237.      *
  238.      *     conf->uri = { 0, NULL };
  239.      */

  240.     conf->vars = NGX_CONF_UNSET_PTR;

  241.     return conf;
  242. }


  243. static char *
  244. ngx_http_auth_request_merge_conf(ngx_conf_t *cf, void *parent, void *child)
  245. {
  246.     ngx_http_auth_request_conf_t *prev = parent;
  247.     ngx_http_auth_request_conf_t *conf = child;

  248.     ngx_conf_merge_str_value(conf->uri, prev->uri, "");
  249.     ngx_conf_merge_ptr_value(conf->vars, prev->vars, NULL);

  250.     return NGX_CONF_OK;
  251. }


  252. static ngx_int_t
  253. ngx_http_auth_request_init(ngx_conf_t *cf)
  254. {
  255.     ngx_http_handler_pt        *h;
  256.     ngx_http_core_main_conf_t  *cmcf;

  257.     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

  258.     h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
  259.     if (h == NULL) {
  260.         return NGX_ERROR;
  261.     }

  262.     *h = ngx_http_auth_request_handler;

  263.     return NGX_OK;
  264. }


  265. static char *
  266. ngx_http_auth_request(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  267. {
  268.     ngx_http_auth_request_conf_t *arcf = conf;

  269.     ngx_str_t        *value;

  270.     if (arcf->uri.data != NULL) {
  271.         return "is duplicate";
  272.     }

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

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

  277.         return NGX_CONF_OK;
  278.     }

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

  280.     return NGX_CONF_OK;
  281. }


  282. static char *
  283. ngx_http_auth_request_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  284. {
  285.     ngx_http_auth_request_conf_t *arcf = conf;

  286.     ngx_str_t                         *value;
  287.     ngx_http_variable_t               *v;
  288.     ngx_http_auth_request_variable_t  *av;
  289.     ngx_http_compile_complex_value_t   ccv;

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

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

  296.     value[1].len--;
  297.     value[1].data++;

  298.     if (arcf->vars == NGX_CONF_UNSET_PTR) {
  299.         arcf->vars = ngx_array_create(cf->pool, 1,
  300.                                       sizeof(ngx_http_auth_request_variable_t));
  301.         if (arcf->vars == NULL) {
  302.             return NGX_CONF_ERROR;
  303.         }
  304.     }

  305.     av = ngx_array_push(arcf->vars);
  306.     if (av == NULL) {
  307.         return NGX_CONF_ERROR;
  308.     }

  309.     v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);
  310.     if (v == NULL) {
  311.         return NGX_CONF_ERROR;
  312.     }

  313.     av->index = ngx_http_get_variable_index(cf, &value[1]);
  314.     if (av->index == NGX_ERROR) {
  315.         return NGX_CONF_ERROR;
  316.     }

  317.     if (v->get_handler == NULL) {
  318.         v->get_handler = ngx_http_auth_request_variable;
  319.         v->data = (uintptr_t) av;
  320.     }

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

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

  323.     ccv.cf = cf;
  324.     ccv.value = &value[2];
  325.     ccv.complex_value = &av->value;

  326.     if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
  327.         return NGX_CONF_ERROR;
  328.     }

  329.     return NGX_CONF_OK;
  330. }