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