]> granicus.if.org Git - apache/blob - modules/aaa/mod_authn_socache.c
move a temporary table from r->pool to a temporary
[apache] / modules / aaa / mod_authn_socache.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "apr_strings.h"
18
19 #include "ap_config.h"
20 #include "ap_provider.h"
21 #include "httpd.h"
22 #include "http_config.h"
23 #include "http_core.h"
24 #include "http_log.h"
25 #include "http_protocol.h"
26 #include "http_request.h"
27
28 #include "mod_auth.h"
29
30 #include "ap_socache.h"
31 #include "util_mutex.h"
32 #include "apr_optional.h"
33
34 module AP_MODULE_DECLARE_DATA authn_socache_module;
35
36 typedef struct authn_cache_dircfg {
37     apr_interval_time_t timeout;
38     apr_array_header_t *providers;
39     const char *context;
40 } authn_cache_dircfg;
41
42 /* FIXME:
43  * I think the cache and mutex should be global
44  */
45 static apr_global_mutex_t *authn_cache_mutex = NULL;
46 static ap_socache_provider_t *socache_provider = NULL;
47 static ap_socache_instance_t *socache_instance = NULL;
48 static const char *const authn_cache_id = "authn-socache";
49 static int configured;
50
51 static apr_status_t remove_lock(void *data)
52 {
53     if (authn_cache_mutex) {
54         apr_global_mutex_destroy(authn_cache_mutex);
55         authn_cache_mutex = NULL;
56     }
57     return APR_SUCCESS;
58 }
59 static apr_status_t destroy_cache(void *data)
60 {
61     if (socache_instance) {
62         socache_provider->destroy(socache_instance, (server_rec*)data);
63         socache_instance = NULL;
64     }
65     return APR_SUCCESS;
66 }
67
68
69 static int authn_cache_precfg(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptmp)
70 {
71     apr_status_t rv = ap_mutex_register(pconf, authn_cache_id,
72                                         NULL, APR_LOCK_DEFAULT, 0);
73     if (rv != APR_SUCCESS) {
74         ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(01673)
75                       "failed to register %s mutex", authn_cache_id);
76         return 500; /* An HTTP status would be a misnomer! */
77     }
78     socache_provider = ap_lookup_provider(AP_SOCACHE_PROVIDER_GROUP,
79                                           AP_SOCACHE_DEFAULT_PROVIDER,
80                                           AP_SOCACHE_PROVIDER_VERSION);
81     configured = 0;
82     return OK;
83 }
84 static int authn_cache_post_config(apr_pool_t *pconf, apr_pool_t *plog,
85                                    apr_pool_t *ptmp, server_rec *s)
86 {
87     apr_status_t rv;
88     static struct ap_socache_hints authn_cache_hints = {64, 32, 60000000};
89     const char *errmsg;
90
91     if (!configured) {
92         return OK;    /* don't waste the overhead of creating mutex & cache */
93     }
94     if (socache_provider == NULL) {
95         ap_log_perror(APLOG_MARK, APLOG_CRIT, 0, plog, APLOGNO(01674)
96                       "Please select a socache provider with AuthnCacheSOCache "
97                       "(no default found on this platform). Maybe you need to "
98                       "load mod_socache_shmcb or another socache module first");
99         return 500; /* An HTTP status would be a misnomer! */
100     }
101
102     /* We have socache_provider, but do not have socache_instance. This should
103      * happen only when using "default" socache_provider, so create default
104      * socache_instance in this case. */
105     if (socache_instance == NULL) {
106         errmsg = socache_provider->create(&socache_instance, NULL,
107                                           ptmp, pconf);
108         if (errmsg) {
109             ap_log_perror(APLOG_MARK, APLOG_CRIT, 0, plog, APLOGNO(02612)
110                         "failed to create mod_socache_shmcb socache "
111                         "instance: %s", errmsg);
112             return 500;
113         }
114     }
115
116     rv = ap_global_mutex_create(&authn_cache_mutex, NULL,
117                                 authn_cache_id, NULL, s, pconf, 0);
118     if (rv != APR_SUCCESS) {
119         ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(01675)
120                       "failed to create %s mutex", authn_cache_id);
121         return 500; /* An HTTP status would be a misnomer! */
122     }
123     apr_pool_cleanup_register(pconf, NULL, remove_lock, apr_pool_cleanup_null);
124
125     rv = socache_provider->init(socache_instance, authn_cache_id,
126                                 &authn_cache_hints, s, pconf);
127     if (rv != APR_SUCCESS) {
128         ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(01677)
129                       "failed to initialise %s cache", authn_cache_id);
130         return 500; /* An HTTP status would be a misnomer! */
131     }
132     apr_pool_cleanup_register(pconf, (void*)s, destroy_cache, apr_pool_cleanup_null);
133     return OK;
134 }
135 static void authn_cache_child_init(apr_pool_t *p, server_rec *s)
136 {
137     const char *lock;
138     apr_status_t rv;
139     if (!configured) {
140         return;       /* don't waste the overhead of creating mutex & cache */
141     }
142     lock = apr_global_mutex_lockfile(authn_cache_mutex);
143     rv = apr_global_mutex_child_init(&authn_cache_mutex, lock, p);
144     if (rv != APR_SUCCESS) {
145         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(01678)
146                      "failed to initialise mutex in child_init");
147     }
148 }
149
150 static const char *authn_cache_socache(cmd_parms *cmd, void *CFG,
151                                        const char *arg)
152 {
153     const char *errmsg = ap_check_cmd_context(cmd, GLOBAL_ONLY);
154     const char *sep, *name;
155
156     if (errmsg)
157         return errmsg;
158
159     /* Argument is of form 'name:args' or just 'name'. */
160     sep = ap_strchr_c(arg, ':');
161     if (sep) {
162         name = apr_pstrmemdup(cmd->pool, arg, sep - arg);
163         sep++;
164     }
165     else {
166         name = arg;
167     }
168
169     socache_provider = ap_lookup_provider(AP_SOCACHE_PROVIDER_GROUP, name,
170                                           AP_SOCACHE_PROVIDER_VERSION);
171     if (socache_provider == NULL) {
172         errmsg = apr_psprintf(cmd->pool,
173                               "Unknown socache provider '%s'. Maybe you need "
174                               "to load the appropriate socache module "
175                               "(mod_socache_%s?)", arg, arg);
176     }
177     else {
178         errmsg = socache_provider->create(&socache_instance, sep,
179                                           cmd->temp_pool, cmd->pool);
180     }
181
182     if (errmsg) {
183         errmsg = apr_psprintf(cmd->pool, "AuthnCacheSOCache: %s", errmsg);
184     }
185     return errmsg;
186 }
187
188 static const char *authn_cache_enable(cmd_parms *cmd, void *CFG)
189 {
190     const char *errmsg = ap_check_cmd_context(cmd, GLOBAL_ONLY);
191     configured = 1;
192     return errmsg;
193 }
194
195 static const char *const directory = "directory";
196 static void* authn_cache_dircfg_create(apr_pool_t *pool, char *s)
197 {
198     authn_cache_dircfg *ret = apr_palloc(pool, sizeof(authn_cache_dircfg));
199     ret->timeout = apr_time_from_sec(300);
200     ret->providers = NULL;
201     ret->context = directory;
202     return ret;
203 }
204 /* not sure we want this.  Might be safer to document use-all-or-none */
205 static void* authn_cache_dircfg_merge(apr_pool_t *pool, void *BASE, void *ADD)
206 {
207     authn_cache_dircfg *base = BASE;
208     authn_cache_dircfg *add = ADD;
209     authn_cache_dircfg *ret = apr_pmemdup(pool, add, sizeof(authn_cache_dircfg));
210     /* preserve context and timeout if not defaults */
211     if (add->context == directory) {
212         ret->context = base->context;
213     }
214     if (add->timeout == apr_time_from_sec(300)) {
215         ret->timeout = base->timeout;
216     }
217     if (add->providers == NULL) {
218         ret->providers = base->providers;
219     }
220     return ret;
221 }
222
223 static const char *authn_cache_setprovider(cmd_parms *cmd, void *CFG,
224                                            const char *arg)
225 {
226     authn_cache_dircfg *cfg = CFG;
227     if (cfg->providers == NULL) {
228         cfg->providers = apr_array_make(cmd->pool, 4, sizeof(const char*));
229     }
230     APR_ARRAY_PUSH(cfg->providers, const char*) = arg;
231     configured = 1;
232     return NULL;
233 }
234
235 static const char *authn_cache_timeout(cmd_parms *cmd, void *CFG,
236                                        const char *arg)
237 {
238     authn_cache_dircfg *cfg = CFG;
239     int secs = atoi(arg);
240     cfg->timeout = apr_time_from_sec(secs);
241     return NULL;
242 }
243
244 static const command_rec authn_cache_cmds[] =
245 {
246     /* global stuff: cache and mutex */
247     AP_INIT_TAKE1("AuthnCacheSOCache", authn_cache_socache, NULL, RSRC_CONF,
248                   "socache provider for authn cache"),
249     AP_INIT_NO_ARGS("AuthnCacheEnable", authn_cache_enable, NULL, RSRC_CONF,
250                     "enable socache configuration in htaccess even if not enabled anywhere else"),
251     /* per-dir stuff */
252     AP_INIT_ITERATE("AuthnCacheProvideFor", authn_cache_setprovider, NULL,
253                     OR_AUTHCFG, "Determine what authn providers to cache for"),
254     AP_INIT_TAKE1("AuthnCacheTimeout", authn_cache_timeout, NULL,
255                   OR_AUTHCFG, "Timeout (secs) for cached credentials"),
256     AP_INIT_TAKE1("AuthnCacheContext", ap_set_string_slot,
257                   (void*)APR_OFFSETOF(authn_cache_dircfg, context),
258                   ACCESS_CONF, "Context for authn cache"),
259     {NULL}
260 };
261
262 static const char *construct_key(request_rec *r, const char *context,
263                                  const char *user, const char *realm)
264 {
265     /* handle "special" context values */
266     if (!strcmp(context, "directory")) {
267         /* FIXME: are we at risk of this blowing up? */
268         char *new_context;
269         char *slash = strrchr(r->uri, '/');
270         new_context = apr_palloc(r->pool, slash - r->uri +
271                                  strlen(r->server->server_hostname) + 1);
272         strcpy(new_context, r->server->server_hostname);
273         strncat(new_context, r->uri, slash - r->uri);
274         context = new_context;
275     }
276     else if (!strcmp(context, "server")) {
277         context = r->server->server_hostname;
278     }
279     /* any other context value is literal */
280
281     if (realm == NULL) {                              /* basic auth */
282         return apr_pstrcat(r->pool, context, ":", user, NULL);
283     }
284     else {                                            /* digest auth */
285         return apr_pstrcat(r->pool, context, ":", user, ":", realm, NULL);
286     }
287 }
288 static void ap_authn_cache_store(request_rec *r, const char *module,
289                                  const char *user, const char *realm,
290                                  const char* data)
291 {
292     apr_status_t rv;
293     authn_cache_dircfg *dcfg;
294     const char *key;
295     apr_time_t expiry;
296     int i;
297     int use_cache = 0;
298
299     /* first check whether we're cacheing for this module */
300     dcfg = ap_get_module_config(r->per_dir_config, &authn_socache_module);
301     if (!configured || !dcfg->providers) {
302         return;
303     }
304     for (i = 0; i < dcfg->providers->nelts; ++i) {
305         if (!strcmp(module, APR_ARRAY_IDX(dcfg->providers, i, const char*))) {
306             use_cache = 1;
307             break;
308         }
309     }
310     if (!use_cache) {
311         return;
312     }
313
314     /* OK, we're on.  Grab mutex to do our business */
315     rv = apr_global_mutex_trylock(authn_cache_mutex);
316     if (APR_STATUS_IS_EBUSY(rv)) {
317         /* don't wait around; just abandon it */
318         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(01679)
319                       "authn credentials for %s not cached (mutex busy)", user);
320         return;
321     }
322     else if (rv != APR_SUCCESS) {
323         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01680)
324                       "Failed to cache authn credentials for %s in %s",
325                       module, dcfg->context);
326         return;
327     }
328
329     /* We have the mutex, so go ahead */
330     /* first build our key and determine expiry time */
331     key = construct_key(r, dcfg->context, user, realm);
332     expiry = apr_time_now() + dcfg->timeout;
333
334     /* store it */
335     rv = socache_provider->store(socache_instance, r->server,
336                                  (unsigned char*)key, strlen(key), expiry,
337                                  (unsigned char*)data, strlen(data), r->pool);
338     if (rv == APR_SUCCESS) {
339         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01681)
340                       "Cached authn credentials for %s in %s",
341                       user, dcfg->context);
342     }
343     else {
344         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01682)
345                       "Failed to cache authn credentials for %s in %s",
346                       module, dcfg->context);
347     }
348
349     /* We're done with the mutex */
350     rv = apr_global_mutex_unlock(authn_cache_mutex);
351     if (rv != APR_SUCCESS) {
352         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01683) "Failed to release mutex!");
353     }
354     return;
355 }
356
357 #define MAX_VAL_LEN 100
358 static authn_status check_password(request_rec *r, const char *user,
359                                    const char *password)
360 {
361
362     /* construct key
363      * look it up
364      * if found, test password
365      *
366      * mutexing here would be a big performance drag.
367      * It's definitely unnecessary with some backends (like ndbm or gdbm)
368      * Is there a risk in the general case?  I guess the only risk we
369      * care about is a race condition that gets us a dangling pointer
370      * to no-longer-defined memory.  Hmmm ...
371      */
372     apr_status_t rv;
373     const char *key;
374     authn_cache_dircfg *dcfg;
375     unsigned char val[MAX_VAL_LEN];
376     unsigned int vallen = MAX_VAL_LEN - 1;
377     dcfg = ap_get_module_config(r->per_dir_config, &authn_socache_module);
378     if (!configured || !dcfg->providers) {
379         return AUTH_USER_NOT_FOUND;
380     }
381     key = construct_key(r, dcfg->context, user, NULL);
382     rv = socache_provider->retrieve(socache_instance, r->server,
383                                     (unsigned char*)key, strlen(key),
384                                     val, &vallen, r->pool);
385
386     if (APR_STATUS_IS_NOTFOUND(rv)) {
387         /* not found - just return */
388         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01684)
389                       "Authn cache: no credentials found for %s", user);
390         return AUTH_USER_NOT_FOUND;
391     }
392     else if (rv == APR_SUCCESS) {
393         /* OK, we got a value */
394         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01685)
395                       "Authn cache: found credentials for %s", user);
396         val[vallen] = 0;
397     }
398     else {
399         /* error: give up and pass the buck */
400         /* FIXME: getting this for NOTFOUND - prolly a bug in mod_socache */
401         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01686)
402                       "Error accessing authentication cache");
403         return AUTH_USER_NOT_FOUND;
404     }
405
406     rv = ap_password_validate(r, user, password, (char*) val);
407     if (rv != APR_SUCCESS) {
408         return AUTH_DENIED;
409     }
410
411     return AUTH_GRANTED;
412 }
413
414 static authn_status get_realm_hash(request_rec *r, const char *user,
415                                    const char *realm, char **rethash)
416 {
417     apr_status_t rv;
418     const char *key;
419     authn_cache_dircfg *dcfg;
420     unsigned char val[MAX_VAL_LEN];
421     unsigned int vallen = MAX_VAL_LEN - 1;
422     dcfg = ap_get_module_config(r->per_dir_config, &authn_socache_module);
423     if (!configured || !dcfg->providers) {
424         return AUTH_USER_NOT_FOUND;
425     }
426     key = construct_key(r, dcfg->context, user, realm);
427     rv = socache_provider->retrieve(socache_instance, r->server,
428                                     (unsigned char*)key, strlen(key),
429                                     val, &vallen, r->pool);
430
431     if (APR_STATUS_IS_NOTFOUND(rv)) {
432         /* not found - just return */
433         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01687)
434                       "Authn cache: no credentials found for %s", user);
435         return AUTH_USER_NOT_FOUND;
436     }
437     else if (rv == APR_SUCCESS) {
438         /* OK, we got a value */
439         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01688)
440                       "Authn cache: found credentials for %s", user);
441     }
442     else {
443         /* error: give up and pass the buck */
444         /* FIXME: getting this for NOTFOUND - prolly a bug in mod_socache */
445         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01689)
446                       "Error accessing authentication cache");
447         return AUTH_USER_NOT_FOUND;
448     }
449     *rethash = apr_pstrmemdup(r->pool, (char *)val, vallen);
450
451     return AUTH_USER_FOUND;
452 }
453
454 static const authn_provider authn_cache_provider =
455 {
456     &check_password,
457     &get_realm_hash,
458 };
459 static void register_hooks(apr_pool_t *p)
460 {
461     ap_register_auth_provider(p, AUTHN_PROVIDER_GROUP, "socache",
462                               AUTHN_PROVIDER_VERSION,
463                               &authn_cache_provider, AP_AUTH_INTERNAL_PER_CONF);
464     APR_REGISTER_OPTIONAL_FN(ap_authn_cache_store);
465     ap_hook_pre_config(authn_cache_precfg, NULL, NULL, APR_HOOK_MIDDLE);
466     ap_hook_post_config(authn_cache_post_config, NULL, NULL, APR_HOOK_MIDDLE);
467     ap_hook_child_init(authn_cache_child_init, NULL, NULL, APR_HOOK_MIDDLE);
468 }
469
470 AP_DECLARE_MODULE(authn_socache) =
471 {
472     STANDARD20_MODULE_STUFF,
473     authn_cache_dircfg_create,
474     authn_cache_dircfg_merge,
475     NULL,
476     NULL,
477     authn_cache_cmds,
478     register_hooks
479 };