Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <stdint.h>
4 : : #include <sys/mount.h>
5 : :
6 : : #include "sd-bus.h"
7 : :
8 : : #include "bus-error.h"
9 : : #include "fs-util.h"
10 : : #include "format-util.h"
11 : : #include "label.h"
12 : : #include "main-func.h"
13 : : #include "mkdir.h"
14 : : #include "mountpoint-util.h"
15 : : #include "path-util.h"
16 : : #include "rm-rf.h"
17 : : #include "selinux-util.h"
18 : : #include "smack-util.h"
19 : : #include "stdio-util.h"
20 : : #include "string-util.h"
21 : : #include "strv.h"
22 : : #include "user-util.h"
23 : :
24 : 0 : static int acquire_runtime_dir_size(uint64_t *ret) {
25 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
26 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
27 : : int r;
28 : :
29 : 0 : r = sd_bus_default_system(&bus);
30 [ # # ]: 0 : if (r < 0)
31 [ # # ]: 0 : return log_error_errno(r, "Failed to connect to system bus: %m");
32 : :
33 : 0 : r = sd_bus_get_property_trivial(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "RuntimeDirectorySize", &error, 't', ret);
34 [ # # ]: 0 : if (r < 0)
35 [ # # ]: 0 : return log_error_errno(r, "Failed to acquire runtime directory size: %s", bus_error_message(&error, r));
36 : :
37 : 0 : return 0;
38 : : }
39 : :
40 : 0 : static int user_mkdir_runtime_path(
41 : : const char *runtime_path,
42 : : uid_t uid,
43 : : gid_t gid,
44 : : uint64_t runtime_dir_size) {
45 : :
46 : : int r;
47 : :
48 [ # # ]: 0 : assert(runtime_path);
49 [ # # ]: 0 : assert(path_is_absolute(runtime_path));
50 [ # # ]: 0 : assert(uid_is_valid(uid));
51 [ # # ]: 0 : assert(gid_is_valid(gid));
52 : :
53 : 0 : r = mkdir_safe_label("/run/user", 0755, 0, 0, MKDIR_WARN_MODE);
54 [ # # ]: 0 : if (r < 0)
55 [ # # ]: 0 : return log_error_errno(r, "Failed to create /run/user: %m");
56 : :
57 [ # # ]: 0 : if (path_is_mount_point(runtime_path, NULL, 0) >= 0)
58 [ # # ]: 0 : log_debug("%s is already a mount point", runtime_path);
59 : : else {
60 : : char options[sizeof("mode=0700,uid=,gid=,size=,smackfsroot=*")
61 : : + DECIMAL_STR_MAX(uid_t)
62 : : + DECIMAL_STR_MAX(gid_t)
63 : : + DECIMAL_STR_MAX(uint64_t)];
64 : :
65 [ # # # # ]: 0 : xsprintf(options,
66 : : "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%" PRIu64 "%s",
67 : : uid, gid, runtime_dir_size,
68 : : mac_smack_use() ? ",smackfsroot=*" : "");
69 : :
70 : 0 : (void) mkdir_label(runtime_path, 0700);
71 : :
72 : 0 : r = mount("tmpfs", runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, options);
73 [ # # ]: 0 : if (r < 0) {
74 [ # # # # ]: 0 : if (!IN_SET(errno, EPERM, EACCES)) {
75 [ # # ]: 0 : r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", runtime_path);
76 : 0 : goto fail;
77 : : }
78 : :
79 [ # # ]: 0 : log_debug_errno(errno, "Failed to mount per-user tmpfs directory %s.\n"
80 : : "Assuming containerized execution, ignoring: %m", runtime_path);
81 : :
82 : 0 : r = chmod_and_chown(runtime_path, 0700, uid, gid);
83 [ # # ]: 0 : if (r < 0) {
84 [ # # ]: 0 : log_error_errno(r, "Failed to change ownership and mode of \"%s\": %m", runtime_path);
85 : 0 : goto fail;
86 : : }
87 : : }
88 : :
89 : 0 : r = label_fix(runtime_path, 0);
90 [ # # ]: 0 : if (r < 0)
91 [ # # ]: 0 : log_warning_errno(r, "Failed to fix label of \"%s\", ignoring: %m", runtime_path);
92 : : }
93 : :
94 : 0 : return 0;
95 : :
96 : 0 : fail:
97 : : /* Try to clean up, but ignore errors */
98 : 0 : (void) rmdir(runtime_path);
99 : 0 : return r;
100 : : }
101 : :
102 : 0 : static int user_remove_runtime_path(const char *runtime_path) {
103 : : int r;
104 : :
105 [ # # ]: 0 : assert(runtime_path);
106 [ # # ]: 0 : assert(path_is_absolute(runtime_path));
107 : :
108 : 0 : r = rm_rf(runtime_path, 0);
109 [ # # ]: 0 : if (r < 0)
110 [ # # ]: 0 : log_debug_errno(r, "Failed to remove runtime directory %s (before unmounting), ignoring: %m", runtime_path);
111 : :
112 : : /* Ignore cases where the directory isn't mounted, as that's quite possible, if we lacked the permissions to
113 : : * mount something */
114 : 0 : r = umount2(runtime_path, MNT_DETACH);
115 [ # # # # : 0 : if (r < 0 && !IN_SET(errno, EINVAL, ENOENT))
# # ]
116 [ # # ]: 0 : log_debug_errno(errno, "Failed to unmount user runtime directory %s, ignoring: %m", runtime_path);
117 : :
118 : 0 : r = rm_rf(runtime_path, REMOVE_ROOT);
119 [ # # # # ]: 0 : if (r < 0 && r != -ENOENT)
120 [ # # ]: 0 : return log_error_errno(r, "Failed to remove runtime directory %s (after unmounting): %m", runtime_path);
121 : :
122 : 0 : return 0;
123 : : }
124 : :
125 : 0 : static int do_mount(const char *user) {
126 : : char runtime_path[sizeof("/run/user") + DECIMAL_STR_MAX(uid_t)];
127 : : uint64_t runtime_dir_size;
128 : : uid_t uid;
129 : : gid_t gid;
130 : : int r;
131 : :
132 : 0 : r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);
133 [ # # ]: 0 : if (r < 0)
134 [ # # # # : 0 : return log_error_errno(r,
# # ]
135 : : r == -ESRCH ? "No such user \"%s\"" :
136 : : r == -ENOMSG ? "UID \"%s\" is invalid or has an invalid main group"
137 : : : "Failed to look up user \"%s\": %m",
138 : : user);
139 : :
140 : 0 : r = acquire_runtime_dir_size(&runtime_dir_size);
141 [ # # ]: 0 : if (r < 0)
142 : 0 : return r;
143 : :
144 [ # # ]: 0 : xsprintf(runtime_path, "/run/user/" UID_FMT, uid);
145 : :
146 [ # # ]: 0 : log_debug("Will mount %s owned by "UID_FMT":"GID_FMT, runtime_path, uid, gid);
147 : 0 : return user_mkdir_runtime_path(runtime_path, uid, gid, runtime_dir_size);
148 : : }
149 : :
150 : 0 : static int do_umount(const char *user) {
151 : : char runtime_path[sizeof("/run/user") + DECIMAL_STR_MAX(uid_t)];
152 : : uid_t uid;
153 : : int r;
154 : :
155 : : /* The user may be already removed. So, first try to parse the string by parse_uid(),
156 : : * and if it fails, fallback to get_user_creds().*/
157 [ # # ]: 0 : if (parse_uid(user, &uid) < 0) {
158 : 0 : r = get_user_creds(&user, &uid, NULL, NULL, NULL, 0);
159 [ # # ]: 0 : if (r < 0)
160 [ # # # # : 0 : return log_error_errno(r,
# # ]
161 : : r == -ESRCH ? "No such user \"%s\"" :
162 : : r == -ENOMSG ? "UID \"%s\" is invalid or has an invalid main group"
163 : : : "Failed to look up user \"%s\": %m",
164 : : user);
165 : : }
166 : :
167 [ # # ]: 0 : xsprintf(runtime_path, "/run/user/" UID_FMT, uid);
168 : :
169 [ # # ]: 0 : log_debug("Will remove %s", runtime_path);
170 : 0 : return user_remove_runtime_path(runtime_path);
171 : : }
172 : :
173 : 0 : static int run(int argc, char *argv[]) {
174 : : int r;
175 : :
176 : 0 : log_parse_environment();
177 : 0 : log_open();
178 : :
179 [ # # ]: 0 : if (argc != 3)
180 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
181 : : "This program takes two arguments.");
182 [ # # ]: 0 : if (!STR_IN_SET(argv[1], "start", "stop"))
183 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
184 : : "First argument must be either \"start\" or \"stop\".");
185 : :
186 : 0 : r = mac_selinux_init();
187 [ # # ]: 0 : if (r < 0)
188 [ # # ]: 0 : return log_error_errno(r, "Could not initialize labelling: %m\n");
189 : :
190 : 0 : umask(0022);
191 : :
192 [ # # ]: 0 : if (streq(argv[1], "start"))
193 : 0 : return do_mount(argv[2]);
194 [ # # ]: 0 : if (streq(argv[1], "stop"))
195 : 0 : return do_umount(argv[2]);
196 : 0 : assert_not_reached("Unknown verb!");
197 : : }
198 : :
199 : 0 : DEFINE_MAIN_FUNCTION(run);
|