mirror of
https://github.com/systemd/systemd
synced 2024-10-01 13:55:20 +00:00
sd-bus: add ability to connect to bus as a specific user
This commit is contained in:
parent
837eda0522
commit
fc772c61e8
|
@ -254,6 +254,9 @@ struct sd_bus {
|
|||
char *address;
|
||||
unsigned address_index;
|
||||
|
||||
uid_t connect_as_uid;
|
||||
gid_t connect_as_gid;
|
||||
|
||||
int last_connect_error;
|
||||
|
||||
enum bus_auth auth;
|
||||
|
|
|
@ -503,11 +503,38 @@ static int bus_socket_write_auth(sd_bus *b) {
|
|||
if (b->prefer_writev)
|
||||
k = writev(b->output_fd, b->auth_iovec + b->auth_index, ELEMENTSOF(b->auth_iovec) - b->auth_index);
|
||||
else {
|
||||
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control = {};
|
||||
|
||||
struct msghdr mh = {
|
||||
.msg_iov = b->auth_iovec + b->auth_index,
|
||||
.msg_iovlen = ELEMENTSOF(b->auth_iovec) - b->auth_index,
|
||||
};
|
||||
|
||||
if (uid_is_valid(b->connect_as_uid) || gid_is_valid(b->connect_as_gid)) {
|
||||
|
||||
/* If we shall connect under some specific UID/GID, then synthesize an
|
||||
* SCM_CREDENTIALS record accordingly. After all we want to adopt this UID/GID both
|
||||
* for SO_PEERCRED (where we have to fork()) and SCM_CREDENTIALS (where we can just
|
||||
* fake it via sendmsg()) */
|
||||
|
||||
struct ucred ucred = {
|
||||
.pid = getpid_cached(),
|
||||
.uid = uid_is_valid(b->connect_as_uid) ? b->connect_as_uid : getuid(),
|
||||
.gid = gid_is_valid(b->connect_as_gid) ? b->connect_as_gid : getgid(),
|
||||
};
|
||||
|
||||
mh.msg_control = &control;
|
||||
mh.msg_controllen = sizeof(control);
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&mh);
|
||||
*cmsg = (struct cmsghdr) {
|
||||
.cmsg_level = SOL_SOCKET,
|
||||
.cmsg_type = SCM_CREDENTIALS,
|
||||
.cmsg_len = CMSG_LEN(sizeof(struct ucred)),
|
||||
};
|
||||
|
||||
memcpy(CMSG_DATA(cmsg), &ucred, sizeof(struct ucred));
|
||||
}
|
||||
|
||||
k = sendmsg(b->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
|
||||
if (k < 0 && errno == ENOTSOCK) {
|
||||
b->prefer_writev = true;
|
||||
|
@ -949,6 +976,66 @@ static int bind_description(sd_bus *b, int fd, int family) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int connect_as(int fd, const struct sockaddr *sa, socklen_t salen, uid_t uid, gid_t gid) {
|
||||
_cleanup_(close_pairp) int pfd[2] = EBADF_PAIR;
|
||||
ssize_t n;
|
||||
int r;
|
||||
|
||||
/* Shortcut if we are not supposed to drop privileges */
|
||||
if (!uid_is_valid(uid) && !gid_is_valid(gid))
|
||||
return RET_NERRNO(connect(fd, sa, salen));
|
||||
|
||||
/* This changes identity to the specified uid/gid and issues connect() as that. This is useful to
|
||||
* make sure SO_PEERCRED reports the selected UID/GID rather than the usual one of the caller. */
|
||||
|
||||
if (pipe2(pfd, O_CLOEXEC) < 0)
|
||||
return -errno;
|
||||
|
||||
r = safe_fork("(sd-setresuid)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_WAIT, /* ret_pid= */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
/* child */
|
||||
|
||||
pfd[0] = safe_close(pfd[0]);
|
||||
|
||||
r = RET_NERRNO(setgroups(0, NULL));
|
||||
if (r < 0)
|
||||
goto child_finish;
|
||||
|
||||
if (gid_is_valid(gid)) {
|
||||
r = RET_NERRNO(setresgid(gid, gid, gid));
|
||||
if (r < 0)
|
||||
goto child_finish;
|
||||
}
|
||||
|
||||
if (uid_is_valid(uid)) {
|
||||
r = RET_NERRNO(setresuid(uid, uid, uid));
|
||||
if (r < 0)
|
||||
goto child_finish;
|
||||
}
|
||||
|
||||
r = RET_NERRNO(connect(fd, sa, salen));
|
||||
if (r < 0)
|
||||
goto child_finish;
|
||||
|
||||
r = 0;
|
||||
|
||||
child_finish:
|
||||
n = write(pfd[1], &r, sizeof(r));
|
||||
if (n != sizeof(r))
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
n = read(pfd[0], &r, sizeof(r));
|
||||
if (n != sizeof(r))
|
||||
return -EIO;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int bus_socket_connect(sd_bus *b) {
|
||||
bool inotify_done = false;
|
||||
int r;
|
||||
|
@ -980,8 +1067,9 @@ int bus_socket_connect(sd_bus *b) {
|
|||
b->output_fd = b->input_fd;
|
||||
bus_socket_setup(b);
|
||||
|
||||
if (connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size) < 0) {
|
||||
if (errno == EINPROGRESS) {
|
||||
r = connect_as(b->input_fd, &b->sockaddr.sa, b->sockaddr_size, b->connect_as_uid, b->connect_as_gid);
|
||||
if (r < 0) {
|
||||
if (r == -EINPROGRESS) {
|
||||
|
||||
/* If we have any inotify watches open, close them now, we don't need them anymore, as
|
||||
* we have successfully initiated a connection */
|
||||
|
@ -994,7 +1082,7 @@ int bus_socket_connect(sd_bus *b) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (IN_SET(errno, ENOENT, ECONNREFUSED) && /* ENOENT → unix socket doesn't exist at all; ECONNREFUSED → unix socket stale */
|
||||
if (IN_SET(r, -ENOENT, -ECONNREFUSED) && /* ENOENT → unix socket doesn't exist at all; ECONNREFUSED → unix socket stale */
|
||||
b->watch_bind &&
|
||||
b->sockaddr.sa.sa_family == AF_UNIX &&
|
||||
b->sockaddr.un.sun_path[0] != 0) {
|
||||
|
@ -1022,7 +1110,7 @@ int bus_socket_connect(sd_bus *b) {
|
|||
inotify_done = true;
|
||||
|
||||
} else
|
||||
return -errno;
|
||||
return r;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -259,6 +259,8 @@ _public_ int sd_bus_new(sd_bus **ret) {
|
|||
.ucred = UCRED_INVALID,
|
||||
.pidfd = -EBADF,
|
||||
.runtime_scope = _RUNTIME_SCOPE_INVALID,
|
||||
.connect_as_uid = UID_INVALID,
|
||||
.connect_as_gid = GID_INVALID,
|
||||
};
|
||||
|
||||
/* We guarantee that wqueue always has space for at least one entry */
|
||||
|
@ -716,7 +718,7 @@ static void skip_address_key(const char **p) {
|
|||
}
|
||||
|
||||
static int parse_unix_address(sd_bus *b, const char **p, char **guid) {
|
||||
_cleanup_free_ char *path = NULL, *abstract = NULL;
|
||||
_cleanup_free_ char *path = NULL, *abstract = NULL, *uids = NULL, *gids = NULL;
|
||||
size_t l;
|
||||
int r;
|
||||
|
||||
|
@ -744,6 +746,18 @@ static int parse_unix_address(sd_bus *b, const char **p, char **guid) {
|
|||
else if (r > 0)
|
||||
continue;
|
||||
|
||||
r = parse_address_key(p, "uid", &uids);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if (r > 0)
|
||||
continue;
|
||||
|
||||
r = parse_address_key(p, "gid", &gids);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if (r > 0)
|
||||
continue;
|
||||
|
||||
skip_address_key(p);
|
||||
}
|
||||
|
||||
|
@ -780,6 +794,17 @@ static int parse_unix_address(sd_bus *b, const char **p, char **guid) {
|
|||
b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
|
||||
}
|
||||
|
||||
if (uids) {
|
||||
r = parse_uid(uids, &b->connect_as_uid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
if (gids) {
|
||||
r = parse_gid(gids, &b->connect_as_gid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
b->is_local = true;
|
||||
|
||||
return 0;
|
||||
|
|
Loading…
Reference in a new issue