NetworkManager/src/nm-helpers/nm-daemon-helper.c
Beniamino Galvani 41e28b900f daemon-helper: add read-file-as-user
Add a new command to read the content of a file after switching to the
given user. This command can be used to enforce Unix filesystem
permissions when accessing a file on behalf of a user.
2025-12-12 12:38:48 +01:00

188 lines
4.5 KiB
C

/* SPDX-License-Identifier: LGPL-2.1-or-later */
/* Copyright (C) 2021 Red Hat, Inc. */
#include "libnm-std-aux/nm-default-std.h"
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netdb.h>
#if defined(__GLIBC__)
#include <nss.h>
#endif
#include <stdarg.h>
enum {
RETURN_SUCCESS = 0,
RETURN_INVALID_CMD = 1,
RETURN_INVALID_ARGS = 2,
RETURN_ERROR = 3,
};
static char *
read_arg(void)
{
nm_auto_free char *arg = NULL;
size_t len = 0;
if (getdelim(&arg, &len, '\0', stdin) < 0)
return NULL;
return nm_steal_pointer(&arg);
}
static int
more_args(void)
{
nm_auto_free char *arg = NULL;
arg = read_arg();
return !!arg;
}
static int
cmd_version(void)
{
if (more_args())
return RETURN_INVALID_ARGS;
printf("1");
return RETURN_SUCCESS;
}
static int
cmd_resolve_address(void)
{
nm_auto_free char *address = NULL;
nm_auto_free char *services = NULL;
union {
struct sockaddr_in in;
struct sockaddr_in6 in6;
} sockaddr;
socklen_t sockaddr_size;
char name[NI_MAXHOST];
char *saveptr = NULL;
char *service;
char *str;
int ret;
address = read_arg();
if (!address)
return RETURN_INVALID_ARGS;
services = read_arg();
if (!services) {
/* Called by an old NM version which doesn't support the 'services'
* argument. Use both services. */
services = strdup("dns,files");
}
memset(&sockaddr, 0, sizeof(sockaddr));
if (inet_pton(AF_INET, address, &sockaddr.in.sin_addr) == 1) {
sockaddr.in.sin_family = AF_INET;
sockaddr_size = sizeof(struct sockaddr_in);
} else if (inet_pton(AF_INET6, address, &sockaddr.in6.sin6_addr) == 1) {
sockaddr.in6.sin6_family = AF_INET6;
sockaddr_size = sizeof(struct sockaddr_in6);
} else
return RETURN_INVALID_ARGS;
for (str = services; (service = strtok_r(str, ",", &saveptr)); str = NULL) {
if (!NM_IN_STRSET(service, "dns", "files")) {
fprintf(stderr, "Unsupported resolver service '%s'\n", service);
continue;
}
#if defined(__GLIBC__)
__nss_configure_lookup("hosts", service);
#endif
ret = getnameinfo((struct sockaddr *) &sockaddr,
sockaddr_size,
name,
sizeof(name),
NULL,
0,
NI_NAMEREQD);
if (ret == 0) {
printf("%s", name);
return RETURN_SUCCESS;
} else if (ret == EAI_SYSTEM) {
char buf[1024];
int errsv = errno;
fprintf(stderr,
"getnameinfo() via service '%s' failed: %d (%s), system error: %d (%s)\n",
service,
ret,
gai_strerror(ret),
errsv,
_nm_strerror_r(errsv, buf, sizeof(buf)));
} else {
fprintf(stderr,
"getnameinfo() via service '%s' failed: %d (%s)\n",
service,
ret,
gai_strerror(ret));
}
#if !defined(__GLIBC__)
break;
#endif
}
return RETURN_ERROR;
}
static int
cmd_read_file_as_user(void)
{
nm_auto_free char *user = NULL;
nm_auto_free char *filename = NULL;
char error[1024];
user = read_arg();
if (!user)
return RETURN_INVALID_ARGS;
filename = read_arg();
if (!filename)
return RETURN_INVALID_ARGS;
if (more_args())
return RETURN_INVALID_ARGS;
if (!nm_utils_set_effective_user(user, error, sizeof(error))) {
fprintf(stderr, "Failed to set effective user '%s': %s", user, error);
return RETURN_ERROR;
}
if (!nm_utils_read_file_to_stdout(filename, error, sizeof(error))) {
fprintf(stderr, "Failed to read file '%s' as user '%s': %s", filename, user, error);
return RETURN_ERROR;
}
return RETURN_SUCCESS;
}
int
main(int argc, char **argv)
{
nm_auto_free char *cmd = NULL;
cmd = read_arg();
if (!cmd)
return RETURN_INVALID_CMD;
if (nm_streq(cmd, "version"))
return cmd_version();
if (nm_streq(cmd, "resolve-address"))
return cmd_resolve_address();
if (nm_streq(cmd, "read-file-as-user"))
return cmd_read_file_as_user();
return RETURN_INVALID_CMD;
}