18abd06a7SGlenn Strauss #include "first.h" 28abd06a7SGlenn Strauss 304d76e7aSGlenn Strauss #include "base.h" 422e8b456SStefan Bühler #include "plugin.h" 522e8b456SStefan Bühler #include "http_auth.h" 63dd3cde9SGlenn Strauss #include "http_header.h" 722e8b456SStefan Bühler #include "log.h" 8bcdc6a3bSJan Kneschke 9b9e2be50SGlenn Strauss #include "sys-crypto.h" /* USE_OPENSSL_CRYPTO */ 10b9e2be50SGlenn Strauss #ifdef USE_OPENSSL_CRYPTO 11b9e2be50SGlenn Strauss #include <openssl/sha.h> 12b9e2be50SGlenn Strauss #endif 13b9e2be50SGlenn Strauss 14bcdc6a3bSJan Kneschke #include <stdlib.h> 15bcdc6a3bSJan Kneschke #include <string.h> 16bcdc6a3bSJan Kneschke 17bcdc6a3bSJan Kneschke /** 187ba06c71SGlenn Strauss * auth framework 19bcdc6a3bSJan Kneschke */ 20bcdc6a3bSJan Kneschke 214b3a91e6SGlenn Strauss typedef struct { 224b3a91e6SGlenn Strauss const http_auth_backend_t *auth_backend; 238e713130SGlenn Strauss const array *auth_require; 248e713130SGlenn Strauss unsigned int auth_extern_authn; 257ba06c71SGlenn Strauss } plugin_config; 264b3a91e6SGlenn Strauss 274b3a91e6SGlenn Strauss typedef struct { 284b3a91e6SGlenn Strauss PLUGIN_DATA; 298e713130SGlenn Strauss plugin_config defaults; 307ba06c71SGlenn Strauss plugin_config conf; 317ba06c71SGlenn Strauss } plugin_data; 327ba06c71SGlenn Strauss 337c7f8c46SGlenn Strauss static handler_t mod_auth_check_basic(request_st *r, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend); 347c7f8c46SGlenn Strauss static handler_t mod_auth_check_digest(request_st *r, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend); 357c7f8c46SGlenn Strauss static handler_t mod_auth_check_extern(request_st *r, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend); 364b3a91e6SGlenn Strauss 37bcdc6a3bSJan Kneschke INIT_FUNC(mod_auth_init) { 387ba06c71SGlenn Strauss static const http_auth_scheme_t http_auth_scheme_basic = { "basic", mod_auth_check_basic, NULL }; 397ba06c71SGlenn Strauss static const http_auth_scheme_t http_auth_scheme_digest = { "digest", mod_auth_check_digest, NULL }; 407ba06c71SGlenn Strauss static const http_auth_scheme_t http_auth_scheme_extern = { "extern", mod_auth_check_extern, NULL }; 417ba06c71SGlenn Strauss plugin_data *p; 427ba06c71SGlenn Strauss 437ba06c71SGlenn Strauss /* register http_auth_scheme_* */ 447ba06c71SGlenn Strauss http_auth_scheme_set(&http_auth_scheme_basic); 457ba06c71SGlenn Strauss http_auth_scheme_set(&http_auth_scheme_digest); 467ba06c71SGlenn Strauss http_auth_scheme_set(&http_auth_scheme_extern); 47bcdc6a3bSJan Kneschke 48bcdc6a3bSJan Kneschke p = calloc(1, sizeof(*p)); 49bcdc6a3bSJan Kneschke 50bcdc6a3bSJan Kneschke return p; 51bcdc6a3bSJan Kneschke } 52bcdc6a3bSJan Kneschke 53b73949e0SGlenn Strauss FREE_FUNC(mod_auth_free) { 54b73949e0SGlenn Strauss plugin_data * const p = p_d; 558e713130SGlenn Strauss if (NULL == p->cvlist) return; 568e713130SGlenn Strauss /* (init i to 0 if global context; to 1 to skip empty global context) */ 578e713130SGlenn Strauss for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { 588e713130SGlenn Strauss config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 598e713130SGlenn Strauss for (; -1 != cpv->k_id; ++cpv) { 608e713130SGlenn Strauss if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue; 618e713130SGlenn Strauss switch (cpv->k_id) { 628e713130SGlenn Strauss case 1: /* auth.require */ 638e713130SGlenn Strauss array_free(cpv->v.v); 648e713130SGlenn Strauss break; 658e713130SGlenn Strauss default: 668e713130SGlenn Strauss break; 678e713130SGlenn Strauss } 688e713130SGlenn Strauss } 698e713130SGlenn Strauss } 708e713130SGlenn Strauss } 718e713130SGlenn Strauss 727ba06c71SGlenn Strauss /* data type for mod_auth structured data 737ba06c71SGlenn Strauss * (parsed from auth.require array of strings) */ 747ba06c71SGlenn Strauss typedef struct { 757ba06c71SGlenn Strauss DATA_UNSET; 767ba06c71SGlenn Strauss http_auth_require_t *require; 777ba06c71SGlenn Strauss } data_auth; 787ba06c71SGlenn Strauss 797ba06c71SGlenn Strauss static void data_auth_free(data_unset *d) 807ba06c71SGlenn Strauss { 817ba06c71SGlenn Strauss data_auth * const dauth = (data_auth *)d; 82ad9b7e00SGlenn Strauss free(dauth->key.ptr); 837ba06c71SGlenn Strauss http_auth_require_free(dauth->require); 847ba06c71SGlenn Strauss free(dauth); 857ba06c71SGlenn Strauss } 867ba06c71SGlenn Strauss 877ba06c71SGlenn Strauss static data_auth *data_auth_init(void) 887ba06c71SGlenn Strauss { 898c7f1dfbSGlenn Strauss static const struct data_methods fn = { 908c7f1dfbSGlenn Strauss NULL, /* copy must not be called on this data */ 918c7f1dfbSGlenn Strauss data_auth_free, 928c7f1dfbSGlenn Strauss NULL, /* insert_dup must not be called on this data */ 938c7f1dfbSGlenn Strauss NULL /* print must not be called on this data */ 948c7f1dfbSGlenn Strauss }; 957ba06c71SGlenn Strauss data_auth * const dauth = calloc(1, sizeof(*dauth)); 967ba06c71SGlenn Strauss force_assert(NULL != dauth); 977ba06c71SGlenn Strauss dauth->type = TYPE_OTHER; 988c7f1dfbSGlenn Strauss dauth->fn = &fn; 997ba06c71SGlenn Strauss 1007ba06c71SGlenn Strauss dauth->require = http_auth_require_init(); 1017ba06c71SGlenn Strauss 1027ba06c71SGlenn Strauss return dauth; 1037ba06c71SGlenn Strauss } 1047ba06c71SGlenn Strauss 105b9e2be50SGlenn Strauss static int mod_auth_algorithm_parse(http_auth_info_t *ai, const char *s) { 106b9e2be50SGlenn Strauss size_t len; 107b9e2be50SGlenn Strauss if (NULL == s) { 108b9e2be50SGlenn Strauss ai->dalgo = HTTP_AUTH_DIGEST_MD5; 109b9e2be50SGlenn Strauss ai->dlen = HTTP_AUTH_DIGEST_MD5_BINLEN; 110b9e2be50SGlenn Strauss return 1; 111b9e2be50SGlenn Strauss } 112b9e2be50SGlenn Strauss 113b9e2be50SGlenn Strauss len = strlen(s); 114b9e2be50SGlenn Strauss if (len > 5 115b9e2be50SGlenn Strauss && (s[len-5] ) == '-' 116b9e2be50SGlenn Strauss && (s[len-4] | 0x20) == 's' 117b9e2be50SGlenn Strauss && (s[len-3] | 0x20) == 'e' 118b9e2be50SGlenn Strauss && (s[len-2] | 0x20) == 's' 119b9e2be50SGlenn Strauss && (s[len-1] | 0x20) == 's') { 120b9e2be50SGlenn Strauss ai->dalgo = HTTP_AUTH_DIGEST_SESS; 121b9e2be50SGlenn Strauss len -= 5; 122b9e2be50SGlenn Strauss } 123b9e2be50SGlenn Strauss else { 124b9e2be50SGlenn Strauss ai->dalgo = HTTP_AUTH_DIGEST_NONE; 125b9e2be50SGlenn Strauss } 126b9e2be50SGlenn Strauss 127b9e2be50SGlenn Strauss if (3 == len 128b9e2be50SGlenn Strauss && 'm' == (s[0] | 0x20) 129b9e2be50SGlenn Strauss && 'd' == (s[1] | 0x20) 130b9e2be50SGlenn Strauss && '5' == (s[2] )) { 131b9e2be50SGlenn Strauss ai->dalgo |= HTTP_AUTH_DIGEST_MD5; 132b9e2be50SGlenn Strauss ai->dlen = HTTP_AUTH_DIGEST_MD5_BINLEN; 133b9e2be50SGlenn Strauss return 1; 134b9e2be50SGlenn Strauss } 135b9e2be50SGlenn Strauss #ifdef USE_OPENSSL_CRYPTO 136b9e2be50SGlenn Strauss else if (len >= 7 137b9e2be50SGlenn Strauss && 's' == (s[0] | 0x20) 138b9e2be50SGlenn Strauss && 'h' == (s[1] | 0x20) 139b9e2be50SGlenn Strauss && 'a' == (s[2] | 0x20) 140b9e2be50SGlenn Strauss && '-' == (s[3] )) { 141b9e2be50SGlenn Strauss if (len == 7 && s[4] == '2' && s[5] == '5' && s[6] == '6') { 142b9e2be50SGlenn Strauss ai->dalgo |= HTTP_AUTH_DIGEST_SHA256; 143b9e2be50SGlenn Strauss ai->dlen = HTTP_AUTH_DIGEST_SHA256_BINLEN; 144b9e2be50SGlenn Strauss return 1; 145b9e2be50SGlenn Strauss } 146b9e2be50SGlenn Strauss #ifdef SHA512_256_DIGEST_LENGTH 147b9e2be50SGlenn Strauss if (len == 11 && 0 == memcmp(s+4, "512-256", 7)) { 148b9e2be50SGlenn Strauss ai->dalgo |= HTTP_AUTH_DIGEST_SHA512_256; 149b9e2be50SGlenn Strauss ai->dlen = HTTP_AUTH_DIGEST_SHA512_256_BINLEN; 150b9e2be50SGlenn Strauss return 1; 151b9e2be50SGlenn Strauss } 152b9e2be50SGlenn Strauss #endif 153b9e2be50SGlenn Strauss } 154b9e2be50SGlenn Strauss #endif 155b9e2be50SGlenn Strauss return 0; /*(error)*/ 156b9e2be50SGlenn Strauss } 157b9e2be50SGlenn Strauss 158b9e2be50SGlenn Strauss static int mod_auth_algorithms_parse(int *algorithm, buffer *algos) { 159b9e2be50SGlenn Strauss for (char *s = algos->ptr, *p; s; s = p ? p+1 : NULL) { 160b9e2be50SGlenn Strauss http_auth_info_t ai; 161b9e2be50SGlenn Strauss int rc; 162b9e2be50SGlenn Strauss p = strchr(s, '|'); 163b9e2be50SGlenn Strauss if (p) *p = '\0'; 164b9e2be50SGlenn Strauss rc = mod_auth_algorithm_parse(&ai, s); 165b9e2be50SGlenn Strauss if (p) *p = '|'; 166b9e2be50SGlenn Strauss if (!rc) return 0; 167b9e2be50SGlenn Strauss *algorithm |= ai.dalgo; 168b9e2be50SGlenn Strauss } 169b9e2be50SGlenn Strauss return 1; 170b9e2be50SGlenn Strauss } 171b9e2be50SGlenn Strauss 172b5775b99SGlenn Strauss static int mod_auth_require_parse (http_auth_require_t * const require, const buffer *b, log_error_st *errh) 1737ba06c71SGlenn Strauss { 1747ba06c71SGlenn Strauss /* user=name1|user=name2|group=name3|host=name4 */ 1757ba06c71SGlenn Strauss 1767ba06c71SGlenn Strauss const char *str = b->ptr; 1777ba06c71SGlenn Strauss const char *p; 1787ba06c71SGlenn Strauss 1797ba06c71SGlenn Strauss if (buffer_is_equal_string(b, CONST_STR_LEN("valid-user"))) { 1807ba06c71SGlenn Strauss require->valid_user = 1; 1817ba06c71SGlenn Strauss return 1; /* success */ 1827ba06c71SGlenn Strauss } 1837ba06c71SGlenn Strauss 1847ba06c71SGlenn Strauss do { 1857ba06c71SGlenn Strauss const char *eq; 1867ba06c71SGlenn Strauss size_t len; 1877ba06c71SGlenn Strauss p = strchr(str, '|'); 1887ba06c71SGlenn Strauss len = NULL != p ? (size_t)(p - str) : strlen(str); 1897ba06c71SGlenn Strauss eq = memchr(str, '=', len); 1907ba06c71SGlenn Strauss if (NULL == eq) { 191b5775b99SGlenn Strauss log_error(errh, __FILE__, __LINE__, 192010c2894SGlenn Strauss "error parsing auth.require 'require' field: missing '=' " 193010c2894SGlenn Strauss "(expecting \"valid-user\" or \"user=a|user=b|group=g|host=h\"). " 194010c2894SGlenn Strauss "error value: %s error near: %s", b->ptr, str); 1957ba06c71SGlenn Strauss return 0; 1967ba06c71SGlenn Strauss } 197b2c18f48SGlenn Strauss if (eq[1] == '|' || eq[1] == '\0') { 198b5775b99SGlenn Strauss log_error(errh, __FILE__, __LINE__, 199010c2894SGlenn Strauss "error parsing auth.require 'require' field: " 200010c2894SGlenn Strauss "missing token after '=' " 201010c2894SGlenn Strauss "(expecting \"valid-user\" or \"user=a|user=b|group=g|host=h\"). " 202010c2894SGlenn Strauss "error value: %s error near: %s", b->ptr, str); 2037ba06c71SGlenn Strauss return 0; 2047ba06c71SGlenn Strauss } 2057ba06c71SGlenn Strauss 2067ba06c71SGlenn Strauss switch ((int)(eq - str)) { 2077ba06c71SGlenn Strauss case 4: 2087ba06c71SGlenn Strauss if (0 == memcmp(str, CONST_STR_LEN("user"))) { 209810109ccSGlenn Strauss /*("user=" is 5)*/ 210a03afc90SGlenn Strauss array_set_key_value(&require->user, str+5, len-5, CONST_STR_LEN("")); 2117ba06c71SGlenn Strauss continue; 2127ba06c71SGlenn Strauss } 2137ba06c71SGlenn Strauss else if (0 == memcmp(str, CONST_STR_LEN("host"))) { 214810109ccSGlenn Strauss /*("host=" is 5)*/ 215a03afc90SGlenn Strauss array_set_key_value(&require->host, str+5, len-5, CONST_STR_LEN("")); 216b5775b99SGlenn Strauss log_error(errh, __FILE__, __LINE__, 217010c2894SGlenn Strauss "warning parsing auth.require 'require' field: " 218010c2894SGlenn Strauss "'host' not implemented; field value: %s", b->ptr); 2197ba06c71SGlenn Strauss continue; 2207ba06c71SGlenn Strauss } 2217ba06c71SGlenn Strauss break; /* to error */ 2227ba06c71SGlenn Strauss case 5: 2237ba06c71SGlenn Strauss if (0 == memcmp(str, CONST_STR_LEN("group"))) { 224810109ccSGlenn Strauss /*("group=" is 6)*/ 225a03afc90SGlenn Strauss array_set_key_value(&require->group, str+6, len-6, CONST_STR_LEN("")); 2269c91af0cSGlenn Strauss #if 0/*(supported by mod_authn_ldap, but not all other backends)*/ 227b5775b99SGlenn Strauss log_error(errh, __FILE__, __LINE__, 228010c2894SGlenn Strauss "warning parsing auth.require 'require' field: " 229010c2894SGlenn Strauss "'group' not implemented; field value: %s", b->ptr); 2309c91af0cSGlenn Strauss #endif 2317ba06c71SGlenn Strauss continue; 2327ba06c71SGlenn Strauss } 2337ba06c71SGlenn Strauss break; /* to error */ 2347ba06c71SGlenn Strauss case 10: 2357ba06c71SGlenn Strauss if (0 == memcmp(str, CONST_STR_LEN("valid-user"))) { 236b5775b99SGlenn Strauss log_error(errh, __FILE__, __LINE__, 237010c2894SGlenn Strauss "error parsing auth.require 'require' field: " 238010c2894SGlenn Strauss "valid user can not be combined with other require rules " 239010c2894SGlenn Strauss "(expecting \"valid-user\" or " 240010c2894SGlenn Strauss "\"user=a|user=b|group=g|host=h\"). error value: %s", b->ptr); 2417ba06c71SGlenn Strauss return 0; 2427ba06c71SGlenn Strauss } 2437ba06c71SGlenn Strauss break; /* to error */ 2447ba06c71SGlenn Strauss default: 2457ba06c71SGlenn Strauss break; /* to error */ 2467ba06c71SGlenn Strauss } 2477ba06c71SGlenn Strauss 248b5775b99SGlenn Strauss log_error(errh, __FILE__, __LINE__, 249010c2894SGlenn Strauss "error parsing auth.require 'require' field: " 250010c2894SGlenn Strauss "invalid/unsupported token " 251010c2894SGlenn Strauss "(expecting \"valid-user\" or \"user=a|user=b|group=g|host=h\"). " 252010c2894SGlenn Strauss "error value: %s error near: %s", b->ptr, str); 2537ba06c71SGlenn Strauss return 0; 2547ba06c71SGlenn Strauss 2557ba06c71SGlenn Strauss } while (p && *((str = p+1))); 2567ba06c71SGlenn Strauss 2577ba06c71SGlenn Strauss return 1; /* success */ 2587ba06c71SGlenn Strauss } 2597ba06c71SGlenn Strauss 260b5775b99SGlenn Strauss static handler_t mod_auth_require_parse_array(const array *value, array * const auth_require, log_error_st *errh) 2618e713130SGlenn Strauss { 2628e713130SGlenn Strauss for (uint32_t n = 0; n < value->used; ++n) { 2637ba06c71SGlenn Strauss size_t m; 2648e713130SGlenn Strauss data_array *da_file = (data_array *)value->data[n]; 2657ba06c71SGlenn Strauss const buffer *method = NULL, *realm = NULL, *require = NULL; 266*e1188e77SGlenn Strauss const buffer *nonce_secret = NULL; 2677ba06c71SGlenn Strauss const http_auth_scheme_t *auth_scheme; 268b9e2be50SGlenn Strauss buffer *algos = NULL; 269b9e2be50SGlenn Strauss int algorithm = HTTP_AUTH_DIGEST_SESS; 2707ba06c71SGlenn Strauss 271c2238256SGlenn Strauss if (!array_is_kvstring(&da_file->value)) { 272b5775b99SGlenn Strauss log_error(errh, __FILE__, __LINE__, 273010c2894SGlenn Strauss "unexpected value for auth.require; expected " 274bd77abe0SGlenn Strauss "auth.require = ( \"urlpath\" => ( \"option\" => \"value\" ) )"); 2757ba06c71SGlenn Strauss 2767ba06c71SGlenn Strauss return HANDLER_ERROR; 2777ba06c71SGlenn Strauss } 2787ba06c71SGlenn Strauss 279c2238256SGlenn Strauss for (m = 0; m < da_file->value.used; m++) { 280c2238256SGlenn Strauss if (da_file->value.data[m]->type == TYPE_STRING) { 281c2238256SGlenn Strauss data_string *ds = (data_string *)da_file->value.data[m]; 282ad9b7e00SGlenn Strauss if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("method"))) { 283601c572cSGlenn Strauss method = &ds->value; 284ad9b7e00SGlenn Strauss } else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("realm"))) { 285601c572cSGlenn Strauss realm = &ds->value; 286ad9b7e00SGlenn Strauss } else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("require"))) { 287601c572cSGlenn Strauss require = &ds->value; 288ad9b7e00SGlenn Strauss } else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("algorithm"))) { 289601c572cSGlenn Strauss algos = &ds->value; 290*e1188e77SGlenn Strauss } else if (buffer_is_equal_string(&ds->key, CONST_STR_LEN("nonce_secret"))) { 291*e1188e77SGlenn Strauss nonce_secret = &ds->value; 2927ba06c71SGlenn Strauss } else { 293b5775b99SGlenn Strauss log_error(errh, __FILE__, __LINE__, 294010c2894SGlenn Strauss "the field is unknown in: " 295010c2894SGlenn Strauss "auth.require = ( \"...\" => ( ..., -> \"%s\" <- => \"...\" ) )", 296010c2894SGlenn Strauss da_file->value.data[m]->key.ptr); 2977ba06c71SGlenn Strauss 2987ba06c71SGlenn Strauss return HANDLER_ERROR; 2997ba06c71SGlenn Strauss } 3007ba06c71SGlenn Strauss } else { 301b5775b99SGlenn Strauss log_error(errh, __FILE__, __LINE__, 302010c2894SGlenn Strauss "a string was expected for: " 303010c2894SGlenn Strauss "auth.require = ( \"...\" => ( ..., -> \"%s\" <- => \"...\" ) )", 304010c2894SGlenn Strauss da_file->value.data[m]->key.ptr); 3057ba06c71SGlenn Strauss 3067ba06c71SGlenn Strauss return HANDLER_ERROR; 3077ba06c71SGlenn Strauss } 3087ba06c71SGlenn Strauss } 3097ba06c71SGlenn Strauss 3107ba06c71SGlenn Strauss if (buffer_string_is_empty(method)) { 311b5775b99SGlenn Strauss log_error(errh, __FILE__, __LINE__, 312010c2894SGlenn Strauss "the method field is missing or blank in: " 3137ba06c71SGlenn Strauss "auth.require = ( \"...\" => ( ..., \"method\" => \"...\" ) )"); 3147ba06c71SGlenn Strauss return HANDLER_ERROR; 3157ba06c71SGlenn Strauss } else { 3167ba06c71SGlenn Strauss auth_scheme = http_auth_scheme_get(method); 3177ba06c71SGlenn Strauss if (NULL == auth_scheme) { 318b5775b99SGlenn Strauss log_error(errh, __FILE__, __LINE__, 319010c2894SGlenn Strauss "unknown method %s (e.g. \"basic\", \"digest\" or \"extern\") in " 320010c2894SGlenn Strauss "auth.require = ( \"...\" => ( ..., \"method\" => \"...\") )", method->ptr); 3217ba06c71SGlenn Strauss return HANDLER_ERROR; 3227ba06c71SGlenn Strauss } 3237ba06c71SGlenn Strauss } 3247ba06c71SGlenn Strauss 3257ba06c71SGlenn Strauss if (buffer_is_empty(realm)) { 326b5775b99SGlenn Strauss log_error(errh, __FILE__, __LINE__, 327010c2894SGlenn Strauss "the realm field is missing in: " 3287ba06c71SGlenn Strauss "auth.require = ( \"...\" => ( ..., \"realm\" => \"...\" ) )"); 3297ba06c71SGlenn Strauss return HANDLER_ERROR; 3307ba06c71SGlenn Strauss } 3317ba06c71SGlenn Strauss 3327ba06c71SGlenn Strauss if (buffer_string_is_empty(require)) { 333b5775b99SGlenn Strauss log_error(errh, __FILE__, __LINE__, 334010c2894SGlenn Strauss "the require field is missing or blank in: " 3357ba06c71SGlenn Strauss "auth.require = ( \"...\" => ( ..., \"require\" => \"...\" ) )"); 3367ba06c71SGlenn Strauss return HANDLER_ERROR; 3377ba06c71SGlenn Strauss } 3387ba06c71SGlenn Strauss 339b9e2be50SGlenn Strauss if (buffer_string_is_empty(algos)) { 340b9e2be50SGlenn Strauss algorithm |= HTTP_AUTH_DIGEST_MD5; 341b9e2be50SGlenn Strauss } else if (!mod_auth_algorithms_parse(&algorithm, algos)) { 342b5775b99SGlenn Strauss log_error(errh, __FILE__, __LINE__, 343010c2894SGlenn Strauss "invalid algorithm in: " 344b9e2be50SGlenn Strauss "auth.require = ( \"...\" => ( ..., \"algorithm\" => \"...\" ) )"); 345b9e2be50SGlenn Strauss return HANDLER_ERROR; 346b9e2be50SGlenn Strauss } 347b9e2be50SGlenn Strauss 348609e9a50SGlenn Strauss if (require) { /*(always true at this point)*/ 3497ba06c71SGlenn Strauss data_auth * const dauth = data_auth_init(); 350ad9b7e00SGlenn Strauss buffer_copy_buffer(&dauth->key, &da_file->key); 3517ba06c71SGlenn Strauss dauth->require->scheme = auth_scheme; 352b9e2be50SGlenn Strauss dauth->require->algorithm = algorithm; 353a03afc90SGlenn Strauss dauth->require->realm = realm; 354*e1188e77SGlenn Strauss dauth->require->nonce_secret = nonce_secret; /*(NULL is ok)*/ 355b5775b99SGlenn Strauss if (!mod_auth_require_parse(dauth->require, require, errh)) { 3568c7f1dfbSGlenn Strauss dauth->fn->free((data_unset *)dauth); 3577ba06c71SGlenn Strauss return HANDLER_ERROR; 3587ba06c71SGlenn Strauss } 3598e713130SGlenn Strauss array_insert_unique(auth_require, (data_unset *)dauth); 3607ba06c71SGlenn Strauss } 3617ba06c71SGlenn Strauss } 3627ba06c71SGlenn Strauss 3637ba06c71SGlenn Strauss return HANDLER_GO_ON; 3647ba06c71SGlenn Strauss } 3657ba06c71SGlenn Strauss 3668e713130SGlenn Strauss static void mod_auth_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { 3678e713130SGlenn Strauss switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 3688e713130SGlenn Strauss case 0: /* auth.backend */ 3698e713130SGlenn Strauss if (cpv->vtype == T_CONFIG_LOCAL) 3708e713130SGlenn Strauss pconf->auth_backend = cpv->v.v; 3718e713130SGlenn Strauss break; 3728e713130SGlenn Strauss case 1: /* auth.require */ 3738e713130SGlenn Strauss if (cpv->vtype == T_CONFIG_LOCAL) 3748e713130SGlenn Strauss pconf->auth_require = cpv->v.v; 3758e713130SGlenn Strauss break; 3768e713130SGlenn Strauss case 2: /* auth.extern-authn */ 3778e713130SGlenn Strauss pconf->auth_extern_authn = cpv->v.u; 3788e713130SGlenn Strauss default:/* should not happen */ 3798e713130SGlenn Strauss return; 3808e713130SGlenn Strauss } 3818e713130SGlenn Strauss } 3824a6ce4aeSJan Kneschke 3838e713130SGlenn Strauss static void mod_auth_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { 3848e713130SGlenn Strauss do { 3858e713130SGlenn Strauss mod_auth_merge_config_cpv(pconf, cpv); 3868e713130SGlenn Strauss } while ((++cpv)->k_id != -1); 3878e713130SGlenn Strauss } 388bcdc6a3bSJan Kneschke 3897c7f8c46SGlenn Strauss static void mod_auth_patch_config(request_st * const r, plugin_data * const p) { 390cc2134c8SGlenn Strauss p->conf = p->defaults; /* copy small struct instead of memcpy() */ 391cc2134c8SGlenn Strauss /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/ 3928e713130SGlenn Strauss for (int i = 1, used = p->nconfig; i < used; ++i) { 3937c7f8c46SGlenn Strauss if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 3948e713130SGlenn Strauss mod_auth_merge_config(&p->conf, p->cvlist + p->cvlist[i].v.u2[0]); 3958e713130SGlenn Strauss } 3968e713130SGlenn Strauss } 39736f64b26SGlenn Strauss 3988e713130SGlenn Strauss SETDEFAULTS_FUNC(mod_auth_set_defaults) { 3998e713130SGlenn Strauss static const config_plugin_keys_t cpk[] = { 4008e713130SGlenn Strauss { CONST_STR_LEN("auth.backend"), 4018e713130SGlenn Strauss T_CONFIG_STRING, 4028e713130SGlenn Strauss T_CONFIG_SCOPE_CONNECTION } 4038e713130SGlenn Strauss ,{ CONST_STR_LEN("auth.require"), 40403b4c993SGlenn Strauss T_CONFIG_ARRAY_KVARRAY, 4058e713130SGlenn Strauss T_CONFIG_SCOPE_CONNECTION } 4068e713130SGlenn Strauss ,{ CONST_STR_LEN("auth.extern-authn"), 4078e713130SGlenn Strauss T_CONFIG_BOOL, 4088e713130SGlenn Strauss T_CONFIG_SCOPE_CONNECTION } 4098e713130SGlenn Strauss ,{ NULL, 0, 4108e713130SGlenn Strauss T_CONFIG_UNSET, 4118e713130SGlenn Strauss T_CONFIG_SCOPE_UNSET } 4128e713130SGlenn Strauss }; 413bcdc6a3bSJan Kneschke 4148e713130SGlenn Strauss plugin_data * const p = p_d; 4158e713130SGlenn Strauss if (!config_plugin_values_init(srv, p, cpk, "mod_auth")) 4168e713130SGlenn Strauss return HANDLER_ERROR; 417bcdc6a3bSJan Kneschke 4188e713130SGlenn Strauss /* process and validate config directives 4198e713130SGlenn Strauss * (init i to 0 if global context; to 1 to skip empty global context) */ 4208e713130SGlenn Strauss for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 4218e713130SGlenn Strauss config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 4228e713130SGlenn Strauss for (; -1 != cpv->k_id; ++cpv) { 4238e713130SGlenn Strauss switch (cpv->k_id) { 4248e713130SGlenn Strauss case 0: /* auth.backend */ 4258e713130SGlenn Strauss if (!buffer_string_is_empty(cpv->v.b)) { 4268e713130SGlenn Strauss const http_auth_backend_t * const auth_backend = 4278e713130SGlenn Strauss http_auth_backend_get(cpv->v.b); 4288e713130SGlenn Strauss if (NULL == auth_backend) { 4298e713130SGlenn Strauss log_error(srv->errh, __FILE__, __LINE__, 4308e713130SGlenn Strauss "auth.backend not supported: %s", cpv->v.b->ptr); 4318e713130SGlenn Strauss return HANDLER_ERROR; 4328e713130SGlenn Strauss } 4338e713130SGlenn Strauss *(const http_auth_backend_t **)&cpv->v.v = auth_backend; 4348e713130SGlenn Strauss cpv->vtype = T_CONFIG_LOCAL; 4358e713130SGlenn Strauss } 4368e713130SGlenn Strauss break; 4378e713130SGlenn Strauss case 1: /* auth.require */ 43803b4c993SGlenn Strauss { 43924680a91SGlenn Strauss array * const a = array_init(4); 4408e713130SGlenn Strauss if (HANDLER_GO_ON != 441b5775b99SGlenn Strauss mod_auth_require_parse_array(cpv->v.a, a, srv->errh)) { 4428e713130SGlenn Strauss array_free(a); 4438e713130SGlenn Strauss return HANDLER_ERROR; 4448e713130SGlenn Strauss } 4458e713130SGlenn Strauss cpv->v.a = a; 4468e713130SGlenn Strauss cpv->vtype = T_CONFIG_LOCAL; 4478e713130SGlenn Strauss } 4488e713130SGlenn Strauss break; 4498e713130SGlenn Strauss case 2: /* auth.extern-authn */ 4508e713130SGlenn Strauss break; 4518e713130SGlenn Strauss default:/* should not happen */ 4528e713130SGlenn Strauss break; 453bcdc6a3bSJan Kneschke } 454bcdc6a3bSJan Kneschke } 455bcdc6a3bSJan Kneschke } 456bcdc6a3bSJan Kneschke 4578e713130SGlenn Strauss /* initialize p->defaults from global config context */ 4588e713130SGlenn Strauss if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 4598e713130SGlenn Strauss const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 4608e713130SGlenn Strauss if (-1 != cpv->k_id) 4618e713130SGlenn Strauss mod_auth_merge_config(&p->defaults, cpv); 462bcdc6a3bSJan Kneschke } 4638e713130SGlenn Strauss 4648e713130SGlenn Strauss return HANDLER_GO_ON; 4658e713130SGlenn Strauss } 466bcdc6a3bSJan Kneschke 4677c7f8c46SGlenn Strauss static handler_t mod_auth_uri_handler(request_st * const r, void *p_d) { 4687ba06c71SGlenn Strauss plugin_data *p = p_d; 469d61f3381SGlenn Strauss data_auth *dauth; 47081b2d1f0SGlenn Strauss 4717c7f8c46SGlenn Strauss mod_auth_patch_config(r, p); 47281b2d1f0SGlenn Strauss 4737ba06c71SGlenn Strauss if (p->conf.auth_require == NULL) return HANDLER_GO_ON; 47481b2d1f0SGlenn Strauss 4757ba06c71SGlenn Strauss /* search auth directives for first prefix match against URL path */ 4767ba06c71SGlenn Strauss /* if we have a case-insensitive FS we have to lower-case the URI here too */ 4777c7f8c46SGlenn Strauss dauth = (!r->conf.force_lowercase_filenames) 4787c7f8c46SGlenn Strauss ? (data_auth *)array_match_key_prefix(p->conf.auth_require, &r->uri.path) 4797c7f8c46SGlenn Strauss : (data_auth *)array_match_key_prefix_nc(p->conf.auth_require, &r->uri.path); 480d61f3381SGlenn Strauss if (NULL == dauth) return HANDLER_GO_ON; 4817ba06c71SGlenn Strauss 482d61f3381SGlenn Strauss { 4837ba06c71SGlenn Strauss const http_auth_scheme_t * const scheme = dauth->require->scheme; 484f54d628cSGlenn Strauss if (p->conf.auth_extern_authn) { 4857c7f8c46SGlenn Strauss const buffer *vb = http_header_env_get(r, CONST_STR_LEN("REMOTE_USER")); 4863dd3cde9SGlenn Strauss if (NULL != vb && http_auth_match_rules(dauth->require, vb->ptr, NULL, NULL)) { 487f54d628cSGlenn Strauss return HANDLER_GO_ON; 488f54d628cSGlenn Strauss } 489f54d628cSGlenn Strauss } 4907c7f8c46SGlenn Strauss return scheme->checkfn(r, scheme->p_d, dauth->require, p->conf.auth_backend); 4917ba06c71SGlenn Strauss } 4927ba06c71SGlenn Strauss } 4937ba06c71SGlenn Strauss 4947ba06c71SGlenn Strauss int mod_auth_plugin_init(plugin *p); 4957ba06c71SGlenn Strauss int mod_auth_plugin_init(plugin *p) { 4967ba06c71SGlenn Strauss p->version = LIGHTTPD_VERSION_ID; 497e2de4e58SGlenn Strauss p->name = "auth"; 4987ba06c71SGlenn Strauss p->init = mod_auth_init; 4997ba06c71SGlenn Strauss p->set_defaults = mod_auth_set_defaults; 5007ba06c71SGlenn Strauss p->handle_uri_clean = mod_auth_uri_handler; 5017ba06c71SGlenn Strauss p->cleanup = mod_auth_free; 5027ba06c71SGlenn Strauss 50381b2d1f0SGlenn Strauss return 0; 50481b2d1f0SGlenn Strauss } 50581b2d1f0SGlenn Strauss 50681b2d1f0SGlenn Strauss 50781b2d1f0SGlenn Strauss 50881b2d1f0SGlenn Strauss 5097ba06c71SGlenn Strauss /* 5107ba06c71SGlenn Strauss * auth schemes (basic, digest, extern) 5117ba06c71SGlenn Strauss * 5127ba06c71SGlenn Strauss * (could be in separate file from mod_auth.c as long as registration occurs) 5137ba06c71SGlenn Strauss */ 51481b2d1f0SGlenn Strauss 5157ba06c71SGlenn Strauss #include "base64.h" 5167ba06c71SGlenn Strauss #include "md5.h" 5177f4e156eSGlenn Strauss #include "rand.h" 5183dd3cde9SGlenn Strauss #include "http_header.h" 51981b2d1f0SGlenn Strauss 520010c2894SGlenn Strauss __attribute_cold__ 5217c7f8c46SGlenn Strauss static handler_t mod_auth_send_400_bad_request(request_st * const r) { 5224b3a91e6SGlenn Strauss 5234b3a91e6SGlenn Strauss /* a field was missing or invalid */ 5247c7f8c46SGlenn Strauss r->http_status = 400; /* Bad Request */ 5257c7f8c46SGlenn Strauss r->handler_module = NULL; 5264b3a91e6SGlenn Strauss 5274b3a91e6SGlenn Strauss return HANDLER_FINISHED; 5284b3a91e6SGlenn Strauss } 5294b3a91e6SGlenn Strauss 5307c7f8c46SGlenn Strauss static handler_t mod_auth_send_401_unauthorized_basic(request_st * const r, const buffer * const realm) { 5317c7f8c46SGlenn Strauss r->http_status = 401; 5327c7f8c46SGlenn Strauss r->handler_module = NULL; 5334b3a91e6SGlenn Strauss 5347c7f8c46SGlenn Strauss buffer * const tb = r->tmp_buf; 535010c2894SGlenn Strauss buffer_copy_string_len(tb, CONST_STR_LEN("Basic realm=\"")); 536010c2894SGlenn Strauss buffer_append_string_buffer(tb, realm); 537010c2894SGlenn Strauss buffer_append_string_len(tb, CONST_STR_LEN("\", charset=\"UTF-8\"")); 5384b3a91e6SGlenn Strauss 5397c7f8c46SGlenn Strauss http_header_response_set(r, HTTP_HEADER_OTHER, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(tb)); 5404b3a91e6SGlenn Strauss 5414b3a91e6SGlenn Strauss return HANDLER_FINISHED; 5424b3a91e6SGlenn Strauss } 5434b3a91e6SGlenn Strauss 5447c7f8c46SGlenn Strauss static handler_t mod_auth_check_basic(request_st * const r, void *p_d, const struct http_auth_require_t * const require, const struct http_auth_backend_t * const backend) { 5457c7f8c46SGlenn Strauss const buffer *b = http_header_request_get(r, HTTP_HEADER_AUTHORIZATION, CONST_STR_LEN("Authorization")); 54681b2d1f0SGlenn Strauss buffer *username; 54781b2d1f0SGlenn Strauss char *pw; 5487ba06c71SGlenn Strauss handler_t rc = HANDLER_UNSET; 54981b2d1f0SGlenn Strauss 5507ba06c71SGlenn Strauss UNUSED(p_d); 55181b2d1f0SGlenn Strauss 5527ba06c71SGlenn Strauss if (NULL == backend) { 5537c7f8c46SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__, "auth.backend not configured for %s", r->uri.path.ptr); 5547c7f8c46SGlenn Strauss r->http_status = 500; 5557c7f8c46SGlenn Strauss r->handler_module = NULL; 5567ba06c71SGlenn Strauss return HANDLER_FINISHED; 5577ba06c71SGlenn Strauss } 5587ba06c71SGlenn Strauss 5593dd3cde9SGlenn Strauss if (NULL == b) { 5607c7f8c46SGlenn Strauss return mod_auth_send_401_unauthorized_basic(r, require->realm); 5617ba06c71SGlenn Strauss } 5627ba06c71SGlenn Strauss 56313008156SGlenn Strauss if (!buffer_eq_icase_ssn(b->ptr, CONST_STR_LEN("Basic "))) { 5647c7f8c46SGlenn Strauss return mod_auth_send_401_unauthorized_basic(r, require->realm); 5654b3a91e6SGlenn Strauss } 5669bc61f16SGlenn Strauss #ifdef __COVERITY__ 5673dd3cde9SGlenn Strauss if (buffer_string_length(b) < sizeof("Basic ")-1) { 5687c7f8c46SGlenn Strauss return mod_auth_send_400_bad_request(r); 5699bc61f16SGlenn Strauss } 5709bc61f16SGlenn Strauss #endif 57181b2d1f0SGlenn Strauss 57281b2d1f0SGlenn Strauss username = buffer_init(); 57381b2d1f0SGlenn Strauss 5748047c2f4SGlenn Strauss /* coverity[overflow_sink : FALSE] */ 5757ba06c71SGlenn Strauss if (!buffer_append_base64_decode(username, b->ptr+sizeof("Basic ")-1, buffer_string_length(b)-(sizeof("Basic ")-1), BASE64_STANDARD)) { 5767c7f8c46SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__, "decoding base64-string failed %s", b->ptr+sizeof("Basic ")-1); 57781b2d1f0SGlenn Strauss 57881b2d1f0SGlenn Strauss buffer_free(username); 5797c7f8c46SGlenn Strauss return mod_auth_send_400_bad_request(r); 58081b2d1f0SGlenn Strauss } 58181b2d1f0SGlenn Strauss 58281b2d1f0SGlenn Strauss /* r2 == user:password */ 58381b2d1f0SGlenn Strauss if (NULL == (pw = strchr(username->ptr, ':'))) { 5847c7f8c46SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__, "missing ':' in %s", username->ptr); 58581b2d1f0SGlenn Strauss 58681b2d1f0SGlenn Strauss buffer_free(username); 5877c7f8c46SGlenn Strauss return mod_auth_send_400_bad_request(r); 58881b2d1f0SGlenn Strauss } 58981b2d1f0SGlenn Strauss 59081b2d1f0SGlenn Strauss buffer_string_set_length(username, pw - username->ptr); 59181b2d1f0SGlenn Strauss pw++; 59281b2d1f0SGlenn Strauss 5937c7f8c46SGlenn Strauss rc = backend->basic(r, backend->p_d, require, username, pw); 5947ba06c71SGlenn Strauss switch (rc) { 5954b3a91e6SGlenn Strauss case HANDLER_GO_ON: 5967c7f8c46SGlenn Strauss http_auth_setenv(r, CONST_BUF_LEN(username), CONST_STR_LEN("Basic")); 5974b3a91e6SGlenn Strauss break; 5984b3a91e6SGlenn Strauss case HANDLER_WAIT_FOR_EVENT: 5994b3a91e6SGlenn Strauss case HANDLER_FINISHED: 6007ba06c71SGlenn Strauss break; 6014b3a91e6SGlenn Strauss case HANDLER_ERROR: 6024b3a91e6SGlenn Strauss default: 6037c7f8c46SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__, 604010c2894SGlenn Strauss "password doesn't match for %s username: %s IP: %s", 6057c7f8c46SGlenn Strauss r->uri.path.ptr, username->ptr, r->con->dst_addr_buf->ptr); 6067c7f8c46SGlenn Strauss r->keep_alive = 0; /*(disable keep-alive if bad password)*/ 6077ba06c71SGlenn Strauss rc = HANDLER_UNSET; 6087ba06c71SGlenn Strauss break; 60981b2d1f0SGlenn Strauss } 61081b2d1f0SGlenn Strauss 6114b3a91e6SGlenn Strauss buffer_free(username); 6127c7f8c46SGlenn Strauss return (HANDLER_UNSET != rc) ? rc : mod_auth_send_401_unauthorized_basic(r, require->realm); 61381b2d1f0SGlenn Strauss } 61481b2d1f0SGlenn Strauss 61581b2d1f0SGlenn Strauss 616b9e2be50SGlenn Strauss #ifdef USE_OPENSSL_CRYPTO 617b9e2be50SGlenn Strauss 618b9e2be50SGlenn Strauss static void mod_auth_digest_mutate_sha256(http_auth_info_t *ai, const char *m, const char *uri, const char *nonce, const char *cnonce, const char *nc, const char *qop) { 619b9e2be50SGlenn Strauss SHA256_CTX ctx; 620b9e2be50SGlenn Strauss char a1[HTTP_AUTH_DIGEST_SHA256_BINLEN*2+1]; 621b9e2be50SGlenn Strauss char a2[HTTP_AUTH_DIGEST_SHA256_BINLEN*2+1]; 622b9e2be50SGlenn Strauss 623b9e2be50SGlenn Strauss if (ai->dalgo & HTTP_AUTH_DIGEST_SESS) { 624b9e2be50SGlenn Strauss SHA256_Init(&ctx); 625b9e2be50SGlenn Strauss li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen); 626b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)a1, sizeof(a1)-1); 627b9e2be50SGlenn Strauss SHA256_Update(&ctx, CONST_STR_LEN(":")); 628b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)nonce, strlen(nonce)); 629b9e2be50SGlenn Strauss SHA256_Update(&ctx, CONST_STR_LEN(":")); 630b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)cnonce, strlen(cnonce)); 631b9e2be50SGlenn Strauss SHA256_Final(ai->digest, &ctx); 632b9e2be50SGlenn Strauss } 633b9e2be50SGlenn Strauss 634b9e2be50SGlenn Strauss li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen); 635b9e2be50SGlenn Strauss 636b9e2be50SGlenn Strauss /* calculate H(A2) */ 637b9e2be50SGlenn Strauss SHA256_Init(&ctx); 638b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)m, strlen(m)); 639b9e2be50SGlenn Strauss SHA256_Update(&ctx, CONST_STR_LEN(":")); 640b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)uri, strlen(uri)); 641b9e2be50SGlenn Strauss #if 0 642b9e2be50SGlenn Strauss /* qop=auth-int not supported, already checked in caller */ 643fc42ec12SGlenn Strauss if (qop && buffer_eq_icase_ss(qop, strlen(qop), CONST_STR_LEN("auth-int"))){ 644b9e2be50SGlenn Strauss SHA256_Update(&ctx, CONST_STR_LEN(":")); 645b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *) [body checksum], ai->dlen*2); 646b9e2be50SGlenn Strauss } 647b9e2be50SGlenn Strauss #endif 648b9e2be50SGlenn Strauss SHA256_Final(ai->digest, &ctx); 649b9e2be50SGlenn Strauss li_tohex(a2, sizeof(a2), (const char *)ai->digest, ai->dlen); 650b9e2be50SGlenn Strauss 651b9e2be50SGlenn Strauss /* calculate response */ 652b9e2be50SGlenn Strauss SHA256_Init(&ctx); 653b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)a1, sizeof(a1)-1); 654b9e2be50SGlenn Strauss SHA256_Update(&ctx, CONST_STR_LEN(":")); 655b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)nonce, strlen(nonce)); 656b9e2be50SGlenn Strauss SHA256_Update(&ctx, CONST_STR_LEN(":")); 657b9e2be50SGlenn Strauss if (qop && *qop) { 658b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)nc, strlen(nc)); 659b9e2be50SGlenn Strauss SHA256_Update(&ctx, CONST_STR_LEN(":")); 660b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)cnonce, strlen(cnonce)); 661b9e2be50SGlenn Strauss SHA256_Update(&ctx, CONST_STR_LEN(":")); 662b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)qop, strlen(qop)); 663b9e2be50SGlenn Strauss SHA256_Update(&ctx, CONST_STR_LEN(":")); 664b9e2be50SGlenn Strauss } 665b9e2be50SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)a2, sizeof(a2)-1); 666b9e2be50SGlenn Strauss SHA256_Final(ai->digest, &ctx); 667b9e2be50SGlenn Strauss } 668b9e2be50SGlenn Strauss 669*e1188e77SGlenn Strauss static void mod_auth_digest_nonce_sha256(buffer *b, time_t cur_ts, int rnd, const buffer *secret) { 670b9e2be50SGlenn Strauss SHA256_CTX ctx; 671f24e6d69SGlenn Strauss size_t len; 672b9e2be50SGlenn Strauss unsigned char h[HTTP_AUTH_DIGEST_SHA256_BINLEN]; 673b9e2be50SGlenn Strauss char hh[HTTP_AUTH_DIGEST_SHA256_BINLEN*2+1]; 674b9e2be50SGlenn Strauss force_assert(sizeof(hh) >= LI_ITOSTRING_LENGTH); 675b9e2be50SGlenn Strauss SHA256_Init(&ctx); 676f24e6d69SGlenn Strauss len = li_itostrn(hh, sizeof(hh), cur_ts); 677f24e6d69SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)hh, len); 678f24e6d69SGlenn Strauss len = li_itostrn(hh, sizeof(hh), rnd); 679f24e6d69SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)hh, len); 680*e1188e77SGlenn Strauss if (secret) { 681*e1188e77SGlenn Strauss len = buffer_string_length(secret); 682*e1188e77SGlenn Strauss SHA256_Update(&ctx, (unsigned char *)secret->ptr, len); 683*e1188e77SGlenn Strauss } 684b9e2be50SGlenn Strauss SHA256_Final(h, &ctx); 685b9e2be50SGlenn Strauss li_tohex(hh, sizeof(hh), (const char *)h, sizeof(h)); 686b9e2be50SGlenn Strauss buffer_append_string_len(b, hh, sizeof(hh)-1); 687b9e2be50SGlenn Strauss } 688b9e2be50SGlenn Strauss 689b9e2be50SGlenn Strauss #ifdef SHA512_256_DIGEST_LENGTH 690b9e2be50SGlenn Strauss 691b9e2be50SGlenn Strauss static void mod_auth_digest_mutate_sha512_256(http_auth_info_t *ai, const char *m, const char *uri, const char *nonce, const char *cnonce, const char *nc, const char *qop) { 692b9e2be50SGlenn Strauss SHA512_CTX ctx; 693b9e2be50SGlenn Strauss char a1[HTTP_AUTH_DIGEST_SHA512_256_BINLEN*2+1]; 694b9e2be50SGlenn Strauss char a2[HTTP_AUTH_DIGEST_SHA512_256_BINLEN*2+1]; 695b9e2be50SGlenn Strauss 696b9e2be50SGlenn Strauss if (ai->dalgo & HTTP_AUTH_DIGEST_SESS) { 697b9e2be50SGlenn Strauss SHA512_256_Init(&ctx); 698b9e2be50SGlenn Strauss li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen); 699b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)a1, sizeof(a1)-1); 700b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 701b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)nonce, strlen(nonce)); 702b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 703b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)cnonce, strlen(cnonce)); 704b9e2be50SGlenn Strauss SHA512_256_Final(ai->digest, &ctx); 705b9e2be50SGlenn Strauss } 706b9e2be50SGlenn Strauss 707b9e2be50SGlenn Strauss li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen); 708b9e2be50SGlenn Strauss 709b9e2be50SGlenn Strauss /* calculate H(A2) */ 710b9e2be50SGlenn Strauss SHA512_256_Init(&ctx); 711b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)m, strlen(m)); 712b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 713b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)uri, strlen(uri)); 714b9e2be50SGlenn Strauss #if 0 715b9e2be50SGlenn Strauss /* qop=auth-int not supported, already checked in caller */ 716fc42ec12SGlenn Strauss if (qop && buffer_eq_icase_ss(qop, strlen(qop), CONST_STR_LEN("auth-int"))){ 717b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 718b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)[body checksum], ai->dlen*2); 719b9e2be50SGlenn Strauss } 720b9e2be50SGlenn Strauss #endif 721b9e2be50SGlenn Strauss SHA512_256_Final(ai->digest, &ctx); 722b9e2be50SGlenn Strauss li_tohex(a2, sizeof(a2), (const char *)ai->digest, ai->dlen); 723b9e2be50SGlenn Strauss 724b9e2be50SGlenn Strauss /* calculate response */ 725b9e2be50SGlenn Strauss SHA512_256_Init(&ctx); 726b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)a1, sizeof(a1)-1); 727b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 728b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)nonce, strlen(nonce)); 729b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 730b9e2be50SGlenn Strauss if (qop && *qop) { 731b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)nc, strlen(nc)); 732b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 733b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)cnonce, strlen(cnonce)); 734b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 735b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)qop, strlen(qop)); 736b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, CONST_STR_LEN(":")); 737b9e2be50SGlenn Strauss } 738b9e2be50SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)a2, sizeof(a2)-1); 739b9e2be50SGlenn Strauss SHA512_256_Final(ai->digest, &ctx); 740b9e2be50SGlenn Strauss } 741b9e2be50SGlenn Strauss 742*e1188e77SGlenn Strauss static void mod_auth_digest_nonce_sha512_256(buffer *b, time_t cur_ts, int rnd, const buffer *secret) { 743b9e2be50SGlenn Strauss SHA512_CTX ctx; 744f24e6d69SGlenn Strauss size_t len; 745b9e2be50SGlenn Strauss unsigned char h[HTTP_AUTH_DIGEST_SHA512_256_BINLEN]; 746b9e2be50SGlenn Strauss char hh[HTTP_AUTH_DIGEST_SHA512_256_BINLEN*2+1]; 747b9e2be50SGlenn Strauss force_assert(sizeof(hh) >= LI_ITOSTRING_LENGTH); 748b9e2be50SGlenn Strauss SHA512_256_Init(&ctx); 749f24e6d69SGlenn Strauss len = li_itostrn(hh, sizeof(hh), cur_ts); 750f24e6d69SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)hh, len); 751f24e6d69SGlenn Strauss len = li_itostrn(hh, sizeof(hh), rnd); 752f24e6d69SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)hh, len); 753*e1188e77SGlenn Strauss if (secret) { 754*e1188e77SGlenn Strauss len = buffer_string_length(secret); 755*e1188e77SGlenn Strauss SHA512_256_Update(&ctx, (unsigned char *)secret->ptr, len); 756*e1188e77SGlenn Strauss } 757b9e2be50SGlenn Strauss SHA512_256_Final(h, &ctx); 758b9e2be50SGlenn Strauss li_tohex(hh, sizeof(hh), (const char *)h, sizeof(h)); 759b9e2be50SGlenn Strauss buffer_append_string_len(b, hh, sizeof(hh)-1); 760b9e2be50SGlenn Strauss } 761b9e2be50SGlenn Strauss 762b9e2be50SGlenn Strauss #endif /* SHA512_256_DIGEST_LENGTH */ 763b9e2be50SGlenn Strauss 764b9e2be50SGlenn Strauss #endif /* USE_OPENSSL_CRYPTO */ 765b9e2be50SGlenn Strauss 766b9e2be50SGlenn Strauss static void mod_auth_digest_mutate_md5(http_auth_info_t *ai, const char *m, const char *uri, const char *nonce, const char *cnonce, const char *nc, const char *qop) { 767b9e2be50SGlenn Strauss li_MD5_CTX ctx; 768b9e2be50SGlenn Strauss char a1[HTTP_AUTH_DIGEST_MD5_BINLEN*2+1]; 769b9e2be50SGlenn Strauss char a2[HTTP_AUTH_DIGEST_MD5_BINLEN*2+1]; 770b9e2be50SGlenn Strauss 771b9e2be50SGlenn Strauss if (ai->dalgo & HTTP_AUTH_DIGEST_SESS) { 772b9e2be50SGlenn Strauss li_MD5_Init(&ctx); 773b9e2be50SGlenn Strauss /* http://www.rfc-editor.org/errata_search.php?rfc=2617 774b9e2be50SGlenn Strauss * Errata ID: 1649 */ 775b9e2be50SGlenn Strauss li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen); 776b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)a1, sizeof(a1)-1); 777b9e2be50SGlenn Strauss li_MD5_Update(&ctx, CONST_STR_LEN(":")); 778b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)nonce, strlen(nonce)); 779b9e2be50SGlenn Strauss li_MD5_Update(&ctx, CONST_STR_LEN(":")); 780b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)cnonce, strlen(cnonce)); 781b9e2be50SGlenn Strauss li_MD5_Final(ai->digest, &ctx); 782b9e2be50SGlenn Strauss } 783b9e2be50SGlenn Strauss 784b9e2be50SGlenn Strauss li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen); 785b9e2be50SGlenn Strauss 786b9e2be50SGlenn Strauss /* calculate H(A2) */ 787b9e2be50SGlenn Strauss li_MD5_Init(&ctx); 788b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)m, strlen(m)); 789b9e2be50SGlenn Strauss li_MD5_Update(&ctx, CONST_STR_LEN(":")); 790b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)uri, strlen(uri)); 791b9e2be50SGlenn Strauss #if 0 792b9e2be50SGlenn Strauss /* qop=auth-int not supported, already checked in caller */ 793fc42ec12SGlenn Strauss if (qop && buffer_eq_icase_ss(qop, strlen(qop), CONST_STR_LEN("auth-int"))){ 794b9e2be50SGlenn Strauss li_MD5_Update(&ctx, CONST_STR_LEN(":")); 795b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *) [body checksum], ai->dlen*2); 796b9e2be50SGlenn Strauss } 797b9e2be50SGlenn Strauss #endif 798b9e2be50SGlenn Strauss li_MD5_Final(ai->digest, &ctx); 799b9e2be50SGlenn Strauss li_tohex(a2, sizeof(a2), (const char *)ai->digest, ai->dlen); 800b9e2be50SGlenn Strauss 801b9e2be50SGlenn Strauss /* calculate response */ 802b9e2be50SGlenn Strauss li_MD5_Init(&ctx); 803b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)a1, sizeof(a1)-1); 804b9e2be50SGlenn Strauss li_MD5_Update(&ctx, CONST_STR_LEN(":")); 805b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)nonce, strlen(nonce)); 806b9e2be50SGlenn Strauss li_MD5_Update(&ctx, CONST_STR_LEN(":")); 807b9e2be50SGlenn Strauss if (qop && *qop) { 808b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)nc, strlen(nc)); 809b9e2be50SGlenn Strauss li_MD5_Update(&ctx, CONST_STR_LEN(":")); 810b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)cnonce, strlen(cnonce)); 811b9e2be50SGlenn Strauss li_MD5_Update(&ctx, CONST_STR_LEN(":")); 812b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)qop, strlen(qop)); 813b9e2be50SGlenn Strauss li_MD5_Update(&ctx, CONST_STR_LEN(":")); 814b9e2be50SGlenn Strauss } 815b9e2be50SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)a2, sizeof(a2)-1); 816b9e2be50SGlenn Strauss li_MD5_Final(ai->digest, &ctx); 817b9e2be50SGlenn Strauss } 818b9e2be50SGlenn Strauss 819*e1188e77SGlenn Strauss static void mod_auth_digest_nonce_md5(buffer *b, time_t cur_ts, int rnd, const buffer *secret) { 820b9e2be50SGlenn Strauss li_MD5_CTX ctx; 821f24e6d69SGlenn Strauss size_t len; 822b9e2be50SGlenn Strauss unsigned char h[HTTP_AUTH_DIGEST_MD5_BINLEN]; 823b9e2be50SGlenn Strauss char hh[HTTP_AUTH_DIGEST_MD5_BINLEN*2+1]; 824b9e2be50SGlenn Strauss force_assert(sizeof(hh) >= LI_ITOSTRING_LENGTH); 825b9e2be50SGlenn Strauss li_MD5_Init(&ctx); 826f24e6d69SGlenn Strauss len = li_itostrn(hh, sizeof(hh), cur_ts); 827f24e6d69SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)hh, len); 828f24e6d69SGlenn Strauss len = li_itostrn(hh, sizeof(hh), rnd); 829f24e6d69SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)hh, len); 830*e1188e77SGlenn Strauss if (secret) { 831*e1188e77SGlenn Strauss len = buffer_string_length(secret); 832*e1188e77SGlenn Strauss li_MD5_Update(&ctx, (unsigned char *)secret->ptr, len); 833*e1188e77SGlenn Strauss } 834b9e2be50SGlenn Strauss li_MD5_Final(h, &ctx); 835b9e2be50SGlenn Strauss li_tohex(hh, sizeof(hh), (const char *)h, sizeof(h)); 836b9e2be50SGlenn Strauss buffer_append_string_len(b, hh, sizeof(hh)-1); 837b9e2be50SGlenn Strauss } 838b9e2be50SGlenn Strauss 839b9e2be50SGlenn Strauss static void mod_auth_digest_mutate(http_auth_info_t *ai, const char *m, const char *uri, const char *nonce, const char *cnonce, const char *nc, const char *qop) { 840b9e2be50SGlenn Strauss if (ai->dalgo & HTTP_AUTH_DIGEST_MD5) 841b9e2be50SGlenn Strauss mod_auth_digest_mutate_md5(ai, m, uri, nonce, cnonce, nc, qop); 842b9e2be50SGlenn Strauss #ifdef USE_OPENSSL_CRYPTO 843b9e2be50SGlenn Strauss else if (ai->dalgo & HTTP_AUTH_DIGEST_SHA256) 844b9e2be50SGlenn Strauss mod_auth_digest_mutate_sha256(ai, m, uri, nonce, cnonce, nc, qop); 845b9e2be50SGlenn Strauss #ifdef SHA512_256_DIGEST_LENGTH 846b9e2be50SGlenn Strauss else if (ai->dalgo & HTTP_AUTH_DIGEST_SHA512_256) 847b9e2be50SGlenn Strauss mod_auth_digest_mutate_sha512_256(ai, m, uri, nonce, cnonce, nc, qop); 848b9e2be50SGlenn Strauss #endif 849b9e2be50SGlenn Strauss #endif 850b9e2be50SGlenn Strauss } 851b9e2be50SGlenn Strauss 852*e1188e77SGlenn Strauss static void mod_auth_append_nonce(buffer *b, time_t cur_ts, const struct http_auth_require_t *require, int dalgo, int *rndptr) { 853*e1188e77SGlenn Strauss buffer_append_uint_hex(b, (uintmax_t)cur_ts); 854*e1188e77SGlenn Strauss buffer_append_string_len(b, CONST_STR_LEN(":")); 855*e1188e77SGlenn Strauss const buffer * const nonce_secret = require->nonce_secret; 856*e1188e77SGlenn Strauss int rnd; 857*e1188e77SGlenn Strauss if (NULL == nonce_secret) 858*e1188e77SGlenn Strauss rnd = rndptr ? *rndptr : li_rand_pseudo(); 859*e1188e77SGlenn Strauss else { /*(do not directly expose random number generator single value)*/ 860*e1188e77SGlenn Strauss rndptr 861*e1188e77SGlenn Strauss ? (void)(rnd = *rndptr) 862*e1188e77SGlenn Strauss : li_rand_pseudo_bytes((unsigned char *)&rnd, sizeof(rnd)); 863*e1188e77SGlenn Strauss buffer_append_uint_hex(b, (uintmax_t)rnd); 864*e1188e77SGlenn Strauss buffer_append_string_len(b, CONST_STR_LEN(":")); 865*e1188e77SGlenn Strauss } 866*e1188e77SGlenn Strauss switch (dalgo) { 867*e1188e77SGlenn Strauss #ifdef USE_OPENSSL_CRYPTO 868*e1188e77SGlenn Strauss #ifdef SHA512_256_DIGEST_LENGTH 869*e1188e77SGlenn Strauss case HTTP_AUTH_DIGEST_SHA512_256: 870*e1188e77SGlenn Strauss mod_auth_digest_nonce_sha512_256(b, cur_ts, rnd, nonce_secret); 871*e1188e77SGlenn Strauss break; 872*e1188e77SGlenn Strauss #endif 873*e1188e77SGlenn Strauss case HTTP_AUTH_DIGEST_SHA256: 874*e1188e77SGlenn Strauss mod_auth_digest_nonce_sha256(b, cur_ts, rnd, nonce_secret); 875*e1188e77SGlenn Strauss break; 876*e1188e77SGlenn Strauss #endif 877*e1188e77SGlenn Strauss /*case HTTP_AUTH_DIGEST_MD5:*/ 878*e1188e77SGlenn Strauss default: 879*e1188e77SGlenn Strauss mod_auth_digest_nonce_md5(b, cur_ts, rnd, nonce_secret); 880*e1188e77SGlenn Strauss break; 881*e1188e77SGlenn Strauss } 882*e1188e77SGlenn Strauss } 883*e1188e77SGlenn Strauss 884b9e2be50SGlenn Strauss static void mod_auth_digest_www_authenticate(buffer *b, time_t cur_ts, const struct http_auth_require_t *require, int nonce_stale) { 885b9e2be50SGlenn Strauss int algos = nonce_stale ? nonce_stale : require->algorithm; 886b9e2be50SGlenn Strauss int n = 0; 887*e1188e77SGlenn Strauss int algoid[3]; 888b9e2be50SGlenn Strauss unsigned int algolen[3]; 889b9e2be50SGlenn Strauss const char *algoname[3]; 890b9e2be50SGlenn Strauss #ifdef USE_OPENSSL_CRYPTO 891b9e2be50SGlenn Strauss #ifdef SHA512_256_DIGEST_LENGTH 892b9e2be50SGlenn Strauss if (algos & HTTP_AUTH_DIGEST_SHA512_256) { 893*e1188e77SGlenn Strauss algoid[n] = HTTP_AUTH_DIGEST_SHA512_256; 894b9e2be50SGlenn Strauss algoname[n] = "SHA-512-256"; 895b9e2be50SGlenn Strauss algolen[n] = sizeof("SHA-512-256")-1; 896b9e2be50SGlenn Strauss ++n; 897b9e2be50SGlenn Strauss } 898b9e2be50SGlenn Strauss #endif 899b9e2be50SGlenn Strauss if (algos & HTTP_AUTH_DIGEST_SHA256) { 900*e1188e77SGlenn Strauss algoid[n] = HTTP_AUTH_DIGEST_SHA256; 901b9e2be50SGlenn Strauss algoname[n] = "SHA-256"; 902b9e2be50SGlenn Strauss algolen[n] = sizeof("SHA-256")-1; 903b9e2be50SGlenn Strauss ++n; 904b9e2be50SGlenn Strauss } 905b9e2be50SGlenn Strauss #endif 906b9e2be50SGlenn Strauss if (algos & HTTP_AUTH_DIGEST_MD5) { 907*e1188e77SGlenn Strauss algoid[n] = HTTP_AUTH_DIGEST_MD5; 908b9e2be50SGlenn Strauss algoname[n] = "MD5"; 909b9e2be50SGlenn Strauss algolen[n] = sizeof("MD5")-1; 910b9e2be50SGlenn Strauss ++n; 911b9e2be50SGlenn Strauss } 912b9e2be50SGlenn Strauss 913b9e2be50SGlenn Strauss buffer_clear(b); 914b9e2be50SGlenn Strauss for (int i = 0; i < n; ++i) { 915b9e2be50SGlenn Strauss if (i > 0) { 916b9e2be50SGlenn Strauss buffer_append_string_len(b,CONST_STR_LEN("\r\nWWW-Authenticate: ")); 917b9e2be50SGlenn Strauss } 918b9e2be50SGlenn Strauss buffer_append_string_len(b, CONST_STR_LEN("Digest realm=\"")); 919b9e2be50SGlenn Strauss buffer_append_string_buffer(b, require->realm); 9201799e4c3SGlenn Strauss buffer_append_string_len(b, CONST_STR_LEN("\", charset=\"UTF-8\", algorithm=")); 921b9e2be50SGlenn Strauss buffer_append_string_len(b, algoname[i], algolen[i]); 9221799e4c3SGlenn Strauss buffer_append_string_len(b, CONST_STR_LEN(", nonce=\"")); 923*e1188e77SGlenn Strauss mod_auth_append_nonce(b, cur_ts, require, algoid[i], NULL); 924b9e2be50SGlenn Strauss buffer_append_string_len(b, CONST_STR_LEN("\", qop=\"auth\"")); 925b9e2be50SGlenn Strauss if (nonce_stale) { 926b9e2be50SGlenn Strauss buffer_append_string_len(b, CONST_STR_LEN(", stale=true")); 927b9e2be50SGlenn Strauss } 928b9e2be50SGlenn Strauss } 92981b2d1f0SGlenn Strauss } 93081b2d1f0SGlenn Strauss 931*e1188e77SGlenn Strauss static void mod_auth_digest_authentication_info(buffer *b, time_t cur_ts, const struct http_auth_require_t *require, int dalgo) { 9326ad325c6SGlenn Strauss buffer_clear(b); 9336ad325c6SGlenn Strauss buffer_append_string_len(b, CONST_STR_LEN("nextnonce=\"")); 934*e1188e77SGlenn Strauss mod_auth_append_nonce(b, cur_ts, require, dalgo, NULL); 9356ad325c6SGlenn Strauss buffer_append_string_len(b, CONST_STR_LEN("\"")); 9366ad325c6SGlenn Strauss } 9376ad325c6SGlenn Strauss 93881b2d1f0SGlenn Strauss typedef struct { 93981b2d1f0SGlenn Strauss const char *key; 94081b2d1f0SGlenn Strauss int key_len; 94181b2d1f0SGlenn Strauss char **ptr; 94281b2d1f0SGlenn Strauss } digest_kv; 94381b2d1f0SGlenn Strauss 9447c7f8c46SGlenn Strauss static handler_t mod_auth_send_401_unauthorized_digest(request_st *r, const struct http_auth_require_t *require, int nonce_stale); 9457ba06c71SGlenn Strauss 9467c7f8c46SGlenn Strauss static handler_t mod_auth_check_digest(request_st * const r, void *p_d, const struct http_auth_require_t * const require, const struct http_auth_backend_t * const backend) { 9477c7f8c46SGlenn Strauss const buffer *vb = http_header_request_get(r, HTTP_HEADER_AUTHORIZATION, CONST_STR_LEN("Authorization")); 9487ba06c71SGlenn Strauss 94981b2d1f0SGlenn Strauss char *username = NULL; 95081b2d1f0SGlenn Strauss char *realm = NULL; 95181b2d1f0SGlenn Strauss char *nonce = NULL; 95281b2d1f0SGlenn Strauss char *uri = NULL; 95381b2d1f0SGlenn Strauss char *algorithm = NULL; 95481b2d1f0SGlenn Strauss char *qop = NULL; 95581b2d1f0SGlenn Strauss char *cnonce = NULL; 95681b2d1f0SGlenn Strauss char *nc = NULL; 95781b2d1f0SGlenn Strauss char *respons = NULL; 95881b2d1f0SGlenn Strauss 95981b2d1f0SGlenn Strauss char *e, *c; 96081b2d1f0SGlenn Strauss int i; 96181b2d1f0SGlenn Strauss buffer *b; 96260f4cf3aSGlenn Strauss http_auth_info_t ai; 963b9e2be50SGlenn Strauss unsigned char rdigest[HTTP_AUTH_DIGEST_SHA256_BINLEN]; 96481b2d1f0SGlenn Strauss 96581b2d1f0SGlenn Strauss 96681b2d1f0SGlenn Strauss /* init pointers */ 96781b2d1f0SGlenn Strauss #define S(x) \ 96881b2d1f0SGlenn Strauss x, sizeof(x)-1, NULL 96981b2d1f0SGlenn Strauss digest_kv dkv[10] = { 97081b2d1f0SGlenn Strauss { S("username=") }, 97181b2d1f0SGlenn Strauss { S("realm=") }, 97281b2d1f0SGlenn Strauss { S("nonce=") }, 97381b2d1f0SGlenn Strauss { S("uri=") }, 97481b2d1f0SGlenn Strauss { S("algorithm=") }, 97581b2d1f0SGlenn Strauss { S("qop=") }, 97681b2d1f0SGlenn Strauss { S("cnonce=") }, 97781b2d1f0SGlenn Strauss { S("nc=") }, 97881b2d1f0SGlenn Strauss { S("response=") }, 97981b2d1f0SGlenn Strauss 98081b2d1f0SGlenn Strauss { NULL, 0, NULL } 98181b2d1f0SGlenn Strauss }; 98281b2d1f0SGlenn Strauss #undef S 98381b2d1f0SGlenn Strauss 98481b2d1f0SGlenn Strauss dkv[0].ptr = &username; 98581b2d1f0SGlenn Strauss dkv[1].ptr = &realm; 98681b2d1f0SGlenn Strauss dkv[2].ptr = &nonce; 98781b2d1f0SGlenn Strauss dkv[3].ptr = &uri; 98881b2d1f0SGlenn Strauss dkv[4].ptr = &algorithm; 98981b2d1f0SGlenn Strauss dkv[5].ptr = &qop; 99081b2d1f0SGlenn Strauss dkv[6].ptr = &cnonce; 99181b2d1f0SGlenn Strauss dkv[7].ptr = &nc; 99281b2d1f0SGlenn Strauss dkv[8].ptr = &respons; 99381b2d1f0SGlenn Strauss 9947ba06c71SGlenn Strauss UNUSED(p_d); 9957ba06c71SGlenn Strauss 9967ba06c71SGlenn Strauss if (NULL == backend) { 9977c7f8c46SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__, 9987c7f8c46SGlenn Strauss "auth.backend not configured for %s", r->uri.path.ptr); 9997c7f8c46SGlenn Strauss r->http_status = 500; 10007c7f8c46SGlenn Strauss r->handler_module = NULL; 10017ba06c71SGlenn Strauss return HANDLER_FINISHED; 10027ba06c71SGlenn Strauss } 10037ba06c71SGlenn Strauss 10043dd3cde9SGlenn Strauss if (NULL == vb) { 10057c7f8c46SGlenn Strauss return mod_auth_send_401_unauthorized_digest(r, require, 0); 10067ba06c71SGlenn Strauss } 10077ba06c71SGlenn Strauss 100813008156SGlenn Strauss if (!buffer_eq_icase_ssn(vb->ptr, CONST_STR_LEN("Digest "))) { 10097c7f8c46SGlenn Strauss return mod_auth_send_401_unauthorized_digest(r, require, 0); 1010dadfb5fcSGlenn Strauss } else { 10113dd3cde9SGlenn Strauss size_t n = buffer_string_length(vb); 10129bc61f16SGlenn Strauss #ifdef __COVERITY__ 1013dadfb5fcSGlenn Strauss if (n < sizeof("Digest ")-1) { 10147c7f8c46SGlenn Strauss return mod_auth_send_400_bad_request(r); 10159bc61f16SGlenn Strauss } 10169bc61f16SGlenn Strauss #endif 1017dadfb5fcSGlenn Strauss n -= (sizeof("Digest ")-1); 10187ba06c71SGlenn Strauss b = buffer_init(); 10193dd3cde9SGlenn Strauss buffer_copy_string_len(b,vb->ptr+sizeof("Digest ")-1,n); 1020dadfb5fcSGlenn Strauss } 102181b2d1f0SGlenn Strauss 102281b2d1f0SGlenn Strauss /* parse credentials from client */ 102381b2d1f0SGlenn Strauss for (c = b->ptr; *c; c++) { 102481b2d1f0SGlenn Strauss /* skip whitespaces */ 102581b2d1f0SGlenn Strauss while (*c == ' ' || *c == '\t') c++; 102681b2d1f0SGlenn Strauss if (!*c) break; 102781b2d1f0SGlenn Strauss 102881b2d1f0SGlenn Strauss for (i = 0; dkv[i].key; i++) { 102981b2d1f0SGlenn Strauss if ((0 == strncmp(c, dkv[i].key, dkv[i].key_len))) { 103081b2d1f0SGlenn Strauss if ((c[dkv[i].key_len] == '"') && 103181b2d1f0SGlenn Strauss (NULL != (e = strchr(c + dkv[i].key_len + 1, '"')))) { 103281b2d1f0SGlenn Strauss /* value with "..." */ 103381b2d1f0SGlenn Strauss *(dkv[i].ptr) = c + dkv[i].key_len + 1; 103481b2d1f0SGlenn Strauss c = e; 103581b2d1f0SGlenn Strauss 103681b2d1f0SGlenn Strauss *e = '\0'; 103781b2d1f0SGlenn Strauss } else if (NULL != (e = strchr(c + dkv[i].key_len, ','))) { 103881b2d1f0SGlenn Strauss /* value without "...", terminated by ',' */ 103981b2d1f0SGlenn Strauss *(dkv[i].ptr) = c + dkv[i].key_len; 104081b2d1f0SGlenn Strauss c = e; 104181b2d1f0SGlenn Strauss 104281b2d1f0SGlenn Strauss *e = '\0'; 104381b2d1f0SGlenn Strauss } else { 104481b2d1f0SGlenn Strauss /* value without "...", terminated by EOL */ 104581b2d1f0SGlenn Strauss *(dkv[i].ptr) = c + dkv[i].key_len; 104681b2d1f0SGlenn Strauss c += strlen(c) - 1; 104781b2d1f0SGlenn Strauss } 104881b2d1f0SGlenn Strauss break; 104981b2d1f0SGlenn Strauss } 105081b2d1f0SGlenn Strauss } 105181b2d1f0SGlenn Strauss } 105281b2d1f0SGlenn Strauss 105381b2d1f0SGlenn Strauss /* check if everything is transmitted */ 105481b2d1f0SGlenn Strauss if (!username || 105581b2d1f0SGlenn Strauss !realm || 105681b2d1f0SGlenn Strauss !nonce || 105781b2d1f0SGlenn Strauss !uri || 105881b2d1f0SGlenn Strauss (qop && (!nc || !cnonce)) || 105981b2d1f0SGlenn Strauss !respons ) { 106081b2d1f0SGlenn Strauss /* missing field */ 106181b2d1f0SGlenn Strauss 10627c7f8c46SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__, 106381b2d1f0SGlenn Strauss "digest: missing field"); 106481b2d1f0SGlenn Strauss 106581b2d1f0SGlenn Strauss buffer_free(b); 10667c7f8c46SGlenn Strauss return mod_auth_send_400_bad_request(r); 106781b2d1f0SGlenn Strauss } 106881b2d1f0SGlenn Strauss 106960f4cf3aSGlenn Strauss ai.username = username; 107060f4cf3aSGlenn Strauss ai.ulen = strlen(username); 107160f4cf3aSGlenn Strauss ai.realm = realm; 107260f4cf3aSGlenn Strauss ai.rlen = strlen(realm); 107360f4cf3aSGlenn Strauss 107460f4cf3aSGlenn Strauss if (!buffer_is_equal_string(require->realm, ai.realm, ai.rlen)) { 10757c7f8c46SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__, 10765a6e4df8SGlenn Strauss "digest: realm mismatch"); 10775a6e4df8SGlenn Strauss buffer_free(b); 10787c7f8c46SGlenn Strauss return mod_auth_send_401_unauthorized_digest(r, require, 0); 10795a6e4df8SGlenn Strauss } 10805a6e4df8SGlenn Strauss 1081b9e2be50SGlenn Strauss if (!mod_auth_algorithm_parse(&ai, algorithm) 1082b9e2be50SGlenn Strauss || !(require->algorithm & ai.dalgo & ~HTTP_AUTH_DIGEST_SESS)) { 10837c7f8c46SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__, 1084010c2894SGlenn Strauss "digest: (%s): invalid", algorithm); 1085b9e2be50SGlenn Strauss buffer_free(b); 10867c7f8c46SGlenn Strauss return mod_auth_send_400_bad_request(r); 1087b9e2be50SGlenn Strauss } 1088b9e2be50SGlenn Strauss 108981b2d1f0SGlenn Strauss /** 109081b2d1f0SGlenn Strauss * protect the md5-sess against missing cnonce and nonce 109181b2d1f0SGlenn Strauss */ 1092b9e2be50SGlenn Strauss if ((ai.dalgo & HTTP_AUTH_DIGEST_SESS) && (!nonce || !cnonce)) { 10937c7f8c46SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__, 1094010c2894SGlenn Strauss "digest: (%s): missing field", algorithm); 109581b2d1f0SGlenn Strauss 109681b2d1f0SGlenn Strauss buffer_free(b); 10977c7f8c46SGlenn Strauss return mod_auth_send_400_bad_request(r); 109881b2d1f0SGlenn Strauss } 109981b2d1f0SGlenn Strauss 1100b9e2be50SGlenn Strauss { 1101b9e2be50SGlenn Strauss size_t resplen = strlen(respons); 1102b9e2be50SGlenn Strauss if (0 != http_auth_digest_hex2bin(respons, resplen, 1103b9e2be50SGlenn Strauss rdigest, sizeof(rdigest)) 1104b9e2be50SGlenn Strauss || resplen != (ai.dlen << 1)) { 11057c7f8c46SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__, 1106010c2894SGlenn Strauss "digest: (%s): invalid format", respons); 1107b9e2be50SGlenn Strauss buffer_free(b); 11087c7f8c46SGlenn Strauss return mod_auth_send_400_bad_request(r); 1109b9e2be50SGlenn Strauss } 1110b9e2be50SGlenn Strauss } 1111b9e2be50SGlenn Strauss 1112fc42ec12SGlenn Strauss if (qop && buffer_eq_icase_ss(qop, strlen(qop), CONST_STR_LEN("auth-int"))){ 11137c7f8c46SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__, 111481b2d1f0SGlenn Strauss "digest: qop=auth-int not supported"); 111581b2d1f0SGlenn Strauss 111681b2d1f0SGlenn Strauss buffer_free(b); 11177c7f8c46SGlenn Strauss return mod_auth_send_400_bad_request(r); 111881b2d1f0SGlenn Strauss } 111981b2d1f0SGlenn Strauss 112081b2d1f0SGlenn Strauss /* detect if attacker is attempting to reuse valid digest for one uri 112181b2d1f0SGlenn Strauss * on a different request uri. Might also happen if intermediate proxy 112281b2d1f0SGlenn Strauss * altered client request line. (Altered request would not result in 112381b2d1f0SGlenn Strauss * the same digest as that calculated by the client.) 112481b2d1f0SGlenn Strauss * Internal redirects such as with mod_rewrite will modify request uri. 112581b2d1f0SGlenn Strauss * Reauthentication is done to detect crossing auth realms, but this 11267c7f8c46SGlenn Strauss * uri validation step is bypassed. r->target_orig is 11278131e439SGlenn Strauss * original request-target sent in client request. */ 112881b2d1f0SGlenn Strauss { 112981b2d1f0SGlenn Strauss const size_t ulen = strlen(uri); 11307c7f8c46SGlenn Strauss if (!buffer_is_equal_string(&r->target_orig, uri, ulen)) { 11317c7f8c46SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__, 1132010c2894SGlenn Strauss "digest: auth failed: uri mismatch (%s != %s), IP: %s", 11337c7f8c46SGlenn Strauss r->target_orig.ptr, uri, r->con->dst_addr_buf->ptr); 113481b2d1f0SGlenn Strauss buffer_free(b); 11357c7f8c46SGlenn Strauss return mod_auth_send_400_bad_request(r); 113681b2d1f0SGlenn Strauss } 113781b2d1f0SGlenn Strauss } 113881b2d1f0SGlenn Strauss 1139*e1188e77SGlenn Strauss /* check age of nonce. Note, random data is used in nonce generation 1140*e1188e77SGlenn Strauss * in mod_auth_send_401_unauthorized_digest(). If that were replaced 1141*e1188e77SGlenn Strauss * with nanosecond time, then nonce secret would remain unique enough 1142*e1188e77SGlenn Strauss * for the purposes of Digest auth, and would be reproducible (and 1143*e1188e77SGlenn Strauss * verifiable) if nanoseconds were included with seconds as part of the 1144*e1188e77SGlenn Strauss * nonce "timestamp:secret". However, doing so would expose a high 1145*e1188e77SGlenn Strauss * precision timestamp of the system to attackers. timestamp in nonce 1146*e1188e77SGlenn Strauss * could theoretically be modified and still produce same md5sum, but 1147*e1188e77SGlenn Strauss * that is highly unlikely within a 10 min (moving) window of valid 1148*e1188e77SGlenn Strauss * time relative to current time (now). 1149*e1188e77SGlenn Strauss * If it is desired to validate that nonces were generated by server, 1150*e1188e77SGlenn Strauss * then specify auth.require = ( ... => ( "secret" => "..." ) ) 1151*e1188e77SGlenn Strauss * When secret is specified, then instead of nanoseconds, the random 1152*e1188e77SGlenn Strauss * data value (included for unique nonces) will be exposed in the nonce 1153*e1188e77SGlenn Strauss * along with the timestamp, and the additional secret will be used to 1154*e1188e77SGlenn Strauss * validate that the server generated the nonce using that secret. */ 1155*e1188e77SGlenn Strauss int send_nextnonce; 1156*e1188e77SGlenn Strauss { 1157*e1188e77SGlenn Strauss time_t ts = 0; 1158*e1188e77SGlenn Strauss const unsigned char * const nonce_uns = (unsigned char *)nonce; 1159*e1188e77SGlenn Strauss for (i = 0; i < 8 && light_isxdigit(nonce_uns[i]); ++i) { 1160*e1188e77SGlenn Strauss ts = (ts << 4) + hex2int(nonce_uns[i]); 1161*e1188e77SGlenn Strauss } 1162*e1188e77SGlenn Strauss const time_t cur_ts = log_epoch_secs; 1163*e1188e77SGlenn Strauss if (nonce[i] != ':' 1164*e1188e77SGlenn Strauss || ts > cur_ts || cur_ts - ts > 600) { /*(10 mins)*/ 1165*e1188e77SGlenn Strauss /* nonce is stale; have client regenerate digest */ 1166*e1188e77SGlenn Strauss buffer_free(b); 1167*e1188e77SGlenn Strauss return mod_auth_send_401_unauthorized_digest(r, require, ai.dalgo); 1168*e1188e77SGlenn Strauss } 1169*e1188e77SGlenn Strauss 1170*e1188e77SGlenn Strauss send_nextnonce = (cur_ts - ts > 540); /*(9 mins)*/ 1171*e1188e77SGlenn Strauss 1172*e1188e77SGlenn Strauss if (require->nonce_secret) { 1173*e1188e77SGlenn Strauss unsigned int rnd = 0; 1174*e1188e77SGlenn Strauss for (int j = i+8; i < j && light_isxdigit(nonce_uns[i]); ++i) { 1175*e1188e77SGlenn Strauss rnd = (rnd << 4) + hex2int(nonce_uns[i]); 1176*e1188e77SGlenn Strauss } 1177*e1188e77SGlenn Strauss if (nonce[i] != ':') { 1178*e1188e77SGlenn Strauss /* nonce is invalid; 1179*e1188e77SGlenn Strauss * expect extra field w/ require->nonce_secret */ 1180*e1188e77SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__, 1181*e1188e77SGlenn Strauss "digest: nonce invalid"); 1182*e1188e77SGlenn Strauss buffer_free(b); 1183*e1188e77SGlenn Strauss return mod_auth_send_400_bad_request(r); 1184*e1188e77SGlenn Strauss } 1185*e1188e77SGlenn Strauss buffer * const tb = r->tmp_buf; 1186*e1188e77SGlenn Strauss buffer_clear(tb); 1187*e1188e77SGlenn Strauss mod_auth_append_nonce(tb, cur_ts, require, ai.dalgo, (int *)&rnd); 1188*e1188e77SGlenn Strauss if (!buffer_eq_slen(tb, nonce, strlen(nonce))) { 1189*e1188e77SGlenn Strauss /* nonce not generated using current require->nonce_secret */ 1190*e1188e77SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__, 1191*e1188e77SGlenn Strauss "digest: nonce mismatch"); 1192*e1188e77SGlenn Strauss buffer_free(b); 1193*e1188e77SGlenn Strauss return mod_auth_send_401_unauthorized_digest(r, require, 0); 1194*e1188e77SGlenn Strauss } 1195*e1188e77SGlenn Strauss } 1196*e1188e77SGlenn Strauss } 1197*e1188e77SGlenn Strauss 11987c7f8c46SGlenn Strauss switch (backend->digest(r, backend->p_d, &ai)) { 11994b3a91e6SGlenn Strauss case HANDLER_GO_ON: 12004b3a91e6SGlenn Strauss break; 12014b3a91e6SGlenn Strauss case HANDLER_WAIT_FOR_EVENT: 120281b2d1f0SGlenn Strauss buffer_free(b); 12034b3a91e6SGlenn Strauss return HANDLER_WAIT_FOR_EVENT; 12044b3a91e6SGlenn Strauss case HANDLER_FINISHED: 12054b3a91e6SGlenn Strauss buffer_free(b); 12064b3a91e6SGlenn Strauss return HANDLER_FINISHED; 12074b3a91e6SGlenn Strauss case HANDLER_ERROR: 12084b3a91e6SGlenn Strauss default: 12097c7f8c46SGlenn Strauss r->keep_alive = 0; /*(disable keep-alive if unknown user)*/ 12104b3a91e6SGlenn Strauss buffer_free(b); 12117c7f8c46SGlenn Strauss return mod_auth_send_401_unauthorized_digest(r, require, 0); 121281b2d1f0SGlenn Strauss } 121381b2d1f0SGlenn Strauss 1214*e1188e77SGlenn Strauss const char *m = get_http_method_name(r->http_method); 1215*e1188e77SGlenn Strauss force_assert(m); 1216*e1188e77SGlenn Strauss 1217b9e2be50SGlenn Strauss mod_auth_digest_mutate(&ai,m,uri,nonce,cnonce,nc,qop); 121881b2d1f0SGlenn Strauss 12190e749c1cSGlenn Strauss if (!http_auth_const_time_memeq(rdigest, ai.digest, ai.dlen)) { 122081b2d1f0SGlenn Strauss /* digest not ok */ 12217c7f8c46SGlenn Strauss log_error(r->conf.errh, __FILE__, __LINE__, 1222010c2894SGlenn Strauss "digest: auth failed for %s: wrong password, IP: %s", 12237c7f8c46SGlenn Strauss username, r->con->dst_addr_buf->ptr); 12247c7f8c46SGlenn Strauss r->keep_alive = 0; /*(disable keep-alive if bad password)*/ 122581b2d1f0SGlenn Strauss 122681b2d1f0SGlenn Strauss buffer_free(b); 12277c7f8c46SGlenn Strauss return mod_auth_send_401_unauthorized_digest(r, require, 0); 122881b2d1f0SGlenn Strauss } 122981b2d1f0SGlenn Strauss 123081b2d1f0SGlenn Strauss /* value is our allow-rules */ 12317ba06c71SGlenn Strauss if (!http_auth_match_rules(require, username, NULL, NULL)) { 12324b3a91e6SGlenn Strauss buffer_free(b); 12337c7f8c46SGlenn Strauss return mod_auth_send_401_unauthorized_digest(r, require, 0); 123481b2d1f0SGlenn Strauss } 123581b2d1f0SGlenn Strauss 1236*e1188e77SGlenn Strauss if (send_nextnonce) { 12376ad325c6SGlenn Strauss /*(send nextnonce when expiration is approaching)*/ 12387c7f8c46SGlenn Strauss buffer * const tb = r->tmp_buf; 1239*e1188e77SGlenn Strauss const time_t cur_ts = log_epoch_secs; 1240*e1188e77SGlenn Strauss mod_auth_digest_authentication_info(tb, cur_ts, require, ai.dalgo); 12417c7f8c46SGlenn Strauss http_header_response_set(r, HTTP_HEADER_OTHER, 1242409bba80SGlenn Strauss CONST_STR_LEN("Authentication-Info"), 1243409bba80SGlenn Strauss CONST_BUF_LEN(tb)); 12446ad325c6SGlenn Strauss } 124581b2d1f0SGlenn Strauss 12467c7f8c46SGlenn Strauss http_auth_setenv(r, ai.username, ai.ulen, CONST_STR_LEN("Digest")); 124781b2d1f0SGlenn Strauss 124881b2d1f0SGlenn Strauss buffer_free(b); 124981b2d1f0SGlenn Strauss 12504b3a91e6SGlenn Strauss return HANDLER_GO_ON; 125181b2d1f0SGlenn Strauss } 125281b2d1f0SGlenn Strauss 12537c7f8c46SGlenn Strauss static handler_t mod_auth_send_401_unauthorized_digest(request_st * const r, const struct http_auth_require_t * const require, int nonce_stale) { 12547c7f8c46SGlenn Strauss buffer * const tb = r->tmp_buf; 1255b5775b99SGlenn Strauss mod_auth_digest_www_authenticate(tb, log_epoch_secs, require, nonce_stale); 12567c7f8c46SGlenn Strauss http_header_response_set(r, HTTP_HEADER_OTHER, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(tb)); 125781b2d1f0SGlenn Strauss 12587c7f8c46SGlenn Strauss r->http_status = 401; 12597c7f8c46SGlenn Strauss r->handler_module = NULL; 1260f9d58670SStefan Bühler return HANDLER_FINISHED; 12617ba06c71SGlenn Strauss } 12627ba06c71SGlenn Strauss 12637c7f8c46SGlenn Strauss static handler_t mod_auth_check_extern(request_st * const r, void *p_d, const struct http_auth_require_t * const require, const struct http_auth_backend_t * const backend) { 12647ba06c71SGlenn Strauss /* require REMOTE_USER already set */ 12657c7f8c46SGlenn Strauss const buffer *vb = http_header_env_get(r, CONST_STR_LEN("REMOTE_USER")); 12667ba06c71SGlenn Strauss UNUSED(p_d); 12677ba06c71SGlenn Strauss UNUSED(backend); 12683dd3cde9SGlenn Strauss if (NULL != vb && http_auth_match_rules(require, vb->ptr, NULL, NULL)) { 1269f9d58670SStefan Bühler return HANDLER_GO_ON; 1270bcdc6a3bSJan Kneschke } else { 12717c7f8c46SGlenn Strauss r->http_status = 401; 12727c7f8c46SGlenn Strauss r->handler_module = NULL; 1273bcdc6a3bSJan Kneschke return HANDLER_FINISHED; 12747187271fSStefan Bühler } 1275bcdc6a3bSJan Kneschke } 1276