]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
include: add getgrgid_r()
authorChristian Brauner <christian.brauner@ubuntu.com>
Thu, 14 Jun 2018 22:17:01 +0000 (00:17 +0200)
committerChristian Brauner <christian.brauner@ubuntu.com>
Mon, 10 Dec 2018 08:19:38 +0000 (09:19 +0100)
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
configure.ac
src/include/getgrgid_r.c [new file with mode: 0644]
src/include/getgrgid_r.h [new file with mode: 0644]
src/lxc/Makefile.am

index a0eeac84d788acf925a0b234a4d0ff590fbbe3bf..d58c84c66c3bf4d2d0176c6d89983858187aa2b9 100644 (file)
@@ -660,6 +660,10 @@ AC_CHECK_FUNCS(pthread_atfork)
 AC_CHECK_FUNCS(statvfs)
 AC_CHECK_LIB(util, openpty)
 AC_CHECK_FUNCS([openpty hasmntopt setmntent endmntent utmpxname])
+AC_CHECK_FUNCS([getgrgid_r],
+       AM_CONDITIONAL(HAVE_GETGRGID_R, true)
+       AC_DEFINE(HAVE_GETGRGID_R,1,[Have getgrgid_r]),
+       AM_CONDITIONAL(HAVE_GETGRGID_R, false))
 AC_CHECK_FUNCS([getline],
        AM_CONDITIONAL(HAVE_GETLINE, true)
        AC_DEFINE(HAVE_GETLINE,1,[Have getline]),
@@ -672,6 +676,10 @@ AC_CHECK_FUNCS([fgetln],
        AM_CONDITIONAL(HAVE_FGETLN, true)
        AC_DEFINE(HAVE_FGETLN,1,[Have fgetln]),
        AM_CONDITIONAL(HAVE_FGETLN, false))
+AC_CHECK_FUNCS([pthread_setcancelstate],
+       AM_CONDITIONAL(HAVE_PTHREAD_SETCANCELSTATE, true)
+       AC_DEFINE(HAVE_PTHREAD_SETCANCELSTATE,1,[Have pthread_setcancelstate]),
+       AM_CONDITIONAL(HAVE_PTHREAD_SETCANCELSTATE, false))
 AC_CHECK_FUNCS([strlcpy],
        AM_CONDITIONAL(HAVE_STRLCPY, true)
        AC_DEFINE(HAVE_STRLCPY,1,[Have strlcpy]),
diff --git a/src/include/getgrgid_r.c b/src/include/getgrgid_r.c
new file mode 100644 (file)
index 0000000..378740f
--- /dev/null
@@ -0,0 +1,484 @@
+/* liblxcapi
+ *
+ * Copyright © 2018 Christian Brauner <christian.brauner@ubuntu.com>.
+ * Copyright © 2018 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * This function has been copied from musl.
+ */
+
+#define _GNU_SOURCE
+#include <byteswap.h>
+#include <errno.h>
+#include <grp.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#define LOGIN_NAME_MAX 256
+#define NSCDVERSION 2
+#define GETPWBYNAME 0
+#define GETPWBYUID 1
+#define GETGRBYNAME 2
+#define GETGRBYGID 3
+#define GETINITGR 15
+
+#define REQVERSION 0
+#define REQTYPE 1
+#define REQKEYLEN 2
+#define REQ_LEN 3
+
+#define PWVERSION 0
+#define PWFOUND 1
+#define PWNAMELEN 2
+#define PWPASSWDLEN 3
+#define PWUID 4
+#define PWGID 5
+#define PWGECOSLEN 6
+#define PWDIRLEN 7
+#define PWSHELLLEN 8
+#define PW_LEN 9
+
+#define GRVERSION 0
+#define GRFOUND 1
+#define GRNAMELEN 2
+#define GRPASSWDLEN 3
+#define GRGID 4
+#define GRMEMCNT 5
+#define GR_LEN 6
+
+#define INITGRVERSION 0
+#define INITGRFOUND 1
+#define INITGRNGRPS 2
+#define INITGR_LEN 3
+
+#define FIX(x) (gr->gr_##x = gr->gr_##x - line + buf)
+
+static unsigned atou(char **s)
+{
+       unsigned x;
+       for (x = 0; **s - '0' < 10U; ++*s)
+               x = 10 * x + (**s - '0');
+       return x;
+}
+
+static int __getgrent_a(FILE *f, struct group *gr, char **line, size_t *size,
+                       char ***mem, size_t *nmem, struct group **res)
+{
+       ssize_t l;
+       char *s, *mems;
+       size_t i;
+       int rv = 0;
+
+#ifdef HAVE_PTHREAD_SETCANCELSTATE
+       int cs;
+
+       pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+#endif
+
+       for (;;) {
+               if ((l = getline(line, size, f)) < 0) {
+                       rv = ferror(f) ? errno : 0;
+                       free(*line);
+                       *line = 0;
+                       gr = 0;
+                       goto end;
+               }
+               line[0][l - 1] = 0;
+
+               s = line[0];
+               gr->gr_name = s++;
+               if (!(s = strchr(s, ':')))
+                       continue;
+
+               *s++ = 0;
+               gr->gr_passwd = s;
+               if (!(s = strchr(s, ':')))
+                       continue;
+
+               *s++ = 0;
+               gr->gr_gid = atou(&s);
+               if (*s != ':')
+                       continue;
+
+               *s++ = 0;
+               mems = s;
+               break;
+       }
+
+       for (*nmem = !!*s; *s; s++)
+               if (*s == ',')
+                       ++*nmem;
+       free(*mem);
+       *mem = calloc(sizeof(char *), *nmem + 1);
+       if (!*mem) {
+               rv = errno;
+               free(*line);
+               *line = 0;
+               gr = 0;
+               goto end;
+       }
+       if (*mems) {
+               mem[0][0] = mems;
+               for (s = mems, i = 0; *s; s++)
+                       if (*s == ',')
+                               *s++ = 0, mem[0][++i] = s;
+               mem[0][++i] = 0;
+       } else {
+               mem[0][0] = 0;
+       }
+       gr->gr_mem = *mem;
+end:
+
+#ifdef HAVE_PTHREAD_SETCANCELSTATE
+       pthread_setcancelstate(cs, 0);
+#endif
+
+       *res = gr;
+       if (rv)
+               errno = rv;
+       return rv;
+}
+
+static char *itoa(char *p, uint32_t x)
+{
+       // number of digits in a uint32_t + NUL
+       p += 11;
+       *--p = 0;
+       do {
+               *--p = '0' + x % 10;
+               x /= 10;
+       } while (x);
+       return p;
+}
+
+static const struct {
+       short sun_family;
+       char sun_path[21];
+} addr = {AF_UNIX, "/var/run/nscd/socket"};
+
+static FILE *__nscd_query(int32_t req, const char *key, int32_t *buf,
+                         size_t len, int *swap)
+{
+       size_t i;
+       int fd;
+       FILE *f = 0;
+       int32_t req_buf[REQ_LEN] = {NSCDVERSION, req,
+                                   strnlen(key, LOGIN_NAME_MAX) + 1};
+       struct msghdr msg = {.msg_iov =
+                                (struct iovec[]){{&req_buf, sizeof(req_buf)},
+                                                 {(char *)key, strlen(key) + 1}},
+                            .msg_iovlen = 2};
+       int errno_save = errno;
+
+       *swap = 0;
+retry:
+       memset(buf, 0, len);
+       buf[0] = NSCDVERSION;
+
+       fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+       if (fd < 0)
+               return NULL;
+
+       if (!(f = fdopen(fd, "r"))) {
+               close(fd);
+               return 0;
+       }
+
+       if (req_buf[2] > LOGIN_NAME_MAX)
+               return f;
+
+       if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+               /* If there isn't a running nscd we simulate a "not found"
+                * result and the caller is responsible for calling
+                * fclose on the (unconnected) socket. The value of
+                * errno must be left unchanged in this case.  */
+               if (errno == EACCES || errno == ECONNREFUSED || errno == ENOENT) {
+                       errno = errno_save;
+                       return f;
+               }
+               goto error;
+       }
+
+       if (sendmsg(fd, &msg, MSG_NOSIGNAL) < 0)
+               goto error;
+
+       if (!fread(buf, len, 1, f)) {
+               /* If the VERSION entry mismatches nscd will disconnect. The
+                * most likely cause is that the endianness mismatched. So, we
+                * byteswap and try once more. (if we already swapped, just
+                * fail out)
+                */
+               if (ferror(f))
+                       goto error;
+               if (!*swap) {
+                       fclose(f);
+                       for (i = 0; i < sizeof(req_buf) / sizeof(req_buf[0]);
+                            i++) {
+                               req_buf[i] = bswap_32(req_buf[i]);
+                       }
+                       *swap = 1;
+                       goto retry;
+               } else {
+                       errno = EIO;
+                       goto error;
+               }
+       }
+
+       if (*swap) {
+               for (i = 0; i < len / sizeof(buf[0]); i++) {
+                       buf[i] = bswap_32(buf[i]);
+               }
+       }
+
+       /* The first entry in every nscd response is the version number. This
+        * really shouldn't happen, and is evidence of some form of malformed
+        * response.
+        */
+       if (buf[0] != NSCDVERSION) {
+               errno = EIO;
+               goto error;
+       }
+
+       return f;
+error:
+       fclose(f);
+       return 0;
+}
+
+static int __getgr_a(const char *name, gid_t gid, struct group *gr, char **buf,
+                    size_t *size, char ***mem, size_t *nmem, struct group **res)
+{
+       FILE *f;
+       int rv = 0;
+#ifdef HAVE_PTHREAD_SETCANCELSTATE
+       int cs;
+
+       pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+#endif
+
+       *res = 0;
+
+       f = fopen("/etc/group", "rbe");
+       if (!f) {
+               rv = errno;
+               goto done;
+       }
+
+       while (!(rv = __getgrent_a(f, gr, buf, size, mem, nmem, res)) && *res) {
+               if ((name && !strcmp(name, (*res)->gr_name)) ||
+                   (!name && (*res)->gr_gid == gid)) {
+                       break;
+               }
+       }
+       fclose(f);
+
+       if (!*res && (rv == 0 || rv == ENOENT || rv == ENOTDIR)) {
+               int32_t req = name ? GETGRBYNAME : GETGRBYGID;
+               int32_t i;
+               const char *key;
+               int32_t groupbuf[GR_LEN] = {0};
+               size_t len = 0;
+               size_t grlist_len = 0;
+               char gidbuf[11] = {0};
+               int swap = 0;
+               char *ptr;
+
+               if (name) {
+                       key = name;
+               } else {
+                       if (gid < 0 || gid > UINT32_MAX) {
+                               rv = 0;
+                               goto done;
+                       }
+                       key = itoa(gidbuf, gid);
+               }
+
+               f = __nscd_query(req, key, groupbuf, sizeof groupbuf, &swap);
+               if (!f) {
+                       rv = errno;
+                       goto done;
+               }
+
+               if (!groupbuf[GRFOUND]) {
+                       rv = 0;
+                       goto cleanup_f;
+               }
+
+               if (!groupbuf[GRNAMELEN] || !groupbuf[GRPASSWDLEN]) {
+                       rv = EIO;
+                       goto cleanup_f;
+               }
+
+               if ((int64_t)groupbuf[GRNAMELEN] >
+                   (int64_t)(SIZE_MAX - groupbuf[GRPASSWDLEN])) {
+                       rv = ENOMEM;
+                       goto cleanup_f;
+               }
+               len = groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN];
+
+               for (i = 0; i < groupbuf[GRMEMCNT]; i++) {
+                       uint32_t name_len;
+                       if (fread(&name_len, sizeof name_len, 1, f) < 1) {
+                               rv = ferror(f) ? errno : EIO;
+                               goto cleanup_f;
+                       }
+                       if (swap) {
+                               name_len = bswap_32(name_len);
+                       }
+                       if (name_len > SIZE_MAX - grlist_len ||
+                           name_len > SIZE_MAX - len) {
+                               rv = ENOMEM;
+                               goto cleanup_f;
+                       }
+                       len += name_len;
+                       grlist_len += name_len;
+               }
+
+               if (len > *size || !*buf) {
+                       char *tmp = realloc(*buf, len);
+                       if (!tmp) {
+                               rv = errno;
+                               goto cleanup_f;
+                       }
+                       *buf = tmp;
+                       *size = len;
+               }
+
+               if (!fread(*buf, len, 1, f)) {
+                       rv = ferror(f) ? errno : EIO;
+                       goto cleanup_f;
+               }
+
+               if (((size_t)(groupbuf[GRMEMCNT] + 1)) > *nmem) {
+                       if (((size_t)(groupbuf[GRMEMCNT] + 1)) >
+                           (SIZE_MAX / sizeof(char *))) {
+                               rv = ENOMEM;
+                               goto cleanup_f;
+                       }
+                       char **tmp = realloc(*mem, (groupbuf[GRMEMCNT] + 1) *
+                                                      sizeof(char *));
+                       if (!tmp) {
+                               rv = errno;
+                               goto cleanup_f;
+                       }
+                       *mem = tmp;
+                       *nmem = groupbuf[GRMEMCNT] + 1;
+               }
+
+               if (groupbuf[GRMEMCNT]) {
+                       mem[0][0] =
+                           *buf + groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN];
+                       for (ptr = mem[0][0], i = 0;
+                            ptr != mem[0][0] + grlist_len; ptr++)
+                               if (!*ptr)
+                                       mem[0][++i] = ptr + 1;
+                       mem[0][i] = 0;
+
+                       if (i != groupbuf[GRMEMCNT]) {
+                               rv = EIO;
+                               goto cleanup_f;
+                       }
+               } else {
+                       mem[0][0] = 0;
+               }
+
+               gr->gr_name = *buf;
+               gr->gr_passwd = gr->gr_name + groupbuf[GRNAMELEN];
+               gr->gr_gid = groupbuf[GRGID];
+               gr->gr_mem = *mem;
+
+               if (gr->gr_passwd[-1] ||
+                   gr->gr_passwd[groupbuf[GRPASSWDLEN] - 1]) {
+                       rv = EIO;
+                       goto cleanup_f;
+               }
+
+               if ((name && strcmp(name, gr->gr_name)) ||
+                   (!name && gid != gr->gr_gid)) {
+                       rv = EIO;
+                       goto cleanup_f;
+               }
+
+               *res = gr;
+
+       cleanup_f:
+               fclose(f);
+               goto done;
+       }
+
+done:
+
+#ifdef HAVE_PTHREAD_SETCANCELSTATE
+       pthread_setcancelstate(cs, 0);
+#endif
+
+       if (rv)
+               errno = rv;
+       return rv;
+}
+
+static int getgr_r(const char *name, gid_t gid, struct group *gr, char *buf,
+                  size_t size, struct group **res)
+{
+       char *line = 0;
+       size_t len = 0;
+       char **mem = 0;
+       size_t nmem = 0;
+       int rv = 0;
+       size_t i;
+
+#ifdef HAVE_PTHREAD_SETCANCELSTATE
+       int cs;
+
+       pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+#endif
+
+       rv = __getgr_a(name, gid, gr, &line, &len, &mem, &nmem, res);
+       if (*res && size < len + (nmem + 1) * sizeof(char *) + 32) {
+               *res = 0;
+               rv = ERANGE;
+       }
+       if (*res) {
+               buf += (16 - (uintptr_t)buf) % 16;
+               gr->gr_mem = (void *)buf;
+               buf += (nmem + 1) * sizeof(char *);
+               memcpy(buf, line, len);
+               FIX(name);
+               FIX(passwd);
+               for (i = 0; mem[i]; i++)
+                       gr->gr_mem[i] = mem[i] - line + buf;
+               gr->gr_mem[i] = 0;
+       }
+       free(mem);
+       free(line);
+
+#ifdef HAVE_PTHREAD_SETCANCELSTATE
+       pthread_setcancelstate(cs, 0);
+#endif
+
+       if (rv)
+               errno = rv;
+       return rv;
+}
+
+int getgrgid_r(gid_t gid, struct group *gr, char *buf, size_t size, struct group **res)
+{
+       return getgr_r(0, gid, gr, buf, size, res);
+}
diff --git a/src/include/getgrgid_r.h b/src/include/getgrgid_r.h
new file mode 100644 (file)
index 0000000..d01395b
--- /dev/null
@@ -0,0 +1,32 @@
+/* liblxcapi
+ *
+ * Copyright © 2018 Christian Brauner <christian.brauner@ubuntu.com>.
+ * Copyright © 2018 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * This function has been copied from musl.
+ */
+
+#ifndef _GETGRGID_R_H
+#define _GETGRGID_R_H
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <grp.h>
+
+extern int getgrgid_r(gid_t gid, struct group *gr, char *buf, size_t size,
+                     struct group **res);
+
+#endif /* _GETGRGID_R_H */
index 1b10d6f63bde327b8ac8c69b6791299699d562ea..065bf058288687f3cb8b9f1cfe69bd51c3aa6f23 100644 (file)
@@ -56,6 +56,10 @@ if !HAVE_GETSUBOPT
 noinst_HEADERS += ../include/getsubopt.h
 endif
 
