]> git.proxmox.com Git - mirror_lxc.git/blobdiff - src/lxc/pam/pam_cgfs.c
rename functions which clash with libsystemd's
[mirror_lxc.git] / src / lxc / pam / pam_cgfs.c
index f23b84774319b5cbddd685cbd7d3baf875ef31a7..e638269bc853603e029f4f897dbe4cf71dff8d13 100644 (file)
@@ -1,42 +1,12 @@
-/* pam-cgfs
- *
- * Copyright © 2016 Canonical, Inc
- * Author: Serge Hallyn <serge.hallyn@ubuntu.com>
- * Author: Christian Brauner <christian.brauner@ubuntu.com>
- *
- * When a user logs in, this pam module will create cgroups which the user may
- * administer. It handles both pure cgroupfs v1 and pure cgroupfs v2, as well as
- * mixed mounts, where some controllers are mounted in a standard cgroupfs v1
- * hierarchy location (/sys/fs/cgroup/<controller>) and others are in the
- * cgroupfs v2 hierarchy.
- * Writeable cgroups are either created for all controllers or, if specified,
- * for any controllers listed on the command line.
- * The cgroup created will be "user/$user/0" for the first session,
- * "user/$user/1" for the second, etc.
- *
- * Systems with a systemd init system are treated specially, both with respect
- * to cgroupfs v1 and cgroupfs v2. For both, cgroupfs v1 and cgroupfs v2, We
- * check whether systemd already placed us in a cgroup it created:
- *
- *     user.slice/user-uid.slice/session-n.scope
- *
- * by checking whether uid == our uid. If it did, we simply chown the last
- * part (session-n.scope). If it did not we create a cgroup as outlined above
- * (user/$user/n) and chown it to our uid.
- * The same holds for cgroupfs v2 where this assumptions becomes crucial:
- * We __have to__ be placed in our under the cgroup systemd created for us on
- * login, otherwise things like starting an xserver or similar will not work.
- *
- * All requested cgroups must be mounted under /sys/fs/cgroup/$controller,
- * no messing around with finding mountpoints.
- *
- * See COPYING file for details.
- */
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "config.h"
 
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/unistd.h>
 #include <pwd.h>
 #include <stdarg.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <syslog.h>
-#include <unistd.h>
-#include <linux/unistd.h>
 #include <sys/mount.h>
 #include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/vfs.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "file_utils.h"
+#include "macro.h"
+#include "memory_utils.h"
+#include "string_utils.h"
 
 #define PAM_SM_SESSION
 #include <security/_pam_macros.h>
 #include <security/pam_modules.h>
 
-#include "utils.h"
-
-#ifndef HAVE_STRLCPY
-#include "include/strlcpy.h"
+#if !HAVE_STRLCPY
+#include "strlcpy.h"
 #endif
 
-#ifndef HAVE_STRLCAT
-#include "include/strlcat.h"
+#if !HAVE_STRLCAT
+#include "strlcat.h"
 #endif
 
