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 static 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 
47 typedef struct {
48     const char *pwfile;
49     const char *dbmtype;
50 } authn_dbm_config_rec;
51 
create_authn_dbm_dir_config(apr_pool_t * p,char * d)52 static 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 
set_dbm_type(cmd_parms * cmd,void * dir_config,const char * arg)62 static 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 
72 static 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 
83 module AP_MODULE_DECLARE_DATA authn_dbm_module;
84 
fetch_dbm_value(const char * dbmtype,const char * dbmfile,const char * user,char ** value,apr_pool_t * pool)85 static 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 
check_dbm_pw(request_rec * r,const char * user,const char * password)118 static 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 
get_dbm_realm_hash(request_rec * r,const char * user,const char * realm,char ** rethash)156 static 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 
191 static const authn_provider authn_dbm_provider =
192 {
193     &check_dbm_pw,
194     &get_dbm_realm_hash,
195 };
196 
opt_retr(void)197 static void opt_retr(void)
198 {
199     authn_cache_store = APR_RETRIEVE_OPTIONAL_FN(ap_authn_cache_store);
200 }
register_hooks(apr_pool_t * p)201 static 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 
209 AP_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