1 /* Copyright (C) 1999 IBM Transarc Labs - All rights reserved. */
3 /* Apache plugin for AFS authentication - should be archived to libapacheafs.a
4 * contains calls to functions in apache_afs_client.o - and is the intermediary
5 * between the module that plugs into apache's source code and the
6 * apache_afs_client. Shares global variables with the module and the client.
12 #include "apache_api.h"
14 #define afslog(level,str) if (level <= afsDebugLevel) (afsLogError str)
15 #define afsassert(str) if(!(str)) { fprintf(stderr, "afs module: assertion failed:%s\t%d\n",__FILE__,__LINE__) ; return SERVER_ERROR; }
17 #define AFS_AUTHTYPE "AFS"
18 #define AFS_DFS_AUTHTYPE "AFS-DFS"
19 #define ERRSTRLEN 1024
22 #include <sys/types.h>
29 const char module_name[] = "AFS Authentication Module";
33 u_long cacheExpiration;
36 static apache_afs_glob *afs_str;
38 /* Global file descriptors for pipes */
39 int readPipe, writePipe;
42 /* Global temp lock file descriptor */
46 * Create a temporary file and unlink it using the file descriptor for locking
47 * as a means of synchronization for providing exclusive access to the pipe
48 * for communicating with the weblog process
50 static int create_temp_file()
52 char tmpFileName[L_tmpnam];
58 lockfd = open(tmpFileName, O_RDWR | O_CREAT);
60 perror("afs_plugin:Error creating temp file:");
69 * Initialization: start up the weblog process. Open the pipes and pass their
70 * file descriptors to the weblog process
72 void afs_plugin_init(int tokenExpiration, char *weblogPath,char *error_fname, char *pf,
73 char *cell,char *dir,int exp,char *loc,int shutdown)
76 int pipe1[2], pipe2[2];
82 FILE *fp; /* for pid_fname */
83 char *afs_weblog_pidfile;
84 char *httpd_pid_fname=(char *) malloc(strlen(pf)+1);
85 if (httpd_pid_fname == NULL) {
86 fprintf(stderr, "%s: malloc failed - out of memory while allocating space for httpd_pid_fname\n", module_name);
89 strcpy(httpd_pid_fname, pf);
90 afs_weblog_pidfile=(char *) malloc(strlen(httpd_pid_fname)+5);
91 if (httpd_pid_fname == NULL) {
92 fprintf(stderr, "%s: malloc failed - out of memory while allocating space for afs_weblog_pidfile\n", module_name);
95 sprintf(afs_weblog_pidfile,"%s.afs", httpd_pid_fname);
98 fprintf(stderr, "%s:Failed to set pag Error:%d\n",
103 afs_str = (apache_afs_glob *) malloc(sizeof(apache_afs_glob));
104 if (afs_str == NULL) {
105 fprintf(stderr, "%s:malloc failed for afs_str\n",module_name);
110 strcpy(afs_str->defaultCell, cell);
112 fprintf(stderr, "%s: NULL argument in cell\n",module_name);
115 afs_str->cacheExpiration = exp;
117 afslog(5, ("Default Cell:%s\nCache Expiration:%d\nDebugLevel:%d",
118 afs_str->defaultCell, afs_str->cacheExpiration,afsDebugLevel));
121 /* Get a temp file fd for locking */
122 tempfd=create_temp_file();
124 fprintf(stderr, "%s: Error creating temp file", module_name);
129 if (pipe(pipe1) < 0 || pipe(pipe2) < 0) {
130 fprintf(stderr, "%s: Error creating pipes - %s",module_name, strerror(errno));
133 if ((childpid = fork()) < 0) {
134 fprintf(stderr,"%s: Error forking - %s",module_name, strerror(errno));
137 else if (childpid > 0) { /* parent */
141 writePipe = pipe1[1];
146 fp=fopen(afs_weblog_pidfile, "w");
149 fprintf(stderr, "%s: Error opening pidfile:%s - %s\n",
150 module_name, afs_weblog_pidfile, strerror(errno));
155 fprintf(fp,"%ld\n", (long)getpid());
157 free(afs_weblog_pidfile);
158 sprintf(weblogarg1, "%d", pipe1[0]);
159 sprintf(weblogarg2, "%d", pipe2[1]);
160 sprintf(weblogarg3, "%d", afs_str->cacheExpiration);
161 sprintf(weblogarg4, "%d", tokenExpiration);
163 execlp(weblogPath,"weblog_starter", weblogPath, error_fname,
164 weblogarg1,weblogarg2,weblogarg3,weblogarg4,
166 fprintf(stderr, "%s: Error executing %s - %s\n",
167 module_name, weblogPath, strerror(errno));
173 /* exit by sending a SIGTERM to the httpd process (how to get the pid?)
174 * since at this point the pid file is outdated and only if we sleep for
175 * a while to allow httpd_main to put it's pid in the pidfile can we
176 * attempt to send it a SIGTERM for graceful shuttdown)
177 * so that everything is brought down - if you want to bring everything
178 * down!! Else continue with httpd without AFS authentication.
180 /*#ifdef SHUTDOWN_IF_AFS_FAILS in afs_module.c */
182 #define KILL_TIME_WAIT 1
183 #define MAX_KILL_ATTEMPTS 3
185 fp=fopen(httpd_pid_fname,"r");
186 fscanf(fp,"%d", &childpid);
189 sleep(KILL_TIME_WAIT);
190 if (kill(childpid,SIGTERM) == -1) {
191 if ((errno == ESRCH) && (attempts < MAX_KILL_ATTEMPTS)) {
193 fprintf(stderr, "%s:SIGTERM to process:%d FAILED attempt:%d\nRetrying "
194 " for %d more attempts every %d seconds\n",
195 module_name,childpid,attempts,
196 (MAX_KILL_ATTEMPTS-attempts), KILL_TIME_WAIT);
201 fprintf(stderr, "%s:Shutdown complete ...\n", module_name);
203 if (attempts >= MAX_KILL_ATTEMPTS) {
205 "%s:httpd is still running-AFS authentication will fail "
206 "because weblog startup failed\n",module_name);
211 fprintf(stderr, "%s:httpd running - AFS Authentication will not work! "
212 "Weblog startup failure", module_name);
219 * Returns HTTP error codes based on the result of a stat error
221 static int sort_stat_error(request_rec *r)
226 status = HTTP_NOT_FOUND;
234 status = HTTP_NOT_FOUND;
238 status = HTTP_NOT_FOUND;
243 char error[ERRSTRLEN];
244 sprintf(error,"%s: stat error: %s",module_name, strerror(errno));
245 status = SERVER_ERROR;
246 LOG_REASON(error,r->uri, r);
254 * recursively stats the path to see where we're going wrong and
255 * chops off the path_info part of it -
256 * Returns OK or an HTTP status code
257 * Called if we get a ENOTDIR from the first stab at statting the
258 * entire path - so we assume that we have some PATH_INFO and try to
259 * chop it off the end and return the path itself
260 * Side effects on request_rec
261 - sets the filename field
262 - sets the path_info field
264 static int remove_path_info(request_rec *r, char *path, struct stat *buf)
268 char *last_cp = NULL;
275 end = &path[strlen(path)];
277 /* Advance over trailing slashes ... NOT part of filename */
278 for (cp = end; cp > path && cp[-1] == '/'; --cp)
282 /* See if the pathname ending here exists... */
285 rc = stat(path, buf);
290 if (S_ISDIR(buf->st_mode) && last_cp) {
291 buf->st_mode = 0; /* No such file... */
294 r->path_info = pstrdup(r->pool, cp);
299 else if (errno == ENOENT || errno == ENOTDIR) {
301 while (--cp > path && *cp != '/')
303 while (cp > path && cp[-1] == '/')
306 else if (errno != EACCES) {
308 * this would be really bad since we checked the entire path
309 * earlier and got ENOTDIR instead of EACCES - so why are we getting
310 * it now? Anyway, we ought to return FORBIDDEN
312 return HTTP_FORBIDDEN;
315 r->filename = pstrdup(r->pool, path);
320 * Checks to see if actual access to the URL is permitted or not
321 * stats the URI first, if failure returns FORBIDDEN, if allowed then
322 * checks to see if it is a file, dir or LINK (TEST), and accordingly does more
324 static int can_access(request_rec *r)
327 char *doc_root = (char *)DOCUMENT_ROOT(r);
329 char path[MAX_STRING_LEN];
335 afslog(10, ("%s: Found r->filename:%s", module_name, r->filename));
336 sprintf(path,"%s",r->filename);
339 afslog(10, ("%s: Composing path from doc_root:%s and r->uri:%s",
340 module_name, doc_root, r->uri));
341 sprintf(path,"%s/%s",doc_root,r->uri);
342 afslog(10, ("%s: Path:%s", module_name, path));
344 rc = stat(path, &buf);
346 afslog(2, ("%s: pid:%d\tpath:%s\tstat error:%s",
347 module_name, getpid(), path, strerror(errno)));
350 * if the requested method is an HTTP PUT and the file does not
351 * exist then well, we'll get a stat error but we shouldn't return
352 * an error - we should try and open the file in append mode and then
353 * see if we get a permission denied error
355 if ((strncmp(r->method,"PUT",3) == 0) && (errno == ENOENT)) {
356 FILE *f=fopen(path,"a");
358 if (errno == EACCES) {
359 afslog(2, ("%s: Either AFS acls or other permissions forbid write"
360 " access to file %s for user %s", module_name, path,
361 r->connection->user ? r->connection->user : "UNKNOWN"));
365 log_reason("afs_module: Error checking file for PUT method",r->uri,r);
370 else if (errno == ENOTDIR) {
372 * maybe the special case of CGI PATH_INFO to be translated to
373 * PATH_TRANSLATED - check each component of this path
374 * and stat it to see what portion of the url is actually
375 * the path and discard the rest for our purposes.
377 rc = remove_path_info(r, path, &buf);
378 afslog(10,("%s:remove_path_info returned %d path:%s",
379 module_name, rc, path));
384 return sort_stat_error(r);
388 * If we get here then we have something - either a file or a directory
391 if (S_IFREG == (buf.st_mode & S_IFMT)) {
394 char permissions[] = { 'r', '\0', '\0', '\0' }; /* room for +, etc... */
396 if ((strncmp(r->method, "PUT",3) == 0)) {
397 strcpy(permissions,"a");
399 if(!(f = fopen(path,permissions))) {
400 if (errno == EACCES) {
401 afslog(2, ("%s: Either AFS acls or other permissions"
402 " forbid access to file %s for user %s", module_name, path,
403 r->connection->user ? r->connection->user : "UNKNOWN"));
407 char error[ERRSTRLEN];
408 sprintf(error,"%s: Error checking file %s for permissions:%s",
409 module_name, path, strerror(errno));
410 log_reason(error, r->uri, r);
417 if (S_IFDIR == (buf.st_mode&S_IFMT)) {
418 /* path is a directory */
420 if (r->uri[strlen(r->uri)-1] != '/'){
421 /* if we don't have a trailing slash, return REDIRECT */
423 if (r->args != NULL) {
424 ifile = PSTRCAT (r->pool, escape_uri(r->pool, r->uri),
425 "/", "?", r->args, NULL);
428 ifile = PSTRCAT (r->pool, escape_uri(r->pool, r->uri),
431 TABLE_SET (r->headers_out, "Location",ifile);
436 if(!(d = opendir(path))) {
437 if (errno == EACCES) {
438 afslog(2, ("%s: Error accessing dir %s - %s",
439 module_name, path, strerror(errno)));
443 char error[ERRSTRLEN];
444 sprintf(error,"%s: opendir failed with Error:%s",
445 module_name, strerror(errno));
446 log_reason(error,r,r->uri);
459 * Logs requests which led to a FORBIDDEN return code provided a token
460 * existed. Added feature (SetAFSAccessLog) in beta2
462 static int log_Access_Error(request_rec *r)
464 if(FIND_LINKED_MODULE("afs_module.c") != NULL) {
470 if (r->connection->user)
471 sprintf(err_msg, "[%s] AFS ACL's deny permission to "
472 "user: %s for URI:%s\n",GET_TIME(), r->connection->user, r->uri);
474 sprintf(err_msg,"[%s] AFS ACL's deny permission to user - for URI:%s\n",
478 rc=write(logfd, err_msg, len);
481 afslog(0, ("%s: Error logging message:%s - %s",
482 module_name, err_msg, strerror(errno)));
490 * The interface - hook to obtain an AFS token if needed based on the
491 * result of a stat (returned by can_access) or an open file
493 int afs_auth_internal(request_rec *r, char *cell)
498 if(FIND_LINKED_MODULE("afs_module.c") != NULL) {
501 static int haveToken = 1; /* assume that we always have a token */
502 extern int logAccessErrors;
505 * Get afs_authtype directive value for that directory or location
507 type = (char *) get_afsauthtype(r);
510 * UserDir (tilde) support
513 if (FIND_LINKED_MODULE("mod_userdir.c") != NULL) {
514 rc=translate_userdir(r);
515 if ((rc != OK) && (rc != DECLINED)) {
516 LOG_REASON("afs_module: Failure while translating userdir", r->uri,r);
522 afslog(20, ("%s: pid:%d r->uri:%s", module_name, getpid(), r->uri));
524 afslog(20, ("%s: AFSAuthType: %s", module_name, type));
526 afslog(20, ("%s: AFSAuthType NULL", module_name));
528 /* if AuthType is not AFS, then unlog any existing tokens and DECLINE */
535 if((strcasecmp(type, AFS_AUTHTYPE)) && (strcasecmp(type,AFS_DFS_AUTHTYPE))) {
538 afslog(10, ("%s: Error unknown AFSAuthType:%s returning DECLINED",
544 status=authenticateUser(r, cell, afs_str->cacheExpiration, type);
546 status=authenticateUser(r, afs_str->defaultCell, afs_str->cacheExpiration, type);
549 afslog(10, ("%s: Returning status %d", module_name, status));
553 /* can we access this URL? */
560 if (rc == REDIRECT) {
564 if (rc == FORBIDDEN) {
565 rc = forbToAuthReqd(r);
566 if (rc == FORBIDDEN) {
567 if (logAccessErrors) {