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/*
18 * http_auth: authentication
19 *
20 * Rob McCool & Brian Behlendorf.
21 *
22 * Adapted to Apache by rst.
23 *
24 */
25
26#define APR_WANT_STRFUNC
27#include "apr_want.h"
28#include "apr_strings.h"
29#include "apr_dbm.h"
30#include "apr_md5.h"        /* for apr_password_validate */
31
32#include "ap_provider.h"
33#include "httpd.h"
34#include "http_config.h"
35#include "http_core.h"
36#include "http_log.h"
37#include "http_protocol.h"
38#include "http_request.h"   /* for ap_hook_(check_user_id | auth_checker)*/
39
40#include "mod_auth.h"
41
42static APR_OPTIONAL_FN_TYPE(ap_authn_cache_store) *authn_cache_store = NULL;
43#define AUTHN_CACHE_STORE(r,user,realm,data) \
44    if (authn_cache_store != NULL) \
45        authn_cache_store((r), "dbm", (user), (realm), (data))
46
47typedef struct {
48    const char *pwfile;
49    const char *dbmtype;
50} authn_dbm_config_rec;
51
52static void *create_authn_dbm_dir_config(apr_pool_t *p, char *d)
53{
54    authn_dbm_config_rec *conf = apr_palloc(p, sizeof(*conf));
55
56    conf->pwfile = NULL;
57    conf->dbmtype = "default";
58
59    return conf;
60}
61
62static const char *set_dbm_type(cmd_parms *cmd,
63                                void *dir_config,
64                                const char *arg)
65{
66    authn_dbm_config_rec *conf = dir_config;
67
68    conf->dbmtype = apr_pstrdup(cmd->pool, arg);
69    return NULL;
70}
71
72static const command_rec authn_dbm_cmds[] =
73{
74    AP_INIT_TAKE1("AuthDBMUserFile", ap_set_file_slot,
75     (void *)APR_OFFSETOF(authn_dbm_config_rec, pwfile),
76     OR_AUTHCFG, "dbm database file containing user IDs and passwords"),
77    AP_INIT_TAKE1("AuthDBMType", set_dbm_type,
78     NULL,
79     OR_AUTHCFG, "what type of DBM file the user file is"),
80    {NULL}
81};
82
83module AP_MODULE_DECLARE_DATA authn_dbm_module;
84
85static apr_status_t fetch_dbm_value(const char *dbmtype, const char *dbmfile,
86                                    const char *user, char **value,
87                                    apr_pool_t *pool)
88{
89    apr_dbm_t *f;
90    apr_datum_t key, val;
91    apr_status_t rv;
92
93    rv = apr_dbm_open_ex(&f, dbmtype, dbmfile, APR_DBM_READONLY,
94                         APR_OS_DEFAULT, pool);
95
96    if (rv != APR_SUCCESS) {
97        return rv;
98    }
99
100    key.dptr = (char*)user;
101#ifndef NETSCAPE_DBM_COMPAT
102    key.dsize = strlen(key.dptr);
103#else
104    key.dsize = strlen(key.dptr) + 1;
105#endif
106
107    *value = NULL;
108
109    if (apr_dbm_fetch(f, key, &val) == APR_SUCCESS && val.dptr) {
110        *value = apr_pstrmemdup(pool, val.dptr, val.dsize);
111    }
112
113    apr_dbm_close(f);
114
115    return rv;
116}
117
118static authn_status check_dbm_pw(request_rec *r, const char *user,
119                                 const char *password)
120{
121    authn_dbm_config_rec *conf = ap_get_module_config(r->per_dir_config,
122                                                      &authn_dbm_module);
123    apr_status_t rv;
124    char *dbm_password;
125    char *colon_pw;
126
127    rv = fetch_dbm_value(conf->dbmtype, conf->pwfile, user, &dbm_password,
128                         r->pool);
129
130    if (rv != APR_SUCCESS) {
131        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01754)
132                      "could not open dbm (type %s) auth file: %s",
133                      conf->dbmtype, conf->pwfile);
134        return AUTH_GENERAL_ERROR;
135    }
136
137    if (!dbm_password) {
138        return AUTH_USER_NOT_FOUND;
139    }
140
141    colon_pw = ap_strchr(dbm_password, ':');
142    if (colon_pw) {
143        *colon_pw = '\0';
144    }
145    AUTHN_CACHE_STORE(r, user, NULL, dbm_password);
146
147    rv = apr_password_validate(password, dbm_password);
148
149    if (rv != APR_SUCCESS) {
150        return AUTH_DENIED;
151    }
152
153    return AUTH_GRANTED;
154}
155
156static authn_status get_dbm_realm_hash(request_rec *r, const char *user,
157                                       const char *realm, char **rethash)
158{
159    authn_dbm_config_rec *conf = ap_get_module_config(r->per_dir_config,
160                                                      &authn_dbm_module);
161    apr_status_t rv;
162    char *dbm_hash;
163    char *colon_hash;
164
165    rv = fetch_dbm_value(conf->dbmtype, conf->pwfile,
166                         apr_pstrcat(r->pool, user, ":", realm, NULL),
167                         &dbm_hash, r->pool);
168
169    if (rv != APR_SUCCESS) {
170        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01755)
171                      "Could not open dbm (type %s) hash file: %s",
172                      conf->dbmtype, conf->pwfile);
173        return AUTH_GENERAL_ERROR;
174    }
175
176    if (!dbm_hash) {
177        return AUTH_USER_NOT_FOUND;
178    }
179
180    colon_hash = ap_strchr(dbm_hash, ':');
181    if (colon_hash) {
182        *colon_hash = '\0';
183    }
184
185    *rethash = dbm_hash;
186    AUTHN_CACHE_STORE(r, user, realm, dbm_hash);
187
188    return AUTH_USER_FOUND;
189}
190
191static const authn_provider authn_dbm_provider =
192{
193    &check_dbm_pw,
194    &get_dbm_realm_hash,
195};
196
197static void opt_retr(void)
198{
199    authn_cache_store = APR_RETRIEVE_OPTIONAL_FN(ap_authn_cache_store);
200}
201static void register_hooks(apr_pool_t *p)
202{
203    ap_register_auth_provider(p, AUTHN_PROVIDER_GROUP, "dbm",
204                              AUTHN_PROVIDER_VERSION,
205                              &authn_dbm_provider, AP_AUTH_INTERNAL_PER_CONF);
206    ap_hook_optional_fn_retrieve(opt_retr, NULL, NULL, APR_HOOK_MIDDLE);
207}
208
209AP_DECLARE_MODULE(authn_dbm) =
210{
211    STANDARD20_MODULE_STUFF,
212    create_authn_dbm_dir_config, /* dir config creater */
213    NULL,                        /* dir merger --- default is to override */
214    NULL,                        /* server config */
215    NULL,                        /* merge server config */
216    authn_dbm_cmds,              /* command apr_table_t */
217    register_hooks               /* register hooks */
218};
219