scm: Don't use struct ucred in NETLINK_CB and struct scm_cookie.
Passing uids and gids on NETLINK_CB from a process in one user namespace to a process in another user namespace can result in the wrong uid or gid being presented to userspace. Avoid that problem by passing kuids and kgids instead. - define struct scm_creds for use in scm_cookie and netlink_skb_parms that holds uid and gid information in kuid_t and kgid_t. - Modify scm_set_cred to fill out scm_creds by heand instead of using cred_to_ucred to fill out struct ucred. This conversion ensures userspace does not get incorrect uid or gid values to look at. - Modify scm_recv to convert from struct scm_creds to struct ucred before copying credential values to userspace. - Modify __scm_send to populate struct scm_creds on in the scm_cookie, instead of just copying struct ucred from userspace. - Modify netlink_sendmsg to copy scm_creds instead of struct ucred into the NETLINK_CB. Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
d679c5324d
commit
dbe9a4173e
@ -153,6 +153,7 @@ struct nlattr {
|
|||||||
|
|
||||||
#include <linux/capability.h>
|
#include <linux/capability.h>
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
|
#include <net/scm.h>
|
||||||
|
|
||||||
struct net;
|
struct net;
|
||||||
|
|
||||||
@ -162,7 +163,7 @@ static inline struct nlmsghdr *nlmsg_hdr(const struct sk_buff *skb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct netlink_skb_parms {
|
struct netlink_skb_parms {
|
||||||
struct ucred creds; /* Skb credentials */
|
struct scm_creds creds; /* Skb credentials */
|
||||||
__u32 pid;
|
__u32 pid;
|
||||||
__u32 dst_group;
|
__u32 dst_group;
|
||||||
struct sock *ssk;
|
struct sock *ssk;
|
||||||
|
@ -12,6 +12,12 @@
|
|||||||
*/
|
*/
|
||||||
#define SCM_MAX_FD 253
|
#define SCM_MAX_FD 253
|
||||||
|
|
||||||
|
struct scm_creds {
|
||||||
|
u32 pid;
|
||||||
|
kuid_t uid;
|
||||||
|
kgid_t gid;
|
||||||
|
};
|
||||||
|
|
||||||
struct scm_fp_list {
|
struct scm_fp_list {
|
||||||
short count;
|
short count;
|
||||||
short max;
|
short max;
|
||||||
@ -22,7 +28,7 @@ struct scm_cookie {
|
|||||||
struct pid *pid; /* Skb credentials */
|
struct pid *pid; /* Skb credentials */
|
||||||
const struct cred *cred;
|
const struct cred *cred;
|
||||||
struct scm_fp_list *fp; /* Passed files */
|
struct scm_fp_list *fp; /* Passed files */
|
||||||
struct ucred creds; /* Skb credentials */
|
struct scm_creds creds; /* Skb credentials */
|
||||||
#ifdef CONFIG_SECURITY_NETWORK
|
#ifdef CONFIG_SECURITY_NETWORK
|
||||||
u32 secid; /* Passed security ID */
|
u32 secid; /* Passed security ID */
|
||||||
#endif
|
#endif
|
||||||
@ -49,7 +55,9 @@ static __inline__ void scm_set_cred(struct scm_cookie *scm,
|
|||||||
{
|
{
|
||||||
scm->pid = get_pid(pid);
|
scm->pid = get_pid(pid);
|
||||||
scm->cred = cred ? get_cred(cred) : NULL;
|
scm->cred = cred ? get_cred(cred) : NULL;
|
||||||
cred_to_ucred(pid, cred, &scm->creds);
|
scm->creds.pid = pid_vnr(pid);
|
||||||
|
scm->creds.uid = cred ? cred->euid : INVALID_UID;
|
||||||
|
scm->creds.gid = cred ? cred->egid : INVALID_GID;
|
||||||
}
|
}
|
||||||
|
|
||||||
static __inline__ void scm_destroy_cred(struct scm_cookie *scm)
|
static __inline__ void scm_destroy_cred(struct scm_cookie *scm)
|
||||||
@ -112,8 +120,15 @@ static __inline__ void scm_recv(struct socket *sock, struct msghdr *msg,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (test_bit(SOCK_PASSCRED, &sock->flags))
|
if (test_bit(SOCK_PASSCRED, &sock->flags)) {
|
||||||
put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(scm->creds), &scm->creds);
|
struct user_namespace *current_ns = current_user_ns();
|
||||||
|
struct ucred ucreds = {
|
||||||
|
.pid = scm->creds.pid,
|
||||||
|
.uid = from_kuid_munged(current_ns, scm->creds.uid),
|
||||||
|
.gid = from_kgid_munged(current_ns, scm->creds.gid),
|
||||||
|
};
|
||||||
|
put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(ucreds), &ucreds);
|
||||||
|
}
|
||||||
|
|
||||||
scm_destroy_cred(scm);
|
scm_destroy_cred(scm);
|
||||||
|
|
||||||
|
@ -155,19 +155,21 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
|
|||||||
break;
|
break;
|
||||||
case SCM_CREDENTIALS:
|
case SCM_CREDENTIALS:
|
||||||
{
|
{
|
||||||
|
struct ucred creds;
|
||||||
kuid_t uid;
|
kuid_t uid;
|
||||||
kgid_t gid;
|
kgid_t gid;
|
||||||
if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred)))
|
if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred)))
|
||||||
goto error;
|
goto error;
|
||||||
memcpy(&p->creds, CMSG_DATA(cmsg), sizeof(struct ucred));
|
memcpy(&creds, CMSG_DATA(cmsg), sizeof(struct ucred));
|
||||||
err = scm_check_creds(&p->creds);
|
err = scm_check_creds(&creds);
|
||||||
if (err)
|
if (err)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (!p->pid || pid_vnr(p->pid) != p->creds.pid) {
|
p->creds.pid = creds.pid;
|
||||||
|
if (!p->pid || pid_vnr(p->pid) != creds.pid) {
|
||||||
struct pid *pid;
|
struct pid *pid;
|
||||||
err = -ESRCH;
|
err = -ESRCH;
|
||||||
pid = find_get_pid(p->creds.pid);
|
pid = find_get_pid(creds.pid);
|
||||||
if (!pid)
|
if (!pid)
|
||||||
goto error;
|
goto error;
|
||||||
put_pid(p->pid);
|
put_pid(p->pid);
|
||||||
@ -175,11 +177,14 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
uid = make_kuid(current_user_ns(), p->creds.uid);
|
uid = make_kuid(current_user_ns(), creds.uid);
|
||||||
gid = make_kgid(current_user_ns(), p->creds.gid);
|
gid = make_kgid(current_user_ns(), creds.gid);
|
||||||
if (!uid_valid(uid) || !gid_valid(gid))
|
if (!uid_valid(uid) || !gid_valid(gid))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
p->creds.uid = uid;
|
||||||
|
p->creds.gid = gid;
|
||||||
|
|
||||||
if (!p->cred ||
|
if (!p->cred ||
|
||||||
!uid_eq(p->cred->euid, uid) ||
|
!uid_eq(p->cred->euid, uid) ||
|
||||||
!gid_eq(p->cred->egid, gid)) {
|
!gid_eq(p->cred->egid, gid)) {
|
||||||
|
@ -1399,7 +1399,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
|
|||||||
|
|
||||||
NETLINK_CB(skb).pid = nlk->pid;
|
NETLINK_CB(skb).pid = nlk->pid;
|
||||||
NETLINK_CB(skb).dst_group = dst_group;
|
NETLINK_CB(skb).dst_group = dst_group;
|
||||||
memcpy(NETLINK_CREDS(skb), &siocb->scm->creds, sizeof(struct ucred));
|
NETLINK_CB(skb).creds = siocb->scm->creds;
|
||||||
|
|
||||||
err = -EFAULT;
|
err = -EFAULT;
|
||||||
if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
|
if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user