ddd9514893fe3578b9e28d17d8f4148fde31532f
[samba.git] / source3 / utils / net_vfs.c
1 /*
2  * Samba Unix/Linux SMB client library
3  * Distributed SMB/CIFS Server Management Utility
4  * Copyright (C) 2019 Ralph Boehme <slow@samba.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "includes.h"
21 #include <talloc.h>
22 #include <tevent.h>
23 #include <ftw.h>
24 #include "system/filesys.h"
25 #include "system/passwd.h"
26 #include "popt_common.h"
27 #include "lib/param/loadparm.h"
28 #include "lib/param/param.h"
29 #include "libcli/security/security.h"
30 #include "smbd/proto.h"
31 #include "locking/proto.h"
32 #include "auth.h"
33 #include "client.h"
34 #include "util_sd.h"
35 #include "lib/adouble.h"
36 #include "lib/string_replace.h"
37 #include "utils/net.h"
38
39 #define NET_VFS_CMD_STREAM_TO_ADOUBLE "stream2adouble"
40
41 static struct net_vfs_state {
42         TALLOC_CTX *mem_ctx;
43         struct net_context *c;
44         struct auth_session_info *session_info;
45         struct conn_struct_tos *conn_tos;
46 } state;
47
48 static void net_vfs_usage(void)
49 {
50         fprintf(stderr,
51                 "Usage:\n"
52                 "net vfs [OPTIONS] <share> ....\n");
53 }
54
55 static void net_vfs_getntacl_usage(void)
56 {
57         fprintf(stderr,
58                 "Usage:\n"
59                 "net vfs getntacl <share> <path>\n");
60 }
61
62 static void net_vfs_stream_to_appledouble_usage(void)
63 {
64         fprintf(stderr,
65                 "Usage:\n"
66                 "net vfs " NET_VFS_CMD_STREAM_TO_ADOUBLE
67                 " [OPTIONS] <share> <path> [<path> ...]\n"
68                 "Options:\n"
69                 "  --verbose             verbose output\n"
70                 "  --continue            continue on error\n"
71                 "  --recursive           traverse directory hierarchy\n"
72                 "  --follow-symlinks     follow symlinks\n");
73 }
74
75 static bool net_vfs_make_session_info(struct auth_session_info **session_info)
76 {
77         NTSTATUS status;
78
79         if (non_root_mode()) {
80                 struct passwd *p = NULL;
81
82                 p = getpwuid(geteuid());
83                 if (p == NULL) {
84                         fprintf(stderr, "getpwuid(%d) failed\n", geteuid());
85                         return false;
86                 }
87
88                 status = make_session_info_from_username(state.mem_ctx,
89                                                          p->pw_name,
90                                                          false,
91                                                          session_info);
92                 if (!NT_STATUS_IS_OK(status)) {
93                         fprintf(stderr, "session_info from username failed\n");
94                         return false;
95                 }
96
97                 return true;
98         }
99
100         status = init_system_session_info(state.mem_ctx);
101         if (!NT_STATUS_IS_OK(status)) {
102                 fprintf(stderr, "init_system_session_info failed\n");
103                 return false;
104         }
105
106         status = make_session_info_system(state.mem_ctx, session_info);
107         if (!NT_STATUS_IS_OK(status)) {
108                 fprintf(stderr, "make_session_info_system failed\n");
109                 return false;
110         }
111
112         return true;
113 }
114
115 static int net_vfs_init(struct net_context *c, int argc, const char **argv)
116 {
117         const struct loadparm_substitution *lp_sub =
118                 loadparm_s3_global_substitution();
119         const char *service = NULL;
120         char *share_root = NULL;
121         int snum;
122         NTSTATUS status;
123         bool ok;
124         int rc = 1;
125
126         state = (struct net_vfs_state) {
127                 .c = c,
128                 .mem_ctx = c,
129         };
130
131         if (argc < 1) {
132                 net_vfs_usage();
133                 goto done;
134         }
135
136         if (geteuid() != 0 && !uid_wrapper_enabled()) {
137                 fprintf(stderr, "'net vfs' must be run as root.\n");
138                 goto done;
139         }
140
141         smb_init_locale();
142         umask(0);
143         sec_init();
144         setup_logging("net", DEBUG_STDOUT);
145         lp_set_cmdline("log level", "0");
146
147         ok = lp_load_with_registry_shares(get_dyn_CONFIGFILE());
148         if (!ok) {
149                 fprintf(stderr, "lp_load_with_registry_shares failed\n");
150                 goto done;
151         }
152
153         ok = locking_init();
154         if (!ok) {
155                 fprintf(stderr, "locking init failed\n");
156                 goto done;
157         }
158
159         ok = net_vfs_make_session_info(&state.session_info);
160         if (!ok) {
161                 goto done;
162         }
163
164         service = argv[0];
165         snum = lp_servicenumber(service);
166         if (snum == -1) {
167                 fprintf(stderr, "unknown service: %s\n", service);
168                 goto done;
169         }
170
171         share_root = lp_path(state.mem_ctx, lp_sub, snum);
172         if (share_root == NULL) {
173                 fprintf(stderr, "Failed to find share root for service: %s\n",
174                         service);
175                 goto done;
176         }
177
178         status = create_conn_struct_tos_cwd(global_messaging_context(),
179                                             snum,
180                                             share_root,
181                                             state.session_info,
182                                             &state.conn_tos);
183         if (!NT_STATUS_IS_OK(status)) {
184                 goto done;
185         }
186
187         state.conn_tos->conn->share_access = FILE_GENERIC_ALL;
188         state.conn_tos->conn->read_only = false;
189         file_init(state.conn_tos->conn->sconn);
190
191         ok = become_user_without_service_by_session(state.conn_tos->conn,
192                                                     state.session_info);
193         if (!ok) {
194                 fprintf(stderr,
195                         "become_user_without_service_by_session failed\n");
196                 goto done;
197         }
198
199         rc = 0;
200 done:
201         return rc;
202 }
203
204 static int net_vfs_get_ntacl(struct net_context *net,
205                              int argc,
206                              const char **argv)
207 {
208         const char *path = NULL;
209         struct smb_filename *smb_fname = NULL;
210         files_struct *fsp = NULL;
211         struct security_descriptor *sd = NULL;
212         NTSTATUS status;
213         int ret;
214         int rc = 1;
215
216         if (argc < 2 || net->display_usage) {
217                 net_vfs_getntacl_usage();
218                 goto done;
219         }
220
221         ret = net_vfs_init(net, argc, argv);
222         if (ret != 0) {
223                 goto done;
224         }
225
226         path = argv[1];
227         smb_fname = synthetic_smb_fname(state.mem_ctx,
228                                         path,
229                                         NULL,
230                                         NULL,
231                                         0,
232                                         0);
233         if (smb_fname == NULL) {
234                 goto done;
235         }
236
237         ret = SMB_VFS_STAT(state.conn_tos->conn, smb_fname);
238         if (ret != 0) {
239                 fprintf(stderr, "stat [%s] failed: %s\n",
240                         smb_fname_str_dbg(smb_fname), strerror(errno));
241                 goto done;
242         }
243
244         status = SMB_VFS_CREATE_FILE(
245                 state.conn_tos->conn,
246                 NULL,                           /* req */
247                 &state.conn_tos->conn->cwd_fsp,
248                 smb_fname,
249                 FILE_READ_ATTRIBUTES|READ_CONTROL_ACCESS,
250                 FILE_SHARE_READ|FILE_SHARE_WRITE,
251                 FILE_OPEN,
252                 0,                              /* create_options */
253                 0,                              /* file_attributes */
254                 INTERNAL_OPEN_ONLY,             /* oplock_request */
255                 NULL,                           /* lease */
256                 0,                              /* allocation_size */
257                 0,                              /* private_flags */
258                 NULL,                           /* sd */
259                 NULL,                           /* ea_list */
260                 &fsp,
261                 NULL,                           /* info */
262                 NULL, NULL);                    /* create context */
263         if (!NT_STATUS_IS_OK(status)) {
264                 DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed: %s\n",
265                         smb_fname_str_dbg(smb_fname), nt_errstr(status));
266                 goto done;
267         }
268
269         status = SMB_VFS_FGET_NT_ACL(fsp,
270                                      SECINFO_OWNER|SECINFO_GROUP|SECINFO_DACL,
271                                      fsp,
272                                      &sd);
273         if (!NT_STATUS_IS_OK(status)) {
274                 DBG_ERR("SMB_VFS_FGET_NT_ACL [%s] failed: %s\n",
275                         smb_fname_str_dbg(smb_fname), nt_errstr(status));
276                 goto done;
277         }
278
279         status = close_file(NULL, fsp, NORMAL_CLOSE);
280         if (!NT_STATUS_IS_OK(status)) {
281                 DBG_ERR("close_file [%s] failed: %s\n",
282                         smb_fname_str_dbg(smb_fname),
283                         nt_errstr(status));
284                 goto done;
285         }
286         fsp = NULL;
287
288         sec_desc_print(NULL, stdout, sd, true);
289
290         rc = 0;
291 done:
292         if (fsp != NULL) {
293                 status = close_file(NULL, fsp, NORMAL_CLOSE);
294                 if (!NT_STATUS_IS_OK(status)) {
295                         DBG_ERR("close_file [%s] failed: %s\n",
296                                 smb_fname_str_dbg(smb_fname),
297                                 nt_errstr(status));
298                         rc = 1;
299                 }
300         }
301         return rc;
302 }
303
304 static bool do_unfruit(const char *path)
305 {
306         struct smb_filename *smb_fname = NULL;
307         char *p = NULL;
308         bool converted;
309         int ret;
310         bool ok;
311
312         p = strrchr_m(path, '/');
313         if (p != NULL) {
314                 if (p[1] == '.' && p[2] == '_') {
315                         return true;
316                 }
317         }
318
319         smb_fname = synthetic_smb_fname(state.mem_ctx,
320                                         path,
321                                         NULL,
322                                         NULL,
323                                         0,
324                                         0);
325         if (smb_fname == NULL) {
326                 return false;
327         }
328
329         ret = SMB_VFS_STAT(state.conn_tos->conn, smb_fname);
330         if (ret != 0) {
331                 fprintf(stderr, "%s: %s\n", path, strerror(errno));
332                 if (state.c->opt_continue_on_error) {
333                         return true;
334                 }
335                 return false;
336         }
337
338         ok = ad_unconvert(state.mem_ctx,
339                           state.conn_tos->conn->vfs_handles,
340                           macos_string_replace_map,
341                           smb_fname,
342                           &converted);
343         if (!ok) {
344                 fprintf(stderr, "Converting failed: %s\n", path);
345                 if (state.c->opt_continue_on_error) {
346                         return true;
347                 }
348                 return false;
349         }
350
351         if (converted) {
352                 fprintf(stdout, "Converted: %s\n", path);
353         } else if (state.c->opt_verbose) {
354                 fprintf(stdout, "%s\n", path);
355         }
356         return true;
357 }
358
359 static int nftw_cb(const char *path,
360                    const struct stat *sb,
361                    int typeflag,
362                    struct FTW *ftwbuf)
363 {
364         bool ok;
365
366         if (typeflag == FTW_SL) {
367                 if (state.c->opt_verbose) {
368                         fprintf(stdout, "Ignoring symlink: %s\n", path);
369                 }
370                 return 0;
371         }
372
373         ok = do_unfruit(path);
374         if (!ok) {
375                 return -1;
376         }
377
378         return 0;
379 }
380
381 static int net_vfs_stream_to_appledouble(struct net_context *net,
382                                          int argc,
383                                          const char **argv)
384 {
385         int i;
386         int ret;
387         bool ok;
388         int rc = 1;
389
390         if (argc < 2 || net->display_usage) {
391                 net_vfs_stream_to_appledouble_usage();
392                 goto done;
393         }
394
395         ret = net_vfs_init(net, argc, argv);
396         if (ret != 0) {
397                 goto done;
398         }
399
400         for (i = 1; i < argc; i++) {
401                 const char *path = argv[i];
402
403                 if (path[0] == '/') {
404                         fprintf(stderr, "ignoring absolute path: %s\n", path);
405                         if (state.c->opt_continue_on_error) {
406                                 continue;
407                         }
408                         goto done;
409                 }
410
411                 if (!state.c->opt_recursive) {
412                         ok = do_unfruit(path);
413                         if (!ok) {
414                                 if (!state.c->opt_continue_on_error) {
415                                         goto done;
416                                 }
417                         }
418                         continue;
419                 }
420
421                 ret = nftw(path,
422                            nftw_cb,
423                            256,
424                            state.c->opt_follow_symlink ? 0 : FTW_PHYS);
425                 if (ret != 0) {
426                         fprintf(stderr, "%s: %s\n", path, strerror(errno));
427                         if (!state.c->opt_continue_on_error) {
428                                 goto done;
429                         }
430                 }
431         }
432
433         rc = 0;
434
435 done:
436         return rc;
437 }
438
439 static struct functable func[] = {
440         {
441                 "getntacl",
442                 net_vfs_get_ntacl,
443                 NET_TRANSPORT_LOCAL,
444                 N_("Display security descriptor of a file or directory"),
445                 N_("net vfs getntacl <share> <path> [<path> ...]")
446         },
447         {
448                 NET_VFS_CMD_STREAM_TO_ADOUBLE,
449                 net_vfs_stream_to_appledouble,
450                 NET_TRANSPORT_LOCAL,
451                 N_("Convert streams to AppleDouble files"),
452                 N_("net vfs " NET_VFS_CMD_STREAM_TO_ADOUBLE " [OPTIONS] <share> <path> [<path> ...]")
453         },
454         {NULL, NULL, 0, NULL, NULL}
455 };
456
457 int net_vfs(struct net_context *c, int argc, const char **argv)
458 {
459         return net_run_function(c, argc, argv, "net vfs", func);
460 }