-#define pam_cgfs_debug_stream(stream, format, ...)                                \
-       do {                                                                   \
-               fprintf(stream, "%s: %d: %s: " format, __FILE__, __LINE__,     \
-                       __func__, __VA_ARGS__);                                \
+#define pam_cgfs_debug_stream(stream, format, ...)                         \
+       do {                                                               \
+               fprintf(stream, "%s: %d: %s: " format, __FILE__, __LINE__, \
+                       __func__, __VA_ARGS__);                            \
        } while (false)
 
 #define pam_cgfs_error(format, ...) pam_cgfs_debug_stream(stderr, format, __VA_ARGS__)
 #ifdef DEBUG
 #define pam_cgfs_debug(format, ...) pam_cgfs_error(format, __VA_ARGS__)
 #else
-#define pam_cgfs_debug(format, ...)
+#define pam_cgfs_debug(format, ...) \
+       do {                     \
+       } while (false)
 #endif /* DEBUG */
 
-/* Taken over modified from the kernel sources. */
-#define NBITS 32 /* bits in uint32_t */
-#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d))
-#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, NBITS)
-
 static enum cg_mount_mode {
        CGROUP_UNKNOWN = -1,
        CGROUP_MIXED = 0,
@@ -98,42 +67,24 @@ static enum cg_mount_mode {
 static void append_line(char **dest, size_t oldlen, char *new, size_t newlen);
 static int append_null_to_list(void ***list);
 static void batch_realloc(char **mem, size_t oldlen, size_t newlen);
-static inline void clear_bit(unsigned bit, uint32_t *bitarr)
-{
-       bitarr[bit / NBITS] &= ~(1 << (bit % NBITS));
-}
 static char *copy_to_eol(char *s);
-static void free_string_list(char **list);
 static char *get_mountpoint(char *line);
 static bool get_uid_gid(const char *user, uid_t *uid, gid_t *gid);
 static int handle_login(const char *user, uid_t uid, gid_t gid);
-static inline bool is_set(unsigned bit, uint32_t *bitarr)
-{
-       return (bitarr[bit / NBITS] & (1 << (bit % NBITS))) != 0;
-}
 static bool is_lxcfs(const char *line);
 static bool is_cgv1(char *line);
 static bool is_cgv2(char *line);
-static void *must_alloc(size_t sz);
 static void must_add_to_list(char ***clist, char *entry);
 static void must_append_controller(char **klist, char **nlist, char ***clist,
                                   char *entry);
 static void must_append_string(char ***list, char *entry);
 static void mysyslog(int err, const char *format, ...) __attribute__((sentinel));
 static char *read_file(char *fnam);
-static int read_from_file(const char *filename, void* buf, size_t count);
 static int recursive_rmdir(char *dirname);
-static inline void set_bit(unsigned bit, uint32_t *bitarr)
-{
-       bitarr[bit / NBITS] |= (1 << (bit % NBITS));
-}
 static bool string_in_list(char **list, const char *entry);
 static char *string_join(const char *sep, const char **parts, bool use_as_prefix);
 static void trim(char *s);
 static bool write_int(char *path, int v);
-static ssize_t write_nointr(int fd, const void* buf, size_t count);
-static int write_to_file(const char *filename, const void *buf, size_t count,
-                        bool add_newline);
 
 /* cgroupfs prototypes. */
 static bool cg_belongs_to_uid_gid(const char *path, uid_t uid, gid_t gid);
@@ -246,7 +197,7 @@ static int do_mkdir(const char *path, mode_t mode)
 }
 
 /* Create directory and (if necessary) its parents. */
-static bool mkdir_parent(const char *root, char *path)
+static bool lxc_mkdir_parent(const char *root, char *path)
 {
        char *b, orig, *e;
 
@@ -257,7 +208,7 @@ static bool mkdir_parent(const char *root, char *path)
                return true;
 
        b = path + strlen(root) + 1;
-       while (true) {
+       for (;;) {
                while (*b && (*b == '/'))
                        b++;
                if (!*b)
@@ -296,8 +247,11 @@ static void mysyslog(int err, const char *format, ...)
        va_list args;
 
        va_start(args, format);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
        openlog("PAM-CGFS", LOG_CONS | LOG_PID, LOG_AUTH);
        vsyslog(err, format, args);
+#pragma GCC diagnostic pop
        va_end(args);
        closelog();
 }
@@ -387,12 +341,6 @@ static void trim(char *s)
                s[--len] = '\0';
 }
 
-/* Allocate pointer; do not fail. */
-static void *must_alloc(size_t sz)
-{
-       return must_realloc(NULL, sz);
-}
-
 /* Make allocated copy of string. End of string is taken to be '\n'. */
 static char *copy_to_eol(char *s)
 {
@@ -404,7 +352,7 @@ static char *copy_to_eol(char *s)
                return NULL;
 
        len = newline - s;
-       sret = must_alloc(len + 1);
+       sret = must_realloc(NULL, len + 1);
        memcpy(sret, s, len);
        sret[len] = '\0';
 
@@ -488,16 +436,6 @@ static size_t string_list_length(char **list)
        return len;
 }
 
-/* Free null-terminated array of strings. */
-static void free_string_list(char **list)
-{
-       char **it;
-
-       for (it = list; it && *it; it++)
-               free(*it);
-       free(list);
-}
-
 /* Write single integer to file. */
 static bool write_int(char *path, int v)
 {
@@ -520,8 +458,8 @@ static bool write_int(char *path, int v)
 /* Recursively remove directory and its parents. */
 static int recursive_rmdir(char *dirname)
 {
+       __do_closedir DIR *dir = NULL;
        struct dirent *direntp;
-       DIR *dir;
        int r = 0;
 
        dir = opendir(dirname);
@@ -561,12 +499,6 @@ next:
                r = -1;
        }
 
-       if (closedir(dir) < 0) {
-               if (!r)
-                       pam_cgfs_debug("Failed to delete %s: %s\n", dirname, strerror(errno));
-               r = -1;
-       }
-
        return r;
 }
 
@@ -602,7 +534,7 @@ static char *get_mountpoint(char *line)
                *p2 = '\0';
 
        len = strlen(p);
-       sret = must_alloc(len + 1);
+       sret = must_realloc(NULL, len + 1);
        memcpy(sret, p, len);
        sret[len] = '\0';
 
@@ -774,7 +706,7 @@ static char *cgv1_must_prefix_named(char *entry)
        size_t len;
 
        len = strlen(entry);
-       s = must_alloc(len + 6);
+       s = must_realloc(NULL, len + 6);
 
        ret = snprintf(s, len + 6, "name=%s", entry);
        if (ret < 0 || (size_t)ret >= (len + 6)) {
@@ -847,8 +779,9 @@ static char **cgv1_get_proc_mountinfo_controllers(char **klist, char **nlist, ch
 /* Check if a cgroupfs v2 controller is present in the string @cgline. */
 static bool cgv1_controller_in_clist(char *cgline, char *c)
 {
+       __do_free char *tmp = NULL;
        size_t len;
-       char *tok, *eol, *tmp;
+       char *tok, *eol;
        char *saveptr = NULL;
 
        eol = strchr(cgline, ':');
@@ -856,7 +789,7 @@ static bool cgv1_controller_in_clist(char *cgline, char *c)
                return false;
 
        len = eol - cgline;
-       tmp = alloca(len + 1);
+       tmp = must_realloc(NULL, len + 1);
        memcpy(tmp, cgline, len);
        tmp[len] = '\0';
 
@@ -878,7 +811,7 @@ static char *cgv1_get_current_cgroup(char *basecginfo, char *controller)
 
        p = basecginfo;
 
-       while (true) {
+       for (;;) {
                p = strchr(p, ':');
                if (!p)
                        return NULL;
@@ -936,7 +869,7 @@ static void cgv1_add_controller(char **clist, char *mountpoint, char *base_cgrou
        struct cgv1_hierarchy *new;
        int newentry;
 
-       new = must_alloc(sizeof(*new));
+       new = must_realloc(NULL, sizeof(*new));
 
        new->controllers = clist;
        new->mountpoint = mountpoint;
@@ -963,7 +896,7 @@ static void cgv2_add_controller(char **clist, char *mountpoint, char *base_cgrou
        struct cgv2_hierarchy *new;
        int newentry;
 
-       new = must_alloc(sizeof(*new));
+       new = must_realloc(NULL, sizeof(*new));
 
        new->controllers = clist;
        new->mountpoint = mountpoint;
@@ -1276,7 +1209,7 @@ static inline int cg_get_version_of_mntpt(const char *path)
 }
 
 /* Detect and store information about the cgroupfs v2 hierarchy. Currently only
- * deals with the empty v2 hierachy as we do not retrieve enabled controllers.
+ * deals with the empty v2 hierarchy as we do not retrieve enabled controllers.
  */
 static bool cgv2_init(uid_t uid, gid_t gid)
 {
@@ -1555,7 +1488,7 @@ static bool get_uid_gid(const char *user, uid_t *uid, gid_t *gid)
        struct passwd pwent;
        struct passwd *pwentp = NULL;
        char *buf;
-       size_t bufsize;
+       ssize_t bufsize;
        int ret;
 
        bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
@@ -1733,49 +1666,6 @@ static ssize_t cg_get_max_cpus(char *cpulist)
        return cpus;
 }
 
-static ssize_t write_nointr(int fd, const void* buf, size_t count)
-{
-       ssize_t ret;
-
-again:
-       ret = write(fd, buf, count);
-       if (ret < 0 && errno == EINTR)
-               goto again;
-
-       return ret;
-}
-
-static int write_to_file(const char *filename, const void* buf, size_t count, bool add_newline)
-{
-       int fd, saved_errno;
-       ssize_t ret;
-
-       fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, 0666);
-       if (fd < 0)
-               return -1;
-
-       ret = write_nointr(fd, buf, count);
-       if (ret < 0)
-               goto out_error;
-       if ((size_t)ret != count)
-               goto out_error;
-
-       if (add_newline) {
-               ret = write_nointr(fd, "\n", 1);
-               if (ret != 1)
-                       goto out_error;
-       }
-
-       close(fd);
-       return 0;
-
-out_error:
-       saved_errno = errno;
-       close(fd);
-       errno = saved_errno;
-       return -1;
-}
-
 #define __ISOL_CPUS "/sys/devices/system/cpu/isolated"
 static bool cg_filter_and_set_cpus(char *path, bool am_initialized)
 {
@@ -1806,7 +1696,7 @@ static bool cg_filter_and_set_cpus(char *path, bool am_initialized)
 
        /* Get maximum number of cpus found in possible cpuset. */
        maxposs = cg_get_max_cpus(posscpus);
-       if (maxposs < 0)
+       if (maxposs < 0 || maxposs >= INT_MAX - 1)
                goto on_error;
 
        if (!file_exists(__ISOL_CPUS)) {
@@ -1856,7 +1746,7 @@ static bool cg_filter_and_set_cpus(char *path, bool am_initialized)
 
        /* Get maximum number of cpus found in isolated cpuset. */
        maxisol = cg_get_max_cpus(isolcpus);
-       if (maxisol < 0)
+       if (maxisol < 0 || maxisol >= INT_MAX - 1)
                goto on_error;
 
        if (maxposs < maxisol)
@@ -1897,11 +1787,10 @@ static bool cg_filter_and_set_cpus(char *path, bool am_initialized)
 copy_parent:
        *lastslash = oldv;
 
-       if (fpath)
-               free(fpath);
+       free(fpath);
 
        fpath = must_make_path(path, "cpuset.cpus", NULL);
-       ret = write_to_file(fpath, cpulist, strlen(cpulist), false);
+       ret = lxc_write_to_file(fpath, cpulist, strlen(cpulist), false, 0660);
        if (ret < 0) {
                pam_cgfs_debug("Could not write cpu list to: %s\n", fpath);
                goto on_error;
@@ -1911,8 +1800,9 @@ on_success:
        bret = true;
 
 on_error:
-       free(fpath);
+       *lastslash = oldv;
 
+       free(fpath);
        free(isolcpus);
        free(isolmask);
 
@@ -1924,37 +1814,6 @@ on_error:
        return bret;
 }
 
-int read_from_file(const char *filename, void* buf, size_t count)
-{
-       int fd = -1, saved_errno;
-       ssize_t ret;
-
-       fd = open(filename, O_RDONLY | O_CLOEXEC);
-       if (fd < 0)
-               return -1;
-
-       if (!buf || !count) {
-               char buf2[100];
-               size_t count2 = 0;
-
-               while ((ret = read(fd, buf2, 100)) > 0)
-                       count2 += ret;
-               if (ret >= 0)
-                       ret = count2;
-       } else {
-               memset(buf, 0, count);
-               ret = read(fd, buf, count);
-       }
-
-       if (ret < 0)
-               pam_cgfs_debug("read %s: %s", filename, strerror(errno));
-
-       saved_errno = errno;
-       close(fd);
-       errno = saved_errno;
-       return ret;
-}
-
 /* Copy contents of parent(@path)/@file to @path/@file */
 static bool cg_copy_parent_file(char *path, char *file)
 {
@@ -1972,19 +1831,23 @@ static bool cg_copy_parent_file(char *path, char *file)
        *lastslash = '\0';
 
        fpath = must_make_path(path, file, NULL);
-       len = read_from_file(fpath, NULL, 0);
-       if (len <= 0)
+       len = lxc_read_from_file(fpath, NULL, 0);
+       if (len <= 0) {
+               pam_cgfs_debug("Failed to read %s: %s", fpath, strerror(errno));
                goto bad;
+       }
 
-       value = must_alloc(len + 1);
-       if (read_from_file(fpath, value, len) != len)
+       value = must_realloc(NULL, len + 1);
+       if (lxc_read_from_file(fpath, value, len) != len) {
+               pam_cgfs_debug("Failed to read %s: %s", fpath, strerror(errno));
                goto bad;
+       }
        free(fpath);
 
        *lastslash = oldv;
 
        fpath = must_make_path(path, file, NULL);
-       ret = write_to_file(fpath, value, len, false);
+       ret = lxc_write_to_file(fpath, value, len, false, 0660);
        if (ret < 0)
                pam_cgfs_debug("Unable to write %s to %s", value, fpath);
 
@@ -2013,8 +1876,8 @@ static bool cgv1_handle_root_cpuset_hierarchy(struct cgv1_hierarchy *h)
 
        clonechildrenpath = must_make_path(h->mountpoint, "cgroup.clone_children", NULL);
 
-       if (read_from_file(clonechildrenpath, &v, 1) < 0) {
-               pam_cgfs_debug("Failed to read '%s'", clonechildrenpath);
+       if (lxc_read_from_file(clonechildrenpath, &v, 1) < 0) {
+               pam_cgfs_debug("Failed to read %s: %s", clonechildrenpath, strerror(errno));
                free(clonechildrenpath);
                return false;
        }
@@ -2024,7 +1887,7 @@ static bool cgv1_handle_root_cpuset_hierarchy(struct cgv1_hierarchy *h)
                return true;
        }
 
-       if (write_to_file(clonechildrenpath, "1", 1, false) < 0) {
+       if (lxc_write_to_file(clonechildrenpath, "1", 1, false, 0660) < 0) {
                /* Set clone_children so children inherit our settings */
                pam_cgfs_debug("Failed to write 1 to %s", clonechildrenpath);
                free(clonechildrenpath);
@@ -2072,8 +1935,8 @@ static bool cgv1_handle_cpuset_hierarchy(struct cgv1_hierarchy *h,
                return true;
        }
 
-       if (read_from_file(clonechildrenpath, &v, 1) < 0) {
-               pam_cgfs_debug("Failed to read '%s'", clonechildrenpath);
+       if (lxc_read_from_file(clonechildrenpath, &v, 1) < 0) {
+               pam_cgfs_debug("Failed to read %s: %s", clonechildrenpath, strerror(errno));
                free(clonechildrenpath);
                free(cgpath);
                return false;
@@ -2103,7 +1966,7 @@ static bool cgv1_handle_cpuset_hierarchy(struct cgv1_hierarchy *h,
        }
        free(cgpath);
 
-       if (write_to_file(clonechildrenpath, "1", 1, false) < 0) {
+       if (lxc_write_to_file(clonechildrenpath, "1", 1, false, 0660) < 0) {
                /* Set clone_children so children inherit our settings */
                pam_cgfs_debug("Failed to write 1 to %s", clonechildrenpath);
                free(clonechildrenpath);
@@ -2179,7 +2042,7 @@ static bool cgv1_create_one(struct cgv1_hierarchy *h, const char *cgroup, uid_t
                        return our_cg;
                }
 
-               created = mkdir_parent(it->mountpoint, path);
+               created = lxc_mkdir_parent(it->mountpoint, path);
                if (!created) {
                        free(path);
                        continue;
@@ -2335,7 +2198,7 @@ static bool cgv2_create(const char *cgroup, uid_t uid, gid_t gid, bool *existed)
                }
        }
 
-       created = mkdir_parent(v2->mountpoint, path);
+       created = lxc_mkdir_parent(v2->mountpoint, path);
        if (!created) {
                free(path);
                return false;
@@ -2403,13 +2266,13 @@ static int handle_login(const char *user, uid_t uid, gid_t gid)
 {
        int idx = 0, ret;
        bool existed;
-       char cg[MAXPATHLEN];
+       char cg[PATH_MAX];
 
        cg_escape();
 
        while (idx >= 0) {
-               ret = snprintf(cg, MAXPATHLEN, "/user/%s/%d", user, idx);
-               if (ret < 0 || ret >= MAXPATHLEN) {
+               ret = snprintf(cg, PATH_MAX, "/user/%s/%d", user, idx);
+               if (ret < 0 || ret >= PATH_MAX) {
                        mysyslog(LOG_ERR, "Username too long\n", NULL);
                        return PAM_SESSION_ERR;
                }