1 How To Implementation External Authentication Programs
2 for mod_authnz_external or mod_auth_external
7 External authenticators can be written in almost any language. The sample
8 authenticators in the 'test' directory are in Perl and PHP. The 'pwauth'
9 authenticator is in ANSI C. The example code fragments in this document
12 If the authenticator is a script rather than a compiled program, it normally
13 has to start with a "#!/bin/sh" or "#!/usr/bin/perl" type directive. Scripts
14 without such directives may get interpreted by the shell, or may just not
15 work, depending on your installation.
19 The authenticator program should be written with great care because it runs
20 as a privileged user and handles privileged data. A poorly written
21 authenticator could substantially compromise the security of your system.
22 You get points for paranoia. Some notes:
24 - Don't make any assumptions about the length of the login names and
25 passwords given by the user. I *think* Apache will never pass you ones
26 that are longer than 8192 characters, but don't depend this. Check very
27 carefully for buffer overflows.
29 - Don't make assumptions about the content of the login and password strings.
30 For example, if you are using them in an SQL query, do proper checking
31 and/or quoting to insure that nobody is doing SQL injection.
33 - Think about locking. It is possible to get lots of hits at your website
34 very fast, so there may be many programs simultaneously reading your
35 authentication database, plus updates may be going on at the same time.
36 Probably some form of locking is needed to make all this work right.
38 - Think about core dumps. On some systems core dump files can be publically
39 readable. A core dump from your authenticator is likely to contain the
40 user's plain text password, and may include large chunks of your password
41 database that may have been in buffers. For C programs on most versions of
42 Unix, it is possible to disable core dumps by doing something like:
44 rlim.rlim_cur = rlim.rlim_max = 0;
45 (void)setrlimit(RLIMIT_CORE, &rlim);
47 Actually, core dumps seem to be mostly a thing of the past. Most modern
48 Unixes don't seem to generate them.
50 It may not hurt to spend a little time looking at the features of the pwauth
51 authenticator, which is the most secure external authenticator that I have
54 PASSWORD AUTHENTICATORS
56 Authenticators communicate their result by the exit status code they return.
57 A value of 0 indicates that the password is correct. Other values indicate
58 that the password is incorrect, or something else is wrong. It can be
59 useful to return different error codes for different kinds of errors. These
60 will be logged in the Apache error log file, and can be helpful in diagnosing
61 problems. This version of mod_authnz_external does not have any provision for
62 returning textual error messages from the external authenticator. You might
63 be able to use syslog() for this. This might be improved in future releases.
65 Returned error codes should not be negative. Negative values are used
66 internally to mod_authnz_external to indicate problems launching your program.
68 How the external authentication program gets its arguments depends on
69 the method used. The method used is determined by the 'SetExternalAuthMethod'
70 command in your Apache configuration file. You need implement only the
71 method that you plan to use in your configuration.
75 In the "pipe" method, the arguments are read from standard input. The
76 user name will be on the first line, and the password will be on the
77 second. Here's a typical chunk of C code to read that:
81 char user[100], password[100], *p;
83 if (fgets(user, sizeof(user), stdin) == NULL) exit(2);
84 if ((p= strchr(user, '\n')) == NULL) exit(4)
87 if (fgets(password, sizeof(password), stdin) == NULL) exit(3);
88 if ((p= strchr(password, '\n')) == NULL) exit(5)
91 if (check_password(user, password) == OK)
92 exit(0); /* Good Password */
94 exit(1); /* Incorrect Password */
97 Here we simply read two lines from stdin, being careful not to allow
98 buffer overflows and stripping off trailing newlines.
100 We assume "check_password()" is some function that checks the validity of a
101 password and returns 'OK' if it is good.
103 Note that we exit with different non-zero error codes in different error
104 cases. This will be helpful for debugging, as those values will be logged
105 when authentication fails, giving you some clue as to what went wrong.
106 It'd really be better for check_password() to return more detailed error
107 codes, but I wanted to keep the example simple.
111 The "checkpassword" method is identical to the "pipe" method, except
112 that the user name and password are terminated by NUL ('\0') characters
113 instead of newline characters, and they must be read from file descriptor
114 3 instead of standard input. Documentation for the checkpassword
115 interface is at http://cr.yp.to/checkpwd.html.
119 In the "environment" method, the arguments are passed in environment
120 variables. The user id and the clear-text password are passed in the
121 USER and PASS environment variables respectively.
123 Note that the environment method has fundamental security weaknesses,
124 and should probably not be used unless you have cause to believe it is
125 safe on your system. I wouldn't be surprised if it is marginally faster
126 than the pipe method. Most applications should use the pipe method instead.
128 A typical chunk of C code to authenticate with the environment method
133 char *user, *password;
135 if ((user= getenv("USER")) == NULL) exit(2);
136 if ((password= getenv("PASS")) == NULL) exit(3);
138 if (check_password(user, password) == OK)
139 exit(0); /* Good Password */
141 exit(1); /* Incorrect Password */
146 Security is generally less of a issue with group authenicators, since they
147 are not handling any data as sensitive as clear-text passwords. They are
148 only passed a user name (presumably already authenticated), and a list of
149 group names. They exit with status code 0 if that user is in one of those
150 groups, and a non-zero code otherwise.
152 In versions of mod_auth_external before 2.1.8, external authenticators were
153 always passed just one group name. If the Apache "require group" directive
154 listed more than one group, then the external authenticator would be called
155 once with each group name, which could be inefficient if you have a large
156 number of groups. Mod_auth_external will still behave this way if you
157 issue the "GroupExternalManyAtOnce off" directive.
159 Newer versions of mod_auth_external and mod_authnz_external will pass all
160 group names, separated by spaces. There will only be multiple calls if more
161 than one "require group" directive applies to the same program (e.g., if
162 different parent directories contain such directives in their .htaccess
163 files - for efficiency, this should be avoided). The list of group names
164 is passed in exactly as they appear on the "require group" directive - if
165 your program can't handle multiple spaces between group names, don't put
168 Arguments are passed in a manner similar to password authenticators. The
169 method used is determined by the 'SetExternalGroupMethod' command in your
170 Apache configuration file.
174 In the "environment" method, the arguments are passed in environment
175 variables. The user id and the group names are passed in the USER and
176 GROUP environment variables respectively. A typical chunk of C code to
177 fetch the arguments and check each group might be like:
181 char *user, *groups, *group;
183 if ((user= getenv("USER")) == NULL) exit(2);
184 if ((groups= getenv("GROUP")) == NULL) exit(3);
186 group= strtok(groups, " ");
187 while (group != NULL)
189 if (check_group(user, group) == OK)
190 exit(0); /* User is in group */
191 group= strtok(NULL, " ");
193 exit(1); /* User is not in any group */
196 Here "check_group()" is some function that looks in your database to see if
197 user is in group and returns 'OK' if he is.
201 In the "pipe" method, the arguments are read from standard input. The
202 user name will be on the first line, and the group name will be on the
203 second. Here's a typical chunk of C code to read that:
207 char user[100], groups[100], *group, *p;
209 if (fgets(user, sizeof(user), stdin) == NULL) exit(2);
210 if ((p= strchr(user, '\n')) == NULL) exit(4)
213 if (fgets(groups, sizeof(groups), stdin) == NULL) exit(3);
214 if ((p= strchr(groups, '\n')) == NULL) exit(5)
217 group= strtok(groups, " ");
218 while (group != NULL)
220 if (check_group(user, group) == OK)
221 exit(0); /* User is in group */
222 group= strtok(NULL, " ");
224 exit(1); /* User is not in any group */
227 Here we simply read two lines from stdin, being careful not to allow
228 buffer overflows and stripping off trailing newlines. We loop through
229 all groups, checking each.
233 Mod_auth_external will happily try to do group authentication via the
234 checkpassword method, piping NUL terminated user and group names to
235 the child process's file descriptor 3, but this isn't actually allowed
236 for in the checkpassword protocol specification, so I don't recommend it.
238 OTHER ENVIRONMENT VARIABLES
240 In all cases (pipe or environment method, password or group authentication),
241 the following additional environment variables will be supplied to the
244 AUTHTYPE either "PASS" or "GROUP" depending on whether we are doing
245 password or group authentication. This is handy if you are
246 using one program to do both.
248 CONTEXT a string whose value is set by an "AuthExternalContext"
249 directive in the .htaccess file or "<Directory>" block for
250 the directory. This can be used to select different
251 authentication behaviors in different directories. It is
252 undefined if there is no "AuthExternalContext" directive.
254 IP the client's ip-address.
256 HOST the host name corresponding to IP, if Apache has
257 "HostnameLookups On".
259 PATH the httpd's path environment variable.
261 COOKIE all cookie values passed in by the client.
263 HTTP_HOST the server's host name, as given in the HTTP request. May
264 be useful if you have multiple virtual hosts sharing an
267 URI the document requested. This is the URL including any extra
268 path information, but not including the hostname or any CGI
271 These may be useful for logging, or you may want to accept logins from
272 certain users only if they are connecting from certain locations or requesting
275 Note that if you have configured Apache with "HostnameLookups Off" then HOST
276 will usually not be set. If you really want hostnames, either turn on
277 HostnameLookups or do your own gethostbyaddr() calls from the authenticator
278 when HOST is not defined. Note that if the user is coming from an
279 unresolvable IP, then hostname lookups can be very slow.
281 Note that using IP addresses to track a user through your site is not
282 reliable. Users of services like AOL and WebTV use proxy servers, so that
283 their IP addresses appear to change constantly since each request may come
284 through a different proxy. A single user's requests for successive pages,
285 or for different images on the same page may all come from different IP
288 The PATH environment variable passed to the authenticator is just whatever
289 PATH was in effect when Apache was launched, and may differ if the server
290 was launched automatically during a reboot or manually by an admin.
291 Probably your program should set its own PATH if it needs one.
293 The COOKIE environment variable contains all cookies set in the current
294 request. This has the same format as the HTTP_COOKIES ("key=val;key=val")
295 passed to a CGI program. This should be used with caution. Cookies come
296 from the user's computer and might have been created, editted or deleted
297 by the user rather than your website. This severely limits their use for
298 authentication. It is not possible to set cookies from an authentication
301 The URI variable is there because various people want it. Mostly it
302 is useful not for authentication ("who is this person?") but for access
303 control ("is this person permitted to do this?"), and good design usually
304 dictates separating those functions. Strictly speaking, an authenticator
305 is not the right place to be doing access control. However,
306 mod_authnz_external is 50% a kludge-builder's tool, so we won't fuss if you
307 want to break the rules.