2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 /* Apache plugin for AFS authentication - should be archived to libapacheafs.a
11 * contains calls to functions in apache_afs_client.o - and is the intermediary
12 * between the module that plugs into apache's source code and the
13 * apache_afs_client. Shares global variables with the module and the client.
19 #include "apache_api.h"
21 #define afslog(level,str) if (level <= afsDebugLevel) (afsLogError str)
22 #define afsassert(str) if(!(str)) { fprintf(stderr, "afs module: assertion failed:%s\t%d\n",__FILE__,__LINE__) ; return SERVER_ERROR; }
24 #define AFS_AUTHTYPE "AFS"
25 #define AFS_DFS_AUTHTYPE "AFS-DFS"
26 #define ERRSTRLEN 1024
29 #include <sys/types.h>
36 const char module_name
[] = "AFS Authentication Module";
40 u_long cacheExpiration
;
43 static apache_afs_glob
*afs_str
;
45 /* Global file descriptors for pipes */
46 int readPipe
, writePipe
;
49 /* Global temp lock file descriptor */
53 * Create a temporary file and unlink it using the file descriptor for locking
54 * as a means of synchronization for providing exclusive access to the pipe
55 * for communicating with the weblog process
60 char tmpFileName
[L_tmpnam
];
66 lockfd
= open(tmpFileName
, O_RDWR
| O_CREAT
);
68 perror("afs_plugin:Error creating temp file:");
77 * Initialization: start up the weblog process. Open the pipes and pass their
78 * file descriptors to the weblog process
81 afs_plugin_init(int tokenExpiration
, char *weblogPath
, char *error_fname
,
82 char *pf
, char *cell
, char *dir
, int exp
, char *loc
,
86 int pipe1
[2], pipe2
[2];
92 FILE *fp
; /* for pid_fname */
93 char *afs_weblog_pidfile
;
94 char *httpd_pid_fname
= strdup(pf
);
95 if (httpd_pid_fname
== NULL
) {
97 "%s: malloc failed - out of memory while allocating space for httpd_pid_fname\n",
101 if (asprintf(&afs_weblog_pidfile
, "%s.afs", httpd_pid_fname
) < 0)
102 afs_weblog_pidfile
== NULL
;
103 if (afs_weblog_pidfile
== NULL
) {
105 "%s: malloc failed - out of memory while allocating space for afs_weblog_pidfile\n",
111 fprintf(stderr
, "%s:Failed to set pag Error:%d\n", module_name
,
116 afs_str
= (apache_afs_glob
*) malloc(sizeof(apache_afs_glob
));
117 if (afs_str
== NULL
) {
118 fprintf(stderr
, "%s:malloc failed for afs_str\n", module_name
);
123 strcpy(afs_str
->defaultCell
, cell
);
125 fprintf(stderr
, "%s: NULL argument in cell\n", module_name
);
128 afs_str
->cacheExpiration
= exp
;
131 ("Default Cell:%s\nCache Expiration:%d\nDebugLevel:%d",
132 afs_str
->defaultCell
, afs_str
->cacheExpiration
, afsDebugLevel
));
135 /* Get a temp file fd for locking */
136 tempfd
= create_temp_file();
138 fprintf(stderr
, "%s: Error creating temp file", module_name
);
143 if (pipe(pipe1
) < 0 || pipe(pipe2
) < 0) {
144 fprintf(stderr
, "%s: Error creating pipes - %s", module_name
,
148 if ((childpid
= fork()) < 0) {
149 fprintf(stderr
, "%s: Error forking - %s", module_name
,
152 } else if (childpid
> 0) { /* parent */
156 writePipe
= pipe1
[1];
160 fp
= fopen(afs_weblog_pidfile
, "w");
163 fprintf(stderr
, "%s: Error opening pidfile:%s - %s\n",
164 module_name
, afs_weblog_pidfile
, strerror(errno
));
169 fprintf(fp
, "%ld\n", (long)getpid());
171 free(afs_weblog_pidfile
);
172 sprintf(weblogarg1
, "%d", pipe1
[0]);
173 sprintf(weblogarg2
, "%d", pipe2
[1]);
174 sprintf(weblogarg3
, "%d", afs_str
->cacheExpiration
);
175 sprintf(weblogarg4
, "%d", tokenExpiration
);
177 execlp(weblogPath
, "weblog_starter", weblogPath
, error_fname
,
178 weblogarg1
, weblogarg2
, weblogarg3
, weblogarg4
, NULL
);
179 fprintf(stderr
, "%s: Error executing %s - %s\n", module_name
,
180 weblogPath
, strerror(errno
));
186 /* exit by sending a SIGTERM to the httpd process (how to get the pid?)
187 * since at this point the pid file is outdated and only if we sleep for
188 * a while to allow httpd_main to put it's pid in the pidfile can we
189 * attempt to send it a SIGTERM for graceful shuttdown)
190 * so that everything is brought down - if you want to bring everything
191 * down!! Else continue with httpd without AFS authentication.
193 /*#ifdef SHUTDOWN_IF_AFS_FAILS in afs_module.c */
195 #define KILL_TIME_WAIT 1
196 #define MAX_KILL_ATTEMPTS 3
198 fp
= fopen(httpd_pid_fname
, "r");
199 fscanf(fp
, "%d", &childpid
);
202 sleep(KILL_TIME_WAIT
);
203 if (kill(childpid
, SIGTERM
) == -1) {
204 if ((errno
== ESRCH
) && (attempts
< MAX_KILL_ATTEMPTS
)) {
207 "%s:SIGTERM to process:%d FAILED attempt:%d\nRetrying "
208 " for %d more attempts every %d seconds\n",
209 module_name
, childpid
, attempts
,
210 (MAX_KILL_ATTEMPTS
- attempts
), KILL_TIME_WAIT
);
214 fprintf(stderr
, "%s:Shutdown complete ...\n", module_name
);
216 if (attempts
>= MAX_KILL_ATTEMPTS
) {
218 "%s:httpd is still running-AFS authentication will fail "
219 "because weblog startup failed\n", module_name
);
224 "%s:httpd running - AFS Authentication will not work! "
225 "Weblog startup failure", module_name
);
232 * Returns HTTP error codes based on the result of a stat error
235 sort_stat_error(request_rec
* r
)
240 status
= HTTP_NOT_FOUND
;
248 status
= HTTP_NOT_FOUND
;
252 status
= HTTP_NOT_FOUND
;
257 char error
[ERRSTRLEN
];
258 sprintf(error
, "%s: stat error: %s", module_name
,
260 status
= SERVER_ERROR
;
261 LOG_REASON(error
, r
->uri
, r
);
269 * recursively stats the path to see where we're going wrong and
270 * chops off the path_info part of it -
271 * Returns OK or an HTTP status code
272 * Called if we get a ENOTDIR from the first stab at statting the
273 * entire path - so we assume that we have some PATH_INFO and try to
274 * chop it off the end and return the path itself
275 * Side effects on request_rec
276 - sets the filename field
277 - sets the path_info field
280 remove_path_info(request_rec
* r
, char *path
, struct stat
*buf
)
284 char *last_cp
= NULL
;
291 end
= &path
[strlen(path
)];
293 /* Advance over trailing slashes ... NOT part of filename */
294 for (cp
= end
; cp
> path
&& cp
[-1] == '/'; --cp
)
298 /* See if the pathname ending here exists... */
301 rc
= stat(path
, buf
);
306 if (S_ISDIR(buf
->st_mode
) && last_cp
) {
307 buf
->st_mode
= 0; /* No such file... */
310 r
->path_info
= pstrdup(r
->pool
, cp
);
315 else if (errno
== ENOENT
|| errno
== ENOTDIR
) {
317 while (--cp
> path
&& *cp
!= '/')
319 while (cp
> path
&& cp
[-1] == '/')
321 } else if (errno
!= EACCES
) {
323 * this would be really bad since we checked the entire path
324 * earlier and got ENOTDIR instead of EACCES - so why are we getting
325 * it now? Anyway, we ought to return FORBIDDEN
327 return HTTP_FORBIDDEN
;
330 r
->filename
= pstrdup(r
->pool
, path
);
335 * Checks to see if actual access to the URL is permitted or not
336 * stats the URI first, if failure returns FORBIDDEN, if allowed then
337 * checks to see if it is a file, dir or LINK (TEST), and accordingly does more
340 can_access(request_rec
* r
)
343 char *doc_root
= (char *)DOCUMENT_ROOT(r
);
345 char path
[MAX_STRING_LEN
];
351 afslog(10, ("%s: Found r->filename:%s", module_name
, r
->filename
));
352 sprintf(path
, "%s", r
->filename
);
355 ("%s: Composing path from doc_root:%s and r->uri:%s",
356 module_name
, doc_root
, r
->uri
));
357 sprintf(path
, "%s/%s", doc_root
, r
->uri
);
358 afslog(10, ("%s: Path:%s", module_name
, path
));
360 rc
= stat(path
, &buf
);
363 ("%s: pid:%d\tpath:%s\tstat error:%s", module_name
, getpid(),
364 path
, strerror(errno
)));
367 * if the requested method is an HTTP PUT and the file does not
368 * exist then well, we'll get a stat error but we shouldn't return
369 * an error - we should try and open the file in append mode and then
370 * see if we get a permission denied error
372 if ((strncmp(r
->method
, "PUT", 3) == 0) && (errno
== ENOENT
)) {
373 FILE *f
= fopen(path
, "a");
375 if (errno
== EACCES
) {
377 ("%s: Either AFS acls or other permissions forbid write"
378 " access to file %s for user %s", module_name
,
380 r
->connection
->user
? r
->connection
->
385 ("afs_module: Error checking file for PUT method",
390 } else if (errno
== ENOTDIR
) {
392 * maybe the special case of CGI PATH_INFO to be translated to
393 * PATH_TRANSLATED - check each component of this path
394 * and stat it to see what portion of the url is actually
395 * the path and discard the rest for our purposes.
397 rc
= remove_path_info(r
, path
, &buf
);
399 ("%s:remove_path_info returned %d path:%s", module_name
,
404 return sort_stat_error(r
);
408 * If we get here then we have something - either a file or a directory
411 if (S_IFREG
== (buf
.st_mode
& S_IFMT
)) {
414 char permissions
[] = { 'r', '\0', '\0', '\0' }; /* room for +, etc... */
416 if ((strncmp(r
->method
, "PUT", 3) == 0)) {
417 strcpy(permissions
, "a");
419 if (!(f
= fopen(path
, permissions
))) {
420 if (errno
== EACCES
) {
422 ("%s: Either AFS acls or other permissions"
423 " forbid access to file %s for user %s",
425 r
->connection
->user
? r
->connection
->
429 char error
[ERRSTRLEN
];
431 "%s: Error checking file %s for permissions:%s",
432 module_name
, path
, strerror(errno
));
433 log_reason(error
, r
->uri
, r
);
440 if (S_IFDIR
== (buf
.st_mode
& S_IFMT
)) {
441 /* path is a directory */
443 if (r
->uri
[strlen(r
->uri
) - 1] != '/') {
444 /* if we don't have a trailing slash, return REDIRECT */
446 if (r
->args
!= NULL
) {
448 PSTRCAT(r
->pool
, escape_uri(r
->pool
, r
->uri
), "/",
452 PSTRCAT(r
->pool
, escape_uri(r
->pool
, r
->uri
), "/",
455 TABLE_SET(r
->headers_out
, "Location", ifile
);
459 if (!(d
= opendir(path
))) {
460 if (errno
== EACCES
) {
462 ("%s: Error accessing dir %s - %s",
463 module_name
, path
, strerror(errno
)));
466 char error
[ERRSTRLEN
];
467 sprintf(error
, "%s: opendir failed with Error:%s",
468 module_name
, strerror(errno
));
469 log_reason(error
, r
, r
->uri
);
482 * Logs requests which led to a FORBIDDEN return code provided a token
483 * existed. Added feature (SetAFSAccessLog) in beta2
486 log_Access_Error(request_rec
* r
)
488 if (FIND_LINKED_MODULE("afs_module.c") != NULL
) {
494 if (r
->connection
->user
)
496 "[%s] AFS ACL's deny permission to "
497 "user: %s for URI:%s\n", GET_TIME(), r
->connection
->user
,
501 "[%s] AFS ACL's deny permission to user - for URI:%s\n",
504 len
= strlen(err_msg
);
505 rc
= write(logfd
, err_msg
, len
);
509 ("%s: Error logging message:%s - %s", module_name
, err_msg
,
518 * The interface - hook to obtain an AFS token if needed based on the
519 * result of a stat (returned by can_access) or an open file
522 afs_auth_internal(request_rec
* r
, char *cell
)
527 if (FIND_LINKED_MODULE("afs_module.c") != NULL
) {
530 static int haveToken
= 1; /* assume that we always have a token */
531 extern int logAccessErrors
;
534 * Get afs_authtype directive value for that directory or location
536 type
= (char *)get_afsauthtype(r
);
539 * UserDir (tilde) support
542 if (FIND_LINKED_MODULE("mod_userdir.c") != NULL
) {
543 rc
= translate_userdir(r
);
544 if ((rc
!= OK
) && (rc
!= DECLINED
)) {
545 LOG_REASON("afs_module: Failure while translating userdir",
552 afslog(20, ("%s: pid:%d r->uri:%s", module_name
, getpid(), r
->uri
));
554 afslog(20, ("%s: AFSAuthType: %s", module_name
, type
));
556 afslog(20, ("%s: AFSAuthType NULL", module_name
));
558 /* if AuthType is not AFS, then unlog any existing tokens and DECLINE */
565 if ((strcasecmp(type
, AFS_AUTHTYPE
))
566 && (strcasecmp(type
, AFS_DFS_AUTHTYPE
))) {
570 ("%s: Error unknown AFSAuthType:%s returning DECLINED",
577 authenticateUser(r
, cell
, afs_str
->cacheExpiration
, type
);
580 authenticateUser(r
, afs_str
->defaultCell
,
581 afs_str
->cacheExpiration
, type
);
584 afslog(10, ("%s: Returning status %d", module_name
, status
));
588 /* can we access this URL? */
595 if (rc
== REDIRECT
) {
599 if (rc
== FORBIDDEN
) {
600 rc
= forbToAuthReqd(r
);
601 if (rc
== FORBIDDEN
) {
602 if (logAccessErrors
) {