]> granicus.if.org Git - apache-authnz-external/blob - mod_authnz_external/mod_authnz_external.c
Final version of sigchild fixes.
[apache-authnz-external] / mod_authnz_external / mod_authnz_external.c
1 /* ====================================================================
2  * Copyright (c) 1995 The Apache Group.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  *
16  * 3. All advertising materials mentioning features or use of this
17  *    software must display the following acknowledgment:
18  *    "This product includes software developed by the Apache Group
19  *    for use in the Apache HTTP server project (http://www.apache.org/)."
20  *
21  * 4. The names "Apache Server" and "Apache Group" must not be used to
22  *    endorse or promote products derived from this software without
23  *    prior written permission.
24  *
25  * 5. Redistributions of any form whatsoever must retain the following
26  *    acknowledgment:
27  *    "This product includes software developed by the Apache Group
28  *    for use in the Apache HTTP server project (http://www.apache.org/)."
29  *
30  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
31  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
34  * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
41  * OF THE POSSIBILITY OF SUCH DAMAGE.
42  * ====================================================================
43  *
44  * This software consists of voluntary contributions made by many
45  * individuals on behalf of the Apache Group and was originally based
46  * on public domain software written at the National Center for
47  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
48  * For more information on the Apache Group and the Apache HTTP server
49  * project, please see <http://www.apache.org/>.
50  *
51  */
52
53
54 /* Uncomment if you want to use a HARDCODE'd check (default off) */
55 /* #define _HARDCODE_ */
56
57 #ifdef _HARDCODE_
58   /* Uncomment if you want to use your own Hardcode (default off) */
59   /*             MUST HAVE _HARDCODE_ defined above!                */
60   /* #include "your_function_here.c" */
61 #endif
62
63
64 #include "apr_lib.h"
65
66 #include "ap_config.h"
67 #include "ap_provider.h"
68 #include "mod_auth.h"
69 #include "apr_signal.h"
70
71 #define APR_WANT_STRFUNC
72 #include "apr_want.h"
73 #include "apr_strings.h"
74
75 #include "httpd.h"
76 #include "http_config.h"
77 #include "http_core.h"
78 #include "http_log.h"
79 #include "http_protocol.h"
80 #include "http_request.h"   /* for ap_hook_(check_user_id | auth_checker)*/
81 #if APR_HAVE_UNISTD_H
82 #include <unistd.h>
83 #endif
84
85 #ifndef STANDARD20_MODULE_STUFF
86 #error This module requires Apache 2.2.0 or later.
87 #endif
88
89 /* Names of environment variables used to pass data to authenticator */
90 #define ENV_USER        "USER"
91 #define ENV_PASS        "PASS"
92 #define ENV_GROUP       "GROUP"
93 #define ENV_URI         "URI"
94 #define ENV_IP          "IP"
95 #define ENV_HOST        "HOST"          /* Remote Host */
96 #define ENV_HTTP_HOST   "HTTP_HOST"     /* Local Host */
97 #define ENV_CONTEXT     "CONTEXT"       /* Arbitrary Data from Config */
98 /* Undefine this if you do not want cookies passed to the script */
99 #define ENV_COOKIE      "COOKIE"
100
101 /* Maximum number of arguments passed to an authenticator */
102 #define MAX_ARG 32
103
104 /* Default authentication method - "pipe", "environment" or "checkpass" */
105 #define DEFAULT_METHOD "pipe"
106
107 /*
108  * Structure for the module itself.  The actual definition of this structure
109  * is at the end of the file.
110  */
111 module AP_MODULE_DECLARE_DATA authnz_external_module;
112
113 /*
114  *  Data types for per-directory and per-server configuration
115  */
116
117 typedef struct
118 {
119     apr_array_header_t *auth_name; /* Auth keyword for current dir */
120     char *group_name;            /* Group keyword for current dir */
121     char *context;               /* Context string from AuthExternalContext */
122     int  authoritative;          /* Are we authoritative in current dir? */
123     int  groupsatonce;           /* Check all groups in one call in this dir? */
124     char *grouperror;            /* What to return if group auth fails */
125
126 } authnz_external_dir_config_rec;
127
128
129 typedef struct
130 {
131     apr_table_t *auth_path;      /* Hash mapping auth keywords to paths */
132     apr_table_t *auth_method;    /* Hash mapping auth keywords to methods */
133
134     apr_table_t *group_path;     /* Hash mapping group keywords to paths */
135     apr_table_t *group_method;   /* Hash mapping group keywords to methods */
136
137 } authnz_external_svr_config_rec;
138
139
140 /*
141  * Creators for per-dir and server configurations.  These are called
142  * via the hooks in the module declaration to allocate and initialize
143  * the per-directory and per-server configuration data structures declared
144  * above.
145  */
146
147 static void *create_authnz_external_dir_config(apr_pool_t *p, char *d)
148 {
149     authnz_external_dir_config_rec *dir= (authnz_external_dir_config_rec *)
150         apr_palloc(p, sizeof(authnz_external_dir_config_rec));
151
152     dir->auth_name= apr_array_make(p,2,sizeof(const char *));   /* no default */
153     dir->group_name= NULL;      /* no default */
154     dir->context= NULL;         /* no default */
155     dir->authoritative= 1;      /* strong by default */
156     dir->groupsatonce= 1;       /* default to on */
157     dir->grouperror= NULL;      /* default to 401 */
158     return dir;
159 }
160
161
162 static void *create_authnz_external_svr_config( apr_pool_t *p, server_rec *s)
163 {
164     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
165         apr_palloc(p, sizeof(authnz_external_svr_config_rec));
166
167     svr->auth_method=  apr_table_make(p, 4);
168     svr->auth_path=    apr_table_make(p, 4);
169     svr->group_method= apr_table_make(p, 4);
170     svr->group_path=   apr_table_make(p, 4);
171     /* Note: 4 is only initial hash size - they can grow bigger) */
172
173     return (void *)svr;
174 }
175
176 /*
177  * Handler for a DefineExternalAuth server config line
178  */
179
180 static const char *def_extauth(cmd_parms *cmd, void *dummy, const char *keyword,
181                                 const char *method, const char *path)
182 {
183     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
184         ap_get_module_config( cmd->server->module_config,
185             &authnz_external_module);
186
187     apr_table_set( svr->auth_path,   keyword, path );
188     apr_table_set( svr->auth_method, keyword, method );
189
190     return NULL;
191 }
192
193
194 /*
195  * Handler for a DefineExternalGroup server config line
196  */
197
198 static const char *def_extgroup(cmd_parms *cmd, void *dummy,
199         const char *keyword, const char *method, const char *path)
200 {
201     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
202         ap_get_module_config( cmd->server->module_config,
203             &authnz_external_module);
204
205     apr_table_set( svr->group_path,   keyword, path );
206     apr_table_set( svr->group_method, keyword, DEFAULT_METHOD );
207
208     return NULL;
209 }
210
211
212
213 /*
214  * Handler for a AddExternalAuth server config line - add a external auth
215  * type to the server configuration
216  */
217
218 static const char *add_extauth(cmd_parms *cmd, void *dummy, const char *keyword,
219                                 const char *path)
220 {
221     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
222         ap_get_module_config( cmd->server->module_config,
223             &authnz_external_module);
224
225     apr_table_set( svr->auth_path,   keyword, path );
226     apr_table_set( svr->auth_method, keyword, DEFAULT_METHOD );
227
228     return NULL;
229 }
230
231
232 /*
233  * Handler for a AddExternalGroup server config line - add a external group
234  * type to the server configuration
235  */
236
237 static const char *add_extgroup(cmd_parms *cmd, void *dummy,
238                                 const char *keyword, const char *path)
239 {
240     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
241         ap_get_module_config( cmd->server->module_config,
242             &authnz_external_module);
243
244     apr_table_set( svr->group_path,   keyword, path );
245     apr_table_set( svr->group_method, keyword, DEFAULT_METHOD );
246
247     return NULL;
248 }
249
250 /*
251  * Handler for a SetExternalAuthMethod server config line - change an external
252  * auth method in the server configuration
253  */
254
255 static const char *set_authnz_external_method(cmd_parms *cmd, void *dummy,
256                                         const char *keyword, const char *method)
257 {
258     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
259         ap_get_module_config( cmd->server->module_config,
260             &authnz_external_module);
261
262     apr_table_set( svr->auth_method, keyword, method );
263
264     return NULL;
265 }
266
267
268 /*
269  * Handler for a SetExternalGroupMethod server config line - change an external
270  * group method in the server configuration
271  */
272
273 static const char *set_extgroup_method(cmd_parms *cmd, void *dummy,
274                                         const char *keyword, const char *method)
275 {
276     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
277         ap_get_module_config( cmd->server->module_config,
278             &authnz_external_module);
279
280     apr_table_set( svr->group_method, keyword, method );
281
282     return NULL;
283 }
284
285 /* Append an argument to an array defined by the offset */
286 static const char *append_array_slot(cmd_parms *cmd, void *struct_ptr,
287                                 const char *arg)
288 {
289     int offset = (int)(long)cmd->info;
290     apr_array_header_t *array=
291         *(apr_array_header_t **)((char *)struct_ptr + offset);
292
293     *(const char **)apr_array_push(array)= apr_pstrdup(array->pool, arg);
294
295     return NULL;
296 }
297
298
299 /*
300  * Config file commands that this module can handle
301  */
302
303 static const command_rec authnz_external_cmds[] =
304 {
305     AP_INIT_ITERATE("AuthExternal",
306         append_array_slot,
307         (void *)APR_OFFSETOF(authnz_external_dir_config_rec,auth_name),
308         OR_AUTHCFG,
309         "one (or more) keywords indicating which authenticators to use"),
310
311     AP_INIT_TAKE3("DefineExternalAuth",
312         def_extauth,
313         NULL,
314         RSRC_CONF,
315         "a keyword followed by auth method and path to authentictor"),
316
317     AP_INIT_TAKE2("AddExternalAuth",
318         add_extauth,
319         NULL,
320         RSRC_CONF,
321         "a keyword followed by a path to the authenticator program"),
322
323     AP_INIT_TAKE2("SetExternalAuthMethod",
324         set_authnz_external_method,
325         NULL,
326         RSRC_CONF,
327         "a keyword followed by the method by which the data is passed"),
328
329     AP_INIT_TAKE1("GroupExternal",
330         ap_set_string_slot,
331         (void *)APR_OFFSETOF(authnz_external_dir_config_rec, group_name),
332         OR_AUTHCFG,
333         "a keyword indicating which group checker to use"),
334
335     AP_INIT_TAKE3("DefineExternalGroup",
336         def_extgroup,
337         NULL,
338         RSRC_CONF,
339         "a keyword followed by auth method type and path to group checker"),
340
341     AP_INIT_TAKE2("AddExternalGroup",
342         add_extgroup,
343         NULL,
344         RSRC_CONF,
345         "a keyword followed by a path to the group check program"),
346
347     AP_INIT_TAKE2("SetExternalGroupMethod",
348         set_extgroup_method,
349         NULL,
350         RSRC_CONF,
351         "a keyword followed by the method by which the data is passed"),
352
353     AP_INIT_FLAG("GroupExternalAuthoritative",
354         ap_set_flag_slot,
355         (void *)APR_OFFSETOF(authnz_external_dir_config_rec, authoritative),
356         OR_AUTHCFG,
357         "Set to 'off' to allow access control to be passed along to lower "
358             "modules if this module can't confirm access rights" ),
359
360     AP_INIT_FLAG("AuthzExternalAuthoritative",
361         ap_set_flag_slot,
362         (void *)APR_OFFSETOF(authnz_external_dir_config_rec, authoritative),
363         OR_AUTHCFG,
364         "Old version of 'GroupExternalAuthoritative'" ),
365
366     AP_INIT_TAKE1("AuthExternalContext",
367         ap_set_string_slot,
368         (void *)APR_OFFSETOF(authnz_external_dir_config_rec, context),
369         OR_AUTHCFG,
370         "An arbitrary context string to pass to the authenticator in the "
371         ENV_CONTEXT " environment variable"),
372
373     AP_INIT_TAKE1("GroupExternalError",
374         ap_set_string_slot,
375         (void *)APR_OFFSETOF(authnz_external_dir_config_rec, grouperror),
376         OR_AUTHCFG,
377         "HTTP error code to return when group authentication fails"),
378
379     AP_INIT_FLAG("GroupExternalManyAtOnce",
380         ap_set_flag_slot,
381         (void *)APR_OFFSETOF(authnz_external_dir_config_rec, groupsatonce),
382         OR_AUTHCFG,
383         "Set to 'off' if group authenticator cannot handle multiple group "
384             "names in one invocation" ),
385
386     AP_INIT_FLAG("AuthExternalGroupsAtOnce",
387         ap_set_flag_slot,
388         (void *)APR_OFFSETOF(authnz_external_dir_config_rec, groupsatonce),
389         OR_AUTHCFG,
390         "Old version of 'GroupExternalManyAtOnce'" ),
391
392     { NULL }
393 };
394
395
396 /* Called from apr_proc_create() if there are errors during launch of child
397  * process.  Mostly just lifted from mod_cgi.
398  */
399
400 static void extchilderr(apr_pool_t *p, apr_status_t err, const char *desc)
401 {
402     apr_file_t *stderr_log;
403     char errbuf[200];
404     apr_file_open_stderr(&stderr_log, p);
405     apr_file_printf(stderr_log,"%s: (%d) %s\n", ap_escape_logitem(p,desc),
406         err, apr_strerror(err,errbuf,sizeof(errbuf)));
407 }
408
409
410 /*
411  * Run an external authentication program using the given method for passing
412  * in the data.  The login name is always passed in.   Dataname is "GROUP" or
413  * "PASS" and data is the group list or password being checked.  To launch
414  * a detached daemon, run this with extmethod=NULL.
415  *
416  * If the authenticator was run, we return the numeric code from the
417  * authenticator, normally 0 if the login was valid, some small positive
418  * number if not.  If we were not able to run the authenticator, we log
419  * an error message and return a numeric error code:
420  *
421  *   -1   Could not execute authenticator, usually a path or permission problem
422  *   -2   The external authenticator crashed or was killed.
423  *   -3   Could not create process attribute structure
424  *   -4   apr_proc_wait() did not return a status code.  Should never happen.
425  *   -5   apr_proc_wait() returned before child finished.  Should never happen.
426  */
427
428 static int exec_external(const char *extpath, const char *extmethod,
429                 const request_rec *r, const char *dataname, const char *data)
430 {
431     conn_rec *c= r->connection;
432     apr_pool_t *p= r->pool;
433     int isdaemon, usecheck= 0, usepipeout= 0, usepipein= 0;
434     apr_procattr_t *procattr;
435     apr_proc_t proc;
436     apr_status_t rc= APR_SUCCESS;
437     char *child_env[12];
438     char *child_arg[MAX_ARG+2];
439     const char *t;
440     int i, status= -4;
441     apr_exit_why_e why= APR_PROC_EXIT;
442     apr_sigfunc_t *sigchld;
443
444     /* Set various flags based on the execution method */
445
446     isdaemon= (extmethod == NULL);
447     if (!isdaemon)
448     {
449         usecheck= extmethod && !strcasecmp(extmethod, "checkpassword");
450         usepipeout= usecheck || (extmethod && !strcasecmp(extmethod, "pipes"));
451         usepipein= usepipeout || (extmethod && !strcasecmp(extmethod, "pipe"));
452     }
453
454     /* Create the environment for the child.  Daemons don't get these, they
455      * just inherit apache's environment variables.
456      */
457
458     if (!isdaemon)
459     {
460         const char *cookie, *host, *remote_host;
461         authnz_external_dir_config_rec *dir= (authnz_external_dir_config_rec *)
462             ap_get_module_config(r->per_dir_config, &authnz_external_module);
463         i= 0;
464
465         if (!usepipein)
466         {
467             /* Put user name and password/group into environment */
468             child_env[i++]= apr_pstrcat(p, ENV_USER"=", r->user, NULL);
469             child_env[i++]= apr_pstrcat(p, dataname, "=", data, NULL);
470         }
471
472         child_env[i++]= apr_pstrcat(p, "PATH=", getenv("PATH"), NULL);
473
474         child_env[i++]= apr_pstrcat(p, "AUTHTYPE=", dataname, NULL);
475
476         remote_host= ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST,NULL);
477         if (remote_host != NULL)
478             child_env[i++]= apr_pstrcat(p, ENV_HOST"=", remote_host,NULL);
479
480         if (c->remote_ip)
481             child_env[i++]= apr_pstrcat(p, ENV_IP"=", c->remote_ip, NULL);
482
483         if (r->uri)
484             child_env[i++]= apr_pstrcat(p, ENV_URI"=", r->uri, NULL);
485
486         if ((host= apr_table_get(r->headers_in, "Host")) != NULL)
487             child_env[i++]= apr_pstrcat(p, ENV_HTTP_HOST"=", host, NULL);
488
489         if (dir->context)
490             child_env[i++]= apr_pstrcat(r->pool, ENV_CONTEXT"=",
491                 dir->context, NULL);
492
493 #ifdef ENV_COOKIE
494         if ((cookie= apr_table_get(r->headers_in, "Cookie")) != NULL)
495             child_env[i++]= apr_pstrcat(p, ENV_COOKIE"=", cookie, NULL);
496 #endif
497         /* NOTE:  If you add environment variables,
498          *   remember to increase the size of the child_env[] array */
499
500         /* End of environment */
501         child_env[i]= NULL;
502     }
503
504     /* Construct argument array */
505     for (t= extpath, i=0; *t != '\0' && (i <= MAX_ARG + 1);
506          child_arg[i++]= ap_getword_white(p, &t)) {}
507     child_arg[i]= NULL;
508
509     /* Create the process attribute structure describing the script we
510      * want to run using the Thread/Process functions from the Apache
511      * portable runtime library. */
512
513     if (((rc= apr_procattr_create(&procattr, p)) != APR_SUCCESS) ||
514
515         /* should we create pipes to stdin, stdout and stderr? */
516         ((rc= apr_procattr_io_set(procattr,
517             usepipein  ? APR_FULL_BLOCK : APR_NO_PIPE,
518             usepipeout ? APR_FULL_BLOCK : APR_NO_PIPE,
519             APR_NO_PIPE)) != APR_SUCCESS) ||
520
521         /* will give full path of program and make a new environment */
522         ((rc= apr_procattr_cmdtype_set(procattr,
523             isdaemon ? APR_PROGRAM_ENV : APR_PROGRAM)) != APR_SUCCESS) ||
524
525         /* detach the child only if it is a daemon */
526         ((rc= apr_procattr_detach_set(procattr, isdaemon)) != APR_SUCCESS) ||
527
528         /* function to call if child has error after fork, before exec */
529         ((rc= apr_procattr_child_errfn_set(procattr, extchilderr)
530               != APR_SUCCESS)))
531     {
532         /* Failed.  Probably never happens. */
533         ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
534             "could not set child process attributes");
535         return -3;
536     }
537
538     /* Sometimes other modules wil mess up sigchild.  Need to fix it for
539      * the wait call to work correctly.  */
540     sigchld= apr_signal(SIGCHLD,SIG_DFL);
541
542     /* Start the child process */
543     rc= apr_proc_create(&proc, child_arg[0],
544         (const char * const *)child_arg,
545         (const char * const *)child_env, procattr, p);
546     if (rc != APR_SUCCESS)
547     {
548         ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
549                 "Could not run external authenticator: %d: %s", rc,
550                 child_arg[0]);
551         return -1;
552     }
553
554     if (isdaemon) return 0;
555
556     apr_pool_note_subprocess(p, &proc, APR_KILL_AFTER_TIMEOUT);
557
558     if (usepipein)
559     {
560         /* Send the user */
561         apr_file_write_full(proc.in, r->user, strlen(r->user), NULL);
562         apr_file_putc(usecheck ? '\0' : '\n', proc.in);
563
564         /* Send the password */
565         apr_file_write_full(proc.in, data, strlen(data), NULL);
566         apr_file_putc(usecheck ? '\0' : '\n', proc.in);
567
568         /* Send dummy timestamp for checkpassword */
569         if (usecheck) apr_file_write_full(proc.in, "0", 2, NULL);
570
571         /* Close the file */
572         apr_file_close(proc.in);
573     }
574
575     /* Wait for the child process to terminate, and get status */
576     rc= apr_proc_wait(&proc,&status,&why,APR_WAIT);
577
578     /* Restore sigchild to whatever it was before we reset it */
579     apr_signal(SIGCHLD,sigchld);
580
581     if (!APR_STATUS_IS_CHILD_DONE(rc))
582     {
583         ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
584                 "Could not get status from child process");
585         return -5;
586     }
587     if (!APR_PROC_CHECK_EXIT(why))
588     {
589         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
590                 "External authenticator died on signal %d",status);
591         return -2;
592     }
593
594     return status;
595 }
596
597
598 /* Call the hardcoded function specified by the external path.  Of course,
599  * you'll have to write the hardcoded functions yourself and insert them
600  * into this source file, as well as inserting a call to them into this
601  * routine.
602  */
603
604 static int exec_hardcode(const request_rec *r, const char *extpath,
605         const char *password)
606 {
607 #ifdef _HARDCODE_
608     char *check_type;           /* Pointer to HARDCODE type check  */
609     char *config_file;          /* Pointer to HARDCODE config file */
610     int standard_auth= 0;
611
612     /* Parse a copy of extpath into type and filename */
613     check_type= apr_pstrdup(r->pool, extpath);
614     config_file= strchr(check_type, ':');
615     if (config_file != NULL)
616     {
617         *config_file= '\0';                /* Mark end of type */
618         config_file++;                     /* Start of filename */
619     }
620
621     /* This is where you make your function call.  Here is an example of
622      * what one looks like:
623      *
624      *   if (strcmp(check_type,"RADIUS")==0)
625      *      code= radcheck(r->user,password,config_file);
626      *
627      * Replace 'radcheck' with whatever the name of your function is.
628      * Replace 'RADIUS' with whatever you are using as the <type> in:
629      *     AddExternalAuth <keyword> <type>:<config file>
630      */
631
632     if (strcmp(check_type,"EXAMPLE")==0)                /* change this! */
633         code= example(r->user,password,config_file);    /* change this! */
634     else
635         code= -5;
636     return code;
637 #else
638     return -4;          /* If _HARDCODE_ is not defined, always fail */
639 #endif /* _HARDCODE_ */
640 }
641
642
643 static int authz_external_check_user_access(request_rec *r)
644 {
645     authnz_external_dir_config_rec *dir= (authnz_external_dir_config_rec *)
646         ap_get_module_config(r->per_dir_config, &authnz_external_module);
647
648     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
649         ap_get_module_config(r->server->module_config, &authnz_external_module);
650
651     int code, ret;
652     int m= r->method_number;
653     const char *extpath, *extmethod;
654     char *extname= dir->group_name;
655     int required_group= 0;
656     register int x;
657     const char *t, *w;
658     const apr_array_header_t *reqs_arr= ap_requires(r);
659     const char *filegroup= NULL;
660     require_line *reqs;
661
662     /* If no external authenticator has been configured, pass */
663     if ( !extname ) return DECLINED;
664
665     /* If there are no Require arguments, pass */
666     if (!reqs_arr) return DECLINED;
667     reqs=  (require_line *)reqs_arr->elts;
668
669
670     /* Loop through the "Require" argument list */
671     for(x= 0; x < reqs_arr->nelts; x++)
672     {
673         if (!(reqs[x].method_mask & (AP_METHOD_BIT << m))) continue;
674
675         t= reqs[x].requirement;
676         w= ap_getword_white(r->pool, &t);
677
678         /* The 'file-group' directive causes mod_authz_owner to store the
679          * group name of the file we are trying to access in a note attached
680          * to the request.  It's our job to decide if the user actually is
681          * in that group.  If the note is missing, we just decline.
682          */
683         if ( !strcasecmp(w, "file-group"))
684         {
685             filegroup= apr_table_get(r->notes, AUTHZ_GROUP_NOTE);
686             if (filegroup == NULL) continue;
687         }
688
689         if( !strcmp(w,"group") || filegroup != NULL)
690         {
691             required_group= 1;
692
693             if (t[0] || filegroup != NULL)
694             {
695                 /* Get the path and method associated with that external */
696                 if (!(extpath= apr_table_get(svr->group_path, extname)) ||
697                     !(extmethod= apr_table_get(svr->group_method,
698                             extname)))
699                 {
700                     errno= 0;
701                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
702                         "invalid GroupExternal keyword (%s)", extname);
703                     ap_note_basic_auth_failure(r);
704                     return HTTP_INTERNAL_SERVER_ERROR;
705                 }
706
707                 if (filegroup != NULL)
708                 {
709                     /* Check if user is in the group that owns the file */
710                     code= exec_external(extpath, extmethod, r, ENV_GROUP,
711                             filegroup);
712                     if (code == 0) return OK;
713                 }
714                 else if (dir->groupsatonce)
715                 {
716                     /* Pass rest of require line to authenticator */
717                     code= exec_external(extpath, extmethod, r, ENV_GROUP, t);
718                     if (code == 0) return OK;
719                 }
720                 else
721                 {
722                     /* Call authenticator once for each group name on line */
723                     do {
724                         w= ap_getword_white(r->pool, &t);
725                         code= exec_external(extpath,
726                                 extmethod, r, ENV_GROUP, w);
727                         if (code == 0) return OK;
728                     } while(t[0]);
729                 }
730             }
731         }
732     }
733
734     /* If we didn't see a 'require group' or aren't authoritive, decline */
735     if (!required_group || !dir->authoritative)
736         return DECLINED;
737
738     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
739         "access to %s failed, reason: user %s not allowed access (%s)",
740         r->uri, r->user, dir->grouperror);
741
742     ap_note_basic_auth_failure(r);
743
744     return (dir->grouperror && (ret= atoi(dir->grouperror)) > 0) ? ret :
745         HTTP_UNAUTHORIZED;
746 }
747
748
749 /* Password checker for basic authentication - given a login/password,
750  * check if it is valid.  Returns one of AUTH_DENIED, AUTH_GRANTED,
751  * or AUTH_GENERAL_ERROR.
752  */
753
754 static authn_status authn_external_check_password(request_rec *r,
755         const char *user, const char *password)
756 {
757     const char *extname, *extpath, *extmethod;
758     int i;
759     authnz_external_dir_config_rec *dir= (authnz_external_dir_config_rec *)
760             ap_get_module_config(r->per_dir_config, &authnz_external_module);
761
762     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
763             ap_get_module_config(r->server->module_config,
764                 &authnz_external_module);
765     int code= 1;
766
767     /* Check if we are supposed to handle this authentication */
768     if (dir->auth_name->nelts == 0)
769     {
770         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
771             "No AuthExternal name has been set");
772         return AUTH_GENERAL_ERROR;
773     }
774
775     for (i= 0; i < dir->auth_name->nelts; i++)
776     {
777         extname= ((const char **)dir->auth_name->elts)[i];
778
779         /* Get the path associated with that external */
780         if (!(extpath= apr_table_get(svr->auth_path, extname)))
781         {
782             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
783                 "Invalid AuthExternal keyword (%s)", extname);
784             return AUTH_GENERAL_ERROR;
785         }
786
787         /* Do the authentication, by the requested method */
788         extmethod= apr_table_get(svr->auth_method, extname);
789         if ( extmethod && !strcasecmp(extmethod, "function") )
790             code= exec_hardcode(r, extpath, password);
791         else
792             code= exec_external(extpath, extmethod, r, ENV_PASS, password);
793
794         /* If return code was zero, authentication succeeded */
795         if (code == 0) return AUTH_GRANTED;
796
797         /* Log a failed authentication */
798         errno= 0;
799         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
800             "AuthExtern %s [%s]: Failed (%d) for user %s",
801             extname, extpath, code, r->user);
802     }
803     /* If no authenticators succeed, refuse authentication */
804     return AUTH_DENIED;
805 }
806
807
808 #if 0
809 /* Password checker for digest authentication - given a login/password,
810  * check if it is valid.  Returns one of AUTH_USER_FOUND, AUTH_USER_NOT_FOUND,
811  * or AUTH_GENERAL_ERROR.   Not implemented at this time.
812  */
813
814 auth_status *authn_external_get_realm_hash(request_rec *r, const char *user,
815     const char *realm, char **rethash);
816 {
817 }
818 #endif
819
820
821 static const authn_provider authn_external_provider =
822 {
823     &authn_external_check_password,
824 #if 0
825     &authn_external_get_realm_hash
826 #else
827     NULL                /* No support for digest authentication at this time */
828 #endif
829 };
830
831
832 static void register_hooks(apr_pool_t *p)
833 {
834     ap_register_provider(p, AUTHN_PROVIDER_GROUP, "external", "0",
835             &authn_external_provider);
836
837     ap_hook_auth_checker(authz_external_check_user_access, NULL, NULL,
838             APR_HOOK_MIDDLE);
839 }
840
841
842 module AP_MODULE_DECLARE_DATA authnz_external_module = {
843     STANDARD20_MODULE_STUFF,
844     create_authnz_external_dir_config,    /* create per-dir config */
845     NULL,                         /* merge per-dir config - dflt is override */
846     create_authnz_external_svr_config, /* create per-server config */
847     NULL,                         /* merge per-server config */
848     authnz_external_cmds,         /* command apr_table_t */
849     register_hooks                /* register hooks */
850 };