+if !HAVE_GETGRGID_R
+noinst_HEADERS += ../include/getgrgid_r.h
+endif
+
 sodir=$(libdir)
 
 LSM_SOURCES = \
@@ -148,6 +152,10 @@ if !HAVE_STRLCPY
 liblxc_la_SOURCES += ../include/strlcpy.c ../include/strlcpy.h
 endif
 
+if !HAVE_GETGRGID_R
+liblxc_la_SOURCES += ../include/getgrgid_r.c ../include/getgrgid_r.h
+endif
+
 AM_CFLAGS=-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
        -DLXCPATH=\"$(LXCPATH)\" \
        -DLXC_GLOBAL_CONF=\"$(LXC_GLOBAL_CONF)\" \
@@ -304,6 +312,10 @@ if !HAVE_STRLCPY
 init_lxc_static_SOURCES += ../include/strlcpy.c ../include/strlcpy.h
 endif
 
+if !HAVE_GETGRGID_R
+liblxc_la_SOURCES += ../include/getgrgid_r.c ../include/getgrgid_r.h
+endif
+
 init_lxc_static_LDFLAGS = -all-static
 init_lxc_static_LDADD = @CAP_LIBS@
 init_lxc_static_CFLAGS = $(AM_CFLAGS) -DNO_LXC_CONF