]> granicus.if.org Git - apache-authnz-external/blob - mod_authnz_external/mod_authnz_external.c
94e52a3844a3012f62262e62b3c9b6b06d35d5a3
[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 && !usecheck) ? APR_FULL_BLOCK : APR_NO_PIPE,
518             usepipeout ? APR_FULL_BLOCK : APR_NO_PIPE,
519             (usepipein && usecheck) ? APR_FULL_BLOCK : APR_NO_PIPE))
520                != APR_SUCCESS ) ||
521
522         /* will give full path of program and make a new environment */
523         ((rc= apr_procattr_cmdtype_set(procattr,
524             isdaemon ? APR_PROGRAM_ENV : APR_PROGRAM)) != APR_SUCCESS) ||
525
526         /* detach the child only if it is a daemon */
527         ((rc= apr_procattr_detach_set(procattr, isdaemon)) != APR_SUCCESS) ||
528
529         /* function to call if child has error after fork, before exec */
530         ((rc= apr_procattr_child_errfn_set(procattr, extchilderr)
531               != APR_SUCCESS)))
532     {
533         /* Failed.  Probably never happens. */
534         ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
535             "could not set child process attributes");
536         return -3;
537     }
538
539     /* Sometimes other modules wil mess up sigchild.  Need to fix it for
540      * the wait call to work correctly.  */
541     sigchld= apr_signal(SIGCHLD,SIG_DFL);
542
543     /* Start the child process */
544     rc= apr_proc_create(&proc, child_arg[0],
545         (const char * const *)child_arg,
546         (const char * const *)child_env, procattr, p);
547     if (rc != APR_SUCCESS)
548     {
549         ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
550                 "Could not run external authenticator: %d: %s", rc,
551                 child_arg[0]);
552         return -1;
553     }
554
555     if (isdaemon) return 0;
556
557     apr_pool_note_subprocess(p, &proc, APR_KILL_AFTER_TIMEOUT);
558
559     if (usepipein)
560     {
561         /* Select appropriate pipe to write to */
562         apr_file_t *pipe= (usecheck ? proc.err : proc.in);
563
564         /* Send the user */
565         apr_file_write_full(pipe, r->user, strlen(r->user), NULL);
566         apr_file_putc(usecheck ? '\0' : '\n', pipe);
567
568         /* Send the password */
569         apr_file_write_full(pipe, data, strlen(data), NULL);
570         apr_file_putc(usecheck ? '\0' : '\n', pipe);
571
572         /* Send dummy timestamp for checkpassword */
573         if (usecheck) apr_file_write_full(pipe, "0", 2, NULL);
574
575         /* Close the file */
576         apr_file_close(pipe);
577     }
578
579     /* Wait for the child process to terminate, and get status */
580     rc= apr_proc_wait(&proc,&status,&why,APR_WAIT);
581
582     /* Restore sigchild to whatever it was before we reset it */
583     apr_signal(SIGCHLD,sigchld);
584
585     if (!APR_STATUS_IS_CHILD_DONE(rc))
586     {
587         ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
588                 "Could not get status from child process");
589         return -5;
590     }
591     if (!APR_PROC_CHECK_EXIT(why))
592     {
593         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
594                 "External authenticator died on signal %d",status);
595         return -2;
596     }
597
598     return status;
599 }
600
601
602 /* Call the hardcoded function specified by the external path.  Of course,
603  * you'll have to write the hardcoded functions yourself and insert them
604  * into this source file, as well as inserting a call to them into this
605  * routine.
606  */
607
608 static int exec_hardcode(const request_rec *r, const char *extpath,
609         const char *password)
610 {
611 #ifdef _HARDCODE_
612     char *check_type;           /* Pointer to HARDCODE type check  */
613     char *config_file;          /* Pointer to HARDCODE config file */
614     int standard_auth= 0;
615
616     /* Parse a copy of extpath into type and filename */
617     check_type= apr_pstrdup(r->pool, extpath);
618     config_file= strchr(check_type, ':');
619     if (config_file != NULL)
620     {
621         *config_file= '\0';                /* Mark end of type */
622         config_file++;                     /* Start of filename */
623     }
624
625     /* This is where you make your function call.  Here is an example of
626      * what one looks like:
627      *
628      *   if (strcmp(check_type,"RADIUS")==0)
629      *      code= radcheck(r->user,password,config_file);
630      *
631      * Replace 'radcheck' with whatever the name of your function is.
632      * Replace 'RADIUS' with whatever you are using as the <type> in:
633      *     AddExternalAuth <keyword> <type>:<config file>
634      */
635
636     if (strcmp(check_type,"EXAMPLE")==0)                /* change this! */
637         code= example(r->user,password,config_file);    /* change this! */
638     else
639         code= -5;
640     return code;
641 #else
642     return -4;          /* If _HARDCODE_ is not defined, always fail */
643 #endif /* _HARDCODE_ */
644 }
645
646
647 static int authz_external_check_user_access(request_rec *r)
648 {
649     authnz_external_dir_config_rec *dir= (authnz_external_dir_config_rec *)
650         ap_get_module_config(r->per_dir_config, &authnz_external_module);
651
652     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
653         ap_get_module_config(r->server->module_config, &authnz_external_module);
654
655     int i, code, ret;
656     int m= r->method_number;
657     const char *extpath, *extmethod;
658     char *extname= dir->group_name;
659     int required_group= 0;
660     const char *t, *w;
661     const apr_array_header_t *reqs_arr= ap_requires(r);
662     const char *filegroup= NULL;
663     require_line *reqs;
664
665     /* If no external authenticator has been configured, pass */
666     if ( !extname ) return DECLINED;
667
668     /* If there are no Require arguments, pass */
669     if (!reqs_arr) return DECLINED;
670     reqs=  (require_line *)reqs_arr->elts;
671
672
673     /* Loop through the "Require" argument list */
674     for(i= 0; i < reqs_arr->nelts; i++)
675     {
676         if (!(reqs[i].method_mask & (AP_METHOD_BIT << m))) continue;
677
678         t= reqs[i].requirement;
679         w= ap_getword_white(r->pool, &t);
680
681         /* The 'file-group' directive causes mod_authz_owner to store the
682          * group name of the file we are trying to access in a note attached
683          * to the request.  It's our job to decide if the user actually is
684          * in that group.  If the note is missing, we just decline.
685          */
686         if ( !strcasecmp(w, "file-group"))
687         {
688             filegroup= apr_table_get(r->notes, AUTHZ_GROUP_NOTE);
689             if (filegroup == NULL) continue;
690         }
691
692         if( !strcmp(w,"group") || filegroup != NULL)
693         {
694             required_group= 1;
695
696             if (t[0] || filegroup != NULL)
697             {
698                 /* Get the path and method associated with that external */
699                 if (!(extpath= apr_table_get(svr->group_path, extname)) ||
700                     !(extmethod= apr_table_get(svr->group_method,
701                             extname)))
702                 {
703                     errno= 0;
704                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
705                         "invalid GroupExternal keyword (%s)", extname);
706                     ap_note_basic_auth_failure(r);
707                     return HTTP_INTERNAL_SERVER_ERROR;
708                 }
709
710                 if (filegroup != NULL)
711                 {
712                     /* Check if user is in the group that owns the file */
713                     code= exec_external(extpath, extmethod, r, ENV_GROUP,
714                             filegroup);
715                     if (code == 0) return OK;
716                 }
717                 else if (dir->groupsatonce)
718                 {
719                     /* Pass rest of require line to authenticator */
720                     code= exec_external(extpath, extmethod, r, ENV_GROUP, t);
721                     if (code == 0) return OK;
722                 }
723                 else
724                 {
725                     /* Call authenticator once for each group name on line */
726                     do {
727                         w= ap_getword_conf(r->pool, &t);
728                         code= exec_external(extpath,
729                                 extmethod, r, ENV_GROUP, w);
730                         if (code == 0) return OK;
731                     } while(t[0]);
732                 }
733             }
734         }
735     }
736
737     /* If we didn't see a 'require group' or aren't authoritive, decline */
738     if (!required_group || !dir->authoritative)
739         return DECLINED;
740
741     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
742         "access to %s failed, reason: user %s not allowed access (%s)",
743         r->uri, r->user, dir->grouperror);
744
745     ap_note_basic_auth_failure(r);
746
747     return (dir->grouperror && (ret= atoi(dir->grouperror)) > 0) ? ret :
748         HTTP_UNAUTHORIZED;
749 }
750
751
752 /* Password checker for basic authentication - given a login/password,
753  * check if it is valid.  Returns one of AUTH_DENIED, AUTH_GRANTED,
754  * or AUTH_GENERAL_ERROR.
755  */
756
757 static authn_status authn_external_check_password(request_rec *r,
758         const char *user, const char *password)
759 {
760     const char *extname, *extpath, *extmethod;
761     int i;
762     authnz_external_dir_config_rec *dir= (authnz_external_dir_config_rec *)
763             ap_get_module_config(r->per_dir_config, &authnz_external_module);
764
765     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
766             ap_get_module_config(r->server->module_config,
767                 &authnz_external_module);
768     int code= 1;
769
770     /* Check if we are supposed to handle this authentication */
771     if (dir->auth_name->nelts == 0)
772     {
773         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
774             "No AuthExternal name has been set");
775         return AUTH_GENERAL_ERROR;
776     }
777
778     for (i= 0; i < dir->auth_name->nelts; i++)
779     {
780         extname= ((const char **)dir->auth_name->elts)[i];
781
782         /* Get the path associated with that external */
783         if (!(extpath= apr_table_get(svr->auth_path, extname)))
784         {
785             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
786                 "Invalid AuthExternal keyword (%s)", extname);
787             return AUTH_GENERAL_ERROR;
788         }
789
790         /* Do the authentication, by the requested method */
791         extmethod= apr_table_get(svr->auth_method, extname);
792         if ( extmethod && !strcasecmp(extmethod, "function") )
793             code= exec_hardcode(r, extpath, password);
794         else
795             code= exec_external(extpath, extmethod, r, ENV_PASS, password);
796
797         /* If return code was zero, authentication succeeded */
798         if (code == 0) return AUTH_GRANTED;
799
800         /* Log a failed authentication */
801         errno= 0;
802         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
803             "AuthExtern %s [%s]: Failed (%d) for user %s",
804             extname, extpath, code, r->user);
805     }
806     /* If no authenticators succeed, refuse authentication */
807     return AUTH_DENIED;
808 }
809
810
811 #if 0
812 /* Password checker for digest authentication - given a login/password,
813  * check if it is valid.  Returns one of AUTH_USER_FOUND, AUTH_USER_NOT_FOUND,
814  * or AUTH_GENERAL_ERROR.   Not implemented at this time.
815  */
816
817 auth_status *authn_external_get_realm_hash(request_rec *r, const char *user,
818     const char *realm, char **rethash);
819 {
820 }
821 #endif
822
823
824 static const authn_provider authn_external_provider =
825 {
826     &authn_external_check_password,
827 #if 0
828     &authn_external_get_realm_hash
829 #else
830     NULL                /* No support for digest authentication at this time */
831 #endif
832 };
833
834
835 static void register_hooks(apr_pool_t *p)
836 {
837     ap_register_provider(p, AUTHN_PROVIDER_GROUP, "external", "0",
838             &authn_external_provider);
839
840     ap_hook_auth_checker(authz_external_check_user_access, NULL, NULL,
841             APR_HOOK_MIDDLE);
842 }
843
844
845 module AP_MODULE_DECLARE_DATA authnz_external_module = {
846     STANDARD20_MODULE_STUFF,
847     create_authnz_external_dir_config,    /* create per-dir config */
848     NULL,                         /* merge per-dir config - dflt is override */
849     create_authnz_external_svr_config, /* create per-server config */
850     NULL,                         /* merge per-server config */
851     authnz_external_cmds,         /* command apr_table_t */
852     register_hooks                /* register hooks */
853 };