]> granicus.if.org Git - apache-authnz-external/blob - mod_authnz_external/mod_authnz_external.c
a602d7a12372f77f567b4ff9619f01f09925f880
[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 #include "apr_sha1.h"
75
76 #include "httpd.h"
77 #include "http_config.h"
78 #include "http_core.h"
79 #include "http_log.h"
80 #include "http_protocol.h"
81 #include "http_request.h"   /* for ap_hook_(check_user_id | auth_checker)*/
82 #if APR_HAVE_UNISTD_H
83 #include <unistd.h>
84 #endif
85
86 #ifndef STANDARD20_MODULE_STUFF
87 #error This module requires Apache 2.2.0 or later.
88 #endif
89
90 /* Names of environment variables used to pass data to authenticator */
91 #define ENV_USER        "USER"
92 #define ENV_PASS        "PASS"
93 #define ENV_GROUP       "GROUP"
94 #define ENV_URI         "URI"
95 #define ENV_IP          "IP"
96 #define ENV_HOST        "HOST"          /* Remote Host */
97 #define ENV_HTTP_HOST   "HTTP_HOST"     /* Local Host */
98 #define ENV_CONTEXT     "CONTEXT"       /* Arbitrary Data from Config */
99 /* Undefine this if you do not want cookies passed to the script */
100 #define ENV_COOKIE      "COOKIE"
101
102 /* Maximum number of arguments passed to an authenticator */
103 #define MAX_ARG 32
104
105 /* Default authentication method - "pipe", "environment" or "checkpass" */
106 #define DEFAULT_METHOD "pipe"
107
108 /*
109  * Structure for the module itself.  The actual definition of this structure
110  * is at the end of the file.
111  */
112 module AP_MODULE_DECLARE_DATA authnz_external_module;
113
114 /*
115  *  Data types for per-directory and per-server configuration
116  */
117
118 typedef struct
119 {
120     apr_array_header_t *auth_name; /* Auth keyword for current dir */
121     char *group_name;            /* Group keyword for current dir */
122     char *context;               /* Context string from AuthExternalContext */
123     int  groupsatonce;           /* Check all groups in one call? */
124     int  providecache;           /* Provide auth data to mod_authn_socache? */
125     int  authncheck;             /* Check for previous authentication? */
126
127 } authnz_external_dir_config_rec;
128
129
130 typedef struct
131 {
132     apr_table_t *auth_path;      /* Hash mapping auth keywords to paths */
133     apr_table_t *auth_method;    /* Hash mapping auth keywords to methods */
134
135     apr_table_t *group_path;     /* Hash mapping group keywords to paths */
136     apr_table_t *group_method;   /* Hash mapping group keywords to methods */
137
138 } authnz_external_svr_config_rec;
139
140
141 /* mod_authz_owner's function for retrieving the requested file's group */
142 APR_DECLARE_OPTIONAL_FN(char*, authz_owner_get_file_group, (request_rec *r));
143 APR_OPTIONAL_FN_TYPE(authz_owner_get_file_group) *authz_owner_get_file_group;
144
145 /* mod_authn_socache's function for adding credentials to its cache */
146 static APR_OPTIONAL_FN_TYPE(ap_authn_cache_store) *authn_cache_store = NULL;
147
148
149 /* Creators for per-dir and server configurations.  These are called
150  * via the hooks in the module declaration to allocate and initialize
151  * the per-directory and per-server configuration data structures declared
152  * above. */
153
154 static void *create_authnz_external_dir_config(apr_pool_t *p, char *d)
155 {
156     authnz_external_dir_config_rec *dir= (authnz_external_dir_config_rec *)
157         apr_palloc(p, sizeof(authnz_external_dir_config_rec));
158
159     dir->auth_name= apr_array_make(p,2,sizeof(const char *)); /* no default */
160     dir->group_name= NULL;      /* no default */
161     dir->context= NULL;         /* no default */
162     dir->groupsatonce= 1;       /* default to on */
163     dir->providecache= 0;       /* default to off */
164     dir->authncheck= 1;         /* default to on */
165     return dir;
166 }
167
168 static void *create_authnz_external_svr_config( apr_pool_t *p, server_rec *s)
169 {
170     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
171         apr_palloc(p, sizeof(authnz_external_svr_config_rec));
172
173     svr->auth_method=  apr_table_make(p, 4);
174     svr->auth_path=    apr_table_make(p, 4);
175     svr->group_method= apr_table_make(p, 4);
176     svr->group_path=   apr_table_make(p, 4);
177     /* Note: 4 is only initial hash size - they can grow bigger) */
178
179     return (void *)svr;
180 }
181
182 /* Handler for a DefineExternalAuth server config line */
183 static const char *def_extauth(cmd_parms *cmd, void *dummy, const char *keyword,
184                                 const char *method, const char *path)
185 {
186     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
187         ap_get_module_config( cmd->server->module_config,
188             &authnz_external_module);
189
190     apr_table_set( svr->auth_path,   keyword, path );
191     apr_table_set( svr->auth_method, keyword, method );
192
193     return NULL;
194 }
195
196
197 /* Handler for a DefineExternalGroup server config line */
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, method );
207
208     return NULL;
209 }
210
211
212
213 /* Handler for a AddExternalAuth server config line - add a external auth
214  * type to the server configuration */
215 static const char *add_extauth(cmd_parms *cmd, void *dummy, const char *keyword,
216                                 const char *path)
217 {
218     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
219         ap_get_module_config( cmd->server->module_config,
220             &authnz_external_module);
221
222     apr_table_set( svr->auth_path,   keyword, path );
223     apr_table_set( svr->auth_method, keyword, DEFAULT_METHOD );
224
225     return NULL;
226 }
227
228
229 /* Handler for a AddExternalGroup server config line - add a external group
230  * type to the server configuration */
231 static const char *add_extgroup(cmd_parms *cmd, void *dummy,
232                                 const char *keyword, const char *path)
233 {
234     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
235         ap_get_module_config( cmd->server->module_config,
236             &authnz_external_module);
237
238     apr_table_set( svr->group_path,   keyword, path );
239     apr_table_set( svr->group_method, keyword, DEFAULT_METHOD );
240
241     return NULL;
242 }
243
244 /* Handler for a SetExternalAuthMethod server config line - change an external
245  * auth method in the server configuration */
246 static const char *set_authnz_external_method(cmd_parms *cmd, void *dummy,
247                                     const char *keyword, const char *method)
248 {
249     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
250         ap_get_module_config( cmd->server->module_config,
251             &authnz_external_module);
252
253     apr_table_set( svr->auth_method, keyword, method );
254
255     return NULL;
256 }
257
258
259 /* Handler for a SetExternalGroupMethod server config line - change an external
260  * group method in the server configuration */
261 static const char *set_extgroup_method(cmd_parms *cmd, void *dummy,
262                                     const char *keyword, const char *method)
263 {
264     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
265         ap_get_module_config( cmd->server->module_config,
266             &authnz_external_module);
267
268     apr_table_set( svr->group_method, keyword, method );
269
270     return NULL;
271 }
272
273 /* Append an argument to an array defined by the offset */
274 static const char *append_array_slot(cmd_parms *cmd, void *struct_ptr,
275                                 const char *arg)
276 {
277     int offset = (int)(long)cmd->info;
278     apr_array_header_t *array=
279         *(apr_array_header_t **)((char *)struct_ptr + offset);
280
281     *(const char **)apr_array_push(array)= apr_pstrdup(array->pool, arg);
282
283     return NULL;
284 }
285
286
287 /* Config file directives for this module */
288 static const command_rec authnz_external_cmds[] =
289 {
290     AP_INIT_ITERATE("AuthExternal",
291         append_array_slot,
292         (void *)APR_OFFSETOF(authnz_external_dir_config_rec,auth_name),
293         OR_AUTHCFG,
294         "one (or more) keywords indicating which authenticators to use"),
295
296     AP_INIT_TAKE3("DefineExternalAuth",
297         def_extauth,
298         NULL,
299         RSRC_CONF,
300         "a keyword followed by auth method and path to authentictor"),
301
302     AP_INIT_TAKE2("AddExternalAuth",
303         add_extauth,
304         NULL,
305         RSRC_CONF,
306         "a keyword followed by a path to the authenticator program"),
307
308     AP_INIT_TAKE2("SetExternalAuthMethod",
309         set_authnz_external_method,
310         NULL,
311         RSRC_CONF,
312         "a keyword followed by the method by which the data is passed"),
313
314     AP_INIT_TAKE1("GroupExternal",
315         ap_set_string_slot,
316         (void *)APR_OFFSETOF(authnz_external_dir_config_rec, group_name),
317         OR_AUTHCFG,
318         "a keyword indicating which group checker to use"),
319
320     AP_INIT_TAKE3("DefineExternalGroup",
321         def_extgroup,
322         NULL,
323         RSRC_CONF,
324         "a keyword followed by auth method type and path to group checker"),
325
326     AP_INIT_TAKE2("AddExternalGroup",
327         add_extgroup,
328         NULL,
329         RSRC_CONF,
330         "a keyword followed by a path to the group check program"),
331
332     AP_INIT_TAKE2("SetExternalGroupMethod",
333         set_extgroup_method,
334         NULL,
335         RSRC_CONF,
336         "a keyword followed by the method by which the data is passed"),
337
338     AP_INIT_TAKE1("AuthExternalContext",
339         ap_set_string_slot,
340         (void *)APR_OFFSETOF(authnz_external_dir_config_rec, context),
341         OR_AUTHCFG,
342         "An arbitrary context string to pass to the authenticator in the "
343         ENV_CONTEXT " environment variable"),
344
345     AP_INIT_FLAG("AuthExternalProvideCache",
346         ap_set_flag_slot,
347         (void *)APR_OFFSETOF(authnz_external_dir_config_rec, providecache),
348         OR_AUTHCFG,
349         "Should we forge authentication credentials for mod_authn_socache?"),
350
351     AP_INIT_FLAG("GroupExternalManyAtOnce",
352         ap_set_flag_slot,
353         (void *)APR_OFFSETOF(authnz_external_dir_config_rec, groupsatonce),
354         OR_AUTHCFG,
355         "Set to 'off' if group authenticator cannot handle multiple group "
356             "names in one invocation" ),
357
358     AP_INIT_FLAG("AuthExternalGroupsAtOnce",
359         ap_set_flag_slot,
360         (void *)APR_OFFSETOF(authnz_external_dir_config_rec, groupsatonce),
361         OR_AUTHCFG,
362         "Old version of 'GroupExternalManyAtOnce'" ),
363         
364         AP_INIT_FLAG("GroupExternalAuthNCheck",
365         ap_set_flag_slot,
366         (void *)APR_OFFSETOF(authnz_external_dir_config_rec, authncheck),
367         OR_AUTHCFG,
368         "Set to 'off' if group authenticator should skip checking whether "
369         "user is validly authenticated"),
370
371     { NULL }
372 };
373
374
375 /* Called from apr_proc_create() if there are errors during launch of child
376  * process.  Mostly just lifted from mod_cgi. */
377 static void extchilderr(apr_pool_t *p, apr_status_t err, const char *desc)
378 {
379     apr_file_t *stderr_log;
380     char errbuf[200];
381     apr_file_open_stderr(&stderr_log, p);
382     apr_file_printf(stderr_log,"%s: (%d) %s\n", ap_escape_logitem(p,desc),
383         err, apr_strerror(err,errbuf,sizeof(errbuf)));
384 }
385
386
387 /* Run an external authentication program using the given method for passing
388  * in the data.  The login name is always passed in.   Dataname is "GROUP" or
389  * "PASS" and data is the group list or password being checked.  To launch
390  * a detached daemon, run this with extmethod=NULL.
391  *
392  * If the authenticator was run, we return the numeric code from the
393  * authenticator, normally 0 if the login was valid, some small positive
394  * number if not.  If we were not able to run the authenticator, we log
395  * an error message and return a numeric error code:
396  *
397  *   -1   Could not execute authenticator, usually a path or permission problem
398  *   -2   The external authenticator crashed or was killed.
399  *   -3   Could not create process attribute structure
400  *   -4   apr_proc_wait() did not return a status code.  Should never happen.
401  *   -5   apr_proc_wait() returned before child finished.  Should never happen.
402  */
403 static int exec_external(const char *extpath, const char *extmethod,
404                 const request_rec *r, const char *dataname, const char *data)
405 {
406     conn_rec *c= r->connection;
407     apr_pool_t *p= r->pool;
408     int isdaemon, usecheck= 0, usepipeout= 0, usepipein= 0;
409     apr_procattr_t *procattr;
410     apr_proc_t proc;
411     apr_status_t rc= APR_SUCCESS;
412     char *child_env[13];
413     char *child_arg[MAX_ARG+2];
414     const char *t;
415     int i, status= -4;
416     apr_exit_why_e why= APR_PROC_EXIT;
417 #ifndef _WINDOWS
418     apr_sigfunc_t *sigchld;
419 #endif
420
421     /* Set various flags based on the execution method */
422
423     isdaemon= (extmethod == NULL);
424     if (!isdaemon)
425     {
426         usecheck= extmethod && !strcasecmp(extmethod, "checkpassword");
427         usepipeout= usecheck || (extmethod && !strcasecmp(extmethod, "pipes"));
428         usepipein= usepipeout || (extmethod && !strcasecmp(extmethod, "pipe"));
429     }
430
431     /* Create the environment for the child.  Daemons don't get these, they
432      * just inherit apache's environment variables.
433      */
434
435     if (!isdaemon)
436     {
437         const char *cookie, *host, *remote_host;
438         authnz_external_dir_config_rec *dir= (authnz_external_dir_config_rec *)
439             ap_get_module_config(r->per_dir_config, &authnz_external_module);
440         i= 0;
441
442         if (!usepipein)
443         {
444             /* Put user name and password/group into environment */
445             child_env[i++]= apr_pstrcat(p, ENV_USER"=", r->user, NULL);
446             child_env[i++]= apr_pstrcat(p, dataname, "=", data, NULL);
447         }
448
449         child_env[i++]= apr_pstrcat(p, "PATH=", getenv("PATH"), NULL);
450
451         child_env[i++]= apr_pstrcat(p, "AUTHTYPE=", dataname, NULL);
452
453         remote_host= ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST,NULL);
454         if (remote_host != NULL)
455             child_env[i++]= apr_pstrcat(p, ENV_HOST"=", remote_host,NULL);
456
457         if (r->useragent_ip)
458             child_env[i++]= apr_pstrcat(p, ENV_IP"=", r->useragent_ip, NULL);
459
460         if (r->uri)
461             child_env[i++]= apr_pstrcat(p, ENV_URI"=", r->uri, NULL);
462
463         if ((host= apr_table_get(r->headers_in, "Host")) != NULL)
464             child_env[i++]= apr_pstrcat(p, ENV_HTTP_HOST"=", host, NULL);
465
466         if (dir->context)
467             child_env[i++]= apr_pstrcat(r->pool, ENV_CONTEXT"=",
468                 dir->context, NULL);
469
470 #ifdef ENV_COOKIE
471         if ((cookie= apr_table_get(r->headers_in, "Cookie")) != NULL)
472             child_env[i++]= apr_pstrcat(p, ENV_COOKIE"=", cookie, NULL);
473 #endif
474                 
475 #ifdef _WINDOWS
476     child_env[i++]= apr_pstrcat(r->pool, "SystemRoot=", getenv("SystemRoot"), NULL);
477 #endif
478         /* NOTE:  If you add environment variables,
479          *   remember to increase the size of the child_env[] array */
480
481         /* End of environment */
482         child_env[i]= NULL;
483     }
484
485     /* Construct argument array */
486     for (t= extpath, i=0; *t != '\0' && (i <= MAX_ARG + 1);
487          child_arg[i++]= ap_getword_white(p, &t)) {}
488     child_arg[i]= NULL;
489
490     /* Create the process attribute structure describing the script we
491      * want to run using the Thread/Process functions from the Apache
492      * portable runtime library. */
493
494     if (((rc= apr_procattr_create(&procattr, p)) != APR_SUCCESS) ||
495
496         /* should we create pipes to stdin, stdout and stderr? */
497         ((rc= apr_procattr_io_set(procattr,
498             (usepipein && !usecheck) ? APR_FULL_BLOCK : APR_NO_PIPE,
499             usepipeout ? APR_FULL_BLOCK : APR_NO_PIPE,
500             (usepipein && usecheck) ? APR_FULL_BLOCK : APR_NO_PIPE))
501                != APR_SUCCESS ) ||
502
503         /* will give full path of program and make a new environment */
504         ((rc= apr_procattr_cmdtype_set(procattr,
505             isdaemon ? APR_PROGRAM_ENV : APR_PROGRAM)) != APR_SUCCESS) ||
506
507         /* detach the child only if it is a daemon */
508         ((rc= apr_procattr_detach_set(procattr, isdaemon)) != APR_SUCCESS) ||
509
510         /* function to call if child has error after fork, before exec */
511         ((rc= apr_procattr_child_errfn_set(procattr, extchilderr)
512               != APR_SUCCESS)))
513     {
514         /* Failed.  Probably never happens. */
515         ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
516             "could not set child process attributes");
517         return -3;
518     }
519
520     /* Sometimes other modules wil mess up sigchild.  Need to fix it for
521      * the wait call to work correctly. (However, there's no need to fix
522      * the handler on Windows, since there are no signals on Windows.) */
523 #ifndef _WINDOWS
524     sigchld= apr_signal(SIGCHLD,SIG_DFL);
525 #endif
526
527     /* Start the child process */
528     rc= apr_proc_create(&proc, child_arg[0],
529         (const char * const *)child_arg,
530         (const char * const *)child_env, procattr, p);
531     if (rc != APR_SUCCESS)
532     {
533         ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
534                 "Could not run external authenticator: %d: %s", rc,
535                 child_arg[0]);
536         return -1;
537     }
538
539     if (isdaemon) return 0;
540
541     apr_pool_note_subprocess(p, &proc, APR_KILL_AFTER_TIMEOUT);
542
543     if (usepipein)
544     {
545         /* Select appropriate pipe to write to */
546         apr_file_t *pipe= (usecheck ? proc.err : proc.in);
547
548         /* Send the user */
549         apr_file_write_full(pipe, r->user, strlen(r->user), NULL);
550         apr_file_putc(usecheck ? '\0' : '\n', pipe);
551
552         /* Send the password */
553         apr_file_write_full(pipe, data, strlen(data), NULL);
554         apr_file_putc(usecheck ? '\0' : '\n', pipe);
555
556         /* Send dummy timestamp for checkpassword */
557         if (usecheck) apr_file_write_full(pipe, "0", 2, NULL);
558
559         /* Close the file */
560         apr_file_close(pipe);
561     }
562
563     /* Wait for the child process to terminate, and get status */
564     rc= apr_proc_wait(&proc,&status,&why,APR_WAIT);
565
566     /* Restore sigchild to whatever it was before we reset it */
567 #ifndef _WINDOWS
568     apr_signal(SIGCHLD,sigchld);
569 #endif
570
571     if (!APR_STATUS_IS_CHILD_DONE(rc))
572     {
573         ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
574                 "Could not get status from child process");
575         return -5;
576     }
577     if (!APR_PROC_CHECK_EXIT(why))
578     {
579         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
580                 "External authenticator died on signal %d",status);
581         return -2;
582     }
583
584     return status;
585 }
586
587
588 /* Call the hardcoded function specified by the external path.  Of course,
589  * you'll have to write the hardcoded functions yourself and insert them
590  * into this source file, as well as inserting a call to them into this
591  * routine.
592  */
593 static int exec_hardcode(const request_rec *r, const char *extpath,
594         const char *password)
595 {
596 #ifdef _HARDCODE_
597     char *check_type;           /* Pointer to HARDCODE type check  */
598     char *config_file;          /* Pointer to HARDCODE config file */
599     int standard_auth= 0;
600
601     /* Parse a copy of extpath into type and filename */
602     check_type= apr_pstrdup(r->pool, extpath);
603     config_file= strchr(check_type, ':');
604     if (config_file != NULL)
605     {
606         *config_file= '\0';                /* Mark end of type */
607         config_file++;                     /* Start of filename */
608     }
609
610     /* This is where you make your function call.  Here is an example of
611      * what one looks like:
612      *
613      *   if (strcmp(check_type,"RADIUS")==0)
614      *      code= radcheck(r->user,password,config_file);
615      *
616      * Replace 'radcheck' with whatever the name of your function is.
617      * Replace 'RADIUS' with whatever you are using as the <type> in:
618      *     AddExternalAuth <keyword> <type>:<config file>
619      */
620
621     if (strcmp(check_type,"EXAMPLE")==0)                /* change this! */
622         code= example(r->user,password,config_file);    /* change this! */
623     else
624         code= -5;
625     return code;
626 #else
627     return -4;          /* If _HARDCODE_ is not defined, always fail */
628 #endif /* _HARDCODE_ */
629 }
630
631
632 /* Handle a group check triggered by a 'Require external-group foo bar baz'
633  * directive. */
634 static authz_status externalgroup_check_authorization(request_rec *r,
635         const char *require_args, const void *parsed_require_args)
636 {
637     authnz_external_dir_config_rec *dir= (authnz_external_dir_config_rec *)
638         ap_get_module_config(r->per_dir_config, &authnz_external_module);
639
640     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
641         ap_get_module_config(r->server->module_config, &authnz_external_module);
642
643     char *user= r->user;
644     char *extname= dir->group_name;
645     const char *extpath, *extmethod;
646     const char *t, *w;
647     int code = 0;
648
649     if (dir->authncheck){
650         /* If no authenticated user, pass */
651         if ( !user ) return AUTHZ_DENIED_NO_USER;
652     }else{
653         /* Prevent crash due to missing user */
654         if ( !user ) r->user = "";
655     }
656
657     /* If no external authenticator has been configured, pass */
658     if ( !extname ) return AUTHZ_DENIED;
659
660     /* Get the path and method associated with that external */
661     if (!(extpath= apr_table_get(svr->group_path, extname)) ||
662         !(extmethod= apr_table_get(svr->group_method,extname)))
663     {
664         errno= 0;
665         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
666             "invalid GroupExternal keyword (%s)", extname);
667         return AUTHZ_DENIED;
668     }
669
670     if (dir->groupsatonce)
671     {
672         /* Pass rest of require line to authenticator */
673         code= exec_external(extpath, extmethod, r, ENV_GROUP, require_args);
674         if (code == 0) return AUTHZ_GRANTED;
675     }
676     else
677     {
678         /* Call authenticator once for each group name on line */
679         t= require_args;
680         while ((w= ap_getword_conf(r->pool, &t)) && w[0])
681         {
682             code= exec_external(extpath, extmethod, r, ENV_GROUP, w);
683             if (code == 0) return AUTHZ_GRANTED;
684         }
685     }
686
687     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
688         "Authorization of user %s to access %s failed. "
689         "User not in Required group. Last result code: %i",
690         r->user, r->uri, code);
691
692     return AUTHZ_DENIED;
693 }
694
695
696 /* Handle a group check triggered by a 'Require external-file-group'
697  * directive. */
698 static authz_status externalfilegroup_check_authorization(request_rec *r,
699         const char *require_args, const void *parsed_require_args)
700 {
701     authnz_external_dir_config_rec *dir= (authnz_external_dir_config_rec *)
702         ap_get_module_config(r->per_dir_config, &authnz_external_module);
703
704     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
705         ap_get_module_config(r->server->module_config, &authnz_external_module);
706
707     char *user= r->user;
708     char *extname= dir->group_name;
709     const char *extpath, *extmethod;
710     const char *filegroup= NULL;
711     int code;
712
713     if (dir->authncheck){
714         /* If no authenticated user, pass */
715         if ( !user ) return AUTHZ_DENIED_NO_USER;
716     }else{
717         /* Prevent crash due to missing user */
718         if ( !user ) r->user = "";
719     }
720
721     /* If no external authenticator has been configured, pass */
722     if ( !extname ) return AUTHZ_DENIED;
723
724     /* Get the path and method associated with that external */
725     if (!(extpath= apr_table_get(svr->group_path, extname)) ||
726         !(extmethod= apr_table_get(svr->group_method,extname)))
727     {
728         errno= 0;
729         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
730             "invalid GroupExternal keyword (%s)", extname);
731         return AUTHZ_DENIED;
732     }
733
734     /* Get group name for requested file from mod_authz_owner */
735     filegroup= authz_owner_get_file_group(r);
736
737     if (!filegroup)
738         /* No errog log entry, because mod_authz_owner already made one */
739         return AUTHZ_DENIED;
740
741     /* Pass the group to the external authenticator */
742     code= exec_external(extpath, extmethod, r, ENV_GROUP, filegroup);
743     if (code == 0) return AUTHZ_GRANTED;
744
745     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
746         "Authorization of user %s to access %s failed. "
747         "User not in Required file group (%s).",
748         r->user, r->uri, filegroup);
749
750     return AUTHZ_DENIED;
751 }
752
753
754 /* Mod_authn_socache wants us to pass it the username and the encrypted
755  * password from the user database to cache. But we have no access to the
756  * actual user database - only the external authenticator can see that -
757  * and chances are, the passwords there aren't encrypted in any way that
758  * mod_authn_socache would understand anyway. So instead, after successful
759  * authentications only, we take the user's plain text password, encrypt
760  * that using an algorithm mod_authn_socache will understand, and cache that
761  * as if we'd actually gotten it from a password database.
762  */
763 void mock_turtle_cache(request_rec *r, const char *plainpw)
764 {
765     char cryptpw[120];
766
767     /* Authn_cache_store will be null if mod_authn_socache does not exist.
768      * If it does exist, but is not set up to cache us, then
769      * authn_cache_store() will do nothing, which is why we turn this off
770      * with "AuthExternalProvideCache Off" to avoid doing the encryption
771      * for no reason. */
772     if (authn_cache_store != NULL)
773     {
774         apr_sha1_base64(plainpw,strlen(plainpw),cryptpw);
775         authn_cache_store(r, "external", r->user, NULL, cryptpw);
776     }
777 }
778
779
780 /* Password checker for basic authentication - given a login/password,
781  * check if it is valid.  Returns one of AUTH_DENIED, AUTH_GRANTED,
782  * or AUTH_GENERAL_ERROR. */
783
784 static authn_status authn_external_check_password(request_rec *r,
785         const char *user, const char *password)
786 {
787     const char *extname, *extpath, *extmethod;
788     int i;
789     authnz_external_dir_config_rec *dir= (authnz_external_dir_config_rec *)
790             ap_get_module_config(r->per_dir_config, &authnz_external_module);
791
792     authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
793             ap_get_module_config(r->server->module_config,
794                 &authnz_external_module);
795     int code= 1;
796
797     /* Check if we are supposed to handle this authentication */
798     if (dir->auth_name->nelts == 0)
799     {
800         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
801             "No AuthExternal name has been set");
802         return AUTH_GENERAL_ERROR;
803     }
804
805     for (i= 0; i < dir->auth_name->nelts; i++)
806     {
807         extname= ((const char **)dir->auth_name->elts)[i];
808
809         /* Get the path associated with that external */
810         if (!(extpath= apr_table_get(svr->auth_path, extname)))
811         {
812             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
813                 "Invalid AuthExternal keyword (%s)", extname);
814             return AUTH_GENERAL_ERROR;
815         }
816
817         /* Do the authentication, by the requested method */
818         extmethod= apr_table_get(svr->auth_method, extname);
819         if ( extmethod && !strcasecmp(extmethod, "function") )
820             code= exec_hardcode(r, extpath, password);
821         else
822             code= exec_external(extpath, extmethod, r, ENV_PASS, password);
823
824         /* If return code was zero, authentication succeeded */
825         if (code == 0)
826         {
827             if (dir->providecache) mock_turtle_cache(r, password);
828             return AUTH_GRANTED;
829         }
830
831         /* Log a failed authentication */
832         errno= 0;
833         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
834             "AuthExtern %s [%s]: Failed (%d) for user %s",
835             extname, extpath, code, r->user);
836     }
837     /* If no authenticators succeed, refuse authentication */
838     return AUTH_DENIED;
839 }
840
841
842 #if 0
843 /* Password checker for digest authentication - given a login/password,
844  * check if it is valid.  Returns one of AUTH_USER_FOUND, AUTH_USER_NOT_FOUND,
845  * or AUTH_GENERAL_ERROR.   Not implemented at this time and probably not ever.
846  */
847
848 auth_status *authn_external_get_realm_hash(request_rec *r, const char *user,
849     const char *realm, char **rethash);
850 {
851 }
852 #endif
853
854 /* This is called after all modules have been initialized to acquire pointers
855  * to some functions from other modules that we would like to use if they are
856  * available. */
857 static void opt_retr(void)
858 {
859     /* Get authn_cache_store from mod_authn_socache */
860     authn_cache_store=
861         APR_RETRIEVE_OPTIONAL_FN(ap_authn_cache_store);
862
863     /* Get authz_owner_get_file_group from mod_authz_owner */
864     authz_owner_get_file_group=
865         APR_RETRIEVE_OPTIONAL_FN(authz_owner_get_file_group);
866 }
867
868 /* This tells mod_auth_basic and mod_auth_digest what to call for
869  * authentication. */
870 static const authn_provider authn_external_provider =
871 {
872     &authn_external_check_password,
873 #if 0
874     &authn_external_get_realm_hash
875 #else
876     NULL        /* No support for digest authentication */
877 #endif
878 };
879
880 /* This tells mod_auth_basic and mod_auth_digest what to call for
881  * access control with 'Require external-group' directives. */
882 static const authz_provider authz_externalgroup_provider =
883 {
884     &externalgroup_check_authorization,
885     NULL,
886 };
887
888 /* This tells mod_auth_basic and mod_auth_digest what to call for
889  * access control with 'Require external-file-group' directives. */
890 static const authz_provider authz_externalfilegroup_provider =
891 {
892     &externalfilegroup_check_authorization,
893     NULL,
894 };
895
896 /* Register this module with Apache */
897 static void register_hooks(apr_pool_t *p)
898 {
899     /* Register authn provider */
900     ap_register_auth_provider(p, AUTHN_PROVIDER_GROUP, "external",
901             AUTHN_PROVIDER_VERSION,
902             &authn_external_provider, AP_AUTH_INTERNAL_PER_CONF);
903
904     /* Register authz providers */
905     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "external-group",
906             AUTHZ_PROVIDER_VERSION,
907             &authz_externalgroup_provider, AP_AUTH_INTERNAL_PER_CONF);
908
909     ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "external-file-group",
910             AUTHZ_PROVIDER_VERSION,
911             &authz_externalfilegroup_provider, AP_AUTH_INTERNAL_PER_CONF);
912
913     /* Ask for opt_retr() to be called after all modules have registered */
914     ap_hook_optional_fn_retrieve(opt_retr, NULL, NULL, APR_HOOK_MIDDLE);
915 }
916
917
918 AP_DECLARE_MODULE(authnz_external) = {
919     STANDARD20_MODULE_STUFF,
920     create_authnz_external_dir_config,    /* create per-dir config */
921     NULL,                         /* merge per-dir config - dflt is override */
922     create_authnz_external_svr_config, /* create per-server config */
923     NULL,                         /* merge per-server config */
924     authnz_external_cmds,         /* command apr_table_t */
925     register_hooks                /* register hooks */
926 };