From b7f4f2023b2b3e1aac46715176940fb50cc75265 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Thu, 31 Oct 2013 09:11:59 -0400 Subject: [PATCH] Linux: Fix build with CONFIG_UIDGID_STRICT_TYPE_CHECKS (user namespaces) MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit With CONFIG_UIDGID_STRICT_TYPE_CHECKS (a dependency of user namespace support, CONFIG_USER_NS) turned on, uid_t and kuid_t are different types, as are gid_t and kgid_t, and we need to use namespace-dependent functions to convert between them. We can’t use init_user_ns as the namespace because it’s GPL-only, so instead we grab the current user_ns at module load time. This is required to support kernels with user namespace support. We don’t yet have full support for independent AFS use by different users in a multiuser container; that will need to wait for future work. Change-Id: Icc03f9098dd25b483d406db5167264ba960cdcb7 Signed-off-by: Anders Kaseorg Reviewed-on: http://gerrit.openafs.org/10386 Tested-by: BuildBot Reviewed-by: Marc Dionne Reviewed-by: Derrick Brashear --- acinclude.m4 | 2 ++ src/afs/LINUX/osi_compat.h | 12 +++++++-- src/afs/LINUX/osi_cred.c | 8 ++++++ src/afs/LINUX/osi_groups.c | 23 ++++++++-------- src/afs/LINUX/osi_machdep.h | 59 ++++++++++++++++++++++++++++++++++++------ src/afs/LINUX/osi_module.c | 8 ++++++ src/afs/LINUX/osi_pag_module.c | 8 ++++++ src/afs/LINUX/osi_vnodeops.c | 8 +++--- src/cf/linux-test4.m4 | 18 +++++++++++-- 9 files changed, 119 insertions(+), 27 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 1c8fb1a..7b48850 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -828,6 +828,7 @@ case $AFS_SYSNAME in *_linux* | *_umlinux*) dnl Type existence checks AC_CHECK_LINUX_TYPE([struct vfs_path], [dcache.h]) + AC_CHECK_LINUX_TYPE([kuid_t], [uidgid.h]) dnl Check for structure elements AC_CHECK_LINUX_STRUCT([address_space_operations], @@ -1011,6 +1012,7 @@ case $AFS_SYSNAME in *_linux* | *_umlinux*) LINUX_LINUX_KEYRING_SUPPORT LINUX_KEY_ALLOC_NEEDS_STRUCT_TASK LINUX_KEY_ALLOC_NEEDS_CRED + LINUX_STRUCT_KEY_UID_IS_KUID_T LINUX_INIT_WORK_HAS_DATA LINUX_REGISTER_SYSCTL_TABLE_NOFLAG LINUX_HAVE_DCACHE_LOCK diff --git a/src/afs/LINUX/osi_compat.h b/src/afs/LINUX/osi_compat.h index 15ea8b4..71ffb40 100644 --- a/src/afs/LINUX/osi_compat.h +++ b/src/afs/LINUX/osi_compat.h @@ -173,11 +173,19 @@ init_once_func(void * foo) { #ifdef LINUX_KEYRING_SUPPORT static inline struct key * -afs_linux_key_alloc(struct key_type *type, const char *desc, uid_t uid, - gid_t gid, key_perm_t perm, unsigned long flags) +afs_linux_key_alloc(struct key_type *type, const char *desc, afs_kuid_t uid, + afs_kgid_t gid, key_perm_t perm, unsigned long flags) { # if defined(KEY_ALLOC_NEEDS_STRUCT_TASK) return key_alloc(type, desc, uid, gid, current, perm, flags); +# elif defined(KEY_ALLOC_NEEDS_CRED) && defined(HAVE_LINUX_KUID_T) && \ + !defined(STRUCT_KEY_UID_IS_KUID_T) + /* In this case, uid and gid are specified relative to + * current_cred() */ + return key_alloc(type, desc, + from_kuid(afs_current_user_ns(), uid), + from_guid(afs_current_user_ns(), gid), + current_cred(), perm, flags); # elif defined(KEY_ALLOC_NEEDS_CRED) return key_alloc(type, desc, uid, gid, current_cred(), perm, flags); # else diff --git a/src/afs/LINUX/osi_cred.c b/src/afs/LINUX/osi_cred.c index f271321..894af77 100644 --- a/src/afs/LINUX/osi_cred.c +++ b/src/afs/LINUX/osi_cred.c @@ -21,10 +21,18 @@ /* Copy one credential structure to another, being careful about references */ static inline void afs_copy_creds(cred_t *to_cred, const cred_t *from_cred) { +#if defined(STRUCT_TASK_STRUCT_HAS_CRED) + /* Skip afs_from_kuid/afs_make_kuid round trip */ + to_cred->fsuid = from_cred->fsuid; + to_cred->fsgid = from_cred->fsgid; + to_cred->uid = from_cred->uid; + to_cred->gid = from_cred->gid; +#else afs_set_cr_uid(to_cred, afs_cr_uid(from_cred)); afs_set_cr_gid(to_cred, afs_cr_gid(from_cred)); afs_set_cr_ruid(to_cred, afs_cr_ruid(from_cred)); afs_set_cr_rgid(to_cred, afs_cr_rgid(from_cred)); +#endif get_group_info(afs_cr_group_info(from_cred)); afs_set_cr_group_info(to_cred, afs_cr_group_info(from_cred)); } diff --git a/src/afs/LINUX/osi_groups.c b/src/afs/LINUX/osi_groups.c index 88eeb61..f3f87c2 100644 --- a/src/afs/LINUX/osi_groups.c +++ b/src/afs/LINUX/osi_groups.c @@ -38,7 +38,7 @@ afs_linux_pag_from_groups(struct group_info *group_info) { return NOPAG; for (i = 0; i < group_info->ngroups; i++) { - g0 = GROUP_AT(group_info, i); + g0 = afs_from_kgid(GROUP_AT(group_info, i)); if (((g0 >> 24) & 0xff) == 'A') return g0; } @@ -51,6 +51,7 @@ afs_linux_pag_to_groups(afs_uint32 newpag, int need_space = 0; int i; int j; + afs_kgid_t newkgid = afs_make_kgid(newpag); if (afs_linux_pag_from_groups(old) == NOPAG) need_space = NUMPAGGROUPS; @@ -58,19 +59,19 @@ afs_linux_pag_to_groups(afs_uint32 newpag, *new = groups_alloc(old->ngroups + need_space); for (i = 0, j = 0; i < old->ngroups; ++i) { - int ths = GROUP_AT(old, i); - int last = i > 0 ? GROUP_AT(old, i-1) : 0; - if ((ths >> 24) == 'A') + afs_kgid_t ths = GROUP_AT(old, i); + if ((afs_from_kgid(ths) >> 24) == 'A') continue; - if (last <= newpag && ths > newpag) { - GROUP_AT(*new, j) = newpag; + if ((i == 0 || !gid_lt(newkgid, GROUP_AT(old, i-1))) && + gid_lt(newkgid, ths)) { + GROUP_AT(*new, j) = newkgid; j++; } GROUP_AT(*new, j) = ths; j++; } if (j != i + need_space) - GROUP_AT(*new, j) = newpag; + GROUP_AT(*new, j) = newkgid; } #else @@ -190,7 +191,7 @@ install_session_keyring(struct key *keyring) /* if we're root, don't count the keyring against our quota. This * avoids starvation issues when dealing with PAM modules that always * setpag() as root */ - if (current_uid() == 0) + if (capable(CAP_SYS_ADMIN)) flags = KEY_ALLOC_NOT_IN_QUOTA; else flags = KEY_ALLOC_IN_QUOTA; @@ -250,7 +251,7 @@ setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag, perm = KEY_POS_VIEW | KEY_POS_SEARCH; perm |= KEY_USR_VIEW | KEY_USR_SEARCH; - key = afs_linux_key_alloc(&key_type_afs_pag, "_pag", 0, 0, perm, KEY_ALLOC_NOT_IN_QUOTA); + key = afs_linux_key_alloc(&key_type_afs_pag, "_pag", GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, perm, KEY_ALLOC_NOT_IN_QUOTA); if (!IS_ERR(key)) { code = key_instantiate_and_link(key, (void *) newpag, sizeof(afs_uint32), @@ -461,7 +462,7 @@ static int afs_pag_instantiate(struct key *key, const void *data, size_t datalen int code; afs_uint32 *userpag, pag = NOPAG; - if (key->uid != 0 || key->gid != 0) + if (!uid_eq(key->uid, GLOBAL_ROOT_UID) || !gid_eq(key->gid, GLOBAL_ROOT_GID)) return -EPERM; code = -EINVAL; @@ -597,7 +598,7 @@ osi_get_keyring_pag(afs_ucred_t *cred) key = afs_linux_search_keyring(cred, &key_type_afs_pag); if (!IS_ERR(key)) { - if (key_validate(key) == 0 && key->uid == 0) { /* also verify in the session keyring? */ + if (key_validate(key) == 0 && uid_eq(key->uid, GLOBAL_ROOT_UID)) { /* also verify in the session keyring? */ keyring_pag = key->payload.value; /* Only set PAG in groups if needed, * and the creds are from the current process */ diff --git a/src/afs/LINUX/osi_machdep.h b/src/afs/LINUX/osi_machdep.h index a144ab7..a59d9fb 100644 --- a/src/afs/LINUX/osi_machdep.h +++ b/src/afs/LINUX/osi_machdep.h @@ -148,33 +148,76 @@ static inline long copyinstr(char *from, char *to, int count, int *length) { typedef struct task_struct afs_proc_t; +#ifdef HAVE_LINUX_KUID_T + +typedef kuid_t afs_kuid_t; +typedef kgid_t afs_kgid_t; +extern struct user_namespace *afs_ns; +# ifdef CONFIG_USER_NS +# define afs_current_user_ns() current_user_ns() +# else +/* Here current_user_ns() expands to GPL-only init_user_ns symbol! */ +# define afs_current_user_ns() ((struct user_namespace *)NULL) +# endif + +static inline kuid_t afs_make_kuid(uid_t uid) { + return make_kuid(afs_ns, uid); +} +static inline kgid_t afs_make_kgid(gid_t gid) { + return make_kgid(afs_ns, gid); +} +static inline uid_t afs_from_kuid(kuid_t kuid) { + return from_kuid(afs_ns, kuid); +} +static inline uid_t afs_from_kgid(kgid_t kgid) { + return from_kgid(afs_ns, kgid); +} + +#else + +typedef uid_t afs_kuid_t; +typedef gid_t afs_kgid_t; + +static inline afs_kuid_t afs_make_kuid(uid_t uid) {return uid;} +static inline afs_kgid_t afs_make_kgid(gid_t gid) {return gid;} +static inline uid_t afs_from_kuid(afs_kuid_t kuid) {return kuid;} +static inline gid_t afs_from_kgid(afs_kgid_t kgid) {return kgid;} +static inline bool uid_eq(uid_t a, uid_t b) {return a == b;} +static inline bool gid_eq(gid_t a, gid_t b) {return a == b;} +static inline bool uid_lt(uid_t a, uid_t b) {return a < b;} +static inline bool gid_lt(gid_t a, gid_t b) {return a < b;} +#define GLOBAL_ROOT_UID ((afs_kuid_t) 0) +#define GLOBAL_ROOT_GID ((afs_kgid_t) 0) + +#endif + /* Credentials. For newer kernels we use the kernel structure directly. */ #if defined(STRUCT_TASK_STRUCT_HAS_CRED) typedef struct cred afs_ucred_t; typedef struct cred cred_t; -# define afs_cr_uid(cred) ((cred)->fsuid) -# define afs_cr_gid(cred) ((cred)->fsgid) -# define afs_cr_ruid(cred) ((cred)->uid) -# define afs_cr_rgid(cred) ((cred)->gid) +# define afs_cr_uid(cred) (afs_from_kuid((cred)->fsuid)) +# define afs_cr_gid(cred) (afs_from_kgid((cred)->fsgid)) +# define afs_cr_ruid(cred) (afs_from_kuid((cred)->uid)) +# define afs_cr_rgid(cred) (afs_from_kgid((cred)->gid)) # define afs_cr_group_info(cred) ((cred)->group_info) # define crhold(c) (get_cred(c)) static inline void afs_set_cr_uid(cred_t *cred, uid_t uid) { - cred->fsuid = uid; + cred->fsuid = afs_make_kuid(uid); } static inline void afs_set_cr_gid(cred_t *cred, gid_t gid) { - cred->fsgid = gid; + cred->fsgid = afs_make_kgid(gid); } static inline void afs_set_cr_ruid(cred_t *cred, uid_t uid) { - cred->uid = uid; + cred->uid = afs_make_kuid(uid); } static inline void afs_set_cr_rgid(cred_t *cred, gid_t gid) { - cred->gid = gid; + cred->gid = afs_make_kgid(gid); } static inline void afs_set_cr_group_info(cred_t *cred, struct group_info *group_info) { diff --git a/src/afs/LINUX/osi_module.c b/src/afs/LINUX/osi_module.c index f5d6afc..2cc0e48 100644 --- a/src/afs/LINUX/osi_module.c +++ b/src/afs/LINUX/osi_module.c @@ -42,12 +42,20 @@ DECLARE_MUTEX(afs_global_lock); #endif int afs_global_owner = 0; +#ifdef HAVE_LINUX_KUID_T +struct user_namespace *afs_ns; +#endif + int __init afs_init(void) { int err; AFS_RWLOCK_INIT(&afs_xosi, "afs_xosi"); +#ifdef HAVE_LINUX_KUID_T + afs_ns = afs_current_user_ns(); +#endif + osi_Init(); #if !defined(AFS_NONFSTRANS) osi_linux_nfssrv_init(); diff --git a/src/afs/LINUX/osi_pag_module.c b/src/afs/LINUX/osi_pag_module.c index 824c6f0..ac4f800 100644 --- a/src/afs/LINUX/osi_pag_module.c +++ b/src/afs/LINUX/osi_pag_module.c @@ -54,6 +54,10 @@ DECLARE_MUTEX(afs_global_lock); struct proc_dir_entry *openafs_procfs; int afs_global_owner = 0; +#ifdef HAVE_LINUX_KUID_T +struct user_namespace *afs_ns; +#endif + int __init afspag_init(void) { @@ -62,6 +66,10 @@ afspag_init(void) #endif int err; +#ifdef HAVE_LINUX_KUID_T + afs_ns = afs_current_user_ns(); +#endif + osi_Init(); err = osi_syscall_init(); diff --git a/src/afs/LINUX/osi_vnodeops.c b/src/afs/LINUX/osi_vnodeops.c index f12304a..2a29625 100644 --- a/src/afs/LINUX/osi_vnodeops.c +++ b/src/afs/LINUX/osi_vnodeops.c @@ -1009,9 +1009,9 @@ iattr2vattr(struct vattr *vattrp, struct iattr *iattrp) if (iattrp->ia_valid & ATTR_MODE) vattrp->va_mode = iattrp->ia_mode; if (iattrp->ia_valid & ATTR_UID) - vattrp->va_uid = iattrp->ia_uid; + vattrp->va_uid = afs_from_kuid(iattrp->ia_uid); if (iattrp->ia_valid & ATTR_GID) - vattrp->va_gid = iattrp->ia_gid; + vattrp->va_gid = afs_from_kgid(iattrp->ia_gid); if (iattrp->ia_valid & ATTR_SIZE) vattrp->va_size = iattrp->ia_size; if (iattrp->ia_valid & ATTR_ATIME) { @@ -1049,8 +1049,8 @@ vattr2inode(struct inode *ip, struct vattr *vp) #endif ip->i_rdev = vp->va_rdev; ip->i_mode = vp->va_mode; - ip->i_uid = vp->va_uid; - ip->i_gid = vp->va_gid; + ip->i_uid = afs_make_kuid(vp->va_uid); + ip->i_gid = afs_make_kgid(vp->va_gid); i_size_write(ip, vp->va_size); ip->i_atime.tv_sec = vp->va_atime.tv_sec; ip->i_atime.tv_nsec = 0; diff --git a/src/cf/linux-test4.m4 b/src/cf/linux-test4.m4 index ab6da1d..68ad2d6 100644 --- a/src/cf/linux-test4.m4 +++ b/src/cf/linux-test4.m4 @@ -353,7 +353,8 @@ AC_DEFUN([LINUX_KEY_ALLOC_NEEDS_STRUCT_TASK], [ [#include #include ], [struct task_struct *t=NULL; - (void) key_alloc(NULL, NULL, 0, 0, t, 0, 0);], + struct key k = {}; + (void) key_alloc(NULL, NULL, k.uid, k.gid, t, 0, 0);], [KEY_ALLOC_NEEDS_STRUCT_TASK], [define if key_alloc takes a struct task *], [-Werror -Wno-pointer-arith]) @@ -366,13 +367,26 @@ AC_DEFUN([LINUX_KEY_ALLOC_NEEDS_CRED], [ [#include #include ], [struct cred *c = NULL; - (void) key_alloc(NULL, NULL, 0, 0, c, 0, 0);], + struct key k = {}; + (void) key_alloc(NULL, NULL, k.uid, k.gid, c, 0, 0);], [KEY_ALLOC_NEEDS_CRED], [define if key_alloc takes credentials], [-Werror -Wno-pointer-arith]) ]) +AC_DEFUN([LINUX_STRUCT_KEY_UID_IS_KUID_T], [ + AC_CHECK_LINUX_BUILD([if struct key.uid is kuid_t], + [ac_cv_struct_key_uid_is_kuid_t], + [#include + #include ], + [struct key k = {}; + kuid_t *kuid = &k.uid;], + [STRUCT_KEY_UID_IS_KUID_T], + [define if struct key.uid is kuid_t]) +]) + + AC_DEFUN([LINUX_INIT_WORK_HAS_DATA], [ AC_CHECK_LINUX_BUILD([whether INIT_WORK has a _data argument], [ac_cv_linux_init_work_has_data], -- 1.9.4