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],
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
#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
/* 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));
}
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;
}
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;
*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
/* 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;
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),
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;
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 */
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) {
#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();
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)
{
#endif
int err;
+#ifdef HAVE_LINUX_KUID_T
+ afs_ns = afs_current_user_ns();
+#endif
+
osi_Init();
err = osi_syscall_init();
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) {
#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;
[#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])
[#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],