environ = cleanenv;
}
+/* Pool account functions */
+
+
+#include <utime.h>
+#include <errno.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <sys/types.h>
+
+/******************************************************************************
+Function: mapdir_otherlink
+Description:
+ find another link in map directory to the same inode as firstlink
+ and change the modification time of firstlink to now (so that we
+ always know when this pair was last used)
+
+Parameters:
+ firstlink, the filename of the link we already know
+
+Returns:
+ a pointer to the other link's filename (without path) or NULL if none
+ found (this is malloc'd and will need freeing)
+
+******************************************************************************/
+static char *mapdir_otherlink(char *mapdir, char *firstlink)
+{
+ int ret;
+ char *firstlinkpath, *otherlinkdup, *otherlinkpath;
+ struct dirent *mapdirentry;
+ DIR *mapdirstream;
+ struct stat statbuf;
+ ino_t firstinode;
+
+ firstlinkpath = malloc(strlen(mapdir) + 2 + strlen(firstlink));
+ sprintf(firstlinkpath, "%s/%s", mapdir, firstlink);
+ ret = stat(firstlinkpath, &statbuf);
+ free(firstlinkpath);
+ if (ret != 0) return NULL;
+ if (statbuf.st_nlink != 2) return NULL;
+
+ firstinode = statbuf.st_ino; /* save for comparisons */
+
+ mapdirstream = opendir(mapdir);
+
+ if (mapdirstream != NULL)
+ {
+ while ((mapdirentry = readdir(mapdirstream)) != NULL)
+ {
+ if (strcmp(mapdirentry->d_name, firstlink) == 0) continue;
+
+ otherlinkpath = malloc(strlen(mapdir) + 2 +
+ strlen(mapdirentry->d_name));
+ sprintf(otherlinkpath, "%s/%s", mapdir,
+ mapdirentry->d_name);
+
+ ret = stat(otherlinkpath, &statbuf);
+ if ((ret == 0) && (statbuf.st_ino == firstinode))
+ {
+ utime(otherlinkpath, (struct utimbuf *) NULL);
+ free(otherlinkpath);
+ otherlinkdup = strdup(mapdirentry->d_name);
+ closedir(mapdirstream);
+ return otherlinkdup;
+ }
+ else free(otherlinkpath);
+ }
+
+ closedir(mapdirstream);
+ }
+
+ return NULL;
+}
+
+/******************************************************************************
+Function: mapdir_urlencode
+Description:
+ Convert string to URL encoded and return pointer to the encoded
+ version, obtained through malloc. Calling routine must free
+ this. Here "URL encoded" means anything other than an isalnum()
+ goes to %HH where HH is its ascii value in hex; also A-Z => a-z
+ This name is suitable for filenames since no / or spaces.
+
+Parameters:
+ rawstring, the string to be converted
+
+Returns:
+ a pointer to the encoded string or NULL if the malloc failed
+
+******************************************************************************/
+static char *mapdir_urlencode(char *rawstring)
+{
+ int encodedchar = 0, rawchar = 0;
+ char * encodedstring;
+
+ encodedstring = (char *) malloc(3 * strlen(rawstring) + 1);
+
+ if (encodedstring == NULL) return (char *) NULL;
+
+ while (rawstring[rawchar] != '\0')
+ {
+ if (isalnum(rawstring[rawchar]))
+ {
+ encodedstring[encodedchar] = tolower(rawstring[rawchar]);
+ ++rawchar;
+ ++encodedchar;
+ }
+ else
+ {
+ sprintf(&encodedstring[encodedchar], "%%%02x",
+ rawstring[rawchar]);
+ ++rawchar;
+ encodedchar = encodedchar + 3;
+ }
+ }
+
+ encodedstring[encodedchar] = '\0';
+
+ return encodedstring;
+}
+
+/******************************************************************************
+Function: mapdir_newlease
+Description:
+ Search for an unleased local username to give to the X.509 DN or
+ directory key corresponding to encodedfilename, and then lease it.
+
+Parameters:
+ encodedfilename, URL-encoded X.509 DN or directory key to associate
+ with an unlease pool username
+
+Returns:
+ no return value
+******************************************************************************/
+
+void mapdir_newlease(char *mapdir, char *encodedkey)
+{
+ int ret;
+ char *userfilename, *encodedfilename;
+ struct dirent *mapdirentry;
+ DIR *mapdirstream;
+ struct stat statbuf;
+
+ encodedfilename = malloc(strlen(mapdir) + (size_t) 2 +
+ strlen(encodedkey));
+ sprintf(encodedfilename, "%s/%s", mapdir, encodedkey);
+
+ mapdirstream = opendir(mapdir);
+
+ while ((mapdirentry = readdir(mapdirstream)) != NULL)
+ {
+ /* we dont want any files that dont look like acceptable usernames */
+ if ((*(mapdirentry->d_name) == '%') ||
+ (strcmp(mapdirentry->d_name, "root") == 0)) continue;
+ else if (*(mapdirentry->d_name) == '.') continue;
+ else if (index(mapdirentry->d_name, '~') != NULL) continue;
+
+ userfilename = malloc(strlen(mapdir) + (size_t) 2 +
+ strlen(mapdirentry->d_name));
+ sprintf(userfilename, "%s/%s", mapdir, mapdirentry->d_name);
+ stat(userfilename, &statbuf);
+
+ if (statbuf.st_nlink == 1) /* this one isnt leased yet */
+ {
+ ret = link(userfilename, encodedfilename);
+ free(userfilename);
+ if (ret != 0)
+ {
+ /* link failed: this is probably because a VERY lucky
+ other process has obtained a lease for encodedfilename
+ while we were faffing around */
+ closedir(mapdirstream);
+ free(encodedfilename);
+ return;
+ }
+
+ stat(encodedfilename, &statbuf);
+ if (statbuf.st_nlink > 2)
+ {
+ /* two keys have grabbed the same username: back off */
+ unlink(encodedfilename);
+ continue;
+ }
+
+ closedir(mapdirstream);
+ free(encodedfilename);
+ return; /* link worked ok, so return */
+ }
+ else free(userfilename); /* already in use, try next one */
+ }
+
+ closedir(mapdirstream);
+ free(encodedfilename);
+ return; /* no unleased names left: give up */
+}
+
+/******************************************************************************
+Function: gridmapdir_userid
+Description:
+ This is equivalent to globus_gss_assist_gridmap but for the dynamic
+ user ids in the gridmapdir: maps a globusID to a local unix user id,
+ either one already leased, or calls gridmapdir_newlease() to obtain
+ a new lease. This is called by globus_gss_assist_gridmap if the
+ local user id in the static gridmap file begins . (for a dynamic id)
+
+Parameters:
+ globusidp, globus client name who requested authentication
+ usernameprefix, prefix of the local usernames which would
+ be acceptable (or "\0" )
+ *userid returned userid name for local system.
+
+Returns:
+
+ 0 on success
+ !=0 on failure
+
+******************************************************************************/
+
+
+
int GRSTexecGetMapping(char **target_uname, char **target_gname,
- char *map_x509dn)
+ char *mapdir, char *key)
{
- return 1;
+ char *encodedkey;
+
+ if (key[0] != '/') return 1; /* must be a proper X.509 DN or path */
+
+ encodedkey = mapdir_urlencode(key);
+log_err("encodedkey=%s\n", encodedkey);
+ *target_uname = mapdir_otherlink(mapdir, encodedkey);
+log_err("*target_uname=%s\n", *target_uname);
+
+ if (*target_uname == NULL) /* maybe no lease yet */
+ {
+ mapdir_newlease(mapdir, encodedkey);
+ /* try making a lease */
+
+ *target_uname = mapdir_otherlink(mapdir, encodedkey);
+ /* check if there is a now a lease - possibly made by someone else */
+
+ if (*target_uname == NULL)
+ {
+ free(encodedkey);
+ return 1; /* still no good */
+ }
+ }
+
+ free(encodedkey);
+
+// nasty hack for now
+*target_gname = strdup(*target_uname);
+
+ return 0;
+}
+
+void internal_server_error(void)
+{
+ /* use this when its probably an httpd.conf configuration error */
+
+ puts("Status: 500 Internal Server Error\n"
+ "Content-Type: text/html\n\n"
+ "<html><head><title>500 Internal Server Error</title></head>\n"
+ "<body><h1>Internal Server Error</h1></body></html>");
+}
+
+void forbidden_error(void)
+{
+ /* use this when unix file permissions/ownerships are probably wrong */
+
+ puts("Status: 403 Forbidden\n"
+ "Content-Type: text/html\n\n"
+ "<html><head><title>403 Forbidden</title></head>\n"
+ "<body><h1>Forbidden</h1></body></html>");
}
int main(int argc, char *argv[])
int userdir = 0; /* ~userdir flag */
uid_t uid; /* user information */
gid_t gid; /* target group placeholder */
+ uid_t httpd_uid; /* uid for AP_HTTPD_USER */
+ gid_t httpd_gid; /* uid for AP_HTTPD_GROUP */
char *mapping_type; /* suexec / X509DN / directory */
char *map_x509dn; /* DN to use as pool acct. key */
char *map_directory; /* directory as pool acct. key */
uid = getuid();
if ((pw = getpwuid(uid)) == NULL) {
log_err("crit: invalid uid: (%ld)\n", uid);
+ internal_server_error();
+ exit(102);
+ }
+ /*
+ * Check existence/validity of the GID of the user
+ * running this program. Error out if invalid.
+ */
+ gid = getgid();
+ if ((gr = getgrgid(gid)) == NULL) {
+ log_err("crit: invalid gid: (%ld)\n", gid);
+ internal_server_error();
exit(102);
}
/*
*/
if (argc < 4) {
log_err("too few arguments\n");
+ internal_server_error();
exit(101);
}
mapping_type = getenv("GRST_EXEC_MAPPING");
+log_err("mapping_type=%s\n",mapping_type);
if ((mapping_type == NULL) ||
(mapping_type[0] == '\0') ||
(strcasecmp(mapping_type, "suexec") == 0))
}
else if (strcasecmp(mapping_type, "X509DN") == 0)
{
+log_err("X509DN mapping type\n");
map_x509dn = getenv("SSL_CLIENT_S_DN");
if (map_x509dn == NULL)
{
log_err("No SSL_CLIENT_S_DN despite X509DN mapping\n");
+ internal_server_error();
exit(151);
}
- if (GRSTexecGetMapping(&target_uname, &target_gname, map_x509dn)
+ if (GRSTexecGetMapping(&target_uname, &target_gname,
+ GRST_EXECMAPDIR, map_x509dn)
!= 0)
{
log_err("GRSTexecGetMapping() failed mapping \"%s\"\n",
map_x509dn);
+ internal_server_error();
exit(152);
}
}
if (map_directory == NULL)
{
log_err("No GRST_EXEC_MAP_DIR despite directory mapping\n");
+ internal_server_error();
exit(153);
}
- if (GRSTexecGetMapping(&target_uname, &target_gname, map_directory)
+ if (GRSTexecGetMapping(&target_uname, &target_gname,
+ GRST_EXECMAPDIR, map_directory)
!= 0)
{
log_err("GRSTexecGetMapping() failed mapping \"%s\"\n",
map_directory);
+ internal_server_error();
exit(154);
}
}
else
{
log_err("mapping type \"%s\" not recognised\n", mapping_type);
+ internal_server_error();
exit(155);
}
/* User name comparisons are case insensitive on BS2000/OSD */
if (strcasecmp(AP_HTTPD_USER, pw->pw_name)) {
log_err("user mismatch (%s instead of %s)\n", pw->pw_name, AP_HTTPD_USER);
+ internal_server_error();
+ exit(103);
+ }
+ /* User name comparisons are case insensitive on BS2000/OSD */
+ if (strcasecmp(AP_HTTPD_GROUP, gr->gr_name)) {
+ log_err("group mismatch (%s instead of %s)\n", gr->gr_name, AP_HTTPD_GROUP);
+ internal_server_error();
exit(103);
}
#else /*_OSD_POSIX*/
if (strcmp(AP_HTTPD_USER, pw->pw_name)) {
log_err("user mismatch (%s instead of %s)\n", pw->pw_name, AP_HTTPD_USER);
+ internal_server_error();
+ exit(103);
+ }
+ if (strcmp(AP_HTTPD_GROUP, gr->gr_name)) {
+ log_err("group mismatch (%s instead of %s)\n", gr->gr_name, AP_HTTPD_GROUP);
+ internal_server_error();
exit(103);
}
#endif /*_OSD_POSIX*/
+ /* Since they match (via name) save these for later */
+
+ httpd_uid = uid;
+ httpd_gid = gid;
+
/*
* Check for a leading '/' (absolute path) in the command to be executed,
* or attempts to back up out of the current directory,
if ((cmd[0] == '/') || (!strncmp(cmd, "../", 3))
|| (strstr(cmd, "/../") != NULL)) {
log_err("invalid command (%s)\n", cmd);
+ internal_server_error();
exit(104);
}
if (strspn(target_uname, "1234567890") != strlen(target_uname)) {
if ((pw = getpwnam(target_uname)) == NULL) {
log_err("invalid target user name: (%s)\n", target_uname);
+ internal_server_error();
exit(105);
}
}
else {
if ((pw = getpwuid(atoi(target_uname))) == NULL) {
log_err("invalid target user id: (%s)\n", target_uname);
+ internal_server_error();
exit(121);
}
}
if (strspn(target_gname, "1234567890") != strlen(target_gname)) {
if ((gr = getgrnam(target_gname)) == NULL) {
log_err("invalid target group name: (%s)\n", target_gname);
+ internal_server_error();
exit(106);
}
gid = gr->gr_gid;
case -1: /* Error */
log_err("failed to setup bs2000 environment for user %s: %s\n",
target_uname, strerror(errno));
+ internal_server_error();
exit(150);
case 0: /* Child */
break;
if (WIFSIGNALED(status)) {
kill (getpid(), WTERMSIG(status));
}
+ internal_server_error();
exit(WEXITSTATUS(status));
}
}
*/
if ((uid == 0) || (uid < AP_UID_MIN)) {
log_err("cannot run as forbidden uid (%d/%s)\n", uid, cmd);
+ internal_server_error();
exit(107);
}
*/
if ((gid == 0) || (gid < AP_GID_MIN)) {
log_err("cannot run as forbidden gid (%d/%s)\n", gid, cmd);
+ internal_server_error();
exit(108);
}
*/
if (((setgid(gid)) != 0) || (initgroups(actual_uname, gid) != 0)) {
log_err("failed to setgid (%ld: %s)\n", gid, cmd);
+ internal_server_error();
exit(109);
}
*/
if ((setuid(uid)) != 0) {
log_err("failed to setuid (%ld: %s)\n", uid, cmd);
+ internal_server_error();
exit(110);
}
*/
if (getcwd(cwd, AP_MAXPATH) == NULL) {
log_err("cannot get current working directory\n");
+ internal_server_error();
exit(111);
}
+#if 0
if (userdir) {
if (((chdir(target_homedir)) != 0) ||
((chdir(AP_USERDIR_SUFFIX)) != 0) ||
((getcwd(dwd, AP_MAXPATH)) == NULL) ||
((chdir(cwd)) != 0)) {
log_err("cannot get docroot information (%s)\n", target_homedir);
+ internal_server_error();
exit(112);
}
}
((getcwd(dwd, AP_MAXPATH)) == NULL) ||
((chdir(cwd)) != 0)) {
log_err("cannot get docroot information (%s)\n", AP_DOC_ROOT);
+ internal_server_error();
exit(113);
}
}
if ((strncmp(cwd, dwd, strlen(dwd))) != 0) {
log_err("command not in docroot (%s/%s)\n", cwd, cmd);
+ internal_server_error();
exit(114);
}
+#endif
/*
* Stat the cwd and verify it is a directory, or error out.
*/
if (((lstat(cwd, &dir_info)) != 0) || !(S_ISDIR(dir_info.st_mode))) {
log_err("cannot stat directory: (%s)\n", cwd);
+ internal_server_error();
exit(115);
}
*/
if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP)) {
log_err("directory is writable by others: (%s)\n", cwd);
+ forbidden_error();
exit(116);
}
*/
if (((lstat(cmd, &prg_info)) != 0) || (S_ISLNK(prg_info.st_mode))) {
log_err("cannot stat program: (%s)\n", cmd);
+ forbidden_error();
exit(117);
}
/*
* Error out if the program is writable by others.
*/
- if ((prg_info.st_mode & S_IWOTH) || (prg_info.st_mode & S_IWGRP)) {
+ if (prg_info.st_mode & S_IWOTH) {
log_err("file is writable by others: (%s/%s)\n", cwd, cmd);
+ forbidden_error();
exit(118);
}
*/
if ((prg_info.st_mode & S_ISUID) || (prg_info.st_mode & S_ISGID)) {
log_err("file is either setuid or setgid: (%s/%s)\n", cwd, cmd);
+ forbidden_error();
exit(119);
}
/*
* Error out if the target name/group is different from
- * the name/group of the cwd or the program.
+ * the name/group of the cwd or the program AND the name/group
+ * of the cwd and program are not the AP_HTTPD_USER/AP_HTTPD_GROUP
+ * AND the name/group of the cwd and program are not root
*/
- if ((uid != dir_info.st_uid) ||
- (gid != dir_info.st_gid) ||
- (uid != prg_info.st_uid) ||
- (gid != prg_info.st_gid)) {
- log_err("target uid/gid (%ld/%ld) mismatch "
- "with directory (%ld/%ld) or program (%ld/%ld)\n",
- uid, gid,
+ if (((uid != dir_info.st_uid) && (httpd_uid != dir_info.st_uid)
+ && (0 != dir_info.st_uid)) ||
+ ((gid != dir_info.st_gid) && (httpd_gid != dir_info.st_gid)
+ && (0 != dir_info.st_gid)) ||
+ ((uid != prg_info.st_uid) && (httpd_uid != prg_info.st_uid)
+ && (0 != prg_info.st_uid)) ||
+ ((gid != prg_info.st_gid) && (httpd_gid != prg_info.st_gid)
+ && (0 != prg_info.st_gid)))
+ {
+ log_err("target (%ld/%ld) or %s (%ld/%ld) or root (0/0) uid/gid "
+ "mismatch with directory (%ld/%ld) or program (%ld/%ld)\n",
+ uid, gid, AP_HTTPD_USER, httpd_uid, httpd_gid,
dir_info.st_uid, dir_info.st_gid,
prg_info.st_uid, prg_info.st_gid);
+ forbidden_error();
exit(120);
- }
+ }
/*
* Error out if the program is not executable for the user.
* Otherwise, she won't find any error in the logs except for
*/
if (!(prg_info.st_mode & S_IXUSR)) {
log_err("file has no execute permission: (%s/%s)\n", cwd, cmd);
+ forbidden_error();
exit(121);
}
* Oh well, log the failure and error out.
*/
log_err("(%d)%s: exec failed (%s)\n", errno, strerror(errno), cmd);
+ internal_server_error();
exit(255);
}