]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/home/homework-cifs.c
home: add new systemd-homed service that can manage LUKS homes
[thirdparty/systemd.git] / src / home / homework-cifs.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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 "mount-util.h"
11 #include "process-util.h"
12 #include "strv.h"
13 #include "tmpfile-util.h"
14
15 int home_prepare_cifs(
16 UserRecord *h,
17 bool already_activated,
18 HomeSetup *setup) {
19
20 char **pw;
21 int r;
22
23 assert(h);
24 assert(setup);
25 assert(user_record_storage(h) == USER_CIFS);
26
27 if (already_activated)
28 setup->root_fd = open(user_record_home_directory(h), O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
29 else {
30 bool mounted = false;
31
32 r = home_unshare_and_mount(NULL, NULL, false);
33 if (r < 0)
34 return r;
35
36 STRV_FOREACH(pw, h->password) {
37 _cleanup_(unlink_and_freep) char *p = NULL;
38 _cleanup_free_ char *options = NULL;
39 _cleanup_(fclosep) FILE *f = NULL;
40 pid_t mount_pid;
41 int exit_status;
42
43 r = fopen_temporary(NULL, &f, &p);
44 if (r < 0)
45 return log_error_errno(r, "Failed to create temporary credentials file: %m");
46
47 fprintf(f,
48 "username=%s\n"
49 "password=%s\n",
50 user_record_cifs_user_name(h),
51 *pw);
52
53 if (h->cifs_domain)
54 fprintf(f, "domain=%s\n", h->cifs_domain);
55
56 r = fflush_and_check(f);
57 if (r < 0)
58 return log_error_errno(r, "Failed to write temporary credentials file: %m");
59
60 f = safe_fclose(f);
61
62 if (asprintf(&options, "credentials=%s,uid=" UID_FMT ",forceuid,gid=" UID_FMT ",forcegid,file_mode=0%3o,dir_mode=0%3o",
63 p, h->uid, h->uid, h->access_mode, h->access_mode) < 0)
64 return log_oom();
65
66 r = safe_fork("(mount)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR, &mount_pid);
67 if (r < 0)
68 return r;
69 if (r == 0) {
70 /* Child */
71 execl("/bin/mount", "/bin/mount", "-n", "-t", "cifs",
72 h->cifs_service, "/run/systemd/user-home-mount",
73 "-o", options, NULL);
74
75 log_error_errno(errno, "Failed to execute fsck: %m");
76 _exit(EXIT_FAILURE);
77 }
78
79 exit_status = wait_for_terminate_and_check("mount", mount_pid, WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS);
80 if (exit_status < 0)
81 return exit_status;
82 if (exit_status != EXIT_SUCCESS)
83 return -EPROTO;
84
85 mounted = true;
86 break;
87 }
88
89 if (!mounted)
90 return log_error_errno(ENOKEY, "Failed to mount home directory with supplied password.");
91
92 setup->root_fd = open("/run/systemd/user-home-mount", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
93 }
94 if (setup->root_fd < 0)
95 return log_error_errno(r, "Failed to open home directory: %m");
96
97 return 0;
98 }
99
100 int home_activate_cifs(
101 UserRecord *h,
102 char ***pkcs11_decrypted_passwords,
103 UserRecord **ret_home) {
104
105 _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
106 _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
107 const char *hdo, *hd;
108 int r;
109
110 assert(h);
111 assert(user_record_storage(h) == USER_CIFS);
112 assert(ret_home);
113
114 if (!h->cifs_service)
115 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks CIFS service, refusing.");
116
117 assert_se(hdo = user_record_home_directory(h));
118 hd = strdupa(hdo); /* copy the string out, since it might change later in the home record object */
119
120 r = home_prepare_cifs(h, false, &setup);
121 if (r < 0)
122 return r;
123
124 r = home_refresh(h, &setup, NULL, pkcs11_decrypted_passwords, NULL, &new_home);
125 if (r < 0)
126 return r;
127
128 setup.root_fd = safe_close(setup.root_fd);
129
130 r = home_move_mount(NULL, hd);
131 if (r < 0)
132 return r;
133
134 setup.undo_mount = false;
135
136 log_info("Everything completed.");
137
138 *ret_home = TAKE_PTR(new_home);
139 return 1;
140 }
141
142 int home_create_cifs(UserRecord *h, UserRecord **ret_home) {
143 _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
144 _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
145 _cleanup_(closedirp) DIR *d = NULL;
146 int r, copy;
147
148 assert(h);
149 assert(user_record_storage(h) == USER_CIFS);
150 assert(ret_home);
151
152 if (!h->cifs_service)
153 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks CIFS service, refusing.");
154
155 if (access("/sbin/mount.cifs", F_OK) < 0) {
156 if (errno == ENOENT)
157 return log_error_errno(SYNTHETIC_ERRNO(ENOLINK), "/sbin/mount.cifs is missing.");
158
159 return log_error_errno(errno, "Unable to detect whether /sbin/mount.cifs exists: %m");
160 }
161
162 r = home_prepare_cifs(h, false, &setup);
163 if (r < 0)
164 return r;
165
166 copy = fcntl(setup.root_fd, F_DUPFD_CLOEXEC, 3);
167 if (copy < 0)
168 return -errno;
169
170 d = fdopendir(copy);
171 if (!d) {
172 safe_close(copy);
173 return -errno;
174 }
175
176 errno = 0;
177 if (readdir_no_dot(d))
178 return log_error_errno(SYNTHETIC_ERRNO(ENOTEMPTY), "Selected CIFS directory not empty, refusing.");
179 if (errno != 0)
180 return log_error_errno(errno, "Failed to detect if CIFS directory is empty: %m");
181
182 r = home_populate(h, setup.root_fd);
183 if (r < 0)
184 return r;
185
186 r = home_sync_and_statfs(setup.root_fd, NULL);
187 if (r < 0)
188 return r;
189
190 r = user_record_clone(h, USER_RECORD_LOAD_MASK_SECRET, &new_home);
191 if (r < 0)
192 return log_error_errno(r, "Failed to clone record: %m");
193
194 r = user_record_add_binding(
195 new_home,
196 USER_CIFS,
197 NULL,
198 SD_ID128_NULL,
199 SD_ID128_NULL,
200 SD_ID128_NULL,
201 NULL,
202 NULL,
203 UINT64_MAX,
204 NULL,
205 NULL,
206 h->uid,
207 (gid_t) h->uid);
208 if (r < 0)
209 return log_error_errno(r, "Failed to add binding to record: %m");
210
211 log_info("Everything completed.");
212
213 *ret_home = TAKE_PTR(new_home);
214 return 0;
215 }