Compare commits

...

10 Commits

Author SHA1 Message Date
1247f5d3e5 util.h: include missing limits.h 2024-05-26 08:26:49 +00:00
Stefano Brivio
765eb0bf16 apparmor: Fix comments after PID file and AF_UNIX socket creation refactoring
Now:
- we don't open the PID file in main() anymore
- PID file and AF_UNIX socket are opened by pidfile_open() and
  tap_sock_unix_open()
- write_pidfile() becomes pidfile_write()

Reported-by: Richard W.M. Jones <rjones@redhat.com>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Acked-by: Richard W.M. Jones <rjones@redhat.com>
2024-05-23 16:44:21 +02:00
Stefano Brivio
0608ec42f2 conf, passt.h: Rename pid_file in struct ctx to pidfile
We have pidfile_fd now, pid_file_fd would be quite ugly.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Reviewed-by: Richard W.M. Jones <rjones@redhat.com>
2024-05-23 16:44:14 +02:00
Stefano Brivio
c9b2413465 conf, passt, tap: Open socket and PID files before switching UID/GID
Otherwise, if the user runs us as root, and gives us paths that are
only accessible by root, we'll fail to open them, which might in turn
encourage users to change permissions or ownerships: definitely a bad
idea in terms of security.

Reported-by: Minxi Hou <mhou@redhat.com>
Reported-by: Richard W.M. Jones <rjones@redhat.com>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Acked-by: Richard W.M. Jones <rjones@redhat.com>
2024-05-23 16:43:26 +02:00
Stefano Brivio
ba23b05545 passt, util: Move opening of PID file to its own function
We won't call it from main() any longer: move it.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Reviewed-by: Richard W.M. Jones <rjones@redhat.com>
2024-05-23 16:43:13 +02:00
Stefano Brivio
57d8aa8ffe util: Rename write_pidfile() to pidfile_write()
As I'm adding pidfile_open() in the next patch. The function comment
didn't match, by the way.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Reviewed-by: Richard W.M. Jones <rjones@redhat.com>
2024-05-23 16:43:05 +02:00
Stefano Brivio
cbca08cd38 tap: Split tap_sock_unix_init() into opening and listening parts
We'll need to open and bind the socket a while before listening to it,
so split that into two different functions. No functional changes
intended.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Reviewed-by: Richard W.M. Jones <rjones@redhat.com>
2024-05-23 16:42:43 +02:00
Stefano Brivio
fcfb592adc passt, tap: Don't use -1 as uninitialised value for fd_tap_listen
This is a remnant from the time we kept access to the original
filesystem and we could reinitialise the listening AF_UNIX socket.

