]> granicus.if.org Git - apache/blob - modules/aaa/mod_auth_db.c
PR:
[apache] / modules / aaa / mod_auth_db.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  * mod_auth_db: authentication
61  * 
62  * Original work by Rob McCool & Brian Behlendorf.
63  * 
64  * Adapted to Apache by rst (mod_auth_dbm)
65  *
66  * Adapted for Berkeley DB by Andrew Cohen 
67  *
68  * apache 2 port by Brian Martin 
69  *
70  * mod_auth_db was based on mod_auth_dbm.
71  * 
72  * Warning, this is not a drop in replacement for mod_auth_dbm, 
73  * for people wanting to switch from dbm to Berkeley DB.
74  * It requires the use of AuthDBUserFile and AuthDBGroupFile
75  *           instead of   AuthDBMUserFile    AuthDBMGroupFile
76  *
77  * Also, in the configuration file you need to specify
78  *  auth_db_module rather than auth_dbm_module
79  *
80  * On some BSD systems (e.g. FreeBSD and NetBSD) dbm is automatically
81  * mapped to Berkeley DB. You can use either mod_auth_dbm or
82  * mod_auth_db. The latter makes it more obvious that it's Berkeley.
83  * On other platforms where you want to use the DB library you
84  * usually have to install it first. See http://www.sleepycat.com/
85  * for the distribution. The interface this module uses is the
86  * one from DB version 1.85 and 1.86, but DB version 2.x
87  * can also be used when compatibility mode is enabled.
88  *
89  * dirkx - Added Authoritative control to allow passing on to lower  
90  *         modules if and only if the userid is not known to this
91  *         module. A known user with a faulty or absent password still
92  *         causes an AuthRequired. The default is 'Authoritative', i.e.
93  *         no control is passed along.
94  */
95
96 #include "ap_config.h"
97 #include "httpd.h"
98 #include "http_config.h"
99 #include "http_core.h"
100 #include "http_log.h"
101 #include "http_protocol.h"
102 #ifdef HAVE_DB_H
103 #include <db.h>
104 #endif
105
106 #if   defined(DB_VERSION_MAJOR) && (DB_VERSION_MAJOR == 3)
107 #define DB_VER 3
108 #elif defined(DB_VERSION_MAJOR) && (DB_VERSION_MAJOR == 2)
109 #define DB_VER 2
110 #else
111 #define DB_VER 1
112 #endif
113
114 typedef struct {
115
116     char *auth_dbpwfile;
117     char *auth_dbgrpfile;
118     int auth_dbauthoritative;
119 } db_auth_config_rec;
120
121 static void *create_db_auth_dir_config(ap_pool_t *p, char *d)
122 {
123     db_auth_config_rec *sec
124     = (db_auth_config_rec *) ap_pcalloc(p, sizeof(db_auth_config_rec));
125     sec->auth_dbpwfile = NULL;
126     sec->auth_dbgrpfile = NULL;
127     sec->auth_dbauthoritative = 1;      /* fortress is secure by default */
128     return sec;
129 }
130
131 static const char *set_db_slot(cmd_parms *cmd, void *offset, char *f, char *t)
132 {
133     if (!t || strcmp(t, "db"))
134         return DECLINE_CMD;
135
136     return ap_set_file_slot(cmd, offset, f);
137 }
138
139 static const command_rec db_auth_cmds[] =
140 {
141     {"AuthDBUserFile", ap_set_file_slot,
142      (void *) XtOffsetOf(db_auth_config_rec, auth_dbpwfile),
143      OR_AUTHCFG, TAKE1, NULL},
144     {"AuthDBGroupFile", ap_set_file_slot,
145      (void *) XtOffsetOf(db_auth_config_rec, auth_dbgrpfile),
146      OR_AUTHCFG, TAKE1, NULL},
147     {"AuthUserFile", set_db_slot,
148      (void *) XtOffsetOf(db_auth_config_rec, auth_dbpwfile),
149      OR_AUTHCFG, TAKE12, NULL},
150     {"AuthGroupFile", set_db_slot,
151      (void *) XtOffsetOf(db_auth_config_rec, auth_dbgrpfile),
152      OR_AUTHCFG, TAKE12, NULL},
153     {"AuthDBAuthoritative", ap_set_flag_slot,
154      (void *) XtOffsetOf(db_auth_config_rec, auth_dbauthoritative),
155      OR_AUTHCFG, FLAG,
156      "Set to 'no' to allow access control to be passed along to lower modules if the userID is not known to this module"},
157     {NULL}
158 };
159
160 module auth_db_module;
161
162 static char *get_db_pw(request_rec *r, char *user, const char *auth_dbpwfile)
163 {
164     DB *f;
165     DBT d, q;
166     char *pw = NULL;
167     int retval;
168
169     memset(&d, 0, sizeof(d));
170     memset(&q, 0, sizeof(q));
171
172     q.data = user;
173     q.size = strlen(q.data);
174
175 #if DB_VER == 3
176     db_create(&f, NULL, 0);
177     if ((retval = f->open(f, auth_dbpwfile, NULL, DB_HASH, DB_RDONLY, 0664)) != 0) {
178         char * reason;
179         switch(retval) {
180         case DB_OLD_VERSION:
181             reason = "Old database version.  Upgrade to version 3";
182             break;
183
184         case EEXIST:
185             reason = "DB_CREATE and DB_EXCL were specified and the file exists";
186             break;
187
188         case EINVAL:
189             reason = "An invalid flag value or parameter was specified";
190             break;
191
192         case ENOENT:
193             reason = "A non-existent re_source file was specified";
194             break;
195
196         default:
197             reason = "And I don't know why";
198             break;
199         }
200         ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r,
201                       "could not open db auth file %s: %s", 
202                       auth_dbpwfile, reason);
203         return NULL;
204     }
205 #elif DB_VER == 2
206     if ((retval = db_open(auth_dbpwfile, DB_HASH, DB_RDONLY, 0664, NULL, NULL, &f)) != 0) {
207         char * reason;
208         switch(retval) {
209
210         case EEXIST:
211             reason = "DB_CREATE and DB_EXCL were specified and the file exists.";
212             break;
213
214         case EINVAL:
215             reason = "An invalid flag value or parameter was specified";
216             break;
217
218         case ENOENT:
219             reason = "A non-existent re_source file was specified";
220             break;
221
222         default:
223             reason = "And I don't know why";
224             break;
225         }
226         ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r,
227                       "could not open db auth file %s: %s", 
228                       auth_dbpwfile, reason);
229         return NULL;
230     }
231 #else
232     if (!(f = dbopen(auth_dbpwfile, O_RDONLY, 0664, DB_HASH, NULL))) {
233         ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r,
234                       "could not open db auth file: %s", auth_dbpwfile);
235         return NULL;
236     }
237 #endif
238
239 #if DB_VER == 3 || DB_VER == 2
240     if (!((f->get) (f, NULL, &q, &d, 0))) {
241 #else
242     if (!((f->get) (f, &q, &d, 0))) {
243 #endif
244         pw = ap_palloc(r->pool, d.size + 1);
245         strncpy(pw, d.data, d.size);
246         pw[d.size] = '\0';      /* Terminate the string */
247     }
248
249 #if DB_VER == 3 || DB_VER == 2
250     (f->close) (f, 0);
251 #else
252     (f->close) (f);
253 #endif
254     return pw;
255 }
256
257 /* We do something strange with the group file.  If the group file
258  * contains any : we assume the format is
259  *      key=username value=":"groupname [":"anything here is ignored]
260  * otherwise we now (0.8.14+) assume that the format is
261  *      key=username value=groupname
262  * The first allows the password and group files to be the same 
263  * physical DB file;   key=username value=password":"groupname[":"anything]
264  *
265  * mark@telescope.org, 22Sep95
266  */
267
268 static char *get_db_grp(request_rec *r, char *user, const char *auth_dbgrpfile)
269 {
270     char *grp_data = get_db_pw(r, user, auth_dbgrpfile);
271     char *grp_colon;
272     char *grp_colon2;
273
274     if (grp_data == NULL)
275         return NULL;
276
277     if ((grp_colon = strchr(grp_data, ':')) != NULL) {
278         grp_colon2 = strchr(++grp_colon, ':');
279         if (grp_colon2)
280             *grp_colon2 = '\0';
281         return grp_colon;
282     }
283     return grp_data;
284 }
285
286 static int db_authenticate_basic_user(request_rec *r)
287 {
288     db_auth_config_rec *sec =
289     (db_auth_config_rec *) ap_get_module_config(r->per_dir_config,
290                                                 &auth_db_module);
291     const char *sent_pw;
292     char *real_pw, *colon_pw;
293     ap_status_t invalid_pw;
294     int res;
295
296     if ((res = ap_get_basic_auth_pw(r, &sent_pw)))
297         return res;
298
299     if (!sec->auth_dbpwfile) {
300         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
301                       "DB file %s not found", sec->auth_dbpwfile);
302         return DECLINED;
303     }
304
305     if (!(real_pw = get_db_pw(r, r->user, sec->auth_dbpwfile))) {
306         if (!(sec->auth_dbauthoritative))
307             return DECLINED;
308         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
309                     "DB user %s not found: %s", r->user, r->filename);
310         ap_note_basic_auth_failure(r);
311         return AUTH_REQUIRED;
312     }
313     /* Password is up to first : if exists */
314     colon_pw = strchr(real_pw, ':');
315     if (colon_pw) {
316         *colon_pw = '\0';
317     }
318
319     invalid_pw = ap_validate_password(sent_pw, real_pw);
320
321     if (invalid_pw != APR_SUCCESS) {
322 #ifdef HAVE_APR_STRERROR
323         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
324                       "DB user %s: authentication failure for \"%s\": %s",
325                       r->user, r->uri, apr_strerror(invalid_pw));
326 #else
327         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
328                       "DB user %s: authentication failure for \"%s\": %d",
329                       r->user, r->uri, "error number",
330                       invalid_pw);
331 #endif
332         ap_note_basic_auth_failure(r);
333         return AUTH_REQUIRED;
334     }
335     return OK;
336 }
337
338 /* Checking ID */
339
340 static int db_check_auth(request_rec *r)
341 {
342     db_auth_config_rec *sec =
343     (db_auth_config_rec *) ap_get_module_config(r->per_dir_config,
344                                                 &auth_db_module);
345     char *user = r->user;
346     int m = r->method_number;
347
348     const ap_array_header_t *reqs_arr = ap_requires(r);
349     require_line *reqs = reqs_arr ? (require_line *) reqs_arr->elts : NULL;
350
351     register int x;
352     const char *t;
353     char *w;
354
355     if (!sec->auth_dbgrpfile)
356         return DECLINED;
357     if (!reqs_arr)
358         return DECLINED;
359
360     for (x = 0; x < reqs_arr->nelts; x++) {
361
362         if (!(reqs[x].method_mask & (1 << m)))
363             continue;
364
365         t = reqs[x].requirement;
366         w = ap_getword_white(r->pool, &t);
367
368         if (!strcmp(w, "group") && sec->auth_dbgrpfile) {
369             const char *orig_groups, *groups;
370             char *v;
371
372             if (!(groups = get_db_grp(r, user, sec->auth_dbgrpfile))) {
373                 if (!(sec->auth_dbauthoritative))
374                     return DECLINED;
375                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
376                               "user %s not in DB group file %s: %s",
377                               user, sec->auth_dbgrpfile, r->filename);
378                 ap_note_basic_auth_failure(r);
379                 return AUTH_REQUIRED;
380             }
381             orig_groups = groups;
382             while (t[0]) {
383                 w = ap_getword_white(r->pool, &t);
384                 groups = orig_groups;
385                 while (groups[0]) {
386                     v = ap_getword(r->pool, &groups, ',');
387                     if (!strcmp(v, w))
388                         return OK;
389                 }
390             }
391             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
392                           "user %s not in right group: %s", user, r->filename);
393             ap_note_basic_auth_failure(r);
394             return AUTH_REQUIRED;
395         }
396     }
397
398     return DECLINED;
399 }
400
401 static void register_hooks(void)
402 {
403     ap_hook_check_user_id(db_authenticate_basic_user,NULL,NULL,AP_HOOK_MIDDLE);
404     ap_hook_auth_checker(db_check_auth,NULL,NULL,AP_HOOK_MIDDLE);
405 }
406
407 module auth_db_module =
408 {
409     STANDARD20_MODULE_STUFF,
410     create_db_auth_dir_config,  /* dir config creater */
411     NULL,                       /* dir merger --- default is to override */
412     NULL,                       /* server config */
413     NULL,                       /* merge server config */
414     db_auth_cmds,               /* command ap_table_t */
415     NULL,                       /* handlers */
416     register_hooks              /* register hooks */
417 };
418