]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
70a5db58 | 2 | |
0a58cd00 YW |
3 | #include <sys/mount.h> |
4 | #if WANT_LINUX_FS_H | |
5 | #include <linux/fs.h> | |
6 | #endif | |
7 | ||
606a1f20 | 8 | #include "data-fd-util.h" |
70a5db58 LP |
9 | #include "dirent-util.h" |
10 | #include "fd-util.h" | |
11 | #include "fileio.h" | |
12 | #include "format-util.h" | |
13 | #include "fs-util.h" | |
14 | #include "homework-cifs.h" | |
15 | #include "homework-mount.h" | |
bf15879b | 16 | #include "mkdir.h" |
70a5db58 LP |
17 | #include "mount-util.h" |
18 | #include "process-util.h" | |
1845ba36 | 19 | #include "stat-util.h" |
70a5db58 LP |
20 | #include "strv.h" |
21 | #include "tmpfile-util.h" | |
22 | ||
aa0a6214 | 23 | int home_setup_cifs( |
70a5db58 | 24 | UserRecord *h, |
e1df968b | 25 | HomeSetupFlags flags, |
70a5db58 LP |
26 | HomeSetup *setup) { |
27 | ||
606a1f20 | 28 | _cleanup_free_ char *chost = NULL, *cservice = NULL, *cdir = NULL, *chost_and_service = NULL, *j = NULL, *options = NULL; |
812e5876 LP |
29 | int r; |
30 | ||
70a5db58 | 31 | assert(h); |
70a5db58 | 32 | assert(user_record_storage(h) == USER_CIFS); |
812e5876 LP |
33 | assert(setup); |
34 | assert(!setup->undo_mount); | |
35 | assert(setup->root_fd < 0); | |
70a5db58 | 36 | |
812e5876 | 37 | if (FLAGS_SET(flags, HOME_SETUP_ALREADY_ACTIVATED)) { |
70a5db58 | 38 | setup->root_fd = open(user_record_home_directory(h), O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW); |
812e5876 LP |
39 | if (setup->root_fd < 0) |
40 | return log_error_errno(errno, "Failed to open home directory: %m"); | |
41 | ||
42 | return 0; | |
43 | } | |
44 | ||
5971c318 LP |
45 | if (!h->cifs_service) |
46 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks CIFS service, refusing."); | |
47 | ||
bf15879b LP |
48 | r = parse_cifs_service(h->cifs_service, &chost, &cservice, &cdir); |
49 | if (r < 0) | |
50 | return log_error_errno(r, "Failed parse CIFS service specification: %m"); | |
51 | ||
52 | /* Just the host and service part, without the directory */ | |
53 | chost_and_service = strjoin("//", chost, "/", cservice); | |
54 | if (!chost_and_service) | |
55 | return log_oom(); | |
56 | ||
606a1f20 AV |
57 | if (asprintf(&options, "user=%s,uid=" UID_FMT ",forceuid,gid=" GID_FMT ",forcegid,file_mode=0%3o,dir_mode=0%3o", |
58 | user_record_cifs_user_name(h), h->uid, user_record_gid(h), user_record_access_mode(h), | |
59 | user_record_access_mode(h)) < 0) | |
60 | return log_oom(); | |
61 | ||
62 | if (h->cifs_domain) | |
63 | if (strextendf_with_separator(&options, ",", "domain=%s", h->cifs_domain) < 0) | |
64 | return log_oom(); | |
65 | ||
66 | if (h->cifs_extra_mount_options) | |
67 | if (!strextend_with_separator(&options, ",", h->cifs_extra_mount_options)) | |
68 | return log_oom(); | |
69 | ||
812e5876 LP |
70 | r = home_unshare_and_mkdir(); |
71 | if (r < 0) | |
72 | return r; | |
73 | ||
74 | STRV_FOREACH(pw, h->password) { | |
606a1f20 | 75 | _cleanup_close_ int passwd_fd = -EBADF; |
812e5876 LP |
76 | pid_t mount_pid; |
77 | int exit_status; | |
70a5db58 | 78 | |
0870fc24 | 79 | passwd_fd = acquire_data_fd(*pw); |
606a1f20 AV |
80 | if (passwd_fd < 0) |
81 | return log_error_errno(passwd_fd, "Failed to create data FD for password: %m"); | |
4c2ee5c7 | 82 | |
e9ccae31 | 83 | r = safe_fork("(mount)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_STDOUT_TO_STDERR, &mount_pid); |
812e5876 LP |
84 | if (r < 0) |
85 | return r; | |
86 | if (r == 0) { | |
87 | /* Child */ | |
606a1f20 AV |
88 | |
89 | r = fd_cloexec(passwd_fd, false); | |
90 | if (r < 0) { | |
91 | log_error_errno(r, "Failed to disable CLOEXEC on password FD: %m"); | |
92 | _exit(EXIT_FAILURE); | |
93 | } | |
94 | ||
95 | r = setenvf("PASSWD_FD", /* overwrite= */ true, "%d", passwd_fd); | |
96 | if (r < 0) { | |
72a22e73 | 97 | log_error_errno(r, "Failed to set $PASSWD_FD: %m"); |
606a1f20 AV |
98 | _exit(EXIT_FAILURE); |
99 | } | |
100 | ||
812e5876 | 101 | execl("/bin/mount", "/bin/mount", "-n", "-t", "cifs", |
bf15879b | 102 | chost_and_service, HOME_RUNTIME_WORK_DIR, |
812e5876 LP |
103 | "-o", options, NULL); |
104 | ||
105 | log_error_errno(errno, "Failed to execute mount: %m"); | |
106 | _exit(EXIT_FAILURE); | |
70a5db58 LP |
107 | } |
108 | ||
812e5876 LP |
109 | exit_status = wait_for_terminate_and_check("mount", mount_pid, WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS); |
110 | if (exit_status < 0) | |
111 | return exit_status; | |
22aba9b2 LP |
112 | if (exit_status == EXIT_SUCCESS) { |
113 | setup->undo_mount = true; | |
114 | break; | |
115 | } | |
70a5db58 | 116 | |
22aba9b2 LP |
117 | if (pw[1]) |
118 | log_info("CIFS mount failed with password #%zu, trying next password.", (size_t) (pw - h->password) + 1); | |
70a5db58 | 119 | } |
812e5876 LP |
120 | |
121 | if (!setup->undo_mount) | |
122 | return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), | |
22aba9b2 | 123 | "Failed to mount home directory, supplied password(s) possibly wrong."); |
812e5876 | 124 | |
c9080dfb LP |
125 | /* Adjust MS_SUID and similar flags */ |
126 | r = mount_nofollow_verbose(LOG_ERR, NULL, HOME_RUNTIME_WORK_DIR, NULL, MS_BIND|MS_REMOUNT|user_record_mount_flags(h), NULL); | |
127 | if (r < 0) | |
128 | return r; | |
129 | ||
bf15879b LP |
130 | if (cdir) { |
131 | j = path_join(HOME_RUNTIME_WORK_DIR, cdir); | |
132 | if (!j) | |
133 | return log_oom(); | |
134 | ||
135 | if (FLAGS_SET(flags, HOME_SETUP_CIFS_MKDIR)) { | |
c29778a1 | 136 | setup->root_fd = open_mkdir(j, O_CLOEXEC, 0700); |
96603ea0 LP |
137 | if (setup->root_fd < 0) |
138 | return log_error_errno(setup->root_fd, "Failed to create CIFS subdirectory: %m"); | |
bf15879b LP |
139 | } |
140 | } | |
141 | ||
96603ea0 LP |
142 | if (setup->root_fd < 0) { |
143 | setup->root_fd = open(j ?: HOME_RUNTIME_WORK_DIR, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW); | |
144 | if (setup->root_fd < 0) | |
145 | return log_error_errno(errno, "Failed to open home directory: %m"); | |
146 | } | |
70a5db58 | 147 | |
bf15879b | 148 | setup->mount_suffix = TAKE_PTR(cdir); |
70a5db58 LP |
149 | return 0; |
150 | } | |
151 | ||
152 | int home_activate_cifs( | |
153 | UserRecord *h, | |
6f2c8136 | 154 | HomeSetupFlags flags, |
a74e2e44 | 155 | HomeSetup *setup, |
7b78db28 | 156 | PasswordCache *cache, |
70a5db58 LP |
157 | UserRecord **ret_home) { |
158 | ||
de7df6c3 | 159 | _cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *header_home = NULL; |
70a5db58 LP |
160 | const char *hdo, *hd; |
161 | int r; | |
162 | ||
163 | assert(h); | |
164 | assert(user_record_storage(h) == USER_CIFS); | |
a74e2e44 | 165 | assert(setup); |
70a5db58 LP |
166 | assert(ret_home); |
167 | ||
70a5db58 | 168 | assert_se(hdo = user_record_home_directory(h)); |
2f82562b | 169 | hd = strdupa_safe(hdo); /* copy the string out, since it might change later in the home record object */ |
70a5db58 | 170 | |
c00b2ddc | 171 | r = home_setup(h, 0, setup, cache, &header_home); |
70a5db58 LP |
172 | if (r < 0) |
173 | return r; | |
174 | ||
6f2c8136 | 175 | r = home_refresh(h, flags, setup, header_home, cache, NULL, &new_home); |
70a5db58 LP |
176 | if (r < 0) |
177 | return r; | |
178 | ||
a74e2e44 | 179 | setup->root_fd = safe_close(setup->root_fd); |
70a5db58 | 180 | |
bf15879b | 181 | r = home_move_mount(setup->mount_suffix, hd); |
70a5db58 LP |
182 | if (r < 0) |
183 | return r; | |
184 | ||
a74e2e44 | 185 | setup->undo_mount = false; |
aa0379f1 | 186 | setup->do_drop_caches = false; |
70a5db58 LP |
187 | |
188 | log_info("Everything completed."); | |
189 | ||
190 | *ret_home = TAKE_PTR(new_home); | |
191 | return 1; | |
192 | } | |
193 | ||
a74e2e44 | 194 | int home_create_cifs(UserRecord *h, HomeSetup *setup, UserRecord **ret_home) { |
70a5db58 | 195 | _cleanup_(user_record_unrefp) UserRecord *new_home = NULL; |
8e06af80 | 196 | int r; |
70a5db58 LP |
197 | |
198 | assert(h); | |
199 | assert(user_record_storage(h) == USER_CIFS); | |
a74e2e44 | 200 | assert(setup); |
70a5db58 LP |
201 | assert(ret_home); |
202 | ||
203 | if (!h->cifs_service) | |
204 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks CIFS service, refusing."); | |
205 | ||
206 | if (access("/sbin/mount.cifs", F_OK) < 0) { | |
207 | if (errno == ENOENT) | |
208 | return log_error_errno(SYNTHETIC_ERRNO(ENOLINK), "/sbin/mount.cifs is missing."); | |
209 | ||
210 | return log_error_errno(errno, "Unable to detect whether /sbin/mount.cifs exists: %m"); | |
211 | } | |
212 | ||
bf15879b | 213 | r = home_setup_cifs(h, HOME_SETUP_CIFS_MKDIR, setup); |
70a5db58 LP |
214 | if (r < 0) |
215 | return r; | |
216 | ||
db55bbf2 | 217 | r = dir_is_empty_at(setup->root_fd, NULL, /* ignore_hidden_or_backup= */ false); |
1845ba36 LP |
218 | if (r < 0) |
219 | return log_error_errno(r, "Failed to detect if CIFS directory is empty: %m"); | |
220 | if (r == 0) | |
70a5db58 | 221 | return log_error_errno(SYNTHETIC_ERRNO(ENOTEMPTY), "Selected CIFS directory not empty, refusing."); |
70a5db58 | 222 | |
a74e2e44 | 223 | r = home_populate(h, setup->root_fd); |
70a5db58 LP |
224 | if (r < 0) |
225 | return r; | |
226 | ||
a74e2e44 | 227 | r = home_sync_and_statfs(setup->root_fd, NULL); |
70a5db58 LP |
228 | if (r < 0) |
229 | return r; | |
230 | ||
bfc0cc1a | 231 | r = user_record_clone(h, USER_RECORD_LOAD_MASK_SECRET|USER_RECORD_PERMISSIVE, &new_home); |
70a5db58 LP |
232 | if (r < 0) |
233 | return log_error_errno(r, "Failed to clone record: %m"); | |
234 | ||
235 | r = user_record_add_binding( | |
236 | new_home, | |
237 | USER_CIFS, | |
238 | NULL, | |
239 | SD_ID128_NULL, | |
240 | SD_ID128_NULL, | |
241 | SD_ID128_NULL, | |
242 | NULL, | |
243 | NULL, | |
244 | UINT64_MAX, | |
245 | NULL, | |
246 | NULL, | |
247 | h->uid, | |
248 | (gid_t) h->uid); | |
249 | if (r < 0) | |
250 | return log_error_errno(r, "Failed to add binding to record: %m"); | |
251 | ||
252 | log_info("Everything completed."); | |
253 | ||
254 | *ret_home = TAKE_PTR(new_home); | |
255 | return 0; | |
256 | } |