mirror of
https://git.openafs.org/openafs.git
synced 2025-01-20 07:51:00 +00:00
872bc94f8c
FIXES 1774 thanks to nneul@umr.edu for providing a script to do this. gnu indent 2.2.9 options: -npro -nbad -bap -nbc -bbo -br -ce -cdw -brs -ncdb -cp1 -ncs -di2 -ndj -nfc1 -nfca -i4 -lp -npcs -nprs -psl -sc -nsob -ts8 ==================== This delta was composed from multiple commits as part of the CVS->Git migration. The checkin message with each commit was inconsistent. The following are the additional commit messages. ==================== FIXES 1774 fix subst mistake
612 lines
16 KiB
C
612 lines
16 KiB
C
/*
|
|
* Copyright 2000, International Business Machines Corporation and others.
|
|
* All Rights Reserved.
|
|
*
|
|
* This software has been released under the terms of the IBM Public
|
|
* License. For details, see the LICENSE file in the top-level source
|
|
* directory or online at http://www.openafs.org/dl/license10.html
|
|
*/
|
|
|
|
/* Apache plugin for AFS authentication - should be archived to libapacheafs.a
|
|
* contains calls to functions in apache_afs_client.o - and is the intermediary
|
|
* between the module that plugs into apache's source code and the
|
|
* apache_afs_client. Shares global variables with the module and the client.
|
|
*/
|
|
|
|
/*
|
|
*/
|
|
|
|
#include "apache_api.h"
|
|
|
|
#define afslog(level,str) if (level <= afsDebugLevel) (afsLogError str)
|
|
#define afsassert(str) if(!(str)) { fprintf(stderr, "afs module: assertion failed:%s\t%d\n",__FILE__,__LINE__) ; return SERVER_ERROR; }
|
|
|
|
#define AFS_AUTHTYPE "AFS"
|
|
#define AFS_DFS_AUTHTYPE "AFS-DFS"
|
|
#define ERRSTRLEN 1024
|
|
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
|
|
/* Global vars */
|
|
|
|
u_long afsDebugLevel;
|
|
const char module_name[] = "AFS Authentication Module";
|
|
|
|
typedef struct {
|
|
char defaultCell[64];
|
|
u_long cacheExpiration;
|
|
} apache_afs_glob;
|
|
|
|
static apache_afs_glob *afs_str;
|
|
|
|
/* Global file descriptors for pipes */
|
|
int readPipe, writePipe;
|
|
|
|
#ifdef AIX
|
|
/* Global temp lock file descriptor */
|
|
int tempfd;
|
|
|
|
/*
|
|
* Create a temporary file and unlink it using the file descriptor for locking
|
|
* as a means of synchronization for providing exclusive access to the pipe
|
|
* for communicating with the weblog process
|
|
*/
|
|
static int
|
|
create_temp_file()
|
|
{
|
|
char tmpFileName[L_tmpnam];
|
|
int lockfd = 0;
|
|
|
|
tmpnam(tmpFileName);
|
|
unlink(tmpFileName);
|
|
|
|
lockfd = open(tmpFileName, O_RDWR | O_CREAT);
|
|
if (lockfd < 0) {
|
|
perror("afs_plugin:Error creating temp file:");
|
|
return lockfd;
|
|
}
|
|
unlink(tmpFileName);
|
|
return lockfd;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Initialization: start up the weblog process. Open the pipes and pass their
|
|
* file descriptors to the weblog process
|
|
*/
|
|
void
|
|
afs_plugin_init(int tokenExpiration, char *weblogPath, char *error_fname,
|
|
char *pf, char *cell, char *dir, int exp, char *loc,
|
|
int shutdown)
|
|
{
|
|
int childpid;
|
|
int pipe1[2], pipe2[2];
|
|
char weblogarg1[32];
|
|
char weblogarg2[32];
|
|
char weblogarg3[32];
|
|
char weblogarg4[32];
|
|
|
|
FILE *fp; /* for pid_fname */
|
|
char *afs_weblog_pidfile;
|
|
char *httpd_pid_fname = (char *)malloc(strlen(pf) + 1);
|
|
if (httpd_pid_fname == NULL) {
|
|
fprintf(stderr,
|
|
"%s: malloc failed - out of memory while allocating space for httpd_pid_fname\n",
|
|
module_name);
|
|
exit(-1);
|
|
}
|
|
strcpy(httpd_pid_fname, pf);
|
|
afs_weblog_pidfile = (char *)malloc(strlen(httpd_pid_fname) + 5);
|
|
if (httpd_pid_fname == NULL) {
|
|
fprintf(stderr,
|
|
"%s: malloc failed - out of memory while allocating space for afs_weblog_pidfile\n",
|
|
module_name);
|
|
exit(-1);
|
|
}
|
|
sprintf(afs_weblog_pidfile, "%s.afs", httpd_pid_fname);
|
|
|
|
if (do_setpag()) {
|
|
fprintf(stderr, "%s:Failed to set pag Error:%d\n", module_name,
|
|
errno);
|
|
exit(-1);
|
|
}
|
|
|
|
afs_str = (apache_afs_glob *) malloc(sizeof(apache_afs_glob));
|
|
if (afs_str == NULL) {
|
|
fprintf(stderr, "%s:malloc failed for afs_str\n", module_name);
|
|
exit(-1);
|
|
}
|
|
|
|
if (cell)
|
|
strcpy(afs_str->defaultCell, cell);
|
|
else {
|
|
fprintf(stderr, "%s: NULL argument in cell\n", module_name);
|
|
exit(-1);
|
|
}
|
|
afs_str->cacheExpiration = exp;
|
|
|
|
afslog(5,
|
|
("Default Cell:%s\nCache Expiration:%d\nDebugLevel:%d",
|
|
afs_str->defaultCell, afs_str->cacheExpiration, afsDebugLevel));
|
|
|
|
#ifdef AIX
|
|
/* Get a temp file fd for locking */
|
|
tempfd = create_temp_file();
|
|
if (tempfd < 0) {
|
|
fprintf(stderr, "%s: Error creating temp file", module_name);
|
|
exit(-1);
|
|
}
|
|
#endif
|
|
|
|
if (pipe(pipe1) < 0 || pipe(pipe2) < 0) {
|
|
fprintf(stderr, "%s: Error creating pipes - %s", module_name,
|
|
strerror(errno));
|
|
exit(-1);
|
|
}
|
|
if ((childpid = fork()) < 0) {
|
|
fprintf(stderr, "%s: Error forking - %s", module_name,
|
|
strerror(errno));
|
|
exit(-1);
|
|
} else if (childpid > 0) { /* parent */
|
|
close(pipe1[0]);
|
|
close(pipe2[1]);
|
|
readPipe = pipe2[0];
|
|
writePipe = pipe1[1];
|
|
} else { /* child */
|
|
close(pipe1[1]);
|
|
close(pipe2[0]);
|
|
fp = fopen(afs_weblog_pidfile, "w");
|
|
if (fp == NULL) {
|
|
perror("fopen");
|
|
fprintf(stderr, "%s: Error opening pidfile:%s - %s\n",
|
|
module_name, afs_weblog_pidfile, strerror(errno));
|
|
close(pipe1[0]);
|
|
close(pipe2[1]);
|
|
exit(-1);
|
|
}
|
|
fprintf(fp, "%ld\n", (long)getpid());
|
|
fclose(fp);
|
|
free(afs_weblog_pidfile);
|
|
sprintf(weblogarg1, "%d", pipe1[0]);
|
|
sprintf(weblogarg2, "%d", pipe2[1]);
|
|
sprintf(weblogarg3, "%d", afs_str->cacheExpiration);
|
|
sprintf(weblogarg4, "%d", tokenExpiration);
|
|
sleep(5);
|
|
execlp(weblogPath, "weblog_starter", weblogPath, error_fname,
|
|
weblogarg1, weblogarg2, weblogarg3, weblogarg4, NULL);
|
|
fprintf(stderr, "%s: Error executing %s - %s\n", module_name,
|
|
weblogPath, strerror(errno));
|
|
perror("execlp");
|
|
close(pipe1[0]);
|
|
close(pipe2[1]);
|
|
fclose(fp);
|
|
|
|
/* exit by sending a SIGTERM to the httpd process (how to get the pid?)
|
|
* since at this point the pid file is outdated and only if we sleep for
|
|
* a while to allow httpd_main to put it's pid in the pidfile can we
|
|
* attempt to send it a SIGTERM for graceful shuttdown)
|
|
* so that everything is brought down - if you want to bring everything
|
|
* down!! Else continue with httpd without AFS authentication.
|
|
*/
|
|
/*#ifdef SHUTDOWN_IF_AFS_FAILS in afs_module.c */
|
|
if (shutdown) {
|
|
#define KILL_TIME_WAIT 1
|
|
#define MAX_KILL_ATTEMPTS 3
|
|
int attempts = 0;
|
|
fp = fopen(httpd_pid_fname, "r");
|
|
fscanf(fp, "%d", &childpid);
|
|
fclose(fp);
|
|
killagain:
|
|
sleep(KILL_TIME_WAIT);
|
|
if (kill(childpid, SIGTERM) == -1) {
|
|
if ((errno == ESRCH) && (attempts < MAX_KILL_ATTEMPTS)) {
|
|
attempts++;
|
|
fprintf(stderr,
|
|
"%s:SIGTERM to process:%d FAILED attempt:%d\nRetrying "
|
|
" for %d more attempts every %d seconds\n",
|
|
module_name, childpid, attempts,
|
|
(MAX_KILL_ATTEMPTS - attempts), KILL_TIME_WAIT);
|
|
goto killagain;
|
|
}
|
|
} else {
|
|
fprintf(stderr, "%s:Shutdown complete ...\n", module_name);
|
|
}
|
|
if (attempts >= MAX_KILL_ATTEMPTS) {
|
|
fprintf(stderr,
|
|
"%s:httpd is still running-AFS authentication will fail "
|
|
"because weblog startup failed\n", module_name);
|
|
}
|
|
exit(0);
|
|
} else {
|
|
fprintf(stderr,
|
|
"%s:httpd running - AFS Authentication will not work! "
|
|
"Weblog startup failure", module_name);
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Returns HTTP error codes based on the result of a stat error
|
|
*/
|
|
static int
|
|
sort_stat_error(request_rec * r)
|
|
{
|
|
int status = 0;
|
|
switch (errno) {
|
|
case ENOENT:
|
|
status = HTTP_NOT_FOUND;
|
|
break;
|
|
|
|
case EACCES:
|
|
status = FORBIDDEN;
|
|
break;
|
|
|
|
case ENOLINK:
|
|
status = HTTP_NOT_FOUND;
|
|
break;
|
|
|
|
case ENODEV:
|
|
status = HTTP_NOT_FOUND;
|
|
break;
|
|
|
|
default:
|
|
{
|
|
char error[ERRSTRLEN];
|
|
sprintf(error, "%s: stat error: %s", module_name,
|
|
strerror(errno));
|
|
status = SERVER_ERROR;
|
|
LOG_REASON(error, r->uri, r);
|
|
break;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* recursively stats the path to see where we're going wrong and
|
|
* chops off the path_info part of it -
|
|
* Returns OK or an HTTP status code
|
|
* Called if we get a ENOTDIR from the first stab at statting the
|
|
* entire path - so we assume that we have some PATH_INFO and try to
|
|
* chop it off the end and return the path itself
|
|
* Side effects on request_rec
|
|
- sets the filename field
|
|
- sets the path_info field
|
|
*/
|
|
static int
|
|
remove_path_info(request_rec * r, char *path, struct stat *buf)
|
|
{
|
|
char *cp;
|
|
char *end;
|
|
char *last_cp = NULL;
|
|
int rc = 0;
|
|
|
|
afsassert(r);
|
|
afsassert(path);
|
|
afsassert(buf);
|
|
|
|
end = &path[strlen(path)];
|
|
|
|
/* Advance over trailing slashes ... NOT part of filename */
|
|
for (cp = end; cp > path && cp[-1] == '/'; --cp)
|
|
continue;
|
|
|
|
while (cp > path) {
|
|
/* See if the pathname ending here exists... */
|
|
*cp = '\0';
|
|
errno = 0;
|
|
rc = stat(path, buf);
|
|
if (cp != end)
|
|
*cp = '/';
|
|
|
|
if (!rc) {
|
|
if (S_ISDIR(buf->st_mode) && last_cp) {
|
|
buf->st_mode = 0; /* No such file... */
|
|
cp = last_cp;
|
|
}
|
|
r->path_info = pstrdup(r->pool, cp);
|
|
*cp = '\0';
|
|
return OK;
|
|
}
|
|
|
|
else if (errno == ENOENT || errno == ENOTDIR) {
|
|
last_cp = cp;
|
|
while (--cp > path && *cp != '/')
|
|
continue;
|
|
while (cp > path && cp[-1] == '/')
|
|
--cp;
|
|
} else if (errno != EACCES) {
|
|
/*
|
|
* this would be really bad since we checked the entire path
|
|
* earlier and got ENOTDIR instead of EACCES - so why are we getting
|
|
* it now? Anyway, we ought to return FORBIDDEN
|
|
*/
|
|
return HTTP_FORBIDDEN;
|
|
}
|
|
}
|
|
r->filename = pstrdup(r->pool, path);
|
|
return OK;
|
|
}
|
|
|
|
/*
|
|
* Checks to see if actual access to the URL is permitted or not
|
|
* stats the URI first, if failure returns FORBIDDEN, if allowed then
|
|
* checks to see if it is a file, dir or LINK (TEST), and accordingly does more
|
|
*/
|
|
static int
|
|
can_access(request_rec * r)
|
|
{
|
|
int rc;
|
|
char *doc_root = (char *)DOCUMENT_ROOT(r);
|
|
struct stat buf;
|
|
char path[MAX_STRING_LEN];
|
|
|
|
afsassert(r->uri);
|
|
afsassert(doc_root);
|
|
|
|
if (r->filename) {
|
|
afslog(10, ("%s: Found r->filename:%s", module_name, r->filename));
|
|
sprintf(path, "%s", r->filename);
|
|
} else {
|
|
afslog(10,
|
|
("%s: Composing path from doc_root:%s and r->uri:%s",
|
|
module_name, doc_root, r->uri));
|
|
sprintf(path, "%s/%s", doc_root, r->uri);
|
|
afslog(10, ("%s: Path:%s", module_name, path));
|
|
}
|
|
rc = stat(path, &buf);
|
|
if (rc == -1) {
|
|
afslog(2,
|
|
("%s: pid:%d\tpath:%s\tstat error:%s", module_name, getpid(),
|
|
path, strerror(errno)));
|
|
|
|
/*
|
|
* if the requested method is an HTTP PUT and the file does not
|
|
* exist then well, we'll get a stat error but we shouldn't return
|
|
* an error - we should try and open the file in append mode and then
|
|
* see if we get a permission denied error
|
|
*/
|
|
if ((strncmp(r->method, "PUT", 3) == 0) && (errno == ENOENT)) {
|
|
FILE *f = fopen(path, "a");
|
|
if (f == NULL) {
|
|
if (errno == EACCES) {
|
|
afslog(2,
|
|
("%s: Either AFS acls or other permissions forbid write"
|
|
" access to file %s for user %s", module_name,
|
|
path,
|
|
r->connection->user ? r->connection->
|
|
user : "UNKNOWN"));
|
|
return FORBIDDEN;
|
|
} else {
|
|
log_reason
|
|
("afs_module: Error checking file for PUT method",
|
|
r->uri, r);
|
|
return SERVER_ERROR;
|
|
}
|
|
}
|
|
} else if (errno == ENOTDIR) {
|
|
/*
|
|
* maybe the special case of CGI PATH_INFO to be translated to
|
|
* PATH_TRANSLATED - check each component of this path
|
|
* and stat it to see what portion of the url is actually
|
|
* the path and discard the rest for our purposes.
|
|
*/
|
|
rc = remove_path_info(r, path, &buf);
|
|
afslog(10,
|
|
("%s:remove_path_info returned %d path:%s", module_name,
|
|
rc, path));
|
|
if (rc)
|
|
return rc;
|
|
} else {
|
|
return sort_stat_error(r);
|
|
}
|
|
}
|
|
/*
|
|
* If we get here then we have something - either a file or a directory
|
|
*/
|
|
else {
|
|
if (S_IFREG == (buf.st_mode & S_IFMT)) {
|
|
/* regular file */
|
|
FILE *f;
|
|
char permissions[] = { 'r', '\0', '\0', '\0' }; /* room for +, etc... */
|
|
|
|
if ((strncmp(r->method, "PUT", 3) == 0)) {
|
|
strcpy(permissions, "a");
|
|
}
|
|
if (!(f = fopen(path, permissions))) {
|
|
if (errno == EACCES) {
|
|
afslog(2,
|
|
("%s: Either AFS acls or other permissions"
|
|
" forbid access to file %s for user %s",
|
|
module_name, path,
|
|
r->connection->user ? r->connection->
|
|
user : "UNKNOWN"));
|
|
return FORBIDDEN;
|
|
} else {
|
|
char error[ERRSTRLEN];
|
|
sprintf(error,
|
|
"%s: Error checking file %s for permissions:%s",
|
|
module_name, path, strerror(errno));
|
|
log_reason(error, r->uri, r);
|
|
return SERVER_ERROR;
|
|
}
|
|
}
|
|
fclose(f);
|
|
return OK;
|
|
}
|
|
if (S_IFDIR == (buf.st_mode & S_IFMT)) {
|
|
/* path is a directory */
|
|
|
|
if (r->uri[strlen(r->uri) - 1] != '/') {
|
|
/* if we don't have a trailing slash, return REDIRECT */
|
|
char *ifile;
|
|
if (r->args != NULL) {
|
|
ifile =
|
|
PSTRCAT(r->pool, escape_uri(r->pool, r->uri), "/",
|
|
"?", r->args, NULL);
|
|
} else {
|
|
ifile =
|
|
PSTRCAT(r->pool, escape_uri(r->pool, r->uri), "/",
|
|
NULL);
|
|
}
|
|
TABLE_SET(r->headers_out, "Location", ifile);
|
|
return REDIRECT;
|
|
} else {
|
|
DIR *d;
|
|
if (!(d = opendir(path))) {
|
|
if (errno == EACCES) {
|
|
afslog(2,
|
|
("%s: Error accessing dir %s - %s",
|
|
module_name, path, strerror(errno)));
|
|
return FORBIDDEN;
|
|
} else {
|
|
char error[ERRSTRLEN];
|
|
sprintf(error, "%s: opendir failed with Error:%s",
|
|
module_name, strerror(errno));
|
|
log_reason(error, r, r->uri);
|
|
return SERVER_ERROR;
|
|
}
|
|
}
|
|
closedir(d);
|
|
return OK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Logs requests which led to a FORBIDDEN return code provided a token
|
|
* existed. Added feature (SetAFSAccessLog) in beta2
|
|
*/
|
|
static int
|
|
log_Access_Error(request_rec * r)
|
|
{
|
|
if (FIND_LINKED_MODULE("afs_module.c") != NULL) {
|
|
char err_msg[1024];
|
|
int rc = 0;
|
|
int len = 0;
|
|
extern int logfd;
|
|
|
|
if (r->connection->user)
|
|
sprintf(err_msg,
|
|
"[%s] AFS ACL's deny permission to "
|
|
"user: %s for URI:%s\n", GET_TIME(), r->connection->user,
|
|
r->uri);
|
|
else
|
|
sprintf(err_msg,
|
|
"[%s] AFS ACL's deny permission to user - for URI:%s\n",
|
|
GET_TIME(), r->uri);
|
|
|
|
len = strlen(err_msg);
|
|
rc = write(logfd, err_msg, len);
|
|
|
|
if (rc != len) {
|
|
afslog(0,
|
|
("%s: Error logging message:%s - %s", module_name, err_msg,
|
|
strerror(errno)));
|
|
return -1;
|
|
}
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The interface - hook to obtain an AFS token if needed based on the
|
|
* result of a stat (returned by can_access) or an open file
|
|
*/
|
|
int
|
|
afs_auth_internal(request_rec * r, char *cell)
|
|
{
|
|
afsassert(r);
|
|
afsassert(r->uri);
|
|
afsassert(cell);
|
|
if (FIND_LINKED_MODULE("afs_module.c") != NULL) {
|
|
int rc, status;
|
|
char *type;
|
|
static int haveToken = 1; /* assume that we always have a token */
|
|
extern int logAccessErrors;
|
|
|
|
/*
|
|
* Get afs_authtype directive value for that directory or location
|
|
*/
|
|
type = (char *)get_afsauthtype(r);
|
|
|
|
/*
|
|
* UserDir (tilde) support
|
|
*/
|
|
#ifndef APACHE_1_3
|
|
if (FIND_LINKED_MODULE("mod_userdir.c") != NULL) {
|
|
rc = translate_userdir(r);
|
|
if ((rc != OK) && (rc != DECLINED)) {
|
|
LOG_REASON("afs_module: Failure while translating userdir",
|
|
r->uri, r);
|
|
return rc;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
afslog(20, ("%s: pid:%d r->uri:%s", module_name, getpid(), r->uri));
|
|
if (type)
|
|
afslog(20, ("%s: AFSAuthType: %s", module_name, type));
|
|
else
|
|
afslog(20, ("%s: AFSAuthType NULL", module_name));
|
|
|
|
/* if AuthType is not AFS, then unlog any existing tokens and DECLINE */
|
|
if (type == NULL) {
|
|
if (haveToken)
|
|
unlog();
|
|
return DECLINED;
|
|
}
|
|
|
|
if ((strcasecmp(type, AFS_AUTHTYPE))
|
|
&& (strcasecmp(type, AFS_DFS_AUTHTYPE))) {
|
|
if (haveToken)
|
|
unlog();
|
|
afslog(10,
|
|
("%s: Error unknown AFSAuthType:%s returning DECLINED",
|
|
module_name, type));
|
|
return DECLINED;
|
|
}
|
|
|
|
if (cell)
|
|
status =
|
|
authenticateUser(r, cell, afs_str->cacheExpiration, type);
|
|
else
|
|
status =
|
|
authenticateUser(r, afs_str->defaultCell,
|
|
afs_str->cacheExpiration, type);
|
|
|
|
if (status != OK) {
|
|
afslog(10, ("%s: Returning status %d", module_name, status));
|
|
return status;
|
|
}
|
|
|
|
/* can we access this URL? */
|
|
rc = can_access(r);
|
|
|
|
if (rc == OK) {
|
|
return DECLINED;
|
|
}
|
|
|
|
if (rc == REDIRECT) {
|
|
return REDIRECT;
|
|
}
|
|
|
|
if (rc == FORBIDDEN) {
|
|
rc = forbToAuthReqd(r);
|
|
if (rc == FORBIDDEN) {
|
|
if (logAccessErrors) {
|
|
log_Access_Error(r);
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
return DECLINED;
|
|
}
|
|
}
|