Since commit 0515adceaa ("passt, pasta: Namespace-based sandboxing,
defer seccomp policy application"), however, we can't re-bind the
listening socket once we're up and running.

Drop the -1 initalisation and the corresponding check.

Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
2024-05-23 16:42:27 +02:00
Stefano Brivio
d02bb6ca05 tap: Move all-ones initialisation of mac_guest to tap_sock_init()
It has nothing to do with tap_sock_unix_init(). It used to be there as
that function could be called multiple times per passt instance, but
it's not the case anymore.

This also takes care of the fact that, with --fd, we wouldn't set the
initial MAC address, so we would need to wait for the guest to send us
an ARP packet before we could exchange data.

Fixes: 6b4e68383c ("passt, tap: Add --fd option")
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Acked-by: Richard W.M. Jones <rjones@redhat.com>
2024-05-23 16:42:06 +02:00
Stefano Brivio
45b8632dcc conf: Don't lecture user about starting us as root
libguestfs tools have a good reason to run as root: if the guest image
is owned by root, it would be counterproductive to encourage users to
invoke them as non-root, as it would require changing permissions or
ownership of the image file.

And if they run as root, we'll start as root, too. Warn users we'll
switch to 'nobody', but don't tell them what to do.

Reported-by: Richard W.M. Jones <rjones@redhat.com>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Reviewed-by: Richard W.M. Jones <rjones@redhat.com>
2024-05-23 16:40:33 +02:00
10 changed files with 106 additions and 52 deletions

23
conf.c
View File

@@ -38,6 +38,7 @@
#include "ip.h"
#include "passt.h"
#include "netlink.h"
#include "tap.h"
#include "udp.h"
#include "tcp.h"
#include "pasta.h"
@@ -1093,7 +1094,7 @@ static void conf_ugid(char *runas, uid_t *uid, gid_t *gid)
return;
/* ...otherwise use nobody:nobody */
warn("Don't run as root. Changing to nobody...");
warn("Started as root, will change to nobody.");
{
#ifndef GLIBC_NO_STATIC_NSS
const struct passwd *pw;
@@ -1113,6 +1114,18 @@ static void conf_ugid(char *runas, uid_t *uid, gid_t *gid)
}
}
/**
* conf_open_files() - Open files as requested by configuration
* @c: Execution context
*/
static void conf_open_files(struct ctx *c)
{
if (c->mode == MODE_PASST && c->fd_tap == -1)
c->fd_tap_listen = tap_sock_unix_open(c->sock_path);
c->pidfile_fd = pidfile_open(c->pidfile);
}
/**
* conf() - Process command-line arguments and set configuration
* @c: Execution context
@@ -1443,12 +1456,12 @@ void conf(struct ctx *c, int argc, char **argv)
break;
case 'P':
if (*c->pid_file)
if (*c->pidfile)
die("Multiple --pid options given");
ret = snprintf(c->pid_file, sizeof(c->pid_file), "%s",
ret = snprintf(c->pidfile, sizeof(c->pidfile), "%s",
optarg);
if (ret <= 0 || ret >= (int)sizeof(c->pid_file))
if (ret <= 0 || ret >= (int)sizeof(c->pidfile))
die("Invalid PID file: %s", optarg);
break;
@@ -1712,6 +1725,8 @@ void conf(struct ctx *c, int argc, char **argv)
else if (optind != argc)
die("Extra non-option argument: %s", argv[optind]);
conf_open_files(c); /* Before any possible setuid() / setgid() */
isolate_user(uid, gid, !netns_only, userns, c->mode);
if (c->pasta_conf_ns)

View File

@@ -27,7 +27,7 @@
@{PROC}/@{pid}/net/udp r,
@{PROC}/@{pid}/net/udp6 r,
@{run}/user/@{uid}/** rw, # pasta_open_ns(), main()
@{run}/user/@{uid}/** rw, # pasta_open_ns()
@{PROC}/[0-9]*/ns/ r, # pasta_netns_quit_init(),
@{PROC}/[0-9]*/ns/net r, # pasta_wait_for_ns(),

View File

@@ -19,9 +19,12 @@ profile passt /usr/bin/passt{,.avx2} {
include <abstractions/passt>
# Alternatively: include <abstractions/user-tmp>
owner /tmp/** w, # tap_sock_unix_init(), pcap(),
# write_pidfile(),
owner /tmp/** w, # tap_sock_unix_open(),
# tap_sock_unix_init(), pcap(),
# pidfile_open(),
# pidfile_write(),
# logfile_init()
owner @{HOME}/** w, # pcap(), write_pidfile()
owner @{HOME}/** w, # pcap(), pidfile_open(),
# pidfile_write()
}

View File

@@ -19,10 +19,13 @@ profile pasta /usr/bin/pasta{,.avx2} flags=(attach_disconnected) {
include <abstractions/pasta>
# Alternatively: include <abstractions/user-tmp>
/tmp/** rw, # tap_sock_unix_init(), pcap(),
# write_pidfile(),
/tmp/** rw, # tap_sock_unix_open(),
# tap_sock_unix_init(), pcap(),
# pidfile_open(),
# pidfile_write(),
# logfile_init(),
# pasta_open_ns()
owner @{HOME}/** w, # pcap(), write_pidfile()
owner @{HOME}/** w, # pcap(), pidfile_open(),
# pidfile_write()
}

17
passt.c
View File

@@ -199,9 +199,9 @@ void exit_handler(int signal)
*/
int main(int argc, char **argv)
{
int nfds, i, devnull_fd = -1, pidfile_fd = -1;
struct epoll_event events[EPOLL_EVENTS];
char *log_name, argv0[PATH_MAX], *name;
int nfds, i, devnull_fd = -1;
struct ctx c = { 0 };
struct rlimit limit;
struct timespec now;
@@ -211,7 +211,7 @@ int main(int argc, char **argv)
isolate_initial();
c.pasta_netns_fd = c.fd_tap = c.fd_tap_listen = -1;
c.pasta_netns_fd = c.fd_tap = c.pidfile_fd = -1;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
@@ -299,15 +299,6 @@ int main(int argc, char **argv)
}
}
if (*c.pid_file) {
if ((pidfile_fd = open(c.pid_file,
O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC,
S_IRUSR | S_IWUSR)) < 0) {
perror("PID file open");
exit(EXIT_FAILURE);
}
}
if (isolate_prefork(&c))
die("Failed to sandbox process, exiting");
@@ -315,9 +306,9 @@ int main(int argc, char **argv)
__openlog(log_name, 0, LOG_DAEMON);
if (!c.foreground)
__daemon(pidfile_fd, devnull_fd);
__daemon(c.pidfile_fd, devnull_fd);
else
write_pidfile(pidfile_fd, getpid());
pidfile_write(c.pidfile_fd, getpid());
if (pasta_child_pid)
kill(pasta_child_pid, SIGUSR1);

