Linux: Fix build with CONFIG_UIDGID_STRICT_TYPE_CHECKS (user namespaces)
authorAnders Kaseorg <andersk@mit.edu>
Thu, 31 Oct 2013 13:11:59 +0000 (09:11 -0400)
committerDerrick Brashear <shadow@your-file-system.com>
Tue, 5 Nov 2013 19:44:55 +0000 (11:44 -0800)
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 <andersk@mit.edu>
Reviewed-on: http://gerrit.openafs.org/10386
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Marc Dionne <marc.c.dionne@gmail.com>
Reviewed-by: Derrick Brashear <shadow@your-file-system.com>

acinclude.m4
src/afs/LINUX/osi_compat.h
src/afs/LINUX/osi_cred.c
src/afs/LINUX/osi_groups.c
src/afs/LINUX/osi_machdep.h
src/afs/LINUX/osi_module.c
src/afs/LINUX/osi_pag_module.c
src/afs/LINUX/osi_vnodeops.c
src/cf/linux-test4.m4

index 1c8fb1a..7b48850 100644 (file)
@@ -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
index 15ea8b4..71ffb40 100644 (file)
@@ -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
index f271321..894af77 100644 (file)
 /* 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));
 }
index 88eeb61..f3f87c2 100644 (file)
@@ -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 */
index a144ab7..a59d9fb 100644 (file)
@@ -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) {
index f5d6afc..2cc0e48 100644 (file)
@@ -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();
index 824c6f0..ac4f800 100644 (file)
@@ -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();
index f12304a..2a29625 100644 (file)
@@ -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;
index ab6da1d..68ad2d6 100644 (file)
@@ -353,7 +353,8 @@ AC_DEFUN([LINUX_KEY_ALLOC_NEEDS_STRUCT_TASK], [
                        [#include <linux/rwsem.h>
                        #include <linux/key.h> ],
                        [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 <linux/rwsem.h>
                        #include <linux/key.h>],
                        [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 <linux/rwsem.h>
+                       #include <linux/key.h>],
+                       [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],