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#define APR_WANT_STRFUNC        /* for strcasecmp */
19#include "apr_want.h"
20
21#define CORE_PRIVATE
22#include "ap_config.h"
23#include "httpd.h"
24#include "http_config.h"
25#include "http_core.h"
26#include "http_request.h"
27#include "ap_provider.h"
28
29#include "mod_auth.h"
30
31typedef struct provider_alias_rec {
32    char *provider_name;
33    char *provider_alias;
34    ap_conf_vector_t *sec_auth;
35    const authn_provider *provider;
36} provider_alias_rec;
37
38typedef struct authn_alias_srv_conf {
39    apr_hash_t *alias_rec;
40} authn_alias_srv_conf;
41
42module AP_MODULE_DECLARE_DATA authn_alias_module;
43
44static authn_status authn_alias_check_password(request_rec *r, const char *user,
45                                              const char *password)
46{
47    /* Look up the provider alias in the alias list */
48    /* Get the the dir_config and call ap_Merge_per_dir_configs() */
49    /* Call the real provider->check_password() function */
50    /* return the result of the above function call */
51
52    const char *provider_name = apr_table_get(r->notes, AUTHN_PROVIDER_NAME_NOTE);
53    authn_status ret = AUTH_USER_NOT_FOUND;
54    authn_alias_srv_conf *authcfg =
55        (authn_alias_srv_conf *)ap_get_module_config(r->server->module_config,
56                                                     &authn_alias_module);
57
58    if (provider_name) {
59        provider_alias_rec *prvdraliasrec = apr_hash_get(authcfg->alias_rec,
60                                                         provider_name, APR_HASH_KEY_STRING);
61        ap_conf_vector_t *orig_dir_config = r->per_dir_config;
62
63        /* If we found the alias provider in the list, then merge the directory
64           configurations and call the real provider */
65        if (prvdraliasrec) {
66            r->per_dir_config = ap_merge_per_dir_configs(r->pool, orig_dir_config,
67                                                         prvdraliasrec->sec_auth);
68            ret = prvdraliasrec->provider->check_password(r,user,password);
69            r->per_dir_config = orig_dir_config;
70        }
71    }
72
73    return ret;
74}
75
76static authn_status authn_alias_get_realm_hash(request_rec *r, const char *user,
77                                               const char *realm, char **rethash)
78{
79    /* Look up the provider alias in the alias list */
80    /* Get the the dir_config and call ap_Merge_per_dir_configs() */
81    /* Call the real provider->get_realm_hash() function */
82    /* return the result of the above function call */
83
84    const char *provider_name = apr_table_get(r->notes, AUTHN_PROVIDER_NAME_NOTE);
85    authn_status ret = AUTH_USER_NOT_FOUND;
86    authn_alias_srv_conf *authcfg =
87        (authn_alias_srv_conf *)ap_get_module_config(r->server->module_config,
88                                                     &authn_alias_module);
89
90    if (provider_name) {
91        provider_alias_rec *prvdraliasrec = apr_hash_get(authcfg->alias_rec,
92                                                         provider_name, APR_HASH_KEY_STRING);
93        ap_conf_vector_t *orig_dir_config = r->per_dir_config;
94
95        /* If we found the alias provider in the list, then merge the directory
96           configurations and call the real provider */
97        if (prvdraliasrec) {
98            r->per_dir_config = ap_merge_per_dir_configs(r->pool, orig_dir_config,
99                                                         prvdraliasrec->sec_auth);
100            ret = prvdraliasrec->provider->get_realm_hash(r,user,realm,rethash);
101            r->per_dir_config = orig_dir_config;
102        }
103    }
104
105    return ret;
106}
107
108static void *create_authn_alias_svr_config(apr_pool_t *p, server_rec *s)
109{
110
111    authn_alias_srv_conf *authcfg;
112
113    authcfg = (authn_alias_srv_conf *) apr_pcalloc(p, sizeof(authn_alias_srv_conf));
114    authcfg->alias_rec = apr_hash_make(p);
115
116    return (void *) authcfg;
117}
118
119static const authn_provider authn_alias_provider =
120{
121    &authn_alias_check_password,
122    &authn_alias_get_realm_hash,
123};
124
125static const authn_provider authn_alias_provider_nodigest =
126{
127    &authn_alias_check_password,
128    NULL,
129};
130
131static const char *authaliassection(cmd_parms *cmd, void *mconfig, const char *arg)
132{
133    int old_overrides = cmd->override;
134    const char *endp = ap_strrchr_c(arg, '>');
135    const char *args;
136    char *provider_alias;
137    char *provider_name;
138    const char *errmsg;
139    const authn_provider *provider = NULL;
140    ap_conf_vector_t *new_auth_config = ap_create_per_dir_config(cmd->pool);
141    authn_alias_srv_conf *authcfg =
142        (authn_alias_srv_conf *)ap_get_module_config(cmd->server->module_config,
143                                                     &authn_alias_module);
144
145    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
146    if (err != NULL) {
147        return err;
148    }
149
150    if (endp == NULL) {
151        return apr_pstrcat(cmd->pool, cmd->cmd->name,
152                           "> directive missing closing '>'", NULL);
153    }
154
155    args = apr_pstrndup(cmd->pool, arg, endp - arg);
156
157    if (!args[0]) {
158        return apr_pstrcat(cmd->pool, cmd->cmd->name,
159                           "> directive requires additional arguments", NULL);
160    }
161
162    /* Pull the real provider name and the alias name from the block header */
163    provider_name = ap_getword_conf(cmd->pool, &args);
164    provider_alias = ap_getword_conf(cmd->pool, &args);
165
166    if (!provider_name[0] || !provider_alias[0]) {
167        return apr_pstrcat(cmd->pool, cmd->cmd->name,
168                           "> directive requires additional arguments", NULL);
169    }
170
171    if (strcasecmp(provider_name, provider_alias) == 0) {
172        return apr_pstrcat(cmd->pool,
173                           "The alias provider name must be different from the base provider name.", NULL);
174    }
175
176    /* Look up the alias provider to make sure that it hasn't already been registered. */
177    provider = ap_lookup_provider(AUTHN_PROVIDER_GROUP, provider_alias, "0");
178    if (provider) {
179        return apr_pstrcat(cmd->pool, "The alias provider ", provider_alias,
180                           " has already be registered previously as either a base provider or an alias provider.",
181                           NULL);
182    }
183
184    /* walk the subsection configuration to get the per_dir config that we will
185       merge just before the real provider is called. */
186    cmd->override = OR_ALL|ACCESS_CONF;
187    errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_auth_config);
188
189    if (!errmsg) {
190        provider_alias_rec *prvdraliasrec = apr_pcalloc(cmd->pool, sizeof(provider_alias_rec));
191        provider = ap_lookup_provider(AUTHN_PROVIDER_GROUP, provider_name, "0");
192
193        /* Save off the new directory config along with the original provider name
194           and function pointer data */
195        prvdraliasrec->sec_auth = new_auth_config;
196        prvdraliasrec->provider_name = provider_name;
197        prvdraliasrec->provider_alias = provider_alias;
198        prvdraliasrec->provider = provider;
199        apr_hash_set(authcfg->alias_rec, provider_alias, APR_HASH_KEY_STRING, prvdraliasrec);
200
201        /* Register the fake provider so that we get called first */
202        ap_register_provider(cmd->pool, AUTHN_PROVIDER_GROUP, provider_alias, "0",
203                             provider->get_realm_hash ?
204                                 &authn_alias_provider :
205                                 &authn_alias_provider_nodigest);
206    }
207
208    cmd->override = old_overrides;
209
210    return errmsg;
211}
212
213static const command_rec authn_alias_cmds[] =
214{
215    AP_INIT_RAW_ARGS("<AuthnProviderAlias", authaliassection, NULL, RSRC_CONF,
216                     "Container for authentication directives grouped under "
217                     "a provider alias"),
218    {NULL}
219};
220
221
222module AP_MODULE_DECLARE_DATA authn_alias_module =
223{
224    STANDARD20_MODULE_STUFF,
225    NULL,                          /* dir config creater */
226    NULL,                          /* dir merger --- default is to override */
227    create_authn_alias_svr_config, /* server config */
228    NULL,                          /* merge server config */
229    authn_alias_cmds,               /* command apr_table_t */
230    NULL                           /* register hooks */
231};
232