Herb's warning fixes. Also the POSIX locking fix.
[samba.git] / source3 / smbd / unix_acls.c
1 #define OLD_NTDOMAIN 1
2 /*
3    Unix SMB/Netbios implementation.
4    Version 1.9.
5    SMB NT Security Descriptor / Unix permission conversion.
6    Copyright (C) Jeremy Allison 1994-2000
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24
25 /****************************************************************************
26  Function to create owner and group SIDs from a SMB_STRUCT_STAT.
27 ****************************************************************************/
28
29 static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
30 {
31         uid_to_sid( powner_sid, psbuf->st_uid );
32         gid_to_sid( pgroup_sid, psbuf->st_gid );
33 }
34
35 /****************************************************************************
36  Map unix perms to NT.
37 ****************************************************************************/
38
39 static SEC_ACCESS map_unix_perms( int *pacl_type, mode_t perm, int r_mask, int w_mask, int x_mask, BOOL is_directory)
40 {
41         SEC_ACCESS sa;
42         uint32 nt_mask = 0;
43
44         *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
45
46         if((perm & (r_mask|w_mask|x_mask)) == (r_mask|w_mask|x_mask)) {
47                 nt_mask = UNIX_ACCESS_RWX;
48         } else if((perm & (r_mask|w_mask|x_mask)) == 0) {
49                 nt_mask = UNIX_ACCESS_NONE;
50         } else {
51                 nt_mask |= (perm & r_mask) ? UNIX_ACCESS_R : 0;
52                 if(is_directory)
53                         nt_mask |= (perm & w_mask) ? UNIX_ACCESS_W : 0;
54                 else
55                         nt_mask |= (perm & w_mask) ? UNIX_ACCESS_W : 0;
56                 nt_mask |= (perm & x_mask) ? UNIX_ACCESS_X : 0;
57         }
58         init_sec_access(&sa,nt_mask);
59         return sa;
60 }
61
62 #if 0
63 /****************************************************************************
64  Validate a SID.
65 ****************************************************************************/
66
67 static BOOL validate_unix_sid( DOM_SID *psid, uint32 *prid, DOM_SID *sd_sid)
68 {
69   extern DOM_SID global_sam_sid;
70   DOM_SID sid;
71
72   if(!sd_sid) {
73     DEBUG(5,("validate_unix_sid: sid missing.\n"));
74     return False;
75   }
76
77   sid_copy(psid, sd_sid);
78   sid_copy(&sid, sd_sid);
79
80   if(!sid_split_rid(&sid, prid)) {
81     DEBUG(5,("validate_unix_sid: cannot get RID from sid.\n"));
82     return False;
83   }
84
85   if(!sid_equal( &sid, &global_sam_sid)) {
86     DEBUG(5,("validate_unix_sid: sid is not ours.\n"));
87     return False;
88   }
89
90   return True;
91 }
92 #endif
93
94 /****************************************************************************
95  Map NT perms to UNIX.
96 ****************************************************************************/
97
98 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
99 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
100 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
101
102 static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
103 {
104   mode_t mode = 0;
105
106   switch(type) {
107   case S_IRUSR:
108     if(sec_access.mask & GENERIC_ALL_ACCESS)
109       mode = S_IRUSR|S_IWUSR|S_IXUSR;
110     else {
111       mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
112       mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
113       mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
114     }
115     break;
116   case S_IRGRP:
117     if(sec_access.mask & GENERIC_ALL_ACCESS)
118       mode = S_IRGRP|S_IWGRP|S_IXGRP;
119     else {
120       mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
121       mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
122       mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
123     }
124     break;
125   case S_IROTH:
126     if(sec_access.mask & GENERIC_ALL_ACCESS)
127       mode = S_IROTH|S_IWOTH|S_IXOTH;
128     else {
129       mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
130       mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
131       mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
132     }
133     break;
134   }
135
136   return mode;
137 }
138
139 /****************************************************************************
140  Unpack a SEC_DESC into a owner, group and set of UNIX permissions.
141 ****************************************************************************/
142
143 static BOOL unpack_nt_permissions(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, mode_t *pmode,
144                                   uint32 security_info_sent, SEC_DESC *psd, BOOL is_directory)
145 {
146   extern DOM_SID global_sid_World;
147   DOM_SID owner_sid;
148   DOM_SID grp_sid;
149   DOM_SID file_owner_sid;
150   DOM_SID file_grp_sid;
151   SEC_ACL *dacl = psd->dacl;
152   BOOL all_aces_are_inherit_only = (is_directory ? True : False);
153   int i;
154   enum SID_NAME_USE sid_type;
155
156   *pmode = 0;
157   *puser = (uid_t)-1;
158   *pgrp = (gid_t)-1;
159
160   if(security_info_sent == 0) {
161     DEBUG(0,("unpack_nt_permissions: no security info sent !\n"));
162     return False;
163   }
164
165   /*
166    * Windows 2000 sends the owner and group SIDs as the logged in
167    * user, not the connected user. But it still sends the file
168    * owner SIDs on an ACL set. So we need to check for the file
169    * owner and group SIDs as well as the owner SIDs. JRA.
170    */
171  
172   create_file_sids(psbuf, &file_owner_sid, &file_grp_sid);
173
174   /*
175    * Validate the owner and group SID's.
176    */
177
178   memset(&owner_sid, '\0', sizeof(owner_sid));
179   memset(&grp_sid, '\0', sizeof(grp_sid));
180
181   DEBUG(5,("unpack_nt_permissions: validating owner_sid.\n"));
182
183   /*
184    * Don't immediately fail if the owner sid cannot be validated.
185    * This may be a group chown only set.
186    */
187
188   if (security_info_sent & OWNER_SECURITY_INFORMATION) {
189     if (!sid_to_uid( &owner_sid, puser, &sid_type))
190       DEBUG(3,("unpack_nt_permissions: unable to validate owner sid.\n"));
191   }
192
193   /*
194    * Don't immediately fail if the group sid cannot be validated.
195    * This may be an owner chown only set.
196    */
197
198   if (security_info_sent & GROUP_SECURITY_INFORMATION) {
199     if (!sid_to_gid( &grp_sid, pgrp, &sid_type))
200       DEBUG(3,("unpack_nt_permissions: unable to validate group sid.\n"));
201   }
202
203   /*
204    * If no DACL then this is a chown only security descriptor.
205    */
206
207   if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !dacl) {
208     *pmode = 0;
209     return True;
210   }
211
212   /*
213    * Now go through the DACL and ensure that
214    * any owner/group sids match.
215    */
216
217   for(i = 0; i < dacl->num_aces; i++) {
218     DOM_SID ace_sid;
219     SEC_ACE *psa = &dacl->ace[i];
220
221     if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) &&
222        (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
223       DEBUG(3,("unpack_nt_permissions: unable to set anything but an ALLOW or DENY ACE.\n"));
224       return False;
225     }
226
227     /*
228      * Ignore or remove bits we don't care about on a directory ACE.
229      */
230
231     if(is_directory) {
232       if(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
233         DEBUG(3,("unpack_nt_permissions: ignoring inherit only ACE.\n"));
234         continue;
235       }
236
237       /*
238        * At least one of the ACE entries wasn't inherit only.
239        * Flag this so we know the returned mode is valid.
240        */
241
242       all_aces_are_inherit_only = False;
243     }
244
245     /*
246      * Windows 2000 sets these flags even on *file* ACE's. This is wrong
247      * but we can ignore them for now. Revisit this when we go to POSIX
248      * ACLs on directories.
249      */
250
251     psa->flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT);
252
253     if(psa->flags != 0) {
254       DEBUG(1,("unpack_nt_permissions: unable to set ACE flags (%x).\n", 
255             (unsigned int)psa->flags));
256       return False;
257     }
258
259     /*
260      * The security mask may be UNIX_ACCESS_NONE which should map into
261      * no permissions (we overload the WRITE_OWNER bit for this) or it
262      * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
263      * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
264      */
265
266     psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS|
267                      GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES);
268
269     if(psa->info.mask != UNIX_ACCESS_NONE)
270       psa->info.mask &= ~UNIX_ACCESS_NONE;
271
272     sid_copy(&ace_sid, &psa->sid);
273
274     if(sid_equal(&ace_sid, &file_owner_sid)) {
275       /*
276        * Map the desired permissions into owner perms.
277        */
278
279       if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
280         *pmode |= map_nt_perms( psa->info, S_IRUSR);
281       else
282         *pmode &= ~(map_nt_perms( psa->info, S_IRUSR));
283
284     } else if( sid_equal(&ace_sid, &file_grp_sid)) {
285       /*
286        * Map the desired permissions into group perms.
287        */
288
289       if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
290         *pmode |= map_nt_perms( psa->info, S_IRGRP);
291       else
292         *pmode &= ~(map_nt_perms( psa->info, S_IRGRP));
293
294     } else if( sid_equal(&ace_sid, &global_sid_World)) {
295       /*
296        * Map the desired permissions into other perms.
297        */
298
299       if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
300         *pmode |= map_nt_perms( psa->info, S_IROTH);
301       else
302         *pmode &= ~(map_nt_perms( psa->info, S_IROTH));
303
304     } else {
305       DEBUG(0,("unpack_nt_permissions: unknown SID used in ACL.\n"));
306       return False;
307     }
308   }
309
310   if (is_directory && all_aces_are_inherit_only) {
311     /*
312      * Windows 2000 is doing one of these weird 'inherit acl'
313      * traverses to conserve NTFS ACL resources. Just pretend
314      * there was no DACL sent. JRA.
315      */
316
317     DEBUG(10,("unpack_nt_permissions: Win2k inherit acl traverse. Ignoring DACL.\n"));
318     free_sec_acl(&psd->dacl);
319   }
320
321   return True;
322 }
323
324 /****************************************************************************
325  Reply to query a security descriptor from an fsp. If it succeeds it allocates
326  the space for the return elements and returns the size needed to return the
327  security descriptor. This should be the only external function needed for
328  the UNIX style get ACL.
329 ****************************************************************************/
330
331 size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
332 {
333   extern DOM_SID global_sid_World;
334   SMB_STRUCT_STAT sbuf;
335   SEC_ACE ace_list[6];
336   DOM_SID owner_sid;
337   DOM_SID group_sid;
338   size_t sd_size;
339   SEC_ACL *psa = NULL;
340   SEC_ACCESS owner_access;
341   int owner_acl_type;
342   SEC_ACCESS group_access;
343   int grp_acl_type;
344   SEC_ACCESS other_access;
345   int other_acl_type;
346   int num_acls = 0;
347  
348   *ppdesc = NULL;
349
350   if(!lp_nt_acl_support()) {
351     sid_copy( &owner_sid, &global_sid_World);
352     sid_copy( &group_sid, &global_sid_World);
353   } else {
354
355     if(fsp->is_directory || fsp->fd == -1) {
356       if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
357         return 0;
358       }
359     } else {
360       if(fsp->conn->vfs_ops.fstat(fsp,fsp->fd,&sbuf) != 0) {
361         return 0;
362       }
363     }
364
365     /*
366      * Get the owner, group and world SIDs.
367      */
368
369     create_file_sids(&sbuf, &owner_sid, &group_sid);
370
371     /*
372      * Create the generic 3 element UNIX acl.
373      */
374
375     owner_access = map_unix_perms(&owner_acl_type, sbuf.st_mode,
376                                                         S_IRUSR, S_IWUSR, S_IXUSR, fsp->is_directory);
377     group_access = map_unix_perms(&grp_acl_type, sbuf.st_mode,
378                                                         S_IRGRP, S_IWGRP, S_IXGRP, fsp->is_directory);
379     other_access = map_unix_perms(&other_acl_type, sbuf.st_mode,
380                                                         S_IROTH, S_IWOTH, S_IXOTH, fsp->is_directory);
381
382     if(owner_access.mask)
383       init_sec_ace(&ace_list[num_acls++], &owner_sid, owner_acl_type,
384                    owner_access, 0);
385
386     if(group_access.mask)
387       init_sec_ace(&ace_list[num_acls++], &group_sid, grp_acl_type,
388                    group_access, 0);
389
390     if(other_access.mask)
391       init_sec_ace(&ace_list[num_acls++], &global_sid_World, other_acl_type,
392                    other_access, 0);
393
394     if(fsp->is_directory) {
395       /*
396        * For directory ACLs we also add in the inherited permissions
397        * ACE entries. These are the permissions a file would get when
398        * being created in the directory.
399        */
400       mode_t mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name);
401
402       owner_access = map_unix_perms(&owner_acl_type, mode,
403                             S_IRUSR, S_IWUSR, S_IXUSR, fsp->is_directory);
404       group_access = map_unix_perms(&grp_acl_type, mode,
405                             S_IRGRP, S_IWGRP, S_IXGRP, fsp->is_directory);
406       other_access = map_unix_perms(&other_acl_type, mode,
407                             S_IROTH, S_IWOTH, S_IXOTH, fsp->is_directory);
408
409       if(owner_access.mask)
410         init_sec_ace(&ace_list[num_acls++], &owner_sid, owner_acl_type,
411                      owner_access, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
412
413       if(group_access.mask)
414         init_sec_ace(&ace_list[num_acls++], &group_sid, grp_acl_type,
415                      group_access, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
416
417       if(other_access.mask)
418         init_sec_ace(&ace_list[num_acls++], &global_sid_World, other_acl_type,
419                      other_access, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
420     }
421
422     if(num_acls)
423       if((psa = make_sec_acl( ACL_REVISION, num_acls, ace_list)) == NULL) {
424         DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
425         return 0;
426       }
427   }
428
429   *ppdesc = make_standard_sec_desc( &owner_sid, &group_sid, psa, &sd_size);
430
431   if(!*ppdesc) {
432     DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
433     sd_size = 0;
434   }
435
436   free_sec_acl(&psa);
437
438   return sd_size;
439 }
440
441 /****************************************************************************
442  Reply to set a security descriptor on an fsp. security_info_sent is the
443  description of the following NT ACL.
444  This should be the only external function needed for the UNIX style set ACL.
445 ****************************************************************************/
446
447 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
448 {
449   connection_struct *conn = fsp->conn;
450   uid_t user = (uid_t)-1;
451   gid_t grp = (gid_t)-1;
452   mode_t perms = 0;
453   SMB_STRUCT_STAT sbuf;  
454   BOOL got_dacl = False;
455
456   /*
457    * Get the current state of the file.
458    */
459
460   if(fsp->is_directory || fsp->fd == -1) {
461     if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0)
462       return False;
463   } else {
464     if(conn->vfs_ops.fstat(fsp,fsp->fd,&sbuf) != 0)
465       return False;
466   }
467
468   /*
469    * Unpack the user/group/world id's and permissions.
470    */
471
472   if (!unpack_nt_permissions( &sbuf, &user, &grp, &perms, security_info_sent, psd, fsp->is_directory))
473     return False;
474
475   if (psd->dacl != NULL)
476     got_dacl = True;
477
478   /*
479    * Do we need to chown ?
480    */
481
482   if((user != (uid_t)-1 || grp != (uid_t)-1) && (sbuf.st_uid != user || sbuf.st_gid != grp)) {
483
484     DEBUG(3,("call_nt_transact_set_security_desc: chown %s. uid = %u, gid = %u.\n",
485           fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
486
487     if(vfs_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
488       DEBUG(3,("call_nt_transact_set_security_desc: chown %s, %u, %u failed. Error = %s.\n",
489             fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
490       return False;
491     }
492
493     /*
494      * Recheck the current state of the file, which may have changed.
495      * (suid/sgid bits, for instance)
496      */
497
498     if(fsp->is_directory) {
499       if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
500         return False;
501       }
502     } else {
503
504       int ret;
505     
506       if(fsp->fd == -1)
507         ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf);
508       else
509         ret = conn->vfs_ops.fstat(fsp,fsp->fd,&sbuf);
510   
511       if(ret != 0)
512         return False;
513     }
514   }
515
516   /*
517    * Only change security if we got a DACL.
518    */
519
520   if((security_info_sent & DACL_SECURITY_INFORMATION) && got_dacl) {
521
522     /*
523      * Check to see if we need to change anything.
524      * Enforce limits on modified bits *only*. Don't enforce masks
525          * on bits not changed by the user.
526      */
527
528     if(fsp->is_directory) {
529
530       perms &= (lp_dir_security_mask(SNUM(conn)) | sbuf.st_mode);
531       perms |= (lp_force_dir_security_mode(SNUM(conn)) & ( perms ^ sbuf.st_mode ));
532
533     } else {
534
535       perms &= (lp_security_mask(SNUM(conn)) | sbuf.st_mode); 
536       perms |= (lp_force_security_mode(SNUM(conn)) & ( perms ^ sbuf.st_mode ));
537
538     }
539
540     /*
541      * Preserve special bits.
542      */
543
544     perms |= (sbuf.st_mode & ~0777);
545
546     /*
547      * Do we need to chmod ?
548      */
549
550     if(sbuf.st_mode != perms) {
551
552       DEBUG(3,("call_nt_transact_set_security_desc: chmod %s. perms = 0%o.\n",
553             fsp->fsp_name, (unsigned int)perms ));
554
555       if(conn->vfs_ops.chmod(conn,dos_to_unix(fsp->fsp_name, False), perms) == -1) {
556         DEBUG(3,("call_nt_transact_set_security_desc: chmod %s, 0%o failed. Error = %s.\n",
557               fsp->fsp_name, (unsigned int)perms, strerror(errno) ));
558         return False;
559       }
560     }
561   }
562
563   return True;
564 }
565 #undef OLD_NTDOMAIN