netlink: dump and restore ucred-s for netlink messages

ucred in a netlink message contains the same pid and
it doesn't metter from which pidns it is read.

Cc: Kirill Tkhai <ktkhai@virtuozzo.com>
Signed-off-by: Andrei Vagin <avagin@virtuozzo.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@virtuozzo.com>

(cherry picked from commit 66f8f7b2ae51a2cef4044df478389137735e1d6e)
Ahmed Elaidy:
- dropped the ENOBUFS bits (SK_QUEUE_TRACK_ENOBUFS and the int *val
  out-parameter): they belong to "netlink: save and generate ENOBUFS
  at restore", which needs a non-upstream kernel extension and is
  intentionally not ported.
- SK_QUEUE_DUMP_ADDR / the int flags signature come from the preceding
  "sk-queue: use flags in dump_sk_queue" cherry-pick rather than being
  glued in here.
Signed-off-by: Ahmed Elaidy <elaidya225@gmail.com>
Reviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
This commit is contained in:
Andrei Vagin 2026-07-08 22:28:19 +03:00 committed by Alexander Mikhalitsyn
parent 86a3c42dda
commit 832801cd81
4 changed files with 85 additions and 5 deletions

View file

@ -3,6 +3,7 @@
extern struct collect_image_info sk_queues_cinfo; extern struct collect_image_info sk_queues_cinfo;
#define SK_QUEUE_REAL_PID 0x1 /* scm creds contains a real pid */
extern int dump_sk_queue(int sock_fd, int sock_id, int flags); extern int dump_sk_queue(int sock_fd, int sock_id, int flags);
extern int restore_sk_queue(int fd, unsigned int peer_id); extern int restore_sk_queue(int fd, unsigned int peer_id);

View file

