]> granicus.if.org Git - apache-authnz-external/blob - mod_authnz_external/AUTHENTICATORS
Add FORWARDS environment variable, as suggested under a different name by Rogier...
[apache-authnz-external] / mod_authnz_external / AUTHENTICATORS
1             How To Implementation External Authentication Programs
2                for mod_authnz_external or mod_auth_external
3                                 Version 3.3.x
4
5 LANGUAGES
6
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
10  are in C.
11
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.
16
17 SECURITY
18
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:
23
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.
28
29  - Think about locking.  It is possible to get lots of hits at your website
30    very fast, so there may be many programs simultaneously reading your
31    authentication database, plus updates may be going on at the same time.
32    Probably some form of locking is needed to make all this work right.
33
34  - Think about core dumps.  On some systems core dump files can be publically
35    readable.  A core dump from your authenticator is likely to contain the
36    user's plain text password, and may include large chunks of your password
37    database that may have been in buffers.  For C programs on most versions of
38    Unix, it is possible to disable core dumps by doing something like:
39    
40     rlim.rlim_cur = rlim.rlim_max = 0;
41     (void)setrlimit(RLIMIT_CORE, &rlim);
42
43    Actually, core dumps seem to be mostly a thing of the past. Most modern
44    Unixes don't seem to generate them.
45
46  It may not hurt to spend a little time looking at the features of the pwauth
47  authenticator, which is the most secure external authenticator that I have
48  written.
49
50 PASSWORD AUTHENTICATORS
51
52  Authenticators communicate their result by the exit status code they return.
53  A value of 0 indicates that the password is correct.  Other values indicate
54  that the password is incorrect, or something else is wrong.  It can be
55  useful to return different error codes for different kinds of errors.  These
56  will be logged in the Apache error log file, and can be helpful in diagnosing
57  problems.  This version of mod_authnz_external does not have any provision for
58  returning textual error messages from the external authenticator.  You might
59  be able to use syslog() for this.  This might be improved in future releases.
60
61  Returned error codes should not be negative.  Negative values are used
62  internally to mod_authnz_external to indicate problems launching your program.
63
64  How the external authentication program gets its arguments depends on
65  the method used.  The method used is determined by the 'SetExternalAuthMethod'
66  command in your Apache configuration file.  You need implement only the
67  method that you plan to use in your configuration.
68
69  PIPE METHOD
70
71   In the "pipe" method, the arguments are read from standard input.  The
72   user name will be on the first line, and the password will be on the
73   second.  Here's a typical chunk of C code to read that:
74
75   main()
76   {
77       char user[100], password[100], *p;
78
79       if (fgets(user, sizeof(user), stdin) == NULL) exit(2);
80       if ((p= strchr(user, '\n')) == NULL) exit(4)
81       *p= '\0';
82
83       if (fgets(password, sizeof(password), stdin) == NULL) exit(3);
84       if ((p= strchr(password, '\n')) == NULL) exit(5)
85       *p= '\0';
86
87       if (check_password(user, password) == OK)
88            exit(0);     /* Good Password */
89       else
90            exit(1);     /* Incorrect Password */
91   }
92
93   Here we simply read two lines from stdin, being careful not to allow
94   buffer overflows and stripping off trailing newlines.
95
96   We assume "check_password()" is some function that checks the validity of a
97   password and returns 'OK' if it is good.
98   
99   Note that we exit with different non-zero error codes in different error
100   cases.  This will be helpful for debugging, as those values will be logged
101   when authentication fails, giving you some clue as to what went wrong.
102   It'd really be better for check_password() to return more detailed error
103   codes, but I wanted to keep the example simple.
104
105  CHECKPASSWORD METHOD
106
107   The "checkpassword" method is identical to the "pipe" method, except
108   that the user name and password are terminated by NUL ('\0') characters
109   instead of newline characters, and they must be read from file descriptor
110   3 instead of standard input.  Documentation for the checkpassword
111   interface is at http://cr.yp.to/checkpwd.html.
112
113  ENVIRONMENT METHOD
114
115   In the "environment" method, the arguments are passed in environment
116   variables.  The user id and the clear-text password are passed in the
117   USER and PASS environment variables respectively.
118   
119   Note that the environment method has fundamental security weaknesses,
120   and should probably not be used unless you have cause to believe it is
121   safe on your system. I wouldn't be surprised if it is marginally faster
122   than the pipe method. Most applications should use the pipe method instead.
123
124   A typical chunk of C code to authenticate with the environment method
125   might be like:
126
127   main()
128   {
129       char *user, *password;
130
131       if ((user= getenv("USER")) == NULL) exit(2);
132       if ((password= getenv("PASS")) == NULL) exit(3);
133
134       if (check_password(user, password) == OK)
135            exit(0);     /* Good Password */
136       else
137            exit(1);     /* Incorrect Password */
138   }
139
140 GROUP AUTHENTICATORS
141
142  Security is generally less of a issue with group authenicators, since they
143  are not handling any data as sensitive as clear-text passwords.  They are
144  only passed a user name (presumably already authenticated), and a list of
145  group names.  They exit with status code 0 if that user is in one of those
146  groups, and a non-zero code otherwise.
147
148  In versions of mod_auth_external before 2.1.8, external authenticators were
149  always passed just one group name.  If the Apache "require group" directive
150  listed more than one group, then the external authenticator would be called
151  once with each group name, which could be inefficient if you have a large
152  number of groups.  Mod_auth_external will still behave this way if you
153  issue the "GroupExternalManyAtOnce off" directive.
154
155  Newer versions of mod_auth_external and mod_authnz_external will pass all
156  group names, separated by spaces.  There will only be multiple calls if more
157  than one "require group" directive applies to the same program (e.g., if
158  different parent directories contain such directives in their .htaccess
159  files - for efficiency, this should be avoided).  The list of group names
160  is passed in exactly as they appear on the "require group" directive - if
161  your program can't handle multiple spaces between group names, don't put
162  them there.
163
164  Arguments are passed in a manner similar to password authenticators.  The
165  method used is determined by the 'SetExternalGroupMethod' command in your
166  Apache configuration file.
167
168  ENVIRONMENT METHOD
169
170   In the "environment" method, the arguments are passed in environment
171   variables.  The user id and the group names are passed in the USER and
172   GROUP environment variables respectively.  A typical chunk of C code to
173   fetch the arguments and check each group might be like:
174
175   main()
176   {
177       char *user, *groups, *group;
178
179       if ((user= getenv("USER")) == NULL) exit(2);
180       if ((groups= getenv("GROUP")) == NULL) exit(3);
181
182       group= strtok(groups, " ");
183       while (group != NULL)
184       {
185           if (check_group(user, group) == OK)
186                 exit(0);        /* User is in group */
187           group= strtok(NULL, " ");
188       }
189       exit(1);                  /* User is not in any group */
190   }
191
192   Here "check_group()" is some function that looks in your database to see if
193   user is in group and returns 'OK' if he is.
194
195  PIPE METHOD
196
197   In the "pipe" method, the arguments are read from standard input.  The
198   user name will be on the first line, and the group name will be on the
199   second.  Here's a typical chunk of C code to read that:
200
201   main()
202   {
203       char user[100], groups[100], *group, *p;
204
205       if (fgets(user, sizeof(user), stdin) == NULL) exit(2);
206       if ((p= strchr(user, '\n')) == NULL) exit(4)
207       *p= '\0';
208
209       if (fgets(groups, sizeof(groups), stdin) == NULL) exit(3);
210       if ((p= strchr(groups, '\n')) == NULL) exit(5)
211       *p= '\0';
212
213       group= strtok(groups, " ");
214       while (group != NULL)
215       {
216           if (check_group(user, group) == OK)
217                 exit(0);        /* User is in group */
218           group= strtok(NULL, " ");
219       }
220       exit(1);                  /* User is not in any group */
221   }
222
223   Here we simply read two lines from stdin, being careful not to allow
224   buffer overflows and stripping off trailing newlines.  We loop through
225   all groups, checking each.
226
227  CHECKPASSWORD METHOD
228
229   Mod_auth_external will happily try to do group authentication via the
230   checkpassword method, piping NUL terminated user and group names to
231   the child process's file descriptor 3, but this isn't actually allowed
232   for in the checkpassword protocol specification, so I don't recommend it.
233
234 OTHER ENVIRONMENT VARIABLES
235
236  In all cases (pipe or environment method, password or group authentication),
237  the following additional environment variables will be supplied to the
238  authenticator:
239
240     AUTHTYPE  either "PASS" or "GROUP" depending on whether we are doing
241               password or group authentication.  This is handy if you are
242               using one program to do both.
243
244     CONTEXT   a string whose value is set by an "AuthExternalContext"
245               directive in the .htaccess file or "<Directory>" block for
246               the directory.  This can be used to select different
247               authentication behaviors in different directories.  It is
248               undefined if there is no "AuthExternalContext" directive.
249
250     IP        the client's ip-address.
251
252     HOST      the host name corresponding to IP, if Apache has
253               "HostnameLookups On".
254
255     FORWARDS  If a load-balancer or proxy is being used, then the "IP" variable
256               is just going to be the ip-address of the server that forwarded
257               the client's request to the current server. In that case, the
258               "FORWARDS" environment variable gives a comma-separated list of
259               ip-addresses, the first of which will be the original client's ip,
260               while the rest are all the other proxies the request was forwarded
261               through before reaching the server named by the the "IP" variable.
262
263     PATH      the httpd's path environment variable.
264
265     COOKIE    all cookie values passed in by the client.
266
267     HTTP_HOST the server's host name, as given in the HTTP request.  May
268               be useful if you have multiple virtual hosts sharing an
269               authenticator.
270
271     URI       the document requested.  This is the URL including any extra
272               path information, but not including the hostname or any CGI
273               arguments.
274
275  These may be useful for logging, or you may want to accept logins from
276  certain users only if they are connecting from certain locations or requesting
277  certain documents.
278
279  Note that if you have configured Apache with "HostnameLookups Off" then HOST
280  will usually not be set.  If you really want hostnames, either turn on
281  HostnameLookups or do your own gethostbyaddr() calls from the authenticator
282  when HOST is not defined.  Note that if the user is coming from an
283  unresolvable IP, then hostname lookups can be very slow.
284
285  Note that using IP addresses to track a user through your site is not
286  reliable.  Users of services like AOL and WebTV use proxy servers, so that 
287  their IP addresses appear to change constantly since each request may come
288  through a different proxy.  A single user's requests for successive pages,
289  or for different images on the same page may all come from different IP
290  addresses.
291
292  The PATH environment variable passed to the authenticator is just whatever
293  PATH was in effect when Apache was launched, and may differ if the server
294  was launched automatically during a reboot or manually by an admin. 
295  Probably your program should set its own PATH if it needs one.
296
297  The COOKIE environment variable contains all cookies set in the current
298  request.  This has the same format as the HTTP_COOKIES ("key=val;key=val")
299  passed to a CGI program.  This should be used with caution.  Cookies come
300  from the user's computer and might have been created, editted or deleted
301  by the user rather than your website.  This severely limits their use for
302  authentication.  It is not possible to set cookies from an authentication
303  module.
304
305  The URI variable is there because various people want it.  Mostly it
306  is useful not for authentication ("who is this person?") but for access
307  control ("is this person permitted to do this?"), and good design usually
308  dictates separating those functions.  Strictly speaking, an authenticator
309  is not the right place to be doing access control.  However,
310  mod_authnz_external is 50% a kludge-builder's tool, so we won't fuss if you
311  want to break the rules.