1 /* ====================================================================
2 * Copyright (c) 1995 The Apache Group. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
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
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/)."
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.
25 * 5. Redistributions of any form whatsoever must retain the following
27 * "This product includes software developed by the Apache Group
28 * for use in the Apache HTTP server project (http://www.apache.org/)."
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 * ====================================================================
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/>.
54 /* Uncomment if you want to use a HARDCODE'd check (default off) */
55 /* #define _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" */
66 #include "ap_config.h"
67 #include "ap_provider.h"
69 #include "apr_signal.h"
71 #define APR_WANT_STRFUNC
73 #include "apr_strings.h"
77 #include "http_config.h"
78 #include "http_core.h"
80 #include "http_protocol.h"
81 #include "http_request.h" /* for ap_hook_(check_user_id | auth_checker)*/
86 #ifndef STANDARD20_MODULE_STUFF
87 #error This module requires Apache 2.2.0 or later.
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"
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"
102 /* Maximum number of arguments passed to an authenticator */
105 /* Default authentication method - "pipe", "environment" or "checkpass" */
106 #define DEFAULT_METHOD "pipe"
109 * Structure for the module itself. The actual definition of this structure
110 * is at the end of the file.
112 module AP_MODULE_DECLARE_DATA authnz_external_module;
115 * Data types for per-directory and per-server configuration
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? */
127 } authnz_external_dir_config_rec;
132 apr_table_t *auth_path; /* Hash mapping auth keywords to paths */
133 apr_table_t *auth_method; /* Hash mapping auth keywords to methods */
135 apr_table_t *group_path; /* Hash mapping group keywords to paths */
136 apr_table_t *group_method; /* Hash mapping group keywords to methods */
138 } authnz_external_svr_config_rec;
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;
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;
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
154 static void *create_authnz_external_dir_config(apr_pool_t *p, char *d)
156 authnz_external_dir_config_rec *dir= (authnz_external_dir_config_rec *)
157 apr_palloc(p, sizeof(authnz_external_dir_config_rec));
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 */
168 static void *create_authnz_external_svr_config( apr_pool_t *p, server_rec *s)
170 authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
171 apr_palloc(p, sizeof(authnz_external_svr_config_rec));
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) */
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)
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);
190 apr_table_set( svr->auth_path, keyword, path );
191 apr_table_set( svr->auth_method, keyword, method );
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)
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);
205 apr_table_set( svr->group_path, keyword, path );
206 apr_table_set( svr->group_method, keyword, method );
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,
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);
222 apr_table_set( svr->auth_path, keyword, path );
223 apr_table_set( svr->auth_method, keyword, DEFAULT_METHOD );
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)
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);
238 apr_table_set( svr->group_path, keyword, path );
239 apr_table_set( svr->group_method, keyword, DEFAULT_METHOD );
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)
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);
253 apr_table_set( svr->auth_method, keyword, method );
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)
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);
268 apr_table_set( svr->group_method, keyword, method );
273 /* Append an argument to an array defined by the offset */
274 static const char *append_array_slot(cmd_parms *cmd, void *struct_ptr,
277 int offset = (int)(long)cmd->info;
278 apr_array_header_t *array=
279 *(apr_array_header_t **)((char *)struct_ptr + offset);
281 *(const char **)apr_array_push(array)= apr_pstrdup(array->pool, arg);
287 /* Config file directives for this module */
288 static const command_rec authnz_external_cmds[] =
290 AP_INIT_ITERATE("AuthExternal",
292 (void *)APR_OFFSETOF(authnz_external_dir_config_rec,auth_name),
294 "one (or more) keywords indicating which authenticators to use"),
296 AP_INIT_TAKE3("DefineExternalAuth",
300 "a keyword followed by auth method and path to authentictor"),
302 AP_INIT_TAKE2("AddExternalAuth",
306 "a keyword followed by a path to the authenticator program"),
308 AP_INIT_TAKE2("SetExternalAuthMethod",
309 set_authnz_external_method,
312 "a keyword followed by the method by which the data is passed"),
314 AP_INIT_TAKE1("GroupExternal",
316 (void *)APR_OFFSETOF(authnz_external_dir_config_rec, group_name),
318 "a keyword indicating which group checker to use"),
320 AP_INIT_TAKE3("DefineExternalGroup",
324 "a keyword followed by auth method type and path to group checker"),
326 AP_INIT_TAKE2("AddExternalGroup",
330 "a keyword followed by a path to the group check program"),
332 AP_INIT_TAKE2("SetExternalGroupMethod",
336 "a keyword followed by the method by which the data is passed"),
338 AP_INIT_TAKE1("AuthExternalContext",
340 (void *)APR_OFFSETOF(authnz_external_dir_config_rec, context),
342 "An arbitrary context string to pass to the authenticator in the "
343 ENV_CONTEXT " environment variable"),
345 AP_INIT_FLAG("AuthExternalProvideCache",
347 (void *)APR_OFFSETOF(authnz_external_dir_config_rec, providecache),
349 "Should we forge authentication credentials for mod_authn_socache?"),
351 AP_INIT_FLAG("GroupExternalManyAtOnce",
353 (void *)APR_OFFSETOF(authnz_external_dir_config_rec, groupsatonce),
355 "Set to 'off' if group authenticator cannot handle multiple group "
356 "names in one invocation" ),
358 AP_INIT_FLAG("AuthExternalGroupsAtOnce",
360 (void *)APR_OFFSETOF(authnz_external_dir_config_rec, groupsatonce),
362 "Old version of 'GroupExternalManyAtOnce'" ),
364 AP_INIT_FLAG("GroupExternalAuthNCheck",
366 (void *)APR_OFFSETOF(authnz_external_dir_config_rec, authncheck),
368 "Set to 'off' if group authenticator should skip checking whether "
369 "user is validly authenticated"),
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)
379 apr_file_t *stderr_log;
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)));
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.
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:
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.
403 static int exec_external(const char *extpath, const char *extmethod,
404 const request_rec *r, const char *dataname, const char *data)
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;
411 apr_status_t rc= APR_SUCCESS;
413 char *child_arg[MAX_ARG+2];
416 apr_exit_why_e why= APR_PROC_EXIT;
418 apr_sigfunc_t *sigchld;
421 /* Set various flags based on the execution method */
423 isdaemon= (extmethod == NULL);
426 usecheck= extmethod && !strcasecmp(extmethod, "checkpassword");
427 usepipeout= usecheck || (extmethod && !strcasecmp(extmethod, "pipes"));
428 usepipein= usepipeout || (extmethod && !strcasecmp(extmethod, "pipe"));
431 /* Create the environment for the child. Daemons don't get these, they
432 * just inherit apache's environment variables.
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);
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);
449 child_env[i++]= apr_pstrcat(p, "PATH=", getenv("PATH"), NULL);
451 child_env[i++]= apr_pstrcat(p, "AUTHTYPE=", dataname, NULL);
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);
458 child_env[i++]= apr_pstrcat(p, ENV_IP"=", r->useragent_ip, NULL);
461 child_env[i++]= apr_pstrcat(p, ENV_URI"=", r->uri, NULL);
463 if ((host= apr_table_get(r->headers_in, "Host")) != NULL)
464 child_env[i++]= apr_pstrcat(p, ENV_HTTP_HOST"=", host, NULL);
467 child_env[i++]= apr_pstrcat(r->pool, ENV_CONTEXT"=",
471 if ((cookie= apr_table_get(r->headers_in, "Cookie")) != NULL)
472 child_env[i++]= apr_pstrcat(p, ENV_COOKIE"=", cookie, NULL);
476 child_env[i++]= apr_pstrcat(r->pool, "SystemRoot=", getenv("SystemRoot"), NULL);
478 /* NOTE: If you add environment variables,
479 * remember to increase the size of the child_env[] array */
481 /* End of environment */
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)) {}
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. */
494 if (((rc= apr_procattr_create(&procattr, p)) != APR_SUCCESS) ||
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))
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) ||
507 /* detach the child only if it is a daemon */
508 ((rc= apr_procattr_detach_set(procattr, isdaemon)) != APR_SUCCESS) ||
510 /* function to call if child has error after fork, before exec */
511 ((rc= apr_procattr_child_errfn_set(procattr, extchilderr)
514 /* Failed. Probably never happens. */
515 ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
516 "could not set child process attributes");
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.) */
524 sigchld= apr_signal(SIGCHLD,SIG_DFL);
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)
533 ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
534 "Could not run external authenticator: %d: %s", rc,
539 if (isdaemon) return 0;
541 apr_pool_note_subprocess(p, &proc, APR_KILL_AFTER_TIMEOUT);
545 /* Select appropriate pipe to write to */
546 apr_file_t *pipe= (usecheck ? proc.err : proc.in);
549 apr_file_write_full(pipe, r->user, strlen(r->user), NULL);
550 apr_file_putc(usecheck ? '\0' : '\n', pipe);
552 /* Send the password */
553 apr_file_write_full(pipe, data, strlen(data), NULL);
554 apr_file_putc(usecheck ? '\0' : '\n', pipe);
556 /* Send dummy timestamp for checkpassword */
557 if (usecheck) apr_file_write_full(pipe, "0", 2, NULL);
560 apr_file_close(pipe);
563 /* Wait for the child process to terminate, and get status */
564 rc= apr_proc_wait(&proc,&status,&why,APR_WAIT);
566 /* Restore sigchild to whatever it was before we reset it */
568 apr_signal(SIGCHLD,sigchld);
571 if (!APR_STATUS_IS_CHILD_DONE(rc))
573 ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
574 "Could not get status from child process");
577 if (!APR_PROC_CHECK_EXIT(why))
579 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
580 "External authenticator died on signal %d",status);
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
593 static int exec_hardcode(const request_rec *r, const char *extpath,
594 const char *password)
597 char *check_type; /* Pointer to HARDCODE type check */
598 char *config_file; /* Pointer to HARDCODE config file */
599 int standard_auth= 0;
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)
606 *config_file= '\0'; /* Mark end of type */
607 config_file++; /* Start of filename */
610 /* This is where you make your function call. Here is an example of
611 * what one looks like:
613 * if (strcmp(check_type,"RADIUS")==0)
614 * code= radcheck(r->user,password,config_file);
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>
621 if (strcmp(check_type,"EXAMPLE")==0) /* change this! */
622 code= example(r->user,password,config_file); /* change this! */
627 return -4; /* If _HARDCODE_ is not defined, always fail */
628 #endif /* _HARDCODE_ */
632 /* Handle a group check triggered by a 'Require external-group foo bar baz'
634 static authz_status externalgroup_check_authorization(request_rec *r,
635 const char *require_args, const void *parsed_require_args)
637 authnz_external_dir_config_rec *dir= (authnz_external_dir_config_rec *)
638 ap_get_module_config(r->per_dir_config, &authnz_external_module);
640 authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
641 ap_get_module_config(r->server->module_config, &authnz_external_module);
644 char *extname= dir->group_name;
645 const char *extpath, *extmethod;
649 if (dir->authncheck){
650 /* If no authenticated user, pass */
651 if ( !user ) return AUTHZ_DENIED_NO_USER;
653 /* Prevent crash due to missing user */
654 if ( !user ) r->user = "";
657 /* If no external authenticator has been configured, pass */
658 if ( !extname ) return AUTHZ_DENIED;
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)))
665 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
666 "invalid GroupExternal keyword (%s)", extname);
670 if (dir->groupsatonce)
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;
678 /* Call authenticator once for each group name on line */
680 while ((w= ap_getword_conf(r->pool, &t)) && w[0])
682 code= exec_external(extpath, extmethod, r, ENV_GROUP, w);
683 if (code == 0) return AUTHZ_GRANTED;
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);
696 /* Handle a group check triggered by a 'Require external-file-group'
698 static authz_status externalfilegroup_check_authorization(request_rec *r,
699 const char *require_args, const void *parsed_require_args)
701 authnz_external_dir_config_rec *dir= (authnz_external_dir_config_rec *)
702 ap_get_module_config(r->per_dir_config, &authnz_external_module);
704 authnz_external_svr_config_rec *svr= (authnz_external_svr_config_rec *)
705 ap_get_module_config(r->server->module_config, &authnz_external_module);
708 char *extname= dir->group_name;
709 const char *extpath, *extmethod;
710 const char *filegroup= NULL;
713 if (dir->authncheck){
714 /* If no authenticated user, pass */
715 if ( !user ) return AUTHZ_DENIED_NO_USER;
717 /* Prevent crash due to missing user */
718 if ( !user ) r->user = "";
721 /* If no external authenticator has been configured, pass */
722 if ( !extname ) return AUTHZ_DENIED;
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)))
729 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
730 "invalid GroupExternal keyword (%s)", extname);
734 /* Get group name for requested file from mod_authz_owner */
735 filegroup= authz_owner_get_file_group(r);
738 /* No errog log entry, because mod_authz_owner already made one */
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;
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);
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.
763 void mock_turtle_cache(request_rec *r, const char *plainpw)
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
772 if (authn_cache_store != NULL)
774 apr_sha1_base64(plainpw,strlen(plainpw),cryptpw);
775 authn_cache_store(r, "external", r->user, NULL, cryptpw);
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. */
784 static authn_status authn_external_check_password(request_rec *r,
785 const char *user, const char *password)
787 const char *extname, *extpath, *extmethod;
789 authnz_external_dir_config_rec *dir= (authnz_external_dir_config_rec *)
790 ap_get_module_config(r->per_dir_config, &authnz_external_module);
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);
797 /* Check if we are supposed to handle this authentication */
798 if (dir->auth_name->nelts == 0)
800 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
801 "No AuthExternal name has been set");
802 return AUTH_GENERAL_ERROR;
805 for (i= 0; i < dir->auth_name->nelts; i++)
807 extname= ((const char **)dir->auth_name->elts)[i];
809 /* Get the path associated with that external */
810 if (!(extpath= apr_table_get(svr->auth_path, extname)))
812 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
813 "Invalid AuthExternal keyword (%s)", extname);
814 return AUTH_GENERAL_ERROR;
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);
822 code= exec_external(extpath, extmethod, r, ENV_PASS, password);
824 /* If return code was zero, authentication succeeded */
827 if (dir->providecache) mock_turtle_cache(r, password);
831 /* Log a failed authentication */
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);
837 /* If no authenticators succeed, refuse authentication */
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.
848 auth_status *authn_external_get_realm_hash(request_rec *r, const char *user,
849 const char *realm, char **rethash);
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
857 static void opt_retr(void)
859 /* Get authn_cache_store from mod_authn_socache */
861 APR_RETRIEVE_OPTIONAL_FN(ap_authn_cache_store);
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);
868 /* This tells mod_auth_basic and mod_auth_digest what to call for
870 static const authn_provider authn_external_provider =
872 &authn_external_check_password,
874 &authn_external_get_realm_hash
876 NULL /* No support for digest authentication */
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 =
884 &externalgroup_check_authorization,
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 =
892 &externalfilegroup_check_authorization,
896 /* Register this module with Apache */
897 static void register_hooks(apr_pool_t *p)
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);
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);
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);
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);
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 */