Commit e3cca068 by Serge Hallyn Committed by GitHub

Merge pull request #1373 from brauner/2016-01-02/fix_execute_and_improve_setgroups

start: fix execute and improve setgroups() calls
parents 4484e6f8 87bf0db0
...@@ -22,20 +22,21 @@ ...@@ -22,20 +22,21 @@
*/ */
#define _GNU_SOURCE #define _GNU_SOURCE
#include <unistd.h> #include "config.h"
#include <errno.h>
#include <limits.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdlib.h> #include <stdlib.h>
#include <limits.h> #include <unistd.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <errno.h>
#include "config.h" #include "caps.h"
#include "log.h" #include "log.h"
lxc_log_define(lxc_caps, lxc); lxc_log_define(lxc_caps, lxc);
#if HAVE_SYS_CAPABILITY_H #if HAVE_SYS_CAPABILITY_H
#include <sys/capability.h>
#ifndef PR_CAPBSET_READ #ifndef PR_CAPBSET_READ
#define PR_CAPBSET_READ 23 #define PR_CAPBSET_READ 23
...@@ -208,4 +209,27 @@ int lxc_caps_last_cap(void) ...@@ -208,4 +209,27 @@ int lxc_caps_last_cap(void)
return last_cap; return last_cap;
} }
bool lxc_cap_is_set(cap_value_t cap, cap_flag_t flag)
{
int ret;
cap_t caps;
cap_flag_value_t flagval;
caps = cap_get_proc();
if (!caps) {
ERROR("Failed to perform cap_get_proc(): %s.", strerror(errno));
return false;
}
ret = cap_get_flag(caps, cap, flag, &flagval);
if (ret < 0) {
ERROR("Failed to perform cap_get_flag(): %s.", strerror(errno));
cap_free(caps);
return false;
}
cap_free(caps);
return flagval == CAP_SET;
}
#endif #endif
...@@ -20,17 +20,23 @@ ...@@ -20,17 +20,23 @@
* License along with this library; if not, write to the Free Software * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include "config.h" #include "config.h"
#include <stdbool.h>
#ifndef __LXC_CAPS_H #ifndef __LXC_CAPS_H
#define __LXC_CAPS_H #define __LXC_CAPS_H
#if HAVE_SYS_CAPABILITY_H #if HAVE_SYS_CAPABILITY_H
#include <sys/capability.h>
extern int lxc_caps_down(void); extern int lxc_caps_down(void);
extern int lxc_caps_up(void); extern int lxc_caps_up(void);
extern int lxc_caps_init(void); extern int lxc_caps_init(void);
extern int lxc_caps_last_cap(void); extern int lxc_caps_last_cap(void);
extern bool lxc_cap_is_set(cap_value_t cap, cap_flag_t flag);
#else #else
static inline int lxc_caps_down(void) { static inline int lxc_caps_down(void) {
return 0; return 0;
...@@ -45,6 +51,12 @@ static inline int lxc_caps_init(void) { ...@@ -45,6 +51,12 @@ static inline int lxc_caps_init(void) {
static inline int lxc_caps_last_cap(void) { static inline int lxc_caps_last_cap(void) {
return 0; return 0;
} }
typedef int cap_value_t;
typedef int cap_flag_t;
static inline bool lxc_cap_is_set(cap_value_t cap, cap_flag_t flag) {
return true;
}
#endif #endif
#define lxc_priv(__lxc_function) \ #define lxc_priv(__lxc_function) \
......
...@@ -769,32 +769,17 @@ static int do_start(void *data) ...@@ -769,32 +769,17 @@ static int do_start(void *data)
goto out_warn_father; goto out_warn_father;
/* If we are in a new user namespace, become root there to have /* If we are in a new user namespace, become root there to have
* privilege over our namespace. When using lxc-execute we default to * privilege over our namespace.
* root, but this can be overriden using the lxc.init_uid and
* lxc.init_gid configuration options.
*/ */
if (!lxc_list_empty(&handler->conf->id_map)) { if (!lxc_list_empty(&handler->conf->id_map)) {
gid_t new_gid = 0; if (lxc_switch_uid_gid(0, 0) < 0)
if (handler->conf->is_execute && handler->conf->init_gid)
new_gid = handler->conf->init_gid;
uid_t new_uid = 0;
if (handler->conf->is_execute && handler->conf->init_uid)
new_uid = handler->conf->init_uid;
NOTICE("Switching to uid=%d and gid=%d in new user namespace.", new_uid, new_gid);
if (setgid(new_gid)) {
SYSERROR("Failed to setgid().");
goto out_warn_father;
}
if (setuid(new_uid)) {
SYSERROR("Failed to setuid().");
goto out_warn_father; goto out_warn_father;
}
if (setgroups(0, NULL)) { /* Drop groups only after we switched to a valid gid in the new
SYSERROR("Failed to setgroups()."); * user namespace.
*/
if (lxc_setgroups(0, NULL) < 0)
goto out_warn_father; goto out_warn_father;
}
} }
if (access(handler->lxcpath, X_OK)) { if (access(handler->lxcpath, X_OK)) {
...@@ -900,6 +885,29 @@ static int do_start(void *data) ...@@ -900,6 +885,29 @@ static int do_start(void *data)
goto out_warn_father; goto out_warn_father;
} }
/* The container has been setup. We can now switch to an unprivileged
* uid/gid.
*/
if (handler->conf->is_execute) {
bool have_cap_setgid;
uid_t new_uid = handler->conf->init_uid;
gid_t new_gid = handler->conf->init_gid;
/* If we are in a new user namespace we already dropped all
* groups when we switched to root in the new user namespace
* further above. Only drop groups if we can, so ensure that we
* have necessary privilege.
*/
have_cap_setgid = lxc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE);
if (lxc_list_empty(&handler->conf->id_map) && have_cap_setgid) {
if (lxc_setgroups(0, NULL) < 0)
goto out_warn_father;
}
if (lxc_switch_uid_gid(new_uid, new_gid) < 0)
goto out_warn_father;
}
/* The clearenv() and putenv() calls have been moved here to allow us to /* The clearenv() and putenv() calls have been moved here to allow us to
* use environment variables passed to the various hooks, such as the * use environment variables passed to the various hooks, such as the
* start hook above. Not all of the variables like CONFIG_PATH or ROOTFS * start hook above. Not all of the variables like CONFIG_PATH or ROOTFS
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <dirent.h> #include <dirent.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <grp.h>
#include <libgen.h> #include <libgen.h>
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
...@@ -2053,3 +2054,32 @@ int lxc_safe_long(const char *numstr, long int *converted) ...@@ -2053,3 +2054,32 @@ int lxc_safe_long(const char *numstr, long int *converted)
*converted = sli; *converted = sli;
return 0; return 0;
} }
int lxc_switch_uid_gid(uid_t uid, gid_t gid)
{
if (setgid(gid) < 0) {
SYSERROR("Failed to switch to gid %d.", gid);
return -errno;
}
NOTICE("Switched to gid %d.", gid);
if (setuid(uid) < 0) {
SYSERROR("Failed to switch to uid %d.", uid);
return -errno;
}
NOTICE("Switched to uid %d.", uid);
return 0;
}
/* Simple covenience function which enables uniform logging. */
int lxc_setgroups(int size, gid_t list[])
{
if (setgroups(size, list) < 0) {
SYSERROR("Failed to setgroups().");
return -errno;
}
NOTICE("Dropped additional groups.");
return 0;
}
...@@ -327,4 +327,8 @@ int lxc_safe_uint(const char *numstr, unsigned int *converted); ...@@ -327,4 +327,8 @@ int lxc_safe_uint(const char *numstr, unsigned int *converted);
int lxc_safe_int(const char *numstr, int *converted); int lxc_safe_int(const char *numstr, int *converted);
int lxc_safe_long(const char *numstr, long int *converted); int lxc_safe_long(const char *numstr, long int *converted);
/* Switch to a new uid and gid. */
int lxc_switch_uid_gid(uid_t uid, gid_t gid);
int lxc_setgroups(int size, gid_t list[]);
#endif /* __LXC_UTILS_H */ #endif /* __LXC_UTILS_H */
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