From 1fae13fbecf48d32ca46f81e87a316bd252ed084 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 29 Apr 2025 22:51:50 +0900 Subject: [PATCH 1/2] network/tuntap: verify User=/Group= settings earlier and ignore the settings if we cannot find the specified user/group. This also replaces get_user_creds()/get_group_creds() with userdb_by_name()/groupdb_by_name(). --- src/network/netdev/tuntap.c | 50 +++++++++++++++++++++---------------- src/network/netdev/tuntap.h | 9 ++++--- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/network/netdev/tuntap.c b/src/network/netdev/tuntap.c index 2d96682ca5..c07c71b8a0 100644 --- a/src/network/netdev/tuntap.c +++ b/src/network/netdev/tuntap.c @@ -16,7 +16,7 @@ #include "networkd-manager.h" #include "socket-util.h" #include "tuntap.h" -#include "user-util.h" +#include "userdb.h" #define TUN_DEV "/dev/net/tun" @@ -174,31 +174,14 @@ static int netdev_create_tuntap(NetDev *netdev) { return log_netdev_error_errno(netdev, errno, "TUNSETQUEUE failed: %m"); } - if (t->user_name) { - const char *user = t->user_name; - uid_t uid; - - r = get_user_creds(&user, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Cannot resolve user name %s: %m", t->user_name); - - if (ioctl(fd, TUNSETOWNER, uid) < 0) + if (t->uid != 0) + if (ioctl(fd, TUNSETOWNER, t->uid) < 0) return log_netdev_error_errno(netdev, errno, "TUNSETOWNER failed: %m"); - } - if (t->group_name) { - const char *group = t->group_name; - gid_t gid; - - r = get_group_creds(&group, &gid, USER_CREDS_ALLOW_MISSING); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Cannot resolve group name %s: %m", t->group_name); - - if (ioctl(fd, TUNSETGROUP, gid) < 0) + if (t->gid != 0) + if (ioctl(fd, TUNSETGROUP, t->gid) < 0) return log_netdev_error_errno(netdev, errno, "TUNSETGROUP failed: %m"); - } - if (ioctl(fd, TUNSETPERSIST, 1) < 0) return log_netdev_error_errno(netdev, errno, "TUNSETPERSIST failed: %m"); @@ -226,6 +209,9 @@ static void tuntap_done(NetDev *netdev) { } static int tuntap_verify(NetDev *netdev, const char *filename) { + TunTap *t = TUNTAP(netdev); + int r; + assert(netdev); if (netdev->mtu != 0) @@ -240,6 +226,26 @@ static int tuntap_verify(NetDev *netdev, const char *filename) { "Please set it in the corresponding .network file.", netdev_kind_to_string(netdev->kind), filename); + if (t->user_name) { + _cleanup_(user_record_unrefp) UserRecord *ur = NULL; + + r = userdb_by_name(t->user_name, /* match = */ NULL, USERDB_PARSE_NUMERIC, &ur); + if (r < 0) + log_netdev_warning_errno(netdev, r, "Cannot resolve user name %s, ignoring: %m", t->user_name); + else + t->uid = ur->uid; + } + + if (t->group_name) { + _cleanup_(group_record_unrefp) GroupRecord *gr = NULL; + + r = groupdb_by_name(t->group_name, /* match = */ NULL, USERDB_PARSE_NUMERIC, &gr); + if (r < 0) + log_netdev_warning_errno(netdev, r, "Cannot resolve group name %s, ignoring: %m", t->group_name); + else + t->gid = gr->gid; + } + return 0; } diff --git a/src/network/netdev/tuntap.h b/src/network/netdev/tuntap.h index e2de8eb6bb..f345548f95 100644 --- a/src/network/netdev/tuntap.h +++ b/src/network/netdev/tuntap.h @@ -1,20 +1,21 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -typedef struct TunTap TunTap; - #include "netdev.h" +#include "user-util.h" -struct TunTap { +typedef struct TunTap { NetDev meta; char *user_name; + uid_t uid; char *group_name; + gid_t gid; bool multi_queue; bool packet_info; bool vnet_hdr; bool keep_fd; -}; +} TunTap; DEFINE_NETDEV_CAST(TUN, TunTap); DEFINE_NETDEV_CAST(TAP, TunTap); From 940441b44c7040d62ae58b66bf124e9a0dae578d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 29 Apr 2025 23:16:02 +0900 Subject: [PATCH 2/2] network/tuntap: deny non-system users/groups from owning Tun/Tap interfaces This is analogous to #36123, but for Tun/Tap interfaces created by systemd-networkd. If a regular user account want to control a Tun/Tap interface, then assign the interface to a system group, e.g., vpn, and add the user to the group. Closes #37279. --- NEWS | 3 +++ man/systemd.netdev.xml | 8 ++++---- src/network/netdev/tuntap.c | 19 +++++++++++++++---- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index 8f6f77c70d..cc640eaa3d 100644 --- a/NEWS +++ b/NEWS @@ -46,6 +46,9 @@ CHANGES WITH 258 in spe: owned by a non-system user/group. It is recommended to check udev rules files with 'udevadm verify' and/or 'udevadm test' commands if the specified user/group in OWNER=/GROUP= are valid. + Similarly, systemd-networkd refuses User=/Group= settings with a + non-system user/group specified in .netdev files for Tun/Tap + interfaces. * systemd-cryptenroll, systemd-repart and systemd-creds no longer default to locking TPM2 enrollments to the current, literal value of diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index 9ea59a7763..3bfaf7c215 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -1970,16 +1970,16 @@ Ports=eth2 User= - User to grant access to the - /dev/net/tun device. + User to grant access to the /dev/net/tun device. The specified + user must be a system user. Group= - Group to grant access to the - /dev/net/tun device. + Group to grant access to the /dev/net/tun device. The specified + group must be a system group. diff --git a/src/network/netdev/tuntap.c b/src/network/netdev/tuntap.c index c07c71b8a0..48bc007970 100644 --- a/src/network/netdev/tuntap.c +++ b/src/network/netdev/tuntap.c @@ -10,6 +10,7 @@ #include #include "alloc-util.h" +#include "bitfield.h" #include "daemon-util.h" #include "fd-util.h" #include "networkd-link.h" @@ -228,9 +229,14 @@ static int tuntap_verify(NetDev *netdev, const char *filename) { if (t->user_name) { _cleanup_(user_record_unrefp) UserRecord *ur = NULL; + UserDBMatch match = USERDB_MATCH_NULL; - r = userdb_by_name(t->user_name, /* match = */ NULL, USERDB_PARSE_NUMERIC, &ur); - if (r < 0) + match.disposition_mask = INDEX_TO_MASK(uint64_t, USER_SYSTEM); + + r = userdb_by_name(t->user_name, &match, USERDB_PARSE_NUMERIC, &ur); + if (r == -ENOEXEC) + log_netdev_warning_errno(netdev, r, "User %s is not a system user, ignoring.", t->user_name); + else if (r < 0) log_netdev_warning_errno(netdev, r, "Cannot resolve user name %s, ignoring: %m", t->user_name); else t->uid = ur->uid; @@ -238,9 +244,14 @@ static int tuntap_verify(NetDev *netdev, const char *filename) { if (t->group_name) { _cleanup_(group_record_unrefp) GroupRecord *gr = NULL; + UserDBMatch match = USERDB_MATCH_NULL; - r = groupdb_by_name(t->group_name, /* match = */ NULL, USERDB_PARSE_NUMERIC, &gr); - if (r < 0) + match.disposition_mask = INDEX_TO_MASK(uint64_t, USER_SYSTEM); + + r = groupdb_by_name(t->group_name, &match, USERDB_PARSE_NUMERIC, &gr); + if (r == -ENOEXEC) + log_netdev_warning_errno(netdev, r, "Group %s is not a system group, ignoring.", t->group_name); + else if (r < 0) log_netdev_warning_errno(netdev, r, "Cannot resolve group name %s, ignoring: %m", t->group_name); else t->gid = gr->gid;