View File

@@ -184,7 +184,8 @@ struct ip6_ctx {
* @nofile: Maximum number of open files (ulimit -n)
* @sock_path: Path for UNIX domain socket
* @pcap: Path for packet capture file
* @pid_file: Path to PID file, empty string if not configured
* @pidfile: Path to PID file, empty string if not configured
* @pidfile_fd: File descriptor for PID file, -1 if none
* @pasta_netns_fd: File descriptor for network namespace in pasta mode
* @no_netns_quit: In pasta mode, don't exit if fs-bound namespace is gone
* @netns_base: Base name for fs-bound namespace, if any, in pasta mode
@@ -234,7 +235,10 @@ struct ctx {
int nofile;
char sock_path[UNIX_PATH_MAX];
char pcap[PATH_MAX];
char pid_file[PATH_MAX];
char pidfile[PATH_MAX];
int pidfile_fd;
int one_off;
int pasta_netns_fd;

55
tap.c
View File

@@ -1095,14 +1095,14 @@ restart:
}
/**
* tap_sock_unix_init() - Create and bind AF_UNIX socket, listen for connection
* @c: Execution context
* tap_sock_unix_open() - Create and bind AF_UNIX socket
* @sock_path: Socket path. If empty, set on return (UNIX_SOCK_PATH as prefix)
*
* Return: socket descriptor on success, won't return on failure
*/
static void tap_sock_unix_init(struct ctx *c)
int tap_sock_unix_open(char *sock_path)
{
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
union epoll_ref ref = { .type = EPOLL_TYPE_TAP_LISTEN };
struct epoll_event ev = { 0 };
struct sockaddr_un addr = {
.sun_family = AF_UNIX,
};
@@ -1111,18 +1111,12 @@ static void tap_sock_unix_init(struct ctx *c)
if (fd < 0)
die("UNIX socket: %s", strerror(errno));
/* In passt mode, we don't know the guest's MAC until it sends
* us packets. Use the broadcast address so our first packets
* will reach it.
*/
memset(&c->mac_guest, 0xff, sizeof(c->mac_guest));
for (i = 1; i < UNIX_SOCK_MAX; i++) {
char *path = addr.sun_path;
int ex, ret;
if (*c->sock_path)
memcpy(path, c->sock_path, UNIX_PATH_MAX);
if (*sock_path)
memcpy(path, sock_path, UNIX_PATH_MAX);
else
snprintf(path, UNIX_PATH_MAX - 1, UNIX_SOCK_PATH, i);
@@ -1133,7 +1127,7 @@ static void tap_sock_unix_init(struct ctx *c)
ret = connect(ex, (const struct sockaddr *)&addr, sizeof(addr));
if (!ret || (errno != ENOENT && errno != ECONNREFUSED &&
errno != EACCES)) {
if (*c->sock_path)
if (*sock_path)
die("Socket path %s already in use", path);
close(ex);
@@ -1143,25 +1137,39 @@ static void tap_sock_unix_init(struct ctx *c)
unlink(path);
if (!bind(fd, (const struct sockaddr *)&addr, sizeof(addr)) ||
*c->sock_path)
*sock_path)
break;
}
if (i == UNIX_SOCK_MAX)
die("UNIX socket bind: %s", strerror(errno));
info("UNIX domain socket bound at %s\n", addr.sun_path);
info("UNIX domain socket bound at %s", addr.sun_path);
if (!*sock_path)
memcpy(sock_path, addr.sun_path, UNIX_PATH_MAX);
listen(fd, 0);
return fd;
}
ref.fd = c->fd_tap_listen = fd;
/**
* tap_sock_unix_init() - Start listening for connections on AF_UNIX socket
* @c: Execution context
*/
static void tap_sock_unix_init(struct ctx *c)
{
union epoll_ref ref = { .type = EPOLL_TYPE_TAP_LISTEN };
struct epoll_event ev = { 0 };
listen(c->fd_tap_listen, 0);
ref.fd = c->fd_tap_listen;
ev.events = EPOLLIN | EPOLLET;
ev.data.u64 = ref.u64;
epoll_ctl(c->epollfd, EPOLL_CTL_ADD, c->fd_tap_listen, &ev);
info("You can now start qemu (>= 7.2, with commit 13c6be96618c):");
info("\nYou can now start qemu (>= 7.2, with commit 13c6be96618c):");
info(" kvm ... -device virtio-net-pci,netdev=s -netdev stream,id=s,server=off,addr.type=unix,addr.path=%s",
addr.sun_path);
c->sock_path);
info("or qrap, for earlier qemu versions:");
info(" ./qrap 5 kvm ... -net socket,fd=5 -net nic,model=virtio");
}
@@ -1310,8 +1318,13 @@ void tap_sock_init(struct ctx *c)
}
if (c->mode == MODE_PASST) {
if (c->fd_tap_listen == -1)
tap_sock_unix_init(c);
/* In passt mode, we don't know the guest's MAC address until it
* sends us packets. Use the broadcast address so that our
* first packets will reach it.
*/
memset(&c->mac_guest, 0xff, sizeof(c->mac_guest));
} else {
tap_sock_tun_init(c);
}

