]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/home/homework-cifs.c
homework: allow specifying a dir component in CIFS services
[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 r = safe_fork("(mount)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR, &mount_pid);
87 if (r < 0)
88 return r;
89 if (r == 0) {
90 /* Child */
91 execl("/bin/mount", "/bin/mount", "-n", "-t", "cifs",
92 chost_and_service, HOME_RUNTIME_WORK_DIR,
93 "-o", options, NULL);
94
95 log_error_errno(errno, "Failed to execute mount: %m");
96 _exit(EXIT_FAILURE);
97 }
98
99 exit_status = wait_for_terminate_and_check("mount", mount_pid, WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS);
100 if (exit_status < 0)
101 return exit_status;
102 if (exit_status != EXIT_SUCCESS)
103 return -EPROTO;
104
105 setup->undo_mount = true;
106 break;
107 }
108
109 if (!setup->undo_mount)
110 return log_error_errno(SYNTHETIC_ERRNO(ENOKEY),
111 "Failed to mount home directory with supplied password.");
112
113 /* Adjust MS_SUID and similar flags */
114 r = mount_nofollow_verbose(LOG_ERR, NULL, HOME_RUNTIME_WORK_DIR, NULL, MS_BIND|MS_REMOUNT|user_record_mount_flags(h), NULL);
115 if (r < 0)
116 return r;
117
118 if (cdir) {
119 j = path_join(HOME_RUNTIME_WORK_DIR, cdir);
120 if (!j)
121 return log_oom();
122
123 if (FLAGS_SET(flags, HOME_SETUP_CIFS_MKDIR)) {
124 r = mkdir_p(j, 0700);
125 if (r < 0)
126 return log_error_errno(r, "Failed to create CIFS subdirectory: %m");
127 }
128 }
129
130 setup->root_fd = open(j ?: HOME_RUNTIME_WORK_DIR, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
131 if (setup->root_fd < 0)
132 return log_error_errno(errno, "Failed to open home directory: %m");
133
134 setup->mount_suffix = TAKE_PTR(cdir);
135 return 0;
136 }
137
138 int home_activate_cifs(
139 UserRecord *h,
140 HomeSetup *setup,
141 PasswordCache *cache,
142 UserRecord **ret_home) {
143
144 _cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *header_home = NULL;
145 const char *hdo, *hd;
146 int r;
147
148 assert(h);
149 assert(user_record_storage(h) == USER_CIFS);
150 assert(setup);
151 assert(ret_home);
152
153 assert_se(hdo = user_record_home_directory(h));
154 hd = strdupa_safe(hdo); /* copy the string out, since it might change later in the home record object */
155
156 r = home_setup(h, 0, cache, setup, &header_home);
157 if (r < 0)
158 return r;
159
160 r = home_refresh(h, setup, header_home, cache, NULL, &new_home);
161 if (r < 0)
162 return r;
163
164 setup->root_fd = safe_close(setup->root_fd);
165
166 r = home_move_mount(setup->mount_suffix, hd);
167 if (r < 0)
168 return r;
169
170 setup->undo_mount = false;
171 setup->do_drop_caches = false;
172
173 log_info("Everything completed.");
174
175 *ret_home = TAKE_PTR(new_home);
176 return 1;
177 }
178
179 int home_create_cifs(UserRecord *h, HomeSetup *setup, UserRecord **ret_home) {
180 _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
181 int r;
182
183 assert(h);
184 assert(user_record_storage(h) == USER_CIFS);
185 assert(setup);
186 assert(ret_home);
187
188 if (!h->cifs_service)
189 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks CIFS service, refusing.");
190
191 if (access("/sbin/mount.cifs", F_OK) < 0) {
192 if (errno == ENOENT)
193 return log_error_errno(SYNTHETIC_ERRNO(ENOLINK), "/sbin/mount.cifs is missing.");
194
195 return log_error_errno(errno, "Unable to detect whether /sbin/mount.cifs exists: %m");
196 }
197
198 r = home_setup_cifs(h, HOME_SETUP_CIFS_MKDIR, setup);
199 if (r < 0)
200 return r;
201
202 r = dir_is_empty_at(setup->root_fd, NULL);
203 if (r < 0)
204 return log_error_errno(r, "Failed to detect if CIFS directory is empty: %m");
205 if (r == 0)
206 return log_error_errno(SYNTHETIC_ERRNO(ENOTEMPTY), "Selected CIFS directory not empty, refusing.");
207
208 r = home_populate(h, setup->root_fd);
209 if (r < 0)
210 return r;
211
212 r = home_sync_and_statfs(setup->root_fd, NULL);
213 if (r < 0)
214 return r;
215
216 r = user_record_clone(h, USER_RECORD_LOAD_MASK_SECRET|USER_RECORD_PERMISSIVE, &new_home);
217 if (r < 0)
218 return log_error_errno(r, "Failed to clone record: %m");
219
220 r = user_record_add_binding(
221 new_home,
222 USER_CIFS,
223 NULL,
224 SD_ID128_NULL,
225 SD_ID128_NULL,
226 SD_ID128_NULL,
227 NULL,
228 NULL,
229 UINT64_MAX,
230 NULL,
231 NULL,
232 h->uid,
233 (gid_t) h->uid);
234 if (r < 0)
235 return log_error_errno(r, "Failed to add binding to record: %m");
236
237 log_info("Everything completed.");
238
239 *ret_home = TAKE_PTR(new_home);
240 return 0;
241 }