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