dosmode.c: prefer use of capabilities at two places over become_root
[samba.git] / source3 / smbd / dosmode.c
1 /*
2    Unix SMB/CIFS implementation.
3    dos mode handling functions
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) James Peach 2006
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "globals.h"
23 #include "system/filesys.h"
24 #include "librpc/gen_ndr/ndr_xattr.h"
25 #include "librpc/gen_ndr/ioctl.h"
26 #include "../libcli/security/security.h"
27 #include "smbd/smbd.h"
28 #include "lib/param/loadparm.h"
29 #include "lib/util/tevent_ntstatus.h"
30 #include "lib/util/string_wrappers.h"
31 #include "fake_file.h"
32
33 static void dos_mode_debug_print(const char *func, uint32_t mode)
34 {
35         fstring modestr;
36
37         if (DEBUGLEVEL < DBGLVL_INFO) {
38                 return;
39         }
40
41         modestr[0] = '\0';
42
43         if (mode & FILE_ATTRIBUTE_HIDDEN) {
44                 fstrcat(modestr, "h");
45         }
46         if (mode & FILE_ATTRIBUTE_READONLY) {
47                 fstrcat(modestr, "r");
48         }
49         if (mode & FILE_ATTRIBUTE_SYSTEM) {
50                 fstrcat(modestr, "s");
51         }
52         if (mode & FILE_ATTRIBUTE_DIRECTORY) {
53                 fstrcat(modestr, "d");
54         }
55         if (mode & FILE_ATTRIBUTE_ARCHIVE) {
56                 fstrcat(modestr, "a");
57         }
58         if (mode & FILE_ATTRIBUTE_SPARSE) {
59                 fstrcat(modestr, "[sparse]");
60         }
61         if (mode & FILE_ATTRIBUTE_OFFLINE) {
62                 fstrcat(modestr, "[offline]");
63         }
64         if (mode & FILE_ATTRIBUTE_COMPRESSED) {
65                 fstrcat(modestr, "[compressed]");
66         }
67
68         DBG_INFO("%s returning (0x%x): \"%s\"\n", func, (unsigned)mode,
69                  modestr);
70 }
71
72 static uint32_t filter_mode_by_protocol(uint32_t mode)
73 {
74         if (get_Protocol() <= PROTOCOL_LANMAN2) {
75                 DEBUG(10,("filter_mode_by_protocol: "
76                         "filtering result 0x%x to 0x%x\n",
77                         (unsigned int)mode,
78                         (unsigned int)(mode & 0x3f) ));
79                 mode &= 0x3f;
80         }
81         return mode;
82 }
83
84 /****************************************************************************
85  Change a dos mode to a unix mode.
86     Base permission for files:
87          if creating file and inheriting (i.e. parent_dir != NULL)
88            apply read/write bits from parent directory.
89          else
90            everybody gets read bit set
91          dos readonly is represented in unix by removing everyone's write bit
92          dos archive is represented in unix by the user's execute bit
93          dos system is represented in unix by the group's execute bit
94          dos hidden is represented in unix by the other's execute bit
95          if !inheriting {
96            Then apply create mask,
97            then add force bits.
98          }
99     Base permission for directories:
100          dos directory is represented in unix by unix's dir bit and the exec bit
101          if !inheriting {
102            Then apply create mask,
103            then add force bits.
104          }
105 ****************************************************************************/
106
107 mode_t unix_mode(connection_struct *conn, int dosmode,
108                  const struct smb_filename *smb_fname,
109                  struct files_struct *parent_dirfsp)
110 {
111         mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
112         mode_t dir_mode = 0; /* Mode of the inherit_from directory if
113                               * inheriting. */
114
115         if ((dosmode & FILE_ATTRIBUTE_READONLY) &&
116             !lp_store_dos_attributes(SNUM(conn))) {
117                 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
118         }
119
120         if ((parent_dirfsp != NULL) && lp_inherit_permissions(SNUM(conn))) {
121                 struct stat_ex sbuf = { .st_ex_nlink = 0, };
122                 int ret;
123
124                 DBG_DEBUG("[%s] inheriting from [%s]\n",
125                           smb_fname_str_dbg(smb_fname),
126                           smb_fname_str_dbg(parent_dirfsp->fsp_name));
127
128                 ret = SMB_VFS_FSTAT(parent_dirfsp, &sbuf);
129                 if (ret != 0) {
130                         DBG_ERR("fstat failed [%s]: %s\n",
131                                 smb_fname_str_dbg(parent_dirfsp->fsp_name),
132                                 strerror(errno));
133                         return(0);      /* *** shouldn't happen! *** */
134                 }
135
136                 /* Save for later - but explicitly remove setuid bit for safety. */
137                 dir_mode = sbuf.st_ex_mode & ~S_ISUID;
138                 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
139                          smb_fname_str_dbg(smb_fname), (int)dir_mode));
140                 /* Clear "result" */
141                 result = 0;
142         }
143
144         if (dosmode & FILE_ATTRIBUTE_DIRECTORY) {
145                 /* We never make directories read only for the owner as under DOS a user
146                 can always create a file in a read-only directory. */
147                 result |= (S_IFDIR | S_IWUSR);
148
149                 if (dir_mode) {
150                         /* Inherit mode of parent directory. */
151                         result |= dir_mode;
152                 } else {
153                         /* Provisionally add all 'x' bits */
154                         result |= (S_IXUSR | S_IXGRP | S_IXOTH);
155
156                         /* Apply directory mask */
157                         result &= lp_directory_mask(SNUM(conn));
158                         /* Add in force bits */
159                         result |= lp_force_directory_mode(SNUM(conn));
160                 }
161         } else {
162                 if ((dosmode & FILE_ATTRIBUTE_ARCHIVE) &&
163                     lp_map_archive(SNUM(conn))) {
164                         result |= S_IXUSR;
165                 }
166
167                 if ((dosmode & FILE_ATTRIBUTE_SYSTEM) &&
168                     lp_map_system(SNUM(conn))) {
169                         result |= S_IXGRP;
170                 }
171
172                 if ((dosmode & FILE_ATTRIBUTE_HIDDEN) &&
173                     lp_map_hidden(SNUM(conn))) {
174                         result |= S_IXOTH;
175                 }
176
177                 if (dir_mode) {
178                         /* Inherit 666 component of parent directory mode */
179                         result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
180                 } else {
181                         /* Apply mode mask */
182                         result &= lp_create_mask(SNUM(conn));
183                         /* Add in force bits */
184                         result |= lp_force_create_mode(SNUM(conn));
185                 }
186         }
187
188         DBG_INFO("unix_mode(%s) returning 0%o\n",
189                  smb_fname_str_dbg(smb_fname), (int)result);
190
191         return(result);
192 }
193
194 /****************************************************************************
195  Change a unix mode to a dos mode.
196 ****************************************************************************/
197
198 static uint32_t dos_mode_from_sbuf(connection_struct *conn,
199                                    const struct stat_ex *st,
200                                    struct files_struct *fsp)
201 {
202         int result = 0;
203         enum mapreadonly_options ro_opts =
204                 (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
205
206 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
207         /* if we can find out if a file is immutable we should report it r/o */
208         if (st->st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
209                 result |= FILE_ATTRIBUTE_READONLY;
210         }
211 #endif
212         if (ro_opts == MAP_READONLY_YES) {
213                 /* Original Samba method - map inverse of user "w" bit. */
214                 if ((st->st_ex_mode & S_IWUSR) == 0) {
215                         result |= FILE_ATTRIBUTE_READONLY;
216                 }
217         } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
218                 /* smb_fname->fsp can be NULL for an MS-DFS link. */
219                 /* Check actual permissions for read-only. */
220                 if ((fsp != NULL) && !can_write_to_fsp(fsp)) {
221                         result |= FILE_ATTRIBUTE_READONLY;
222                 }
223         } /* Else never set the readonly bit. */
224
225         if (MAP_ARCHIVE(conn) && ((st->st_ex_mode & S_IXUSR) != 0)) {
226                 result |= FILE_ATTRIBUTE_ARCHIVE;
227         }
228
229         if (MAP_SYSTEM(conn) && ((st->st_ex_mode & S_IXGRP) != 0)) {
230                 result |= FILE_ATTRIBUTE_SYSTEM;
231         }
232
233         if (MAP_HIDDEN(conn) && ((st->st_ex_mode & S_IXOTH) != 0)) {
234                 result |= FILE_ATTRIBUTE_HIDDEN;
235         }
236
237         if (S_ISDIR(st->st_ex_mode)) {
238                 result = FILE_ATTRIBUTE_DIRECTORY |
239                          (result & FILE_ATTRIBUTE_READONLY);
240         }
241
242         dos_mode_debug_print(__func__, result);
243
244         return result;
245 }
246
247 /****************************************************************************
248  Get DOS attributes from an EA.
249  This can also pull the create time into the stat struct inside smb_fname.
250 ****************************************************************************/
251
252 NTSTATUS parse_dos_attribute_blob(struct smb_filename *smb_fname,
253                                   DATA_BLOB blob,
254                                   uint32_t *pattr)
255 {
256         struct xattr_DOSATTRIB dosattrib;
257         enum ndr_err_code ndr_err;
258         uint32_t dosattr;
259
260         ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
261                         (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
262
263         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
264                 DBG_WARNING("bad ndr decode "
265                             "from EA on file %s: Error = %s\n",
266                             smb_fname_str_dbg(smb_fname),
267                             ndr_errstr(ndr_err));
268                 return ndr_map_error2ntstatus(ndr_err);
269         }
270
271         DBG_DEBUG("%s attr = %s\n",
272                   smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex);
273
274         switch (dosattrib.version) {
275         case 0xFFFF:
276                 dosattr = dosattrib.info.compatinfoFFFF.attrib;
277                 break;
278         case 1:
279                 dosattr = dosattrib.info.info1.attrib;
280                 if (!null_nttime(dosattrib.info.info1.create_time)) {
281                         struct timespec create_time =
282                                 nt_time_to_unix_timespec(
283                                         dosattrib.info.info1.create_time);
284
285                         update_stat_ex_create_time(&smb_fname->st,
286                                                    create_time);
287
288                         DBG_DEBUG("file %s case 1 set btime %s",
289                                   smb_fname_str_dbg(smb_fname),
290                                   time_to_asc(convert_timespec_to_time_t(
291                                                       create_time)));
292                 }
293                 break;
294         case 2:
295                 dosattr = dosattrib.info.oldinfo2.attrib;
296                 /* Don't know what flags to check for this case. */
297                 break;
298         case 3:
299                 dosattr = dosattrib.info.info3.attrib;
300                 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
301                     !null_nttime(dosattrib.info.info3.create_time)) {
302                         struct timespec create_time =
303                                 nt_time_to_full_timespec(
304                                         dosattrib.info.info3.create_time);
305
306                         update_stat_ex_create_time(&smb_fname->st,
307                                                    create_time);
308
309                         DBG_DEBUG("file %s case 3 set btime %s",
310                                   smb_fname_str_dbg(smb_fname),
311                                   time_to_asc(convert_timespec_to_time_t(
312                                                       create_time)));
313                 }
314                 break;
315         case 4:
316         case 5:
317         {
318                 uint32_t info_valid_flags;
319                 NTTIME info_create_time;
320
321                 if (dosattrib.version == 4) {
322                         info_valid_flags = dosattrib.info.info4.valid_flags;
323                         info_create_time = dosattrib.info.info4.create_time;
324                         dosattr = dosattrib.info.info4.attrib;
325                 } else {
326                         info_valid_flags = dosattrib.info.info5.valid_flags;
327                         info_create_time = dosattrib.info.info5.create_time;
328                         dosattr = dosattrib.info.info5.attrib;
329                 }
330
331                 if ((info_valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
332                     !null_nttime(info_create_time))
333                 {
334                         struct timespec creat_time;
335
336                         creat_time = nt_time_to_full_timespec(info_create_time);
337                         update_stat_ex_create_time(&smb_fname->st, creat_time);
338
339                         DBG_DEBUG("file [%s] creation time [%s]\n",
340                                 smb_fname_str_dbg(smb_fname),
341                                 nt_time_string(talloc_tos(), info_create_time));
342                 }
343
344                 break;
345         }
346         default:
347                 DBG_WARNING("Badly formed DOSATTRIB on file %s - %s\n",
348                             smb_fname_str_dbg(smb_fname), blob.data);
349                 /* Should this be INTERNAL_ERROR? */
350                 return NT_STATUS_INVALID_PARAMETER;
351         }
352
353         if (S_ISDIR(smb_fname->st.st_ex_mode)) {
354                 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
355         }
356
357         /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
358         *pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
359
360         dos_mode_debug_print(__func__, *pattr);
361
362         return NT_STATUS_OK;
363 }
364
365 NTSTATUS fget_ea_dos_attribute(struct files_struct *fsp,
366                               uint32_t *pattr)
367 {
368         DATA_BLOB blob;
369         ssize_t sizeret;
370         fstring attrstr;
371         NTSTATUS status;
372
373         if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
374                 return NT_STATUS_NOT_IMPLEMENTED;
375         }
376
377         /* Don't reset pattr to zero as we may already have filename-based attributes we
378            need to preserve. */
379
380         sizeret = SMB_VFS_FGETXATTR(fsp,
381                                     SAMBA_XATTR_DOS_ATTRIB,
382                                     attrstr,
383                                     sizeof(attrstr));
384         if (sizeret == -1 && ( errno == EPERM || errno == EACCES )) {
385                 /* we may also retrieve dos attribs for unreadable files, this
386                    is why we'll retry as root. We don't use root in the first
387                    run because in cases like NFS, root might have even less
388                    rights than the real user
389                 */
390                 set_effective_capability(DAC_OVERRIDE_CAPABILITY);
391                 sizeret = SMB_VFS_FGETXATTR(fsp,
392                                             SAMBA_XATTR_DOS_ATTRIB,
393                                             attrstr,
394                                             sizeof(attrstr));
395                 drop_effective_capability(DAC_OVERRIDE_CAPABILITY);
396         }
397         if (sizeret == -1) {
398                 DBG_INFO("Cannot get attribute "
399                          "from EA on file %s: Error = %s\n",
400                          fsp_str_dbg(fsp), strerror(errno));
401                 return map_nt_error_from_unix(errno);
402         }
403
404         blob.data = (uint8_t *)attrstr;
405         blob.length = sizeret;
406
407         status = parse_dos_attribute_blob(fsp->fsp_name, blob, pattr);
408         if (!NT_STATUS_IS_OK(status)) {
409                 return status;
410         }
411
412         return NT_STATUS_OK;
413 }
414
415 /****************************************************************************
416  Set DOS attributes in an EA.
417  Also sets the create time.
418 ****************************************************************************/
419
420 NTSTATUS set_ea_dos_attribute(connection_struct *conn,
421                               struct smb_filename *smb_fname,
422                               uint32_t dosmode)
423 {
424         struct xattr_DOSATTRIB dosattrib = { .version = 0, };
425         enum ndr_err_code ndr_err;
426         DATA_BLOB blob = { .data = NULL, };
427         struct timespec btime;
428         int ret;
429
430         if (!lp_store_dos_attributes(SNUM(conn))) {
431                 return NT_STATUS_NOT_IMPLEMENTED;
432         }
433
434         if (smb_fname->fsp == NULL) {
435                 /* symlink */
436                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
437         }
438         /*
439          * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
440          * vfs_default via DMAPI if that is enabled.
441          */
442         dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
443
444         dosattrib.version = 5;
445         dosattrib.info.info5.valid_flags = XATTR_DOSINFO_ATTRIB |
446                                         XATTR_DOSINFO_CREATE_TIME;
447         dosattrib.info.info5.attrib = dosmode;
448         dosattrib.info.info5.create_time = full_timespec_to_nt_time(
449                 &smb_fname->st.st_ex_btime);
450
451         DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
452                 (unsigned int)dosmode,
453                 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
454                 smb_fname_str_dbg(smb_fname) ));
455
456         ndr_err = ndr_push_struct_blob(
457                         &blob, talloc_tos(), &dosattrib,
458                         (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
459
460         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
461                 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
462                         ndr_errstr(ndr_err)));
463                 return ndr_map_error2ntstatus(ndr_err);
464         }
465
466         if (blob.data == NULL || blob.length == 0) {
467                 /* Should this be INTERNAL_ERROR? */
468                 return NT_STATUS_INVALID_PARAMETER;
469         }
470
471         ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
472                                SAMBA_XATTR_DOS_ATTRIB,
473                                blob.data, blob.length, 0);
474         if (ret != 0) {
475                 NTSTATUS status = NT_STATUS_OK;
476                 bool set_dosmode_ok = false;
477
478                 if ((errno != EPERM) && (errno != EACCES)) {
479                         DBG_INFO("Cannot set "
480                                  "attribute EA on file %s: Error = %s\n",
481                                  smb_fname_str_dbg(smb_fname), strerror(errno));
482                         return map_nt_error_from_unix(errno);
483                 }
484
485                 /* We want DOS semantics, ie allow non owner with write permission to change the
486                         bits on a file. Just like file_ntimes below.
487                 */
488
489                 /* Check if we have write access. */
490                 if (!CAN_WRITE(conn)) {
491                         return NT_STATUS_ACCESS_DENIED;
492                 }
493
494                 status = smbd_check_access_rights_fsp(conn->cwd_fsp,
495                                         smb_fname->fsp,
496                                         false,
497                                         FILE_WRITE_ATTRIBUTES);
498                 if (NT_STATUS_IS_OK(status)) {
499                         set_dosmode_ok = true;
500                 }
501
502                 if (!set_dosmode_ok && lp_dos_filemode(SNUM(conn))) {
503                         set_dosmode_ok = can_write_to_fsp(smb_fname->fsp);
504                 }
505
506                 if (!set_dosmode_ok) {
507                         return NT_STATUS_ACCESS_DENIED;
508                 }
509
510                 set_effective_capability(DAC_OVERRIDE_CAPABILITY);
511                 ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
512                                         SAMBA_XATTR_DOS_ATTRIB,
513                                         blob.data, blob.length, 0);
514                 drop_effective_capability(DAC_OVERRIDE_CAPABILITY);
515                 if (ret == 0) {
516                         status = NT_STATUS_OK;
517                 }
518                 if (!NT_STATUS_IS_OK(status)) {
519                         return status;
520                 }
521         }
522
523         /*
524          * We correctly stored the create time.
525          * We *always* set XATTR_DOSINFO_CREATE_TIME,
526          * so now it can no longer be considered
527          * calculated. Make sure to use the value rounded
528          * to NTTIME granularity we've stored in the xattr.
529          */
530         btime = nt_time_to_full_timespec(dosattrib.info.info5.create_time);
531         update_stat_ex_create_time(&smb_fname->st, btime);
532
533         DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
534                 (unsigned int)dosmode,
535                 smb_fname_str_dbg(smb_fname)));
536         return NT_STATUS_OK;
537 }
538
539 static uint32_t
540 dos_mode_from_name(connection_struct *conn, const char *name, uint32_t dosmode)
541 {
542         const char *p = NULL;
543         uint32_t result = dosmode;
544
545         if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
546             lp_hide_dot_files(SNUM(conn)))
547         {
548                 p = strrchr_m(name, '/');
549                 if (p) {
550                         p++;
551                 } else {
552                         p = name;
553                 }
554
555                 /* Only . and .. are not hidden. */
556                 if ((p[0] == '.') && !(ISDOT(p) || ISDOTDOT(p))) {
557                         result |= FILE_ATTRIBUTE_HIDDEN;
558                 }
559         }
560
561         if (!(result & FILE_ATTRIBUTE_HIDDEN) && IS_HIDDEN_PATH(conn, name)) {
562                 result |= FILE_ATTRIBUTE_HIDDEN;
563         }
564
565         return result;
566 }
567
568 /****************************************************************************
569  Change a unix mode to a dos mode for an ms dfs link.
570 ****************************************************************************/
571
572 uint32_t dos_mode_msdfs(connection_struct *conn,
573                         const char *name,
574                         const struct stat_ex *st)
575 {
576         uint32_t result = 0;
577
578         DEBUG(8, ("dos_mode_msdfs: %s\n", name));
579
580         if (!VALID_STAT(*st)) {
581                 return 0;
582         }
583
584         result = dos_mode_from_name(conn, name, result);
585         result |= dos_mode_from_sbuf(conn, st, NULL);
586
587         if (result == 0) {
588                 result = FILE_ATTRIBUTE_NORMAL;
589         }
590
591         result = filter_mode_by_protocol(result);
592
593         /*
594          * Add in that it is a reparse point
595          */
596         result |= FILE_ATTRIBUTE_REPARSE_POINT;
597
598         dos_mode_debug_print(__func__, result);
599
600         return(result);
601 }
602
603 /*
604  * check whether a file or directory is flagged as compressed.
605  */
606 static NTSTATUS dos_mode_check_compressed(struct files_struct *fsp,
607                                           bool *is_compressed)
608 {
609         NTSTATUS status;
610         uint16_t compression_fmt;
611
612         status = SMB_VFS_FGET_COMPRESSION(
613                 fsp->conn, talloc_tos(), fsp, &compression_fmt);
614         if (!NT_STATUS_IS_OK(status)) {
615                 return status;
616         }
617
618         if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
619                 *is_compressed = true;
620         } else {
621                 *is_compressed = false;
622         }
623         return NT_STATUS_OK;
624 }
625
626 static uint32_t dos_mode_post(uint32_t dosmode,
627                               struct files_struct *fsp,
628                               const char *func)
629 {
630         struct smb_filename *smb_fname = NULL;
631         NTSTATUS status;
632
633         if (fsp != NULL) {
634                 smb_fname = fsp->fsp_name;
635         }
636         SMB_ASSERT(smb_fname != NULL);
637
638         /*
639          * According to MS-FSA a stream name does not have
640          * separate DOS attribute metadata, so we must return
641          * the DOS attribute from the base filename. With one caveat,
642          * a non-default stream name can never be a directory.
643          *
644          * As this is common to all streams data stores, we handle
645          * it here instead of inside all stream VFS modules.
646          *
647          * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13380
648          */
649
650         if (is_named_stream(smb_fname)) {
651                 /* is_ntfs_stream_smb_fname() returns false for a POSIX path. */
652                 dosmode &= ~(FILE_ATTRIBUTE_DIRECTORY);
653         }
654
655         if (fsp->conn->fs_capabilities & FILE_FILE_COMPRESSION) {
656                 bool compressed = false;
657
658                 status = dos_mode_check_compressed(fsp, &compressed);
659                 if (NT_STATUS_IS_OK(status) && compressed) {
660                         dosmode |= FILE_ATTRIBUTE_COMPRESSED;
661                 }
662         }
663
664         dosmode |= dos_mode_from_name(fsp->conn, smb_fname->base_name, dosmode);
665
666         if (S_ISDIR(smb_fname->st.st_ex_mode)) {
667                 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
668         } else if (dosmode == 0) {
669                 dosmode = FILE_ATTRIBUTE_NORMAL;
670         }
671
672         dosmode = filter_mode_by_protocol(dosmode);
673
674         dos_mode_debug_print(func, dosmode);
675         return dosmode;
676 }
677
678 /****************************************************************************
679  Change a unix mode to a dos mode.
680  May also read the create timespec into the stat struct in smb_fname
681  if "store dos attributes" is true.
682 ****************************************************************************/
683
684 uint32_t fdos_mode(struct files_struct *fsp)
685 {
686         uint32_t result = 0;
687         NTSTATUS status = NT_STATUS_OK;
688
689         DBG_DEBUG("%s\n", fsp_str_dbg(fsp));
690
691         if (fsp->fake_file_handle != NULL) {
692                 return dosmode_from_fake_filehandle(fsp->fake_file_handle);
693         }
694
695         if (!VALID_STAT(fsp->fsp_name->st)) {
696                 return 0;
697         }
698
699         if (S_ISLNK(fsp->fsp_name->st.st_ex_mode)) {
700                 return FILE_ATTRIBUTE_NORMAL;
701         }
702
703         if (fsp->fsp_name->st.cached_dos_attributes != FILE_ATTRIBUTE_INVALID) {
704                 return fsp->fsp_name->st.cached_dos_attributes;
705         }
706
707         /* Get the DOS attributes via the VFS if we can */
708         status = SMB_VFS_FGET_DOS_ATTRIBUTES(fsp->conn,
709                                              metadata_fsp(fsp),
710                                              &result);
711         if (!NT_STATUS_IS_OK(status)) {
712                 /*
713                  * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
714                  */
715                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
716                         result |= dos_mode_from_sbuf(fsp->conn,
717                                                      &fsp->fsp_name->st,
718                                                      fsp);
719                 }
720         }
721
722         fsp->fsp_name->st.cached_dos_attributes = dos_mode_post(result, fsp, __func__);
723         return fsp->fsp_name->st.cached_dos_attributes;
724 }
725
726 struct dos_mode_at_state {
727         files_struct *dir_fsp;
728         struct smb_filename *smb_fname;
729         uint32_t dosmode;
730 };
731
732 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq);
733
734 struct tevent_req *dos_mode_at_send(TALLOC_CTX *mem_ctx,
735                                     struct tevent_context *ev,
736                                     files_struct *dir_fsp,
737                                     struct smb_filename *smb_fname)
738 {
739         struct tevent_req *req = NULL;
740         struct dos_mode_at_state *state = NULL;
741         struct tevent_req *subreq = NULL;
742
743         DBG_DEBUG("%s\n", smb_fname_str_dbg(smb_fname));
744
745         req = tevent_req_create(mem_ctx, &state,
746                                 struct dos_mode_at_state);
747         if (req == NULL) {
748                 return NULL;
749         }
750
751         *state = (struct dos_mode_at_state) {
752                 .dir_fsp = dir_fsp,
753                 .smb_fname = smb_fname,
754         };
755
756         if (!VALID_STAT(smb_fname->st)) {
757                 tevent_req_done(req);
758                 return tevent_req_post(req, ev);
759         }
760
761         if (smb_fname->fsp == NULL) {
762                 if (ISDOTDOT(smb_fname->base_name)) {
763                         /*
764                          * smb_fname->fsp is explicitly closed
765                          * for ".." to prevent meta-data leakage.
766                          */
767                         state->dosmode = FILE_ATTRIBUTE_DIRECTORY;
768                 } else {
769                         /*
770                          * This is a symlink in POSIX context.
771                          * FIXME ? Should we move to returning
772                          * FILE_ATTRIBUTE_REPARSE_POINT here ?
773                          */
774                         state->dosmode = FILE_ATTRIBUTE_NORMAL;
775                 }
776                 tevent_req_done(req);
777                 return tevent_req_post(req, ev);
778         }
779
780         subreq = SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state,
781                                                  ev,
782                                                  dir_fsp,
783                                                  smb_fname);
784         if (tevent_req_nomem(subreq, req)) {
785                 return tevent_req_post(req, ev);
786         }
787         tevent_req_set_callback(subreq, dos_mode_at_vfs_get_dosmode_done, req);
788
789         return req;
790 }
791
792 static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
793 {
794         struct tevent_req *req =
795                 tevent_req_callback_data(subreq,
796                 struct tevent_req);
797         struct dos_mode_at_state *state =
798                 tevent_req_data(req,
799                 struct dos_mode_at_state);
800         struct vfs_aio_state aio_state;
801         NTSTATUS status;
802         bool ok;
803
804         /*
805          * Make sure we run as the user again
806          */
807         ok = change_to_user_and_service_by_fsp(state->dir_fsp);
808         SMB_ASSERT(ok);
809
810         status = SMB_VFS_GET_DOS_ATTRIBUTES_RECV(subreq,
811                                                  &aio_state,
812                                                  &state->dosmode);
813         TALLOC_FREE(subreq);
814         if (!NT_STATUS_IS_OK(status)) {
815                 /*
816                  * Both the sync dos_mode() as well as the async
817                  * dos_mode_at_[send|recv] have no real error return, the only
818                  * unhandled error is when the stat info in smb_fname is not
819                  * valid (cf the checks in dos_mode() and dos_mode_at_send().
820                  *
821                  * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
822                  * dos_mode_post() which also does the mapping of a last resort
823                  * from S_IFMT(st_mode).
824                  *
825                  * Only if we get NT_STATUS_NOT_IMPLEMENTED or
826                  * NT_STATUS_NOT_SUPPORTED from a stacked VFS module we must
827                  * fallback to sync processing.
828                  */
829                 if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) &&
830                     !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED))
831                 {
832                         /*
833                          * state->dosmode should still be 0, but reset
834                          * it to be sure.
835                          */
836                         state->dosmode = 0;
837                         status = NT_STATUS_OK;
838                 }
839         }
840         if (NT_STATUS_IS_OK(status)) {
841                 state->dosmode = dos_mode_post(state->dosmode,
842                                                state->smb_fname->fsp,
843                                                __func__);
844                 tevent_req_done(req);
845                 return;
846         }
847
848         /*
849          * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
850          */
851
852         state->dosmode = fdos_mode(state->smb_fname->fsp);
853         tevent_req_done(req);
854         return;
855 }
856
857 NTSTATUS dos_mode_at_recv(struct tevent_req *req, uint32_t *dosmode)
858 {
859         struct dos_mode_at_state *state =
860                 tevent_req_data(req,
861                 struct dos_mode_at_state);
862         NTSTATUS status;
863
864         if (tevent_req_is_nterror(req, &status)) {
865                 tevent_req_received(req);
866                 return status;
867         }
868
869         *dosmode = state->dosmode;
870         tevent_req_received(req);
871         return NT_STATUS_OK;
872 }
873
874 /*******************************************************************
875  chmod a file - but preserve some bits.
876  If "store dos attributes" is also set it will store the create time
877  from the stat struct in smb_fname (in NTTIME format) in the EA
878  attribute also.
879 ********************************************************************/
880
881 int file_set_dosmode(connection_struct *conn,
882                      struct smb_filename *smb_fname,
883                      uint32_t dosmode,
884                      struct smb_filename *parent_dir,
885                      bool newfile)
886 {
887         int mask=0;
888         mode_t tmp;
889         mode_t unixmode;
890         int ret = -1;
891         NTSTATUS status;
892
893         if (!CAN_WRITE(conn)) {
894                 errno = EROFS;
895                 return -1;
896         }
897
898         if (S_ISLNK(smb_fname->st.st_ex_mode)) {
899                 /* A symlink in POSIX context, ignore */
900                 return 0;
901         }
902
903         if ((S_ISDIR(smb_fname->st.st_ex_mode)) &&
904             (dosmode & FILE_ATTRIBUTE_TEMPORARY))
905         {
906                 errno = EINVAL;
907                 return -1;
908         }
909
910         dosmode &= SAMBA_ATTRIBUTES_MASK;
911
912         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
913                   dosmode, smb_fname_str_dbg(smb_fname)));
914
915         if (smb_fname->fsp == NULL) {
916                 errno = ENOENT;
917                 return -1;
918         }
919
920         if (smb_fname->fsp->posix_flags & FSP_POSIX_FLAGS_OPEN &&
921             !lp_store_dos_attributes(SNUM(conn)))
922         {
923                 return 0;
924         }
925
926         unixmode = smb_fname->st.st_ex_mode;
927
928         get_acl_group_bits(conn, smb_fname->fsp, &smb_fname->st.st_ex_mode);
929
930         if (S_ISDIR(smb_fname->st.st_ex_mode))
931                 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
932         else
933                 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
934
935         /* Store the DOS attributes in an EA by preference. */
936         status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn,
937                                              metadata_fsp(smb_fname->fsp),
938                                              dosmode);
939         if (NT_STATUS_IS_OK(status)) {
940                 smb_fname->st.cached_dos_attributes = dosmode;
941                 ret = 0;
942                 goto done;
943         }
944
945         /*
946          * Only fall back to using UNIX modes if
947          * we get NOT_IMPLEMENTED.
948          */
949         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
950                 errno = map_errno_from_nt_status(status);
951                 return -1;
952         }
953
954         /* Fall back to UNIX modes. */
955         unixmode = unix_mode(
956                 conn,
957                 dosmode,
958                 smb_fname,
959                 parent_dir != NULL ? parent_dir->fsp : NULL);
960
961         /* preserve the file type bits */
962         mask |= S_IFMT;
963
964         /* preserve the s bits */
965         mask |= (S_ISUID | S_ISGID);
966
967         /* preserve the t bit */
968 #ifdef S_ISVTX
969         mask |= S_ISVTX;
970 #endif
971
972         /* possibly preserve the x bits */
973         if (!MAP_ARCHIVE(conn))
974                 mask |= S_IXUSR;
975         if (!MAP_SYSTEM(conn))
976                 mask |= S_IXGRP;
977         if (!MAP_HIDDEN(conn))
978                 mask |= S_IXOTH;
979
980         unixmode |= (smb_fname->st.st_ex_mode & mask);
981
982         /* if we previously had any r bits set then leave them alone */
983         if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
984                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
985                 unixmode |= tmp;
986         }
987
988         /* if we previously had any w bits set then leave them alone
989                 whilst adding in the new w bits, if the new mode is not rdonly */
990         if (!(dosmode & FILE_ATTRIBUTE_READONLY)) {
991                 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
992         }
993
994         /*
995          * From the chmod 2 man page:
996          *
997          * "If the calling process is not privileged, and the group of the file
998          * does not match the effective group ID of the process or one of its
999          * supplementary group IDs, the S_ISGID bit will be turned off, but
1000          * this will not cause an error to be returned."
1001          *
1002          * Simply refuse to do the chmod in this case.
1003          */
1004
1005         if (S_ISDIR(smb_fname->st.st_ex_mode) &&
1006             (unixmode & S_ISGID) &&
1007             geteuid() != sec_initial_uid() &&
1008             !current_user_in_group(conn, smb_fname->st.st_ex_gid))
1009         {
1010                 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
1011                         "set for directory %s\n",
1012                         smb_fname_str_dbg(smb_fname)));
1013                 errno = EPERM;
1014                 return -1;
1015         }
1016
1017         ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1018         if (ret == 0) {
1019                 goto done;
1020         }
1021
1022         if((errno != EPERM) && (errno != EACCES))
1023                 return -1;
1024
1025         if(!lp_dos_filemode(SNUM(conn)))
1026                 return -1;
1027
1028         /* We want DOS semantics, ie allow non owner with write permission to change the
1029                 bits on a file. Just like file_ntimes below.
1030         */
1031
1032         if (!can_write_to_fsp(smb_fname->fsp))
1033         {
1034                 errno = EACCES;
1035                 return -1;
1036         }
1037
1038         become_root();
1039         ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
1040         unbecome_root();
1041
1042 done:
1043         if (!newfile) {
1044                 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1045                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
1046                              smb_fname->base_name);
1047         }
1048         if (ret == 0) {
1049                 smb_fname->st.st_ex_mode = unixmode;
1050         }
1051
1052         return( ret );
1053 }
1054
1055
1056 NTSTATUS file_set_sparse(connection_struct *conn,
1057                          files_struct *fsp,
1058                          bool sparse)
1059 {
1060         const struct loadparm_substitution *lp_sub =
1061                 loadparm_s3_global_substitution();
1062         uint32_t old_dosmode;
1063         uint32_t new_dosmode;
1064         NTSTATUS status;
1065
1066         if (!CAN_WRITE(conn)) {
1067                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1068                         "on readonly share[%s]\n",
1069                         smb_fname_str_dbg(fsp->fsp_name),
1070                         sparse,
1071                         lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
1072                 return NT_STATUS_MEDIA_WRITE_PROTECTED;
1073         }
1074
1075         /*
1076          * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
1077          * following access flags are granted.
1078          */
1079         if ((fsp->access_mask & (FILE_WRITE_DATA
1080                                 | FILE_WRITE_ATTRIBUTES
1081                                 | SEC_FILE_APPEND_DATA)) == 0) {
1082                 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
1083                         "access_mask[0x%08X] - access denied\n",
1084                         smb_fname_str_dbg(fsp->fsp_name),
1085                         sparse,
1086                         fsp->access_mask));
1087                 return NT_STATUS_ACCESS_DENIED;
1088         }
1089
1090         if (fsp->fsp_flags.is_directory) {
1091                 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
1092                           (sparse ? "set" : "clear"),
1093                           smb_fname_str_dbg(fsp->fsp_name)));
1094                 return NT_STATUS_INVALID_PARAMETER;
1095         }
1096
1097         if (IS_IPC(conn) || IS_PRINT(conn)) {
1098                 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
1099                           (sparse ? "set" : "clear")));
1100                 return NT_STATUS_INVALID_PARAMETER;
1101         }
1102
1103         if (fsp_is_alternate_stream(fsp)) {
1104                 /*
1105                  * MS-FSA 2.1.1.5 IsSparse
1106                  *
1107                  * This is a per stream attribute, but our backends don't
1108                  * support it a consistent way, therefore just pretend
1109                  * success and ignore the request.
1110                  */
1111                 DBG_DEBUG("Ignoring request to set FILE_ATTRIBUTE_SPARSE on "
1112                           "[%s]\n", fsp_str_dbg(fsp));
1113                 return NT_STATUS_OK;
1114         }
1115
1116         DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1117                   sparse, smb_fname_str_dbg(fsp->fsp_name)));
1118
1119         if (!lp_store_dos_attributes(SNUM(conn))) {
1120                 return NT_STATUS_INVALID_DEVICE_REQUEST;
1121         }
1122
1123         status = vfs_stat_fsp(fsp);
1124         if (!NT_STATUS_IS_OK(status)) {
1125                 return status;
1126         }
1127
1128         old_dosmode = fdos_mode(fsp);
1129
1130         if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1131                 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
1132         } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1133                 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
1134         } else {
1135                 return NT_STATUS_OK;
1136         }
1137
1138         /* Store the DOS attributes in an EA. */
1139         status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn, fsp, new_dosmode);
1140         if (!NT_STATUS_IS_OK(status)) {
1141                 return status;
1142         }
1143
1144         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1145                      FILE_NOTIFY_CHANGE_ATTRIBUTES,
1146                      fsp->fsp_name->base_name);
1147
1148         fsp->fsp_name->st.cached_dos_attributes = new_dosmode;
1149         fsp->fsp_flags.is_sparse = sparse;
1150
1151         return NT_STATUS_OK;
1152 }
1153
1154 /*******************************************************************
1155  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1156  than POSIX.
1157 *******************************************************************/
1158
1159 int file_ntimes(connection_struct *conn,
1160                 files_struct *fsp,
1161                 struct smb_file_time *ft)
1162 {
1163         int ret = -1;
1164
1165         errno = 0;
1166
1167         DBG_INFO("actime: %s",
1168                  time_to_asc(convert_timespec_to_time_t(ft->atime)));
1169         DBG_INFO("modtime: %s",
1170                  time_to_asc(convert_timespec_to_time_t(ft->mtime)));
1171         DBG_INFO("ctime: %s",
1172                  time_to_asc(convert_timespec_to_time_t(ft->ctime)));
1173         DBG_INFO("createtime: %s",
1174                  time_to_asc(convert_timespec_to_time_t(ft->create_time)));
1175
1176         /* Don't update the time on read-only shares */
1177         /* We need this as set_filetime (which can be called on
1178            close and other paths) can end up calling this function
1179            without the NEED_WRITE protection. Found by :
1180            Leo Weppelman <leo@wau.mis.ah.nl>
1181         */
1182
1183         if (!CAN_WRITE(conn)) {
1184                 return 0;
1185         }
1186
1187         if (SMB_VFS_FNTIMES(fsp, ft) == 0) {
1188                 return 0;
1189         }
1190
1191         if((errno != EPERM) && (errno != EACCES)) {
1192                 return -1;
1193         }
1194
1195         if(!lp_dos_filetimes(SNUM(conn))) {
1196                 return -1;
1197         }
1198
1199         /* We have permission (given by the Samba admin) to
1200            break POSIX semantics and allow a user to change
1201            the time on a file they don't own but can write to
1202            (as DOS does).
1203          */
1204
1205         /* Check if we have write access. */
1206         if (can_write_to_fsp(fsp)) {
1207                 /* We are allowed to become root and change the filetime. */
1208                 become_root();
1209                 ret = SMB_VFS_FNTIMES(fsp, ft);
1210                 unbecome_root();
1211         }
1212
1213         return ret;
1214 }
1215
1216 /******************************************************************
1217  Force a "sticky" write time on a pathname. This will always be
1218  returned on all future write time queries and set on close.
1219 ******************************************************************/
1220
1221 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1222 {
1223         if (is_omit_timespec(&mtime)) {
1224                 return true;
1225         }
1226
1227         if (!set_sticky_write_time(fileid, mtime)) {
1228                 return false;
1229         }
1230
1231         return true;
1232 }
1233
1234 /******************************************************************
1235  Force a "sticky" write time on an fsp. This will always be
1236  returned on all future write time queries and set on close.
1237 ******************************************************************/
1238
1239 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1240 {
1241         if (is_omit_timespec(&mtime)) {
1242                 return true;
1243         }
1244
1245         fsp->fsp_flags.write_time_forced = true;
1246         TALLOC_FREE(fsp->update_write_time_event);
1247
1248         return set_sticky_write_time_path(fsp->file_id, mtime);
1249 }
1250
1251 /******************************************************************
1252  Set a create time EA.
1253 ******************************************************************/
1254
1255 NTSTATUS set_create_timespec_ea(struct files_struct *fsp,
1256                                 struct timespec create_time)
1257 {
1258         uint32_t dosmode;
1259         int ret;
1260
1261         if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
1262                 return NT_STATUS_OK;
1263         }
1264
1265         dosmode = fdos_mode(fsp);
1266
1267         fsp->fsp_name->st.st_ex_btime = create_time;
1268         ret = file_set_dosmode(fsp->conn, fsp->fsp_name, dosmode, NULL, false);
1269         if (ret == -1) {
1270                 return map_nt_error_from_unix(errno);
1271         }
1272
1273         DBG_DEBUG("wrote create time EA for file %s\n",
1274                 smb_fname_str_dbg(fsp->fsp_name));
1275
1276         return NT_STATUS_OK;
1277 }
1278
1279 /******************************************************************
1280  Return a create time.
1281 ******************************************************************/
1282
1283 struct timespec get_create_timespec(connection_struct *conn,
1284                                 struct files_struct *fsp,
1285                                 const struct smb_filename *smb_fname)
1286 {
1287         if (fsp != NULL) {
1288                 struct files_struct *meta_fsp = metadata_fsp(fsp);
1289                 return meta_fsp->fsp_name->st.st_ex_btime;
1290         }
1291         return smb_fname->st.st_ex_btime;
1292 }
1293
1294 /******************************************************************
1295  Return a change time (may look at EA in future).
1296 ******************************************************************/
1297
1298 struct timespec get_change_timespec(connection_struct *conn,
1299                                 struct files_struct *fsp,
1300                                 const struct smb_filename *smb_fname)
1301 {
1302         return smb_fname->st.st_ex_mtime;
1303 }