1
tap.h
View File

@@ -68,6 +68,7 @@ void tap_handler_pasta(struct ctx *c, uint32_t events,
const struct timespec *now);
void tap_handler_passt(struct ctx *c, uint32_t events,
const struct timespec *now);
int tap_sock_unix_open(char *sock_path);
void tap_sock_init(struct ctx *c);
#endif /* TAP_H */

28
util.c
View File

@@ -380,11 +380,11 @@ int open_in_ns(const struct ctx *c, const char *path, int flags)
}
/**
* pid_file() - Write PID to file, if requested to do so, and close it
* pidfile_write() - Write PID to file, if requested to do so, and close it
* @fd: Open PID file descriptor, closed on exit, -1 to skip writing it
* @pid: PID value to write
*/
void write_pidfile(int fd, pid_t pid)
void pidfile_write(int fd, pid_t pid)
{
char pid_buf[12];
int n;
@@ -402,6 +402,28 @@ void write_pidfile(int fd, pid_t pid)
close(fd);
}
/**
* pidfile_open() - Open PID file if needed
* @path: Path for PID file, empty string if no PID file is requested
*
* Return: descriptor for PID file, -1 if path is NULL, won't return on failure
*/
int pidfile_open(const char *path)
{
int fd;
if (!*path)
return -1;
if ((fd = open(path, O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC,
S_IRUSR | S_IWUSR)) < 0) {
perror("PID file open");
exit(EXIT_FAILURE);
}
return fd;
}
/**
* __daemon() - daemon()-like function writing PID file before parent exits
* @pidfile_fd: Open PID file descriptor
@@ -419,7 +441,7 @@ int __daemon(int pidfile_fd, int devnull_fd)
}
if (pid) {
write_pidfile(pidfile_fd, pid);
pidfile_write(pidfile_fd, pid);
exit(EXIT_SUCCESS);
}

4
util.h
View File

@@ -6,6 +6,7 @@
#ifndef UTIL_H
#define UTIL_H
#include <limits.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
@@ -156,7 +157,8 @@ char *line_read(char *buf, size_t len, int fd);
void ns_enter(const struct ctx *c);
bool ns_is_init(void);
int open_in_ns(const struct ctx *c, const char *path, int flags);
void write_pidfile(int fd, pid_t pid);
int pidfile_open(const char *path);
void pidfile_write(int fd, pid_t pid);
int __daemon(int pidfile_fd, int devnull_fd);
int fls(unsigned long x);
int write_file(const char *path, const char *buf);