socket-util: add new getpeergroups() call
authorLennart Poettering <lennart@poettering.net>
Sat, 30 Dec 2017 13:02:36 +0000 (14:02 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 4 Jan 2018 12:27:27 +0000 (13:27 +0100)
It's a wrapper around the new SO_PEERGROUPS sockopt, similar in style as
getpeersec() and getpeercred().

src/basic/missing.h
src/basic/socket-util.c
src/basic/socket-util.h
src/test/test-socket-util.c

index a6b10b1..1280e6c 100644 (file)
@@ -621,6 +621,10 @@ struct btrfs_ioctl_quota_ctl_args {
 #  define SO_REUSEPORT 15
 #endif
 
+#ifndef SO_PEERGROUPS
+#  define SO_PEERGROUPS 59
+#endif
+
 #ifndef EVIOCREVOKE
 #  define EVIOCREVOKE _IOW('E', 0x91, int)
 #endif
index 54d526a..56e0e8e 100644 (file)
@@ -1019,6 +1019,39 @@ int getpeersec(int fd, char **ret) {
         return 0;
 }
 
+int getpeergroups(int fd, gid_t **ret) {
+        socklen_t n = sizeof(gid_t) * 64;
+        _cleanup_free_ gid_t *d = NULL;
+
+        assert(fd);
+        assert(ret);
+
+        for (;;) {
+                d = malloc(n);
+                if (!d)
+                        return -ENOMEM;
+
+                if (getsockopt(fd, SOL_SOCKET, SO_PEERGROUPS, d, &n) >= 0)
+                        break;
+
+                if (errno != ERANGE)
+                        return -errno;
+
+                d = mfree(d);
+        }
+
+        assert_se(n % sizeof(gid_t) == 0);
+        n /= sizeof(gid_t);
+
+        if ((socklen_t) (int) n != n)
+                return -E2BIG;
+
+        *ret = d;
+        d = NULL;
+
+        return (int) n;
+}
+
 int send_one_fd_sa(
                 int transport_fd,
                 int fd,
index ba5be9b..83af91d 100644 (file)
@@ -138,6 +138,7 @@ bool address_label_valid(const char *p);
 
 int getpeercred(int fd, struct ucred *ucred);
 int getpeersec(int fd, char **ret);
+int getpeergroups(int fd, gid_t **ret);
 
 int send_one_fd_sa(int transport_fd,
                    int fd,
index 6a91baf..201ce66 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <sys/types.h>
+#include <unistd.h>
+#include <grp.h>
+
 #include "alloc-util.h"
 #include "async.h"
 #include "fd-util.h"
@@ -474,6 +478,68 @@ static void test_in_addr_is_multicast(void) {
         assert_se(in_addr_is_multicast(f, &b) == 0);
 }
 
+static void test_getpeercred_getpeergroups(void) {
+        int r;
+
+        r = safe_fork("(getpeercred)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
+        assert_se(r >= 0);
+
+        if (r == 0) {
+                static const gid_t gids[] = { 3, 4, 5, 6, 7 };
+                gid_t *test_gids;
+                gid_t *peer_groups = NULL;
+                size_t n_test_gids;
+                uid_t test_uid;
+                gid_t test_gid;
+                struct ucred ucred;
+                int pair[2];
+
+                if (geteuid() == 0) {
+                        test_uid = 1;
+                        test_gid = 2;
+                        test_gids = (gid_t*) gids;
+                        n_test_gids = ELEMENTSOF(gids);
+
+                        assert_se(setgroups(n_test_gids, test_gids) >= 0);
+                        assert_se(setresgid(test_gid, test_gid, test_gid) >= 0);
+                        assert_se(setresuid(test_uid, test_uid, test_uid) >= 0);
+
+                } else {
+                        long ngroups_max;
+
+                        test_uid = getuid();
+                        test_gid = getgid();
+
+                        ngroups_max = sysconf(_SC_NGROUPS_MAX);
+                        assert(ngroups_max > 0);
+
+                        test_gids = newa(gid_t, ngroups_max);
+
+                        r = getgroups(ngroups_max, test_gids);
+                        assert_se(r >= 0);
+                        n_test_gids = (size_t) r;
+                }
+
+                assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) >= 0);
+
+                assert_se(getpeercred(pair[0], &ucred) >= 0);
+
+                assert_se(ucred.uid == test_uid);
+                assert_se(ucred.gid == test_gid);
+                assert_se(ucred.pid == getpid_cached());
+
+                r = getpeergroups(pair[0], &peer_groups);
+                assert_se(r >= 0 || IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT));
+
+                if (r >= 0) {
+                        assert_se((size_t) r == n_test_gids);
+                        assert_se(memcmp(peer_groups, test_gids, sizeof(gid_t) * n_test_gids) == 0);
+                }
+
+                safe_close_pair(pair);
+        }
+}
+
 int main(int argc, char *argv[]) {
 
         log_set_max_level(LOG_DEBUG);
@@ -502,5 +568,7 @@ int main(int argc, char *argv[]) {
 
         test_in_addr_is_multicast();
 
+        test_getpeercred_getpeergroups();
+
         return 0;
 }