@ -18,6 +18,10 @@
#include "util-pie.h" #include "util-pie.h"
#include "sockets.h" #include "sockets.h"
#include "xmalloc.h" #include "xmalloc.h"
#include "namespaces.h"
#include "pstree.h"
#include "util.h"
#include "sk-queue.h" #include "sk-queue.h"
#include "files.h" #include "files.h"
#include "protobuf.h" #include "protobuf.h"
@ -132,7 +136,49 @@ static int dump_scm_rights(struct cmsghdr *ch, SkPacketEntry *pe)
*/ */
#define CMSG_MAX_SIZE 2048 #define CMSG_MAX_SIZE 2048
static int dump_packet_cmsg(struct msghdr *mh, SkPacketEntry *pe) static int dump_sk_creds(struct ucred *ucred, SkPacketEntry *pe, int flags)
{
SkUcredEntry *ent;
ent = xmalloc(sizeof(*ent));
if (!ent)
return -1;
sk_ucred_entry__init(ent);
ent->uid = userns_uid(ucred->uid);
ent->gid = userns_gid(ucred->gid);
if (flags & SK_QUEUE_REAL_PID) {
/*
* It is impossible to convert pid from real to virt,
* because virt pid-s are known for dumped task only
*/
pr_err("ucred-s for unix sockets aren't supported yet\n");
return -1;
} else {
int pidns = root_ns_mask & CLONE_NEWPID;
char path[64];
int ret;
/* Does a process exist? */
if (pidns) {
snprintf(path, sizeof(path), "%d", ucred->pid);
ret = faccessat(get_service_fd(CR_PROC_FD_OFF), path, R_OK, 0);
} else {
snprintf(path, sizeof(path), "/proc/%d", ucred->pid);
ret = access(path, R_OK);
}
if (ret) {
pr_err("Unable to dump ucred for a dead process %d\n", ucred->pid);
return -1;
}
ent->pid = ucred->pid;
}
pe->ucred = ent;
return 0;
}
static int dump_packet_cmsg(struct msghdr *mh, SkPacketEntry *pe, int flags)
{ {
struct cmsghdr *ch; struct cmsghdr *ch;
int n_rights = 0; int n_rights = 0;
@ -155,6 +201,16 @@ static int dump_packet_cmsg(struct msghdr *mh, SkPacketEntry *pe)
continue; continue;
} }
if (ch->cmsg_len == CMSG_LEN(sizeof(struct ucred)) &&
ch->cmsg_type == SCM_CREDENTIALS &&
ch->cmsg_level == SOL_SOCKET) {
struct ucred *ucred = (struct ucred *)CMSG_DATA(ch);
if (dump_sk_creds(ucred, pe, flags))
return -1;
continue;
}
pr_err("Control messages in queue, not supported\n"); pr_err("Control messages in queue, not supported\n");
return -1; return -1;
} }
@ -270,7 +326,7 @@ int dump_sk_queue(int sock_fd, int sock_id, int flags)
goto err_set_sock; goto err_set_sock;
} }
if (dump_packet_cmsg(&msg, &pe)) if (dump_packet_cmsg(&msg, &pe, flags))
goto err_set_sock; goto err_set_sock;
ret = pb_write_one(img_from_set(glob_imgset, CR_FD_SK_QUEUES), &pe, PB_SK_QUEUES); ret = pb_write_one(img_from_set(glob_imgset, CR_FD_SK_QUEUES), &pe, PB_SK_QUEUES);
@ -311,6 +367,7 @@ static int send_one_pkt(int fd, struct sk_packet *pkt)
SkPacketEntry *entry = pkt->entry; SkPacketEntry *entry = pkt->entry;
struct msghdr mh = {}; struct msghdr mh = {};
struct iovec iov; struct iovec iov;
char cmsg[CMSG_MAX_SIZE];
mh.msg_iov = &iov; mh.msg_iov = &iov;
mh.msg_iovlen = 1; mh.msg_iovlen = 1;
@ -330,6 +387,23 @@ static int send_one_pkt(int fd, struct sk_packet *pkt)
* boundaries messages should be saved. * boundaries messages should be saved.
*/ */
if (entry->ucred) {
struct ucred *ucred;
struct cmsghdr *ch;
mh.msg_control = cmsg;
mh.msg_controllen = sizeof(cmsg);
ch = CMSG_FIRSTHDR(&mh);
ch->cmsg_len = CMSG_LEN(sizeof(struct ucred));
ch->cmsg_level = SOL_SOCKET;
ch->cmsg_type = SCM_CREDENTIALS;
ucred = (struct ucred *)CMSG_DATA(ch);
ucred->pid = entry->ucred->pid;
ucred->uid = entry->ucred->uid;
ucred->gid = entry->ucred->gid;
mh.msg_controllen = CMSG_SPACE(sizeof(struct ucred));
}
ret = sendmsg(fd, &mh, 0); ret = sendmsg(fd, &mh, 0);
xfree(pkt->data); xfree(pkt->data);
xfree(pkt->scm); xfree(pkt->scm);

View file

@ -408,7 +408,7 @@ static int dump_one_unix_fd(int lfd, uint32_t id, const struct fd_parms *p)
* (i stands for in-flight, cons -- for connections) things. * (i stands for in-flight, cons -- for connections) things.
*/ */
if (sk->rqlen != 0 && sk->state != TCP_LISTEN) { if (sk->rqlen != 0 && sk->state != TCP_LISTEN) {
if (dump_sk_queue(lfd, id, 0)) if (dump_sk_queue(lfd, id, SK_QUEUE_REAL_PID))
goto err; goto err;
} }

View file

@ -7,12 +7,17 @@ message scm_entry {
repeated uint32 rights = 2; repeated uint32 rights = 2;
} }
message sk_ucred_entry {
required uint32 uid = 1;
required uint32 gid = 2;
required uint32 pid = 3;
}
message sk_packet_entry { message sk_packet_entry {
required uint32 id_for = 1; required uint32 id_for = 1;
required uint32 length = 2; required uint32 length = 2;
// Reserved for message address // Reserved for message address
// optional bytes addr = 3; // optional bytes addr = 3;
repeated scm_entry scm = 4; repeated scm_entry scm = 4;
// Reserved for ucred restore optional sk_ucred_entry ucred = 128;
// optional sk_ucred_entry ucred = 128;
} }