diff options
author | Laurent Vivier <laurent@vivier.eu> | 2020-02-04 22:19:01 +0100 |
---|---|---|
committer | Laurent Vivier <laurent@vivier.eu> | 2020-02-12 18:56:45 +0100 |
commit | 6d485a55d0cd8fbb8b4337b298f79ddb0c2a5511 (patch) | |
tree | f1a9d76265211a101344ae481773c13d44d751ba /linux-user | |
parent | 6bc024e713fd35eb5fddbe16acd8dc92d27872a9 (diff) | |
download | qemu-6d485a55d0cd8fbb8b4337b298f79ddb0c2a5511.zip qemu-6d485a55d0cd8fbb8b4337b298f79ddb0c2a5511.tar.gz qemu-6d485a55d0cd8fbb8b4337b298f79ddb0c2a5511.tar.bz2 |
linux-user: implement TARGET_SO_PEERSEC
"The purpose of this option is to allow an application to obtain the
security credentials of a Unix stream socket peer. It is analogous to
SO_PEERCRED (which provides authentication using standard Unix credentials
of pid, uid and gid), and extends this concept to other security
models." -- https://lwn.net/Articles/62370/
Until now it was passed to the kernel with an "int" argument and
fails when it was supported by the host because the parameter is
like a filename: it is always a \0-terminated string with no embedded
\0 characters, but is not guaranteed to be ASCII or UTF-8.
I've tested the option with the following program:
/*
* cc -o getpeercon getpeercon.c
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(void)
{
int fd;
struct sockaddr_in server, addr;
int ret;
socklen_t len;
char buf[256];
fd = socket(PF_INET, SOCK_STREAM, 0);
if (fd == -1) {
perror("socket");
return 1;
}
server.sin_family = AF_INET;
inet_aton("127.0.0.1", &server.sin_addr);
server.sin_port = htons(40390);
connect(fd, (struct sockaddr*)&server, sizeof(server));
len = sizeof(buf);
ret = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, buf, &len);
if (ret == -1) {
perror("getsockopt");
return 1;
}
printf("%d %s\n", len, buf);
return 0;
}
On host:
$ ./getpeercon
33 system_u:object_r:unlabeled_t:s0
With qemu-aarch64/bionic without the patch:
$ ./getpeercon
getsockopt: Numerical result out of range
With the patch:
$ ./getpeercon
33 system_u:object_r:unlabeled_t:s0
Bug: https://bugs.launchpad.net/qemu/+bug/1823790
Reported-by: Matthias Lüscher <lueschem@gmail.com>
Tested-by: Matthias Lüscher <lueschem@gmail.com>
Signed-off-by: Laurent Vivier <laurent@vivier.eu>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Message-Id: <20200204211901.1731821-1-laurent@vivier.eu>
Diffstat (limited to 'linux-user')
-rw-r--r-- | linux-user/syscall.c | 22 |
1 files changed, 22 insertions, 0 deletions
diff --git a/linux-user/syscall.c b/linux-user/syscall.c index d60142f..c930577 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -2344,6 +2344,28 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, } break; } + case TARGET_SO_PEERSEC: { + char *name; + + if (get_user_u32(len, optlen)) { + return -TARGET_EFAULT; + } + if (len < 0) { + return -TARGET_EINVAL; + } + name = lock_user(VERIFY_WRITE, optval_addr, len, 0); + if (!name) { + return -TARGET_EFAULT; + } + lv = len; + ret = get_errno(getsockopt(sockfd, level, SO_PEERSEC, + name, &lv)); + if (put_user_u32(lv, optlen)) { + ret = -TARGET_EFAULT; + } + unlock_user(name, optval_addr, lv); + break; + } case TARGET_SO_LINGER: { struct linger lg; |