Commit ae22a220 by S.Çağlar Onur Committed by Stéphane Graber

make lxcapi_get_interfaces and lxcapi_get_ips unprivileged container aware

Based on Stéphane's suggestion, those two API methods now; * fork a new process, * switch to appropriate namespace(s), * do what we want, * return the data over a pipe to the parent which returns the result to the original caller. For the whole thread please see; https://lists.linuxcontainers.org/pipermail/lxc-devel/2014-January/007362.html This patch also makes lxc-ls and lxc-info call those functions. I'm adding Stéphane as an author here since both the idea as well as the initial setns code come from him. Author: S.Çağlar Onur <caglar@10ur.org> Author: Stéphane Graber <stgraber@ubuntu.com> Signed-off-by: 's avatarS.Çağlar Onur <caglar@10ur.org> Acked-by: 's avatarStéphane Graber <stgraber@ubuntu.com> Acked-by: 's avatarSerge E. Hallyn <serge.hallyn@ubuntu.com>
parent adf4b408
...@@ -265,7 +265,7 @@ for container_name in lxc.list_containers(config_path=nest_lxcpath): ...@@ -265,7 +265,7 @@ for container_name in lxc.list_containers(config_path=nest_lxcpath):
# FIXME: We should get get_ips working as non-root # FIXME: We should get get_ips working as non-root
if container.running: if container.running:
if not SUPPORT_SETNS_NET or os.geteuid() != 0: if not SUPPORT_SETNS_NET:
entry[protocol] = 'UNKNOWN' entry[protocol] = 'UNKNOWN'
continue continue
......
...@@ -310,19 +310,15 @@ static int print_info(const char *name, const char *lxcpath) ...@@ -310,19 +310,15 @@ static int print_info(const char *name, const char *lxcpath)
} }
if (ips) { if (ips) {
if (geteuid() == 0) { char **addresses = c->get_ips(c, NULL, NULL, 0);
char **addresses = c->get_ips(c, NULL, NULL, 0); if (addresses) {
if (addresses) { char *address;
char *address; i = 0;
i = 0; while (addresses[i]) {
while (addresses[i]) { address = addresses[i];
address = addresses[i]; print_info_msg_str("IP:", address);
print_info_msg_str("IP:", address); i++;
i++;
}
} }
} else {
print_info_msg_str("IP:", "UNKNOWN");
} }
} }
......
...@@ -1372,48 +1372,51 @@ static bool lxcapi_clear_config_item(struct lxc_container *c, const char *key) ...@@ -1372,48 +1372,51 @@ static bool lxcapi_clear_config_item(struct lxc_container *c, const char *key)
return ret == 0; return ret == 0;
} }
static inline void exit_from_ns(struct lxc_container *c, int *old_netns, int *new_netns) { static inline bool enter_to_ns(struct lxc_container *c) {
/* Switch back to original netns */ int netns, userns, ret = 0, init_pid = 0;;
if (*old_netns >= 0 && setns(*old_netns, CLONE_NEWNET))
SYSERROR("failed to setns");
if (*new_netns >= 0)
close(*new_netns);
if (*old_netns >= 0)
close(*old_netns);
}
static inline bool enter_to_ns(struct lxc_container *c, int *old_netns, int *new_netns) {
int ret = 0;
char new_netns_path[MAXPATHLEN]; char new_netns_path[MAXPATHLEN];
char new_userns_path[MAXPATHLEN];
if (!c->is_running(c)) if (!c->is_running(c))
goto out; goto out;
/* Save reference to old netns */ init_pid = c->init_pid(c);
*old_netns = open("/proc/self/ns/net", O_RDONLY);
if (*old_netns < 0) { /* Switch to new userns */
SYSERROR("failed to open /proc/self/ns/net"); if (geteuid() && access("/proc/self/ns/user", F_OK) == 0) {
goto out; ret = snprintf(new_userns_path, MAXPATHLEN, "/proc/%d/ns/user", init_pid);
if (ret < 0 || ret >= MAXPATHLEN)
goto out;
userns = open(new_userns_path, O_RDONLY);
if (userns < 0) {
SYSERROR("failed to open %s", new_userns_path);
goto out;
}
if (setns(userns, CLONE_NEWUSER)) {
SYSERROR("failed to setns for CLONE_NEWUSER");
goto out;
}
} }
/* Switch to new netns */ /* Switch to new netns */
ret = snprintf(new_netns_path, MAXPATHLEN, "/proc/%d/ns/net", c->init_pid(c)); ret = snprintf(new_netns_path, MAXPATHLEN, "/proc/%d/ns/net", init_pid);
if (ret < 0 || ret >= MAXPATHLEN) if (ret < 0 || ret >= MAXPATHLEN)
goto out; goto out;
*new_netns = open(new_netns_path, O_RDONLY); netns = open(new_netns_path, O_RDONLY);
if (*new_netns < 0) { if (netns < 0) {
SYSERROR("failed to open %s", new_netns_path); SYSERROR("failed to open %s", new_netns_path);
goto out; goto out;
} }
if (setns(*new_netns, CLONE_NEWNET)) { if (setns(netns, CLONE_NEWNET)) {
SYSERROR("failed to setns"); SYSERROR("failed to setns for CLONE_NEWNET");
goto out; goto out;
} }
return true; return true;
out: out:
exit_from_ns(c, old_netns, new_netns);
return false; return false;
} }
...@@ -1490,135 +1493,206 @@ static bool remove_from_array(char ***names, char *cname, int size) ...@@ -1490,135 +1493,206 @@ static bool remove_from_array(char ***names, char *cname, int size)
static char** lxcapi_get_interfaces(struct lxc_container *c) static char** lxcapi_get_interfaces(struct lxc_container *c)
{ {
int i, count = 0; pid_t pid;
struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; int i, count = 0, pipefd[2];
char **interfaces = NULL; char **interfaces = NULL;
int old_netns = -1, new_netns = -1; char interface[IFNAMSIZ];
if (am_unpriv()) { if(pipe(pipefd) < 0) {
ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__); SYSERROR("pipe failed");
goto out; return NULL;
} }
if (!enter_to_ns(c, &old_netns, &new_netns)) pid = fork();
goto out; if (pid < 0) {
SYSERROR("failed to fork task to get interfaces information\n");
close(pipefd[0]);
close(pipefd[1]);
return NULL;
}
/* Grab the list of interfaces */ if (pid == 0) { // child
if (getifaddrs(&interfaceArray)) { int ret = 1, nbytes;
SYSERROR("failed to get interfaces list"); struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL;
goto out;
/* close the read-end of the pipe */
close(pipefd[0]);
if (!enter_to_ns(c)) {
SYSERROR("failed to enter namespace");
goto out;
}
/* Grab the list of interfaces */
if (getifaddrs(&interfaceArray)) {
SYSERROR("failed to get interfaces list");
goto out;
}
/* Iterate through the interfaces */
for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) {
nbytes = write(pipefd[1], tempIfAddr->ifa_name, IFNAMSIZ);
if (nbytes < 0) {
ERROR("write failed");
goto out;
}
count++;
}
ret = 0;
out:
if (interfaceArray)
freeifaddrs(interfaceArray);
/* close the write-end of the pipe, thus sending EOF to the reader */
close(pipefd[1]);
exit(ret);
} }
/* Iterate through the interfaces */ /* close the write-end of the pipe */
for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) { close(pipefd[1]);
if (array_contains(&interfaces, tempIfAddr->ifa_name, count))
continue;
if(!add_to_array(&interfaces, tempIfAddr->ifa_name, count)) while (read(pipefd[0], &interface, IFNAMSIZ) > 0) {
goto err; if (array_contains(&interfaces, interface, count))
continue;
if(!add_to_array(&interfaces, interface, count))
ERROR("PARENT: add_to_array failed");
count++; count++;
} }
out: if (wait_for_pid(pid) != 0) {
if (interfaceArray) for(i=0;i<count;i++)
freeifaddrs(interfaceArray); free(interfaces[i]);
free(interfaces);
interfaces = NULL;
}
exit_from_ns(c, &old_netns, &new_netns); /* close the read-end of the pipe */
close(pipefd[0]);
/* Append NULL to the array */ /* Append NULL to the array */
if(interfaces) if(interfaces)
interfaces = (char **)lxc_append_null_to_array((void **)interfaces, count); interfaces = (char **)lxc_append_null_to_array((void **)interfaces, count);
return interfaces; return interfaces;
err:
for(i=0;i<count;i++)
free(interfaces[i]);
free(interfaces);
interfaces = NULL;
goto out;
} }
static char** lxcapi_get_ips(struct lxc_container *c, const char* interface, const char* family, int scope) static char** lxcapi_get_ips(struct lxc_container *c, const char* interface, const char* family, int scope)
{ {
int i, count = 0; pid_t pid;
struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; int i, count = 0, pipefd[2];
char addressOutputBuffer[INET6_ADDRSTRLEN];
void *tempAddrPtr = NULL;
char **addresses = NULL; char **addresses = NULL;
char *address = NULL; char address[INET6_ADDRSTRLEN];
int old_netns = -1, new_netns = -1;
if (am_unpriv()) { if(pipe(pipefd) < 0) {
ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__); SYSERROR("pipe failed");
goto out; return NULL;
} }
if (!enter_to_ns(c, &old_netns, &new_netns)) pid = fork();
goto out; if (pid < 0) {
SYSERROR("failed to fork task to get container ips\n");
/* Grab the list of interfaces */ close(pipefd[0]);
if (getifaddrs(&interfaceArray)) { close(pipefd[1]);
SYSERROR("failed to get interfaces list"); return NULL;
goto out;
} }
/* Iterate through the interfaces */ if (pid == 0) { // child
for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) { int ret = 1, nbytes;
if (tempIfAddr->ifa_addr == NULL) struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL;
continue; char addressOutputBuffer[INET6_ADDRSTRLEN];
void *tempAddrPtr = NULL;
char *address = NULL;
if(tempIfAddr->ifa_addr->sa_family == AF_INET) { /* close the read-end of the pipe */
if (family && strcmp(family, "inet")) close(pipefd[0]);
continue;
tempAddrPtr = &((struct sockaddr_in *)tempIfAddr->ifa_addr)->sin_addr; if (!enter_to_ns(c)) {
SYSERROR("failed to enter namespace");
goto out;
} }
else {
if (family && strcmp(family, "inet6")) /* Grab the list of interfaces */
if (getifaddrs(&interfaceArray)) {
SYSERROR("failed to get interfaces list");
goto out;
}
/* Iterate through the interfaces */
for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) {
if (tempIfAddr->ifa_addr == NULL)
continue; continue;
if (((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_scope_id != scope) if(tempIfAddr->ifa_addr->sa_family == AF_INET) {
if (family && strcmp(family, "inet"))
continue;
tempAddrPtr = &((struct sockaddr_in *)tempIfAddr->ifa_addr)->sin_addr;
}
else {
if (family && strcmp(family, "inet6"))
continue;
if (((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_scope_id != scope)
continue;
tempAddrPtr = &((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_addr;
}
if (interface && strcmp(interface, tempIfAddr->ifa_name))
continue;
else if (!interface && strcmp("lo", tempIfAddr->ifa_name) == 0)
continue; continue;
tempAddrPtr = &((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_addr; address = (char *)inet_ntop(tempIfAddr->ifa_addr->sa_family,
tempAddrPtr,
addressOutputBuffer,
sizeof(addressOutputBuffer));
if (!address)
continue;
nbytes = write(pipefd[1], address, INET6_ADDRSTRLEN);
if (nbytes < 0) {
ERROR("write failed");
goto out;
}
count++;
} }
ret = 0;
if (interface && strcmp(interface, tempIfAddr->ifa_name)) out:
continue; if(interfaceArray)
else if (!interface && strcmp("lo", tempIfAddr->ifa_name) == 0) freeifaddrs(interfaceArray);
continue;
address = (char *)inet_ntop(tempIfAddr->ifa_addr->sa_family, /* close the write-end of the pipe, thus sending EOF to the reader */
tempAddrPtr, close(pipefd[1]);
addressOutputBuffer, exit(ret);
sizeof(addressOutputBuffer)); }
if (!address)
continue; /* close the write-end of the pipe */
close(pipefd[1]);
while (read(pipefd[0], &address, INET6_ADDRSTRLEN) > 0) {
if(!add_to_array(&addresses, address, count)) if(!add_to_array(&addresses, address, count))
goto err; ERROR("PARENT: add_to_array failed");
count++; count++;
} }
out: if (wait_for_pid(pid) != 0) {
if(interfaceArray) for(i=0;i<count;i++)
freeifaddrs(interfaceArray); free(addresses[i]);
free(addresses);
addresses = NULL;
}
exit_from_ns(c, &old_netns, &new_netns); /* close the read-end of the pipe */
close(pipefd[0]);
/* Append NULL to the array */ /* Append NULL to the array */
if(addresses) if(addresses)
addresses = (char **)lxc_append_null_to_array((void **)addresses, count); addresses = (char **)lxc_append_null_to_array((void **)addresses, count);
return addresses; return addresses;
err:
for(i=0;i<count;i++)
free(addresses[i]);
free(addresses);
addresses = NULL;
goto out;
} }
static int lxcapi_get_config_item(struct lxc_container *c, const char *key, char *retv, int inlen) static int lxcapi_get_config_item(struct lxc_container *c, const char *key, char *retv, int inlen)
......
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