]> granicus.if.org Git - apache/blob - modules/aaa/mod_auth_dbm.c
f2d5107757142be4ad54e0266e252a4628a06ddd
[apache] / modules / aaa / mod_auth_dbm.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58
59 /*
60  * http_auth: authentication
61  * 
62  * Rob McCool & Brian Behlendorf.
63  * 
64  * Adapted to Apache by rst.
65  *
66  * dirkx - Added Authoritative control to allow passing on to lower  
67  *         modules if and only if the userid is not known to this
68  *         module. A known user with a faulty or absent password still
69  *         causes an AuthRequired. The default is 'Authoritative', i.e.
70  *         no control is passed along.
71  */
72
73 #include "httpd.h"
74 #include "http_config.h"
75 #include "http_core.h"
76 #include "http_log.h"
77 #include "http_protocol.h"
78 #include "http_request.h"   /* for ap_hook_(check_user_id | auth_checker)*/
79
80 #if defined(AP_AUTH_DBM_USE_APR)
81 #include "apr_dbm.h"
82 #define DBM apr_dbm_t
83 #define datum apr_datum_t
84 #define dbm_open apr_dbm_open
85 #define dbm_fetch apr_dbm_fetch
86 #define dbm_close apr_dbm_close
87 #elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) \
88     && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
89 #include <db1/ndbm.h>
90 #else
91 #include <ndbm.h>
92 #endif
93
94 /*
95  * Module definition information - the part between the -START and -END
96  * lines below is used by Configure. This could be stored in a separate
97  * instead.
98  *
99  * XXX: this needs updating for apache-2.0 configuration method
100  * MODULE-DEFINITION-START
101  * Name: dbm_auth_module
102  * ConfigStart
103     . ./helpers/find-dbm-lib
104  * ConfigEnd
105  * MODULE-DEFINITION-END
106  */
107
108 typedef struct {
109
110     char *auth_dbmpwfile;
111     char *auth_dbmgrpfile;
112     int auth_dbmauthoritative;
113
114 } dbm_auth_config_rec;
115
116 static void *create_dbm_auth_dir_config(apr_pool_t *p, char *d)
117 {
118     dbm_auth_config_rec *sec
119     = (dbm_auth_config_rec *) apr_pcalloc(p, sizeof(dbm_auth_config_rec));
120
121     sec->auth_dbmpwfile = NULL;
122     sec->auth_dbmgrpfile = NULL;
123     sec->auth_dbmauthoritative = 1;     /* fortress is secure by default */
124
125     return sec;
126 }
127
128 static const char *set_dbm_slot(cmd_parms *cmd, void *offset, const char *f, const char *t)
129 {
130     if (!t || strcmp(t, "dbm"))
131         return DECLINE_CMD;
132
133     return ap_set_file_slot(cmd, offset, f);
134 }
135
136 static const command_rec dbm_auth_cmds[] =
137 {
138     AP_INIT_TAKE1("AuthDBMUserFile", ap_set_file_slot,
139      (void *) XtOffsetOf(dbm_auth_config_rec, auth_dbmpwfile),
140      OR_AUTHCFG, NULL),
141     AP_INIT_TAKE1("AuthDBMGroupFile", ap_set_file_slot,
142      (void *) XtOffsetOf(dbm_auth_config_rec, auth_dbmgrpfile),
143      OR_AUTHCFG, NULL),
144     AP_INIT_TAKE12("AuthUserFile", set_dbm_slot,
145      (void *) XtOffsetOf(dbm_auth_config_rec, auth_dbmpwfile),
146      OR_AUTHCFG, NULL),
147     AP_INIT_TAKE12("AuthGroupFile", set_dbm_slot,
148      (void *) XtOffsetOf(dbm_auth_config_rec, auth_dbmgrpfile),
149      OR_AUTHCFG, NULL),
150     AP_INIT_FLAG("AuthDBMAuthoritative", ap_set_flag_slot,
151      (void *) XtOffsetOf(dbm_auth_config_rec, auth_dbmauthoritative),
152      OR_AUTHCFG, "Set to 'no' to allow access control to be passed along to lower modules, if the UserID is not known in this module"),
153     {NULL}
154 };
155
156 module AP_MODULE_DECLARE_DATA dbm_auth_module;
157
158 static char *get_dbm_pw(request_rec *r, char *user, char *auth_dbmpwfile)
159 {
160     DBM *f;
161     datum d, q;
162     char *pw = NULL;
163 #ifdef AP_AUTH_DBM_USE_APR
164     apr_status_t retval;
165 #endif
166     q.dptr = user;
167 #ifndef NETSCAPE_DBM_COMPAT
168     q.dsize = strlen(q.dptr);
169 #else
170     q.dsize = strlen(q.dptr) + 1;
171 #endif
172
173 #ifdef AP_AUTH_DBM_USE_APR
174     if (!(retval = dbm_open(&f, auth_dbmpwfile, APR_DBM_READONLY, r->pool))) {
175         ap_log_rerror(APLOG_MARK, APLOG_ERR, retval, r,
176                     "could not open sdbm auth file: %s", auth_dbmpwfile);
177         return NULL;
178     }
179     if (dbm_fetch(f, q, &d) == APR_SUCCESS)
180         /* sorry for the obscurity ... falls through to the 
181          * if (d.dptr) {  block ...
182          */
183
184 #else
185     if (!(f = dbm_open(auth_dbmpwfile, O_RDONLY, 0664))) {
186         ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r,
187                     "could not open dbm auth file: %s", auth_dbmpwfile);
188         return NULL;
189     }
190     d = dbm_fetch(f, q);
191
192 #endif
193
194     if (d.dptr) {
195         pw = apr_palloc(r->pool, d.dsize + 1);
196         strncpy(pw, d.dptr, d.dsize);
197         pw[d.dsize] = '\0';     /* Terminate the string */
198     }
199
200     dbm_close(f);
201     return pw;
202 }
203
204 /* We do something strange with the group file.  If the group file
205  * contains any : we assume the format is
206  *      key=username value=":"groupname [":"anything here is ignored]
207  * otherwise we now (0.8.14+) assume that the format is
208  *      key=username value=groupname
209  * The first allows the password and group files to be the same 
210  * physical DBM file;   key=username value=password":"groupname[":"anything]
211  *
212  * mark@telescope.org, 22Sep95
213  */
214
215 static char *get_dbm_grp(request_rec *r, char *user, char *auth_dbmgrpfile)
216 {
217     char *grp_data = get_dbm_pw(r, user, auth_dbmgrpfile);
218     char *grp_colon;
219     char *grp_colon2;
220
221     if (grp_data == NULL)
222         return NULL;
223
224     if ((grp_colon = strchr(grp_data, ':')) != NULL) {
225         grp_colon2 = strchr(++grp_colon, ':');
226         if (grp_colon2)
227             *grp_colon2 = '\0';
228         return grp_colon;
229     }
230     return grp_data;
231 }
232
233 static int dbm_authenticate_basic_user(request_rec *r)
234 {
235     dbm_auth_config_rec *sec =
236     (dbm_auth_config_rec *) ap_get_module_config(r->per_dir_config,
237                                               &dbm_auth_module);
238     const char *sent_pw;
239     char *real_pw, *colon_pw;
240     apr_status_t invalid_pw;
241     int res;
242
243     if ((res = ap_get_basic_auth_pw(r, &sent_pw)))
244         return res;
245
246     if (!sec->auth_dbmpwfile)
247         return DECLINED;
248
249     if (!(real_pw = get_dbm_pw(r, r->user, sec->auth_dbmpwfile))) {
250         if (!(sec->auth_dbmauthoritative))
251             return DECLINED;
252         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
253                     "DBM user %s not found: %s", r->user, r->filename);
254         ap_note_basic_auth_failure(r);
255         return HTTP_UNAUTHORIZED;
256     }
257     /* Password is up to first : if exists */
258     colon_pw = strchr(real_pw, ':');
259     if (colon_pw) {
260         *colon_pw = '\0';
261     }
262     invalid_pw = apr_validate_password(sent_pw, real_pw);
263     if (invalid_pw != APR_SUCCESS) {
264         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
265                       "DBM user %s: authentication failure for \"%s\": "
266                       "Password Mismatch",
267                       r->user, r->uri);
268         ap_note_basic_auth_failure(r);
269         return HTTP_UNAUTHORIZED;
270     }
271     return OK;
272 }
273
274 /* Checking ID */
275
276 static int dbm_check_auth(request_rec *r)
277 {
278     dbm_auth_config_rec *sec =
279     (dbm_auth_config_rec *) ap_get_module_config(r->per_dir_config,
280                                               &dbm_auth_module);
281     char *user = r->user;
282     int m = r->method_number;
283
284     const apr_array_header_t *reqs_arr = ap_requires(r);
285     require_line *reqs = reqs_arr ? (require_line *) reqs_arr->elts : NULL;
286
287     register int x;
288     const char *t;
289     char *w;
290
291     if (!sec->auth_dbmgrpfile)
292         return DECLINED;
293     if (!reqs_arr)
294         return DECLINED;
295
296     for (x = 0; x < reqs_arr->nelts; x++) {
297
298         if (!(reqs[x].method_mask & (1 << m)))
299             continue;
300
301         t = reqs[x].requirement;
302         w = ap_getword_white(r->pool, &t);
303
304         if (!strcmp(w, "group") && sec->auth_dbmgrpfile) {
305             const char *orig_groups, *groups;
306             char *v;
307
308             if (!(groups = get_dbm_grp(r, user, sec->auth_dbmgrpfile))) {
309                 if (!(sec->auth_dbmauthoritative))
310                     return DECLINED;
311                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
312                             "user %s not in DBM group file %s: %s",
313                             user, sec->auth_dbmgrpfile, r->filename);
314                 ap_note_basic_auth_failure(r);
315                 return HTTP_UNAUTHORIZED;
316             }
317             orig_groups = groups;
318             while (t[0]) {
319                 w = ap_getword_white(r->pool, &t);
320                 groups = orig_groups;
321                 while (groups[0]) {
322                     v = ap_getword(r->pool, &groups, ',');
323                     if (!strcmp(v, w))
324                         return OK;
325                 }
326             }
327             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
328                         "user %s not in right group: %s",
329                         user, r->filename);
330             ap_note_basic_auth_failure(r);
331             return HTTP_UNAUTHORIZED;
332         }
333     }
334
335     return DECLINED;
336 }
337
338 static void register_hooks(void)
339 {
340     ap_hook_check_user_id(dbm_authenticate_basic_user, NULL, NULL, AP_HOOK_MIDDLE);
341     ap_hook_auth_checker(dbm_check_auth, NULL, NULL, AP_HOOK_MIDDLE);
342 }
343
344 module AP_MODULE_DECLARE_DATA dbm_auth_module =
345 {
346     STANDARD20_MODULE_STUFF,
347     create_dbm_auth_dir_config, /* dir config creater */
348     NULL,                       /* dir merger --- default is to override */
349     NULL,                       /* server config */
350     NULL,                       /* merge server config */
351     dbm_auth_cmds,              /* command apr_table_t */
352     NULL,                       /* handlers */
353     register_hooks              /* register hooks */
354 };