r23474: Here's a small patch that disables the libkrb5.so replay cache
[tprouty/samba.git] / source / 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 2 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, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
25 {
26 #if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
27         if (sbuf->st_size > sbuf->st_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
28                 return FILE_ATTRIBUTE_SPARSE;
29         }
30 #endif
31         return 0;
32 }
33
34 /****************************************************************************
35  Work out whether this file is offline
36 ****************************************************************************/
37
38 static uint32 set_offline_flag(connection_struct *conn, const char *const path)
39 {
40         if (ISDOT(path) || ISDOTDOT(path)) {
41                 return 0;
42         }
43
44         if (!lp_dmapi_support(SNUM(conn)) || !dmapi_have_session()) {
45                 return 0;
46         }
47
48         return dmapi_file_flags(path);
49 }
50
51 /****************************************************************************
52  Change a dos mode to a unix mode.
53     Base permission for files:
54          if creating file and inheriting (i.e. parent_dir != NULL)
55            apply read/write bits from parent directory.
56          else   
57            everybody gets read bit set
58          dos readonly is represented in unix by removing everyone's write bit
59          dos archive is represented in unix by the user's execute bit
60          dos system is represented in unix by the group's execute bit
61          dos hidden is represented in unix by the other's execute bit
62          if !inheriting {
63            Then apply create mask,
64            then add force bits.
65          }
66     Base permission for directories:
67          dos directory is represented in unix by unix's dir bit and the exec bit
68          if !inheriting {
69            Then apply create mask,
70            then add force bits.
71          }
72 ****************************************************************************/
73
74 mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname,
75                  const char *inherit_from_dir)
76 {
77         mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
78         mode_t dir_mode = 0; /* Mode of the inherit_from directory if
79                               * inheriting. */
80
81         if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
82                 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
83         }
84
85         if (fname && (inherit_from_dir != NULL)
86             && lp_inherit_perms(SNUM(conn))) {
87                 SMB_STRUCT_STAT sbuf;
88
89                 DEBUG(2, ("unix_mode(%s) inheriting from %s\n", fname,
90                           inherit_from_dir));
91                 if (SMB_VFS_STAT(conn, inherit_from_dir, &sbuf) != 0) {
92                         DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n", fname,
93                                  inherit_from_dir, strerror(errno)));
94                         return(0);      /* *** shouldn't happen! *** */
95                 }
96
97                 /* Save for later - but explicitly remove setuid bit for safety. */
98                 dir_mode = sbuf.st_mode & ~S_ISUID;
99                 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
100                 /* Clear "result" */
101                 result = 0;
102         } 
103
104         if (IS_DOS_DIR(dosmode)) {
105                 /* We never make directories read only for the owner as under DOS a user
106                 can always create a file in a read-only directory. */
107                 result |= (S_IFDIR | S_IWUSR);
108
109                 if (dir_mode) {
110                         /* Inherit mode of parent directory. */
111                         result |= dir_mode;
112                 } else {
113                         /* Provisionally add all 'x' bits */
114                         result |= (S_IXUSR | S_IXGRP | S_IXOTH);                 
115
116                         /* Apply directory mask */
117                         result &= lp_dir_mask(SNUM(conn));
118                         /* Add in force bits */
119                         result |= lp_force_dir_mode(SNUM(conn));
120                 }
121         } else { 
122                 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
123                         result |= S_IXUSR;
124
125                 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
126                         result |= S_IXGRP;
127  
128                 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
129                         result |= S_IXOTH;  
130
131                 if (dir_mode) {
132                         /* Inherit 666 component of parent directory mode */
133                         result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
134                 } else {
135                         /* Apply mode mask */
136                         result &= lp_create_mask(SNUM(conn));
137                         /* Add in force bits */
138                         result |= lp_force_create_mode(SNUM(conn));
139                 }
140         }
141
142         DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
143         return(result);
144 }
145
146 /****************************************************************************
147  Change a unix mode to a dos mode.
148 ****************************************************************************/
149
150 static uint32 dos_mode_from_sbuf(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
151 {
152         int result = 0;
153         enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
154
155         if (ro_opts == MAP_READONLY_YES) {
156                 /* Original Samba method - map inverse of user "w" bit. */
157                 if ((sbuf->st_mode & S_IWUSR) == 0) {
158                         result |= aRONLY;
159                 }
160         } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
161                 /* Check actual permissions for read-only. */
162                 if (!can_write_to_file(conn, path, sbuf)) {
163                         result |= aRONLY;
164                 }
165         } /* Else never set the readonly bit. */
166
167         if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
168                 result |= aARCH;
169
170         if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
171                 result |= aSYSTEM;
172         
173         if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
174                 result |= aHIDDEN;   
175   
176         if (S_ISDIR(sbuf->st_mode))
177                 result = aDIR | (result & aRONLY);
178
179         result |= set_sparse_flag(sbuf);
180  
181 #ifdef S_ISLNK
182 #if LINKS_READ_ONLY
183         if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
184                 result |= aRONLY;
185 #endif
186 #endif
187
188         DEBUG(8,("dos_mode_from_sbuf returning "));
189
190         if (result & aHIDDEN) DEBUG(8, ("h"));
191         if (result & aRONLY ) DEBUG(8, ("r"));
192         if (result & aSYSTEM) DEBUG(8, ("s"));
193         if (result & aDIR   ) DEBUG(8, ("d"));
194         if (result & aARCH  ) DEBUG(8, ("a"));
195         
196         DEBUG(8,("\n"));
197         return result;
198 }
199
200 /****************************************************************************
201  Get DOS attributes from an EA.
202 ****************************************************************************/
203
204 static BOOL get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr)
205 {
206         ssize_t sizeret;
207         fstring attrstr;
208         unsigned int dosattr;
209
210         if (!lp_store_dos_attributes(SNUM(conn))) {
211                 return False;
212         }
213
214         /* Don't reset pattr to zero as we may already have filename-based attributes we
215            need to preserve. */
216
217         sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
218         if (sizeret == -1) {
219 #if defined(ENOTSUP) && defined(ENOATTR)
220                 if ((errno != ENOTSUP) && (errno != ENOATTR) && (errno != EACCES) && (errno != EPERM)) {
221                         DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
222                                 path, strerror(errno) ));
223                         set_store_dos_attributes(SNUM(conn), False);
224                 }
225 #endif
226                 return False;
227         }
228         /* Null terminate string. */
229         attrstr[sizeret] = 0;
230         DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
231
232         if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
233                         sscanf(attrstr, "%x", &dosattr) != 1) {
234                 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
235                 return False;
236         }
237
238         if (S_ISDIR(sbuf->st_mode)) {
239                 dosattr |= aDIR;
240         }
241         *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
242
243         DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
244
245         if (dosattr & aHIDDEN) DEBUG(8, ("h"));
246         if (dosattr & aRONLY ) DEBUG(8, ("r"));
247         if (dosattr & aSYSTEM) DEBUG(8, ("s"));
248         if (dosattr & aDIR   ) DEBUG(8, ("d"));
249         if (dosattr & aARCH  ) DEBUG(8, ("a"));
250         
251         DEBUG(8,("\n"));
252
253         return True;
254 }
255
256 /****************************************************************************
257  Set DOS attributes in an EA.
258 ****************************************************************************/
259
260 static BOOL set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
261 {
262         fstring attrstr;
263         files_struct *fsp = NULL;
264         BOOL ret = False;
265
266         if (!lp_store_dos_attributes(SNUM(conn))) {
267                 return False;
268         }
269
270         snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
271         if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
272                 if((errno != EPERM) && (errno != EACCES)) {
273                         if (errno == ENOSYS
274 #if defined(ENOTSUP)
275                                 || errno == ENOTSUP) {
276 #else
277                                 ) {
278 #endif
279                                 set_store_dos_attributes(SNUM(conn), False);
280                         }
281                         return False;
282                 }
283
284                 /* We want DOS semantics, ie allow non owner with write permission to change the
285                         bits on a file. Just like file_ntimes below.
286                 */
287
288                 /* Check if we have write access. */
289                 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
290                         return False;
291
292                 /*
293                  * We need to open the file with write access whilst
294                  * still in our current user context. This ensures we
295                  * are not violating security in doing the setxattr.
296                  */
297
298                 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,path,sbuf,&fsp)))
299                         return ret;
300                 become_root();
301                 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
302                         ret = True;
303                 }
304                 unbecome_root();
305                 close_file_fchmod(fsp);
306                 return ret;
307         }
308         DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
309         return True;
310 }
311
312 /****************************************************************************
313  Change a unix mode to a dos mode for an ms dfs link.
314 ****************************************************************************/
315
316 uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
317 {
318         uint32 result = 0;
319
320         DEBUG(8,("dos_mode_msdfs: %s\n", path));
321
322         if (!VALID_STAT(*sbuf)) {
323                 return 0;
324         }
325
326         /* First do any modifications that depend on the path name. */
327         /* hide files with a name starting with a . */
328         if (lp_hide_dot_files(SNUM(conn))) {
329                 const char *p = strrchr_m(path,'/');
330                 if (p) {
331                         p++;
332                 } else {
333                         p = path;
334                 }
335                 
336                 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
337                         result |= aHIDDEN;
338                 }
339         }
340         
341         result |= dos_mode_from_sbuf(conn, path, sbuf);
342
343         /* Optimization : Only call is_hidden_path if it's not already
344            hidden. */
345         if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
346                 result |= aHIDDEN;
347         }
348
349         DEBUG(8,("dos_mode_msdfs returning "));
350
351         if (result & aHIDDEN) DEBUG(8, ("h"));
352         if (result & aRONLY ) DEBUG(8, ("r"));
353         if (result & aSYSTEM) DEBUG(8, ("s"));
354         if (result & aDIR   ) DEBUG(8, ("d"));
355         if (result & aARCH  ) DEBUG(8, ("a"));
356         if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
357         
358         DEBUG(8,("\n"));
359
360         return(result);
361 }
362
363 /****************************************************************************
364  Change a unix mode to a dos mode.
365 ****************************************************************************/
366
367 uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
368 {
369         uint32 result = 0;
370
371         DEBUG(8,("dos_mode: %s\n", path));
372
373         if (!VALID_STAT(*sbuf)) {
374                 return 0;
375         }
376
377         /* First do any modifications that depend on the path name. */
378         /* hide files with a name starting with a . */
379         if (lp_hide_dot_files(SNUM(conn))) {
380                 const char *p = strrchr_m(path,'/');
381                 if (p) {
382                         p++;
383                 } else {
384                         p = path;
385                 }
386                 
387                 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
388                         result |= aHIDDEN;
389                 }
390         }
391         
392         /* Get the DOS attributes from an EA by preference. */
393         if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
394                 result |= set_sparse_flag(sbuf);
395         } else {
396                 result |= dos_mode_from_sbuf(conn, path, sbuf);
397         }
398
399         if (S_ISREG(sbuf->st_mode)) {
400                 result |= set_offline_flag(conn, path);
401         }
402
403         /* Optimization : Only call is_hidden_path if it's not already
404            hidden. */
405         if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
406                 result |= aHIDDEN;
407         }
408
409         DEBUG(8,("dos_mode returning "));
410
411         if (result & aHIDDEN) DEBUG(8, ("h"));
412         if (result & aRONLY ) DEBUG(8, ("r"));
413         if (result & aSYSTEM) DEBUG(8, ("s"));
414         if (result & aDIR   ) DEBUG(8, ("d"));
415         if (result & aARCH  ) DEBUG(8, ("a"));
416         if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
417         
418         DEBUG(8,("\n"));
419
420         return(result);
421 }
422
423 /*******************************************************************
424  chmod a file - but preserve some bits.
425 ********************************************************************/
426
427 int file_set_dosmode(connection_struct *conn, const char *fname,
428                      uint32 dosmode, SMB_STRUCT_STAT *st,
429                      const char *parent_dir)
430 {
431         SMB_STRUCT_STAT st1;
432         int mask=0;
433         mode_t tmp;
434         mode_t unixmode;
435         int ret = -1;
436
437         /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
438         dosmode &= SAMBA_ATTRIBUTES_MASK;
439
440         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
441         if (!st || (st && !VALID_STAT(*st))) {
442                 st = &st1;
443                 if (SMB_VFS_STAT(conn,fname,st))
444                         return(-1);
445         }
446
447         get_acl_group_bits(conn, fname, &st->st_mode);
448
449         if (S_ISDIR(st->st_mode))
450                 dosmode |= aDIR;
451         else
452                 dosmode &= ~aDIR;
453
454         if (dos_mode(conn,fname,st) == dosmode)
455                 return(0);
456
457         /* Store the DOS attributes in an EA by preference. */
458         if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
459                 return 0;
460         }
461
462         unixmode = unix_mode(conn,dosmode,fname, parent_dir);
463
464         /* preserve the s bits */
465         mask |= (S_ISUID | S_ISGID);
466
467         /* preserve the t bit */
468 #ifdef S_ISVTX
469         mask |= S_ISVTX;
470 #endif
471
472         /* possibly preserve the x bits */
473         if (!MAP_ARCHIVE(conn))
474                 mask |= S_IXUSR;
475         if (!MAP_SYSTEM(conn))
476                 mask |= S_IXGRP;
477         if (!MAP_HIDDEN(conn))
478                 mask |= S_IXOTH;
479
480         unixmode |= (st->st_mode & mask);
481
482         /* if we previously had any r bits set then leave them alone */
483         if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
484                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
485                 unixmode |= tmp;
486         }
487
488         /* if we previously had any w bits set then leave them alone 
489                 whilst adding in the new w bits, if the new mode is not rdonly */
490         if (!IS_DOS_READONLY(dosmode)) {
491                 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
492         }
493
494         if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0) {
495                 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
496                              FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
497                 return 0;
498         }
499
500         if((errno != EPERM) && (errno != EACCES))
501                 return -1;
502
503         if(!lp_dos_filemode(SNUM(conn)))
504                 return -1;
505
506         /* We want DOS semantics, ie allow non owner with write permission to change the
507                 bits on a file. Just like file_ntimes below.
508         */
509
510         /* Check if we have write access. */
511         if (CAN_WRITE(conn)) {
512                 /*
513                  * We need to open the file with write access whilst
514                  * still in our current user context. This ensures we
515                  * are not violating security in doing the fchmod.
516                  * This file open does *not* break any oplocks we are
517                  * holding. We need to review this.... may need to
518                  * break batch oplocks open by others. JRA.
519                  */
520                 files_struct *fsp;
521                 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp)))
522                         return -1;
523                 become_root();
524                 ret = SMB_VFS_FCHMOD(fsp, fsp->fh->fd, unixmode);
525                 unbecome_root();
526                 close_file_fchmod(fsp);
527                 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
528                              FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
529         }
530
531         return( ret );
532 }
533
534 /*******************************************************************
535  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
536  than POSIX.
537 *******************************************************************/
538
539 int file_ntimes(connection_struct *conn, const char *fname, const struct timespec ts[2])
540 {
541         SMB_STRUCT_STAT sbuf;
542         int ret = -1;
543
544         errno = 0;
545         ZERO_STRUCT(sbuf);
546
547         /* Don't update the time on read-only shares */
548         /* We need this as set_filetime (which can be called on
549            close and other paths) can end up calling this function
550            without the NEED_WRITE protection. Found by : 
551            Leo Weppelman <leo@wau.mis.ah.nl>
552         */
553
554         if (!CAN_WRITE(conn)) {
555                 return 0;
556         }
557
558         if(SMB_VFS_NTIMES(conn, fname, ts) == 0) {
559                 return 0;
560         }
561
562         if((errno != EPERM) && (errno != EACCES)) {
563                 return -1;
564         }
565
566         if(!lp_dos_filetimes(SNUM(conn))) {
567                 return -1;
568         }
569
570         /* We have permission (given by the Samba admin) to
571            break POSIX semantics and allow a user to change
572            the time on a file they don't own but can write to
573            (as DOS does).
574          */
575
576         /* Check if we have write access. */
577         if (can_write_to_file(conn, fname, &sbuf)) {
578                 /* We are allowed to become root and change the filetime. */
579                 become_root();
580                 ret = SMB_VFS_NTIMES(conn, fname, ts);
581                 unbecome_root();
582         }
583
584         return ret;
585 }
586   
587 /*******************************************************************
588  Change a filetime - possibly allowing DOS semantics.
589 *******************************************************************/
590
591 BOOL set_filetime(connection_struct *conn, const char *fname,
592                 const struct timespec mtime)
593 {
594         struct timespec ts[2];
595
596         if (null_timespec(mtime)) {
597                 return(True);
598         }
599
600         ts[1] = mtime; /* mtime. */
601         ts[0] = ts[1]; /* atime. */
602
603         if (file_ntimes(conn, fname, ts)) {
604                 DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
605                 return False;
606         }
607
608         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
609                      FILE_NOTIFY_CHANGE_LAST_WRITE, fname);
610   
611         return True;
612 }