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 
42 #include "apr_version.h"
43 #if !APR_VERSION_AT_LEAST(2,0,0)
44 #include "apu_version.h"
45 #endif
46 
47 static APR_OPTIONAL_FN_TYPE(ap_authn_cache_store) *authn_cache_store = NULL;
48 #define AUTHN_CACHE_STORE(r,user,realm,data) \
49     if (authn_cache_store != NULL) \
50         authn_cache_store((r), "dbm", (user), (realm), (data))
51 
52 typedef struct {
53     const char *pwfile;
54     const char *dbmtype;
55 } authn_dbm_config_rec;
56 
create_authn_dbm_dir_config(apr_pool_t * p,char * d)57 static void *create_authn_dbm_dir_config(apr_pool_t *p, char *d)
58 {
59     authn_dbm_config_rec *conf = apr_palloc(p, sizeof(*conf));
60 
61     conf->pwfile = NULL;
62     conf->dbmtype = "default";
63 
64     return conf;
65 }
66 
67 static const command_rec authn_dbm_cmds[] =
68 {
69     AP_INIT_TAKE1("AuthDBMUserFile", ap_set_file_slot,
70      (void *)APR_OFFSETOF(authn_dbm_config_rec, pwfile),
71      OR_AUTHCFG, "dbm database file containing user IDs and passwords"),
72     AP_INIT_TAKE1("AuthDBMType", ap_set_string_slot,
73      (void *)APR_OFFSETOF(authn_dbm_config_rec, dbmtype),
74      OR_AUTHCFG, "what type of DBM file the user file is"),
75     {NULL}
76 };
77 
78 module AP_MODULE_DECLARE_DATA authn_dbm_module;
79 
fetch_dbm_value(request_rec * r,const char * dbmtype,const char * dbmfile,const char * user,char ** value)80 static apr_status_t fetch_dbm_value(request_rec *r, const char *dbmtype,
81                                     const char *dbmfile,
82                                     const char *user, char **value)
83 {
84 #if APU_MAJOR_VERSION > 1 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 7)
85     const apr_dbm_driver_t *driver;
86     const apu_err_t *err;
87 #endif
88     apr_dbm_t *f;
89     apr_datum_t key, val;
90     apr_status_t rv;
91 
92 #if APU_MAJOR_VERSION > 1 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 7)
93     rv = apr_dbm_get_driver(&driver, dbmtype, &err, r->pool);
94 
95     if (rv != APR_SUCCESS) {
96         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(10284)
97                 "could not load '%s' dbm library: %s",
98                      err->reason, err->msg);
99         return rv;
100     }
101 
102     rv = apr_dbm_open2(&f, driver, dbmfile, APR_DBM_READONLY,
103                          APR_OS_DEFAULT, r->pool);
104 #else
105     rv = apr_dbm_open_ex(&f, dbmtype, dbmfile, APR_DBM_READONLY,
106                          APR_OS_DEFAULT, r->pool);
107 #endif
108 
109     if (rv != APR_SUCCESS) {
110         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(10285)
111                       "could not open dbm (type %s) file: %s",
112                       dbmtype, dbmfile);
113         return rv;
114     }
115 
116     key.dptr = (char*)user;
117 #ifndef NETSCAPE_DBM_COMPAT
118     key.dsize = strlen(key.dptr);
119 #else
120     key.dsize = strlen(key.dptr) + 1;
121 #endif
122 
123     *value = NULL;
124 
125     if (apr_dbm_fetch(f, key, &val) == APR_SUCCESS && val.dptr) {
126         *value = apr_pstrmemdup(r->pool, val.dptr, val.dsize);
127     }
128 
129     apr_dbm_close(f);
130 
131     /* NOT FOUND is not an error case; this is indicated by a NULL result.
132      * Treat all NULL lookup/error results as success for the simple case
133      * of auth credential lookup, these are DECLINED in both cases.
134      */
135     return APR_SUCCESS;
136 }
137 
check_dbm_pw(request_rec * r,const char * user,const char * password)138 static authn_status check_dbm_pw(request_rec *r, const char *user,
139                                  const char *password)
140 {
141     authn_dbm_config_rec *conf = ap_get_module_config(r->per_dir_config,
142                                                       &authn_dbm_module);
143     apr_status_t rv;
144     char *dbm_password;
145     char *colon_pw;
146 
147     rv = fetch_dbm_value(r, conf->dbmtype, conf->pwfile, user, &dbm_password);
148 
149     if (rv != APR_SUCCESS) {
150         return AUTH_GENERAL_ERROR;
151     }
152 
153     if (!dbm_password) {
154         return AUTH_USER_NOT_FOUND;
155     }
156 
157     colon_pw = ap_strchr(dbm_password, ':');
158     if (colon_pw) {
159         *colon_pw = '\0';
160     }
161     AUTHN_CACHE_STORE(r, user, NULL, dbm_password);
162 
163     rv = apr_password_validate(password, dbm_password);
164 
165     if (rv != APR_SUCCESS) {
166         return AUTH_DENIED;
167     }
168 
169     return AUTH_GRANTED;
170 }
171 
get_dbm_realm_hash(request_rec * r,const char * user,const char * realm,char ** rethash)172 static authn_status get_dbm_realm_hash(request_rec *r, const char *user,
173                                        const char *realm, char **rethash)
174 {
175     authn_dbm_config_rec *conf = ap_get_module_config(r->per_dir_config,
176                                                       &authn_dbm_module);
177     apr_status_t rv;
178     char *dbm_hash;
179     char *colon_hash;
180 
181     rv = fetch_dbm_value(r, conf->dbmtype, conf->pwfile,
182                          apr_pstrcat(r->pool, user, ":", realm, NULL),
183                          &dbm_hash);
184 
185     if (rv != APR_SUCCESS) {
186         return AUTH_GENERAL_ERROR;
187     }
188 
189     if (!dbm_hash) {
190         return AUTH_USER_NOT_FOUND;
191     }
192 
193     colon_hash = ap_strchr(dbm_hash, ':');
194     if (colon_hash) {
195         *colon_hash = '\0';
196     }
197 
198     *rethash = dbm_hash;
199     AUTHN_CACHE_STORE(r, user, realm, dbm_hash);
200 
201     return AUTH_USER_FOUND;
202 }
203 
204 static const authn_provider authn_dbm_provider =
205 {
206     &check_dbm_pw,
207     &get_dbm_realm_hash,
208 };
209 
opt_retr(void)210 static void opt_retr(void)
211 {
212     authn_cache_store = APR_RETRIEVE_OPTIONAL_FN(ap_authn_cache_store);
213 }
register_hooks(apr_pool_t * p)214 static void register_hooks(apr_pool_t *p)
215 {
216     ap_register_auth_provider(p, AUTHN_PROVIDER_GROUP, "dbm",
217                               AUTHN_PROVIDER_VERSION,
218                               &authn_dbm_provider, AP_AUTH_INTERNAL_PER_CONF);
219     ap_hook_optional_fn_retrieve(opt_retr, NULL, NULL, APR_HOOK_MIDDLE);
220 }
221 
222 AP_DECLARE_MODULE(authn_dbm) =
223 {
224     STANDARD20_MODULE_STUFF,
225     create_authn_dbm_dir_config, /* dir config creater */
226     NULL,                        /* dir merger --- default is to override */
227     NULL,                        /* server config */
228     NULL,                        /* merge server config */
229     authn_dbm_cmds,              /* command apr_table_t */
230     register_hooks               /* register hooks */
231 };
232