From: S.Çağlar Onur Date: Tue, 21 Jan 2014 00:57:28 +0000 (-0500) Subject: make lxcapi_get_interfaces and lxcapi_get_ips unprivileged container aware X-Git-Tag: lxc-1.0.0.beta3~73 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ae22a2206489f1a4eba40e26853113772db50929;p=thirdparty%2Flxc.git 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 Author: Stéphane Graber Signed-off-by: S.Çağlar Onur Acked-by: Stéphane Graber Acked-by: Serge E. Hallyn --- diff --git a/src/lxc/lxc-ls b/src/lxc/lxc-ls index 68c0b4118..fa53facb0 100755 --- a/src/lxc/lxc-ls +++ b/src/lxc/lxc-ls @@ -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 if container.running: - if not SUPPORT_SETNS_NET or os.geteuid() != 0: + if not SUPPORT_SETNS_NET: entry[protocol] = 'UNKNOWN' continue diff --git a/src/lxc/lxc_info.c b/src/lxc/lxc_info.c index 0990036b6..b51508768 100644 --- a/src/lxc/lxc_info.c +++ b/src/lxc/lxc_info.c @@ -310,19 +310,15 @@ static int print_info(const char *name, const char *lxcpath) } if (ips) { - if (geteuid() == 0) { - char **addresses = c->get_ips(c, NULL, NULL, 0); - if (addresses) { - char *address; - i = 0; - while (addresses[i]) { - address = addresses[i]; - print_info_msg_str("IP:", address); - i++; - } + char **addresses = c->get_ips(c, NULL, NULL, 0); + if (addresses) { + char *address; + i = 0; + while (addresses[i]) { + address = addresses[i]; + print_info_msg_str("IP:", address); + i++; } - } else { - print_info_msg_str("IP:", "UNKNOWN"); } } diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 794d27812..37239979a 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -1372,48 +1372,51 @@ static bool lxcapi_clear_config_item(struct lxc_container *c, const char *key) return ret == 0; } -static inline void exit_from_ns(struct lxc_container *c, int *old_netns, int *new_netns) { - /* Switch back to original netns */ - 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; +static inline bool enter_to_ns(struct lxc_container *c) { + int netns, userns, ret = 0, init_pid = 0;; char new_netns_path[MAXPATHLEN]; + char new_userns_path[MAXPATHLEN]; if (!c->is_running(c)) goto out; - /* Save reference to old netns */ - *old_netns = open("/proc/self/ns/net", O_RDONLY); - if (*old_netns < 0) { - SYSERROR("failed to open /proc/self/ns/net"); - goto out; + init_pid = c->init_pid(c); + + /* Switch to new userns */ + if (geteuid() && access("/proc/self/ns/user", F_OK) == 0) { + 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 */ - 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) goto out; - *new_netns = open(new_netns_path, O_RDONLY); - if (*new_netns < 0) { + netns = open(new_netns_path, O_RDONLY); + if (netns < 0) { SYSERROR("failed to open %s", new_netns_path); goto out; } - if (setns(*new_netns, CLONE_NEWNET)) { - SYSERROR("failed to setns"); + if (setns(netns, CLONE_NEWNET)) { + SYSERROR("failed to setns for CLONE_NEWNET"); goto out; } return true; out: - exit_from_ns(c, old_netns, new_netns); return false; } @@ -1490,135 +1493,206 @@ static bool remove_from_array(char ***names, char *cname, int size) static char** lxcapi_get_interfaces(struct lxc_container *c) { - int i, count = 0; - struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; + pid_t pid; + int i, count = 0, pipefd[2]; char **interfaces = NULL; - int old_netns = -1, new_netns = -1; + char interface[IFNAMSIZ]; - if (am_unpriv()) { - ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__); - goto out; + if(pipe(pipefd) < 0) { + SYSERROR("pipe failed"); + return NULL; } - if (!enter_to_ns(c, &old_netns, &new_netns)) - goto out; + pid = fork(); + 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 (getifaddrs(&interfaceArray)) { - SYSERROR("failed to get interfaces list"); - goto out; + if (pid == 0) { // child + int ret = 1, nbytes; + struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; + + /* 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 */ - for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) { - if (array_contains(&interfaces, tempIfAddr->ifa_name, count)) - continue; + /* close the write-end of the pipe */ + close(pipefd[1]); - if(!add_to_array(&interfaces, tempIfAddr->ifa_name, count)) - goto err; + while (read(pipefd[0], &interface, IFNAMSIZ) > 0) { + if (array_contains(&interfaces, interface, count)) + continue; + + if(!add_to_array(&interfaces, interface, count)) + ERROR("PARENT: add_to_array failed"); count++; } -out: - if (interfaceArray) - freeifaddrs(interfaceArray); + if (wait_for_pid(pid) != 0) { + for(i=0;iifa_next) { - if (tempIfAddr->ifa_addr == NULL) - continue; + if (pid == 0) { // child + int ret = 1, nbytes; + struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL; + char addressOutputBuffer[INET6_ADDRSTRLEN]; + void *tempAddrPtr = NULL; + char *address = NULL; - if(tempIfAddr->ifa_addr->sa_family == AF_INET) { - if (family && strcmp(family, "inet")) - continue; - tempAddrPtr = &((struct sockaddr_in *)tempIfAddr->ifa_addr)->sin_addr; + /* close the read-end of the pipe */ + close(pipefd[0]); + + 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; - 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; - 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)) - continue; - else if (!interface && strcmp("lo", tempIfAddr->ifa_name) == 0) - continue; + out: + if(interfaceArray) + freeifaddrs(interfaceArray); - address = (char *)inet_ntop(tempIfAddr->ifa_addr->sa_family, - tempAddrPtr, - addressOutputBuffer, - sizeof(addressOutputBuffer)); - if (!address) - continue; + /* close the write-end of the pipe, thus sending EOF to the reader */ + close(pipefd[1]); + exit(ret); + } + + /* 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)) - goto err; + ERROR("PARENT: add_to_array failed"); count++; } -out: - if(interfaceArray) - freeifaddrs(interfaceArray); + if (wait_for_pid(pid) != 0) { + for(i=0;i