Commit bf97b597 authored by Mike Hibler's avatar Mike Hibler

Make sure upload/download server run with proper group list.

Attempts to load an image from a subgroup of a project didn't work because
I was only setting the primary group ID based on the node's experiment's
project. Now we use setgroups() to establish all groups that the swapper
of the experiment belongs to.
parent 5ab6fad1
......@@ -53,7 +53,8 @@ struct emulab_ha_extra_info {
char *eid; /* experiment */
char *sname; /* swapper user name */
int suid; /* swapper's unix uid */
int egid; /* experiment's unix gid */
gid_t sgids[MAXGIDS]; /* swapper's unix gids */
int ngids; /* number of gids */
};
static char *MC_BASEADDR = FRISEBEEMCASTADDR;
......@@ -547,7 +548,7 @@ allow_stddirs(char *imageid,
* that are accessible.
*/
if (imageid == NULL) {
int ni, i;
int ni, i, j;
size_t ns;
char *dirs[8];
......@@ -579,7 +580,9 @@ allow_stddirs(char *imageid,
} else
ci->sig = NULL;
ci->uid = ei->suid;
ci->gid = ei->egid;
for (j = 0; j < ei->ngids; j++)
ci->gids[j] = ei->sgids[j];
ci->ngids = ei->ngids;
set_put_values(put, i);
ci->extra = NULL;
}
......@@ -614,7 +617,9 @@ allow_stddirs(char *imageid,
} else
ci->sig = NULL;
ci->uid = ei->suid;
ci->gid = ei->egid;
for (j = 0; j < ei->ngids; j++)
ci->gids[j] = ei->sgids[j];
ci->ngids = ei->ngids;
set_get_values(get, i);
ci->extra = NULL;
}
......@@ -651,6 +656,7 @@ allow_stddirs(char *imageid,
(fdir = isindir(gdir, fpath)) ||
(fdir = isindir(udir, fpath)) ||
(fdir = isindir(scdir, fpath)))) {
int j;
assert(put->imageinfo == NULL);
put->imageinfo = mymalloc(sizeof(struct config_imageinfo));
......@@ -668,7 +674,9 @@ allow_stddirs(char *imageid,
} else
ci->sig = NULL;
ci->uid = ei->suid;
ci->gid = ei->egid;
for (j = 0; j < ei->ngids; j++)
ci->gids[j] = ei->sgids[j];
ci->ngids = ei->ngids;
set_put_values(put, 0);
ci->extra = NULL;
}
......@@ -678,6 +686,7 @@ allow_stddirs(char *imageid,
(fdir = isindir(gdir, fpath)) ||
(fdir = isindir(scdir, fpath)) ||
(fdir = isindir(udir, fpath)))) {
int j;
assert(get->imageinfo == NULL);
get->imageinfo = mymalloc(sizeof(struct config_imageinfo));
......@@ -695,7 +704,9 @@ allow_stddirs(char *imageid,
} else
ci->sig = NULL;
ci->uid = ei->suid;
ci->gid = ei->egid;
for (j = 0; j < ei->ngids; j++)
ci->gids[j] = ei->sgids[j];
ci->ngids = ei->ngids;
set_get_values(get, 0);
ci->extra = NULL;
}
......@@ -789,9 +800,10 @@ emulab_get_host_authinfo(struct in_addr *req, struct in_addr *host,
MYSQL_RES *res;
MYSQL_ROW row;
char *node, *proxy, *role = NULL;
int i, nrows;
int i, j, nrows;
char *wantpid = NULL, *wantname = NULL;
struct config_host_authinfo *get = NULL, *put = NULL;
struct emulab_ha_extra_info *ei = NULL;
if (getp == NULL && putp == NULL)
return 0;
......@@ -935,15 +947,14 @@ emulab_get_host_authinfo(struct in_addr *req, struct in_addr *host,
/*
* Find the pid/gid to which the node is currently assigned
* and also the unix uid of the swapper and unix gid for the
* experiment in case we need to run a server process.
* and also the unix uid of the swapper of the experiment and
* their gids in case we need to run a server process.
*/
res = mydb_query("SELECT e.pid,e.gid,r.eid,u.uid,u.unix_uid,g.unix_gid"
" FROM reserved AS r,experiments AS e,"
" users AS u,groups as g"
res = mydb_query("SELECT e.pid,e.gid,r.eid,u.uid,u.unix_uid"
" FROM reserved AS r,experiments AS e,users AS u"
" WHERE r.pid=e.pid AND r.eid=e.eid"
" AND e.swapper_idx=u.uid_idx AND e.gid=g.gid"
" AND r.node_id='%s'", 6, node);
" AND e.swapper_idx=u.uid_idx"
" AND r.node_id='%s'", 5, node);
assert(res != NULL);
/* Node is free */
......@@ -956,7 +967,7 @@ emulab_get_host_authinfo(struct in_addr *req, struct in_addr *host,
}
row = mysql_fetch_row(res);
if (!row[0] || !row[1] || !row[2] || !row[3] || !row[4] || !row[5]) {
if (!row[0] || !row[1] || !row[2] || !row[3] || !row[4]) {
error("config_host_authinfo: null pid/gid/eid/uname/uid!?");
mysql_free_result(res);
emulab_free_host_authinfo(get);
......@@ -966,27 +977,57 @@ emulab_get_host_authinfo(struct in_addr *req, struct in_addr *host,
}
if (get != NULL) {
struct emulab_ha_extra_info *ei = mymalloc(sizeof *ei);
ei = mymalloc(sizeof *ei);
ei->pid = mystrdup(row[0]);
ei->gid = mystrdup(row[1]);
ei->eid = mystrdup(row[2]);
ei->sname = mystrdup(row[3]);
ei->suid = atoi(row[4]);
ei->egid = atoi(row[5]);
ei->ngids = 0;
get->extra = ei;
}
if (put != NULL) {
struct emulab_ha_extra_info *ei = mymalloc(sizeof *ei);
ei = mymalloc(sizeof *ei);
ei->pid = mystrdup(row[0]);
ei->gid = mystrdup(row[1]);
ei->eid = mystrdup(row[2]);
ei->sname = mystrdup(row[3]);
ei->suid = atoi(row[4]);
ei->egid = atoi(row[5]);
ei->ngids = 0;
put->extra = ei;
}
mysql_free_result(res);
/*
* Get all unix groups the swapper is a member of.
* XXX this does not include the extra non-Emulab managed groups
* from unixgroup_membership.
*/
res = mydb_query("SELECT g.unix_gid"
" FROM groups AS g,group_membership AS gm"
" WHERE g.gid_idx=gm.gid_idx AND gm.uid='%s'",
1, ei->sname);
assert(res != NULL);
nrows = mysql_num_rows(res);
if (nrows > MAXGIDS)
warning("User '%s' in more than %d groups, truncating list",
ei->sname, MAXGIDS);
for (i = 0; i < nrows; i++) {
row = mysql_fetch_row(res);
if (get != NULL) {
ei = (struct emulab_ha_extra_info *)get->extra;
ei->sgids[i] = atoi(row[0]);
ei->ngids++;
}
if (put != NULL) {
ei = (struct emulab_ha_extra_info *)put->extra;
ei->sgids[i] = atoi(row[0]);
ei->ngids++;
}
}
mysql_free_result(res);
/*
* If imageid is specified and starts with a '/', it is a path.
* In this case we compare that path to those accessible by
......@@ -1091,7 +1132,14 @@ emulab_get_host_authinfo(struct in_addr *req, struct in_addr *host,
} else
ci->sig = NULL;
ci->uid = ei->suid;
ci->gid = ei->egid;
/*
* XXX note that we don't really need all of the
* swapper's GIDs here, we could just include the
* project and any group GID for the image.
*/
for (j = 0; j < ei->ngids; j++)
ci->gids[j] = ei->sgids[j];
ci->ngids = ei->ngids;
set_put_values(put, put->numimages);
ii = mymalloc(sizeof *ii);
ii->DB_imageid = atoi(row[4]);
......@@ -1161,7 +1209,14 @@ emulab_get_host_authinfo(struct in_addr *req, struct in_addr *host,
} else
ci->sig = NULL;
ci->uid = ei->suid;
ci->gid = ei->egid;
/*
* XXX note that we don't really need all of the
* swapper's GIDs here, we could just include the
* project and any group GID for the image.
*/
for (j = 0; j < ei->ngids; j++)
ci->gids[j] = ei->sgids[j];
ci->ngids = ei->ngids;
set_get_values(get, get->numimages);
ii = mymalloc(sizeof *ii);
ii->DB_imageid = atoi(row[4]);
......
......@@ -352,7 +352,8 @@ allow_stddirs(char *imageid,
ci->flags |= CONFIG_SIG_ISMTIME;
} else
ci->sig = NULL;
ci->uid = ci->gid = -1;
ci->uid = NOUID;
ci->ngids = 0;
set_put_values(put, i);
ci->extra = NULL;
}
......@@ -382,7 +383,8 @@ allow_stddirs(char *imageid,
ci->flags |= CONFIG_SIG_ISMTIME;
} else
ci->sig = NULL;
ci->uid = ci->gid = -1;
ci->uid = NOUID;
ci->ngids = 0;
set_get_values(get, i);
ci->extra = NULL;
}
......@@ -575,7 +577,8 @@ null_get_host_authinfo(struct in_addr *req, struct in_addr *host,
ci->flags |= CONFIG_SIG_ISMTIME;
} else
ci->sig = NULL;
ci->uid = ci->gid = -1;
ci->uid = NOUID;
ci->ngids = 0;
set_put_values(put, 0);
ci->extra = NULL;
}
......@@ -595,7 +598,8 @@ null_get_host_authinfo(struct in_addr *req, struct in_addr *host,
ci->flags |= CONFIG_SIG_ISMTIME;
} else
ci->sig = NULL;
ci->uid = ci->gid = -1;
ci->uid = NOUID;
ci->ngids = 0;
set_get_values(get, 0);
ci->extra = NULL;
}
......
#include <netinet/in.h>
#include <arpa/inet.h>
/*
* Maximum groups for a server process.
* We don't use NGROUPS_MAX because it is huge on Linux (64K).
*/
#define MAXGIDS (16+1)
#define NOUID (-1)
/*
* Config info for a single image
* XXX needs to be extended for REs.
......@@ -12,7 +20,8 @@ struct config_imageinfo {
void *sig; /* signature of image */
int flags; /* */
int uid; /* UID to run server process as */
int gid; /* GID to run server process as */
gid_t gids[MAXGIDS]; /* GIDs to run server process as */
int ngids; /* number of valid GIDs */
char *get_options; /* command line options for GET server */
int get_methods; /* allowed GET transfer mechanisms */
int get_timeout; /* max time to allow GET server to run */
......
......@@ -9,8 +9,6 @@
* handler processes.
*
* TODO:
* - FIX SOON: child servers are started with the single primary gid of the
* user; we need to ferret out the whole group list and set that
* - timeouts for child frisbee processes
* - record the state of running frisbeeds in persistant store so that
* they can be restarted if we die and restart
......@@ -45,14 +43,15 @@ static void get_options(int argc, char **argv);
static int makesocket(int portnum, struct in_addr *ifip, int *tcpsockp);
static void handle_request(int sock);
static int reapchildren(int apid, int *status);
static char * gidstr(int ngids, gid_t gids[]);
static int daemonize = 1;
int debug = 0;
static int dumpconfig = 0;
static int onlymethods = (MS_METHOD_UNICAST|MS_METHOD_MULTICAST);
static int parentmethods = (MS_METHOD_UNICAST|MS_METHOD_MULTICAST);
static int myuid = -1;
static int mygid = -1;
static int myuid = NOUID;
static int mygid = NOUID;
/*
* For recursively GETing images:
......@@ -212,7 +211,8 @@ struct childinfo {
int pid;
char *pidfile;
int uid; /* UID to run child as */
int gid; /* GID to run child as */
gid_t gids[MAXGIDS]; /* GID to run child as */
int ngids; /* number of GIDs */
int retries; /* # times to try starting up child */
int timeout; /* max runtime (sec) for child */
in_addr_t servaddr; /* -S arg */
......@@ -283,6 +283,7 @@ static struct config_imageinfo *
copy_imageinfo(struct config_imageinfo *ii)
{
struct config_imageinfo *nii;
int i;
if ((nii = calloc(1, sizeof *nii)) == NULL)
goto fail;
......@@ -296,7 +297,9 @@ copy_imageinfo(struct config_imageinfo *ii)
goto fail;
nii->flags = ii->flags;
nii->uid = ii->uid;
nii->gid = ii->gid;
for (i = 0; i < ii->ngids; i++)
nii->gids[i] = ii->gids[i];
nii->ngids = ii->ngids;
if (ii->get_options &&
(nii->get_options = strdup(ii->get_options)) == NULL)
goto fail;
......@@ -906,10 +909,13 @@ handle_get(int sock, struct sockaddr_in *sip, struct sockaddr_in *cip,
}
in.s_addr = htonl(ci->addr);
log("%s: started %s server on %s:%d (pid %d, ugid %d/%d, timo %ds)",
log("%s: started %s server on %s:%d (pid %d, timo %ds)",
imageid, GetMSMethods(ci->method),
inet_ntoa(in), ci->port, ci->pid, ci->uid, ci->gid,
ci->timeout);
inet_ntoa(in), ci->port, ci->pid, ci->timeout);
if (debug) {
log(" uid: %d, gids: %s",
ci->uid, gidstr(ci->ngids, ci->gids));
}
/*
* Watch for an immediate death so we don't tell our client
......@@ -938,13 +944,16 @@ handle_get(int sock, struct sockaddr_in *sip, struct sockaddr_in *cip,
}
in.s_addr = htonl(ci->addr);
if (wantstatus)
log("%s: STATUS is running %s on %s:%d (pid %d, ugid %d/%d)",
log("%s: STATUS is running %s on %s:%d (pid %d)",
imageid, GetMSMethods(ci->method),
inet_ntoa(in), ci->port, ci->pid, ci->uid, ci->gid);
inet_ntoa(in), ci->port, ci->pid);
else
log("%s: %s server already running on %s:%d (pid %d, ugid %d/%d)",
log("%s: %s server already running on %s:%d (pid %d)",
imageid, GetMSMethods(ci->method),
inet_ntoa(in), ci->port, ci->pid, ci->uid, ci->gid);
inet_ntoa(in), ci->port, ci->pid);
if (debug)
log(" uid: %d, gids: %s",
ci->uid, gidstr(ci->ngids, ci->gids));
}
msg->body.getreply.hisize = htonl(isize >> 32);
......@@ -1188,9 +1197,11 @@ handle_put(int sock, struct sockaddr_in *sip, struct sockaddr_in *cip,
}
in.s_addr = htonl(ci->addr);
log("%s: started uploader on %s:%d (pid %d, ugid %d/%d, timo %ds)",
imageid, inet_ntoa(in), ci->port, ci->pid, ci->uid, ci->gid,
ci->timeout);
log("%s: started uploader on %s:%d (pid %d, timo %ds)",
imageid, inet_ntoa(in), ci->port, ci->pid, ci->timeout);
if (debug)
log(" uid: %d, gids: %s",
ci->uid, gidstr(ci->ngids, ci->gids));
/*
* Watch for an immediate death so we don't tell our client
......@@ -1513,10 +1524,16 @@ startchild(struct childinfo *ci)
char *pname, *opts;
if (myuid == 0) {
if ((ci->gid != mygid && setgid(ci->gid) != 0) ||
assert(ci->ngids >= 1);
if (setgroups(ci->ngids, ci->gids) != 0) {
perror("child: setgroups");
exit(-2);
}
if ((ci->gids[0] != mygid && setgid(ci->gids[0]) != 0) ||
(ci->uid != myuid && setuid(ci->uid) != 0)) {
error("child: could not setuid/gid to %d/%d",
ci->uid, ci->gid);
ci->uid, ci->gids[0]);
exit(-2);
}
}
......@@ -1683,14 +1700,19 @@ startserver(struct config_imageinfo *ii, in_addr_t meaddr, in_addr_t youaddr,
* Otherwise just run it as our uid/gid.
*/
if (myuid == 0) {
if (ii->uid >= 0 && myuid != ii->uid)
if (ii->uid != NOUID && myuid != ii->uid)
ci->uid = ii->uid;
else
ci->uid = myuid;
if (ii->gid >= 0 && mygid != ii->gid)
ci->gid = ii->gid;
else
ci->gid = mygid;
if (ii->ngids > 0) {
int i;
for (i = 0; i < ii->ngids; i++)
ci->gids[i] = ii->gids[i];
ci->ngids = ii->ngids;
} else {
ci->gids[0] = mygid;
ci->ngids = 1;
}
}
ci->timeout = ii->get_timeout;
......@@ -1786,7 +1808,8 @@ startclient(struct config_imageinfo *ii, in_addr_t meaddr, in_addr_t youaddr,
/* For now, we just run the client as us */
ci->uid = myuid;
ci->gid = mygid;
ci->gids[0] = mygid;
ci->ngids = 1;
ce = ci->extra = calloc(1, sizeof(struct clientextra));
if (ce == NULL)
......@@ -1935,14 +1958,19 @@ startuploader(struct config_imageinfo *ii, in_addr_t meaddr, in_addr_t youaddr,
* Otherwise just run it as our uid/gid.
*/
if (myuid == 0) {
if (ii->uid >= 0 && myuid != ii->uid)
if (ii->uid != NOUID && myuid != ii->uid)
ci->uid = ii->uid;
else
ci->uid = myuid;
if (ii->gid >= 0 && mygid != ii->gid)
ci->gid = ii->gid;
else
ci->gid = mygid;
if (ii->ngids > 0) {
int i;
for (i = 0; i < ii->ngids; i++)
ci->gids[i] = ii->gids[i];
ci->ngids = ii->ngids;
} else {
ci->gids[0] = mygid;
ci->ngids = 1;
}
}
ci->timeout = timo;
......@@ -2088,3 +2116,22 @@ reapchildren(int wpid, int *statusp)
return corpses;
}
/*
* XXX debug
*/
static char *
gidstr(int ngids, gid_t gids[])
{
static char str[MAXGIDS*6+1];
char *cp = str;
int i;
assert(ngids > 0);
for (i = 0; i < ngids; i++) {
sprintf(cp, "%d%c", gids[i], (i == ngids-1) ? '\0' : '/');
cp = &str[strlen(str)];
}
return str;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment