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