Changes from APPLIANCE_HEAD:
[ira/wip.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         sid_copy(&owner_sid, psd->owner_sid);
190     if (!sid_to_uid( &owner_sid, puser, &sid_type))
191       DEBUG(3,("unpack_nt_permissions: unable to validate owner sid.\n"));
192   }
193
194   /*
195    * Don't immediately fail if the group sid cannot be validated.
196    * This may be an owner chown only set.
197    */
198
199   if (security_info_sent & GROUP_SECURITY_INFORMATION) {
200         sid_copy(&grp_sid, psd->grp_sid);
201     if (!sid_to_gid( &grp_sid, pgrp, &sid_type))
202       DEBUG(3,("unpack_nt_permissions: unable to validate group sid.\n"));
203   }
204
205   /*
206    * If no DACL then this is a chown only security descriptor.
207    */
208
209   if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !dacl) {
210     *pmode = 0;
211     return True;
212   }
213
214   /*
215    * Now go through the DACL and ensure that
216    * any owner/group sids match.
217    */
218
219   for(i = 0; i < dacl->num_aces; i++) {
220     DOM_SID ace_sid;
221     SEC_ACE *psa = &dacl->ace[i];
222
223     if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) &&
224        (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
225       DEBUG(3,("unpack_nt_permissions: unable to set anything but an ALLOW or DENY ACE.\n"));
226       return False;
227     }
228
229     /*
230      * Ignore or remove bits we don't care about on a directory ACE.
231      */
232
233     if(is_directory) {
234       if(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
235         DEBUG(3,("unpack_nt_permissions: ignoring inherit only ACE.\n"));
236         continue;
237       }
238
239       /*
240        * At least one of the ACE entries wasn't inherit only.
241        * Flag this so we know the returned mode is valid.
242        */
243
244       all_aces_are_inherit_only = False;
245     }
246
247     /*
248      * Windows 2000 sets these flags even on *file* ACE's. This is wrong
249      * but we can ignore them for now. Revisit this when we go to POSIX
250      * ACLs on directories.
251      */
252
253     psa->flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT);
254
255     if(psa->flags != 0) {
256       DEBUG(1,("unpack_nt_permissions: unable to set ACE flags (%x).\n", 
257             (unsigned int)psa->flags));
258       return False;
259     }
260
261     /*
262      * The security mask may be UNIX_ACCESS_NONE which should map into
263      * no permissions (we overload the WRITE_OWNER bit for this) or it
264      * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
265      * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
266      */
267
268     psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS|
269                      GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES);
270
271     if(psa->info.mask != UNIX_ACCESS_NONE)
272       psa->info.mask &= ~UNIX_ACCESS_NONE;
273
274     sid_copy(&ace_sid, &psa->sid);
275
276     if(sid_equal(&ace_sid, &file_owner_sid)) {
277       /*
278        * Map the desired permissions into owner perms.
279        */
280
281       if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
282         *pmode |= map_nt_perms( psa->info, S_IRUSR);
283       else
284         *pmode &= ~(map_nt_perms( psa->info, S_IRUSR));
285
286     } else if( sid_equal(&ace_sid, &file_grp_sid)) {
287       /*
288        * Map the desired permissions into group perms.
289        */
290
291       if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
292         *pmode |= map_nt_perms( psa->info, S_IRGRP);
293       else
294         *pmode &= ~(map_nt_perms( psa->info, S_IRGRP));
295
296     } else if( sid_equal(&ace_sid, &global_sid_World)) {
297       /*
298        * Map the desired permissions into other perms.
299        */
300
301       if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
302         *pmode |= map_nt_perms( psa->info, S_IROTH);
303       else
304         *pmode &= ~(map_nt_perms( psa->info, S_IROTH));
305
306     } else {
307       DEBUG(0,("unpack_nt_permissions: unknown SID used in ACL.\n"));
308       return False;
309     }
310   }
311
312   if (is_directory && all_aces_are_inherit_only) {
313     /*
314      * Windows 2000 is doing one of these weird 'inherit acl'
315      * traverses to conserve NTFS ACL resources. Just pretend
316      * there was no DACL sent. JRA.
317      */
318
319     DEBUG(10,("unpack_nt_permissions: Win2k inherit acl traverse. Ignoring DACL.\n"));
320     free_sec_acl(&psd->dacl);
321   }
322
323   return True;
324 }
325
326 /****************************************************************************
327  Reply to query a security descriptor from an fsp. If it succeeds it allocates
328  the space for the return elements and returns the size needed to return the
329  security descriptor. This should be the only external function needed for
330  the UNIX style get ACL.
331 ****************************************************************************/
332
333 size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
334 {
335   extern DOM_SID global_sid_World;
336   SMB_STRUCT_STAT sbuf;
337   SEC_ACE ace_list[6];
338   DOM_SID owner_sid;
339   DOM_SID group_sid;
340   size_t sd_size;
341   SEC_ACL *psa = NULL;
342   SEC_ACCESS owner_access;
343   int owner_acl_type;
344   SEC_ACCESS group_access;
345   int grp_acl_type;
346   SEC_ACCESS other_access;
347   int other_acl_type;
348   int num_acls = 0;
349  
350   *ppdesc = NULL;
351
352   if(!lp_nt_acl_support()) {
353     sid_copy( &owner_sid, &global_sid_World);
354     sid_copy( &group_sid, &global_sid_World);
355   } else {
356
357     if(fsp->is_directory || fsp->fd == -1) {
358       if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
359         return 0;
360       }
361     } else {
362       if(fsp->conn->vfs_ops.fstat(fsp,fsp->fd,&sbuf) != 0) {
363         return 0;
364       }
365     }
366
367     /*
368      * Get the owner, group and world SIDs.
369      */
370
371     create_file_sids(&sbuf, &owner_sid, &group_sid);
372
373     /*
374      * Create the generic 3 element UNIX acl.
375      */
376
377     owner_access = map_unix_perms(&owner_acl_type, sbuf.st_mode,
378                                                         S_IRUSR, S_IWUSR, S_IXUSR, fsp->is_directory);
379     group_access = map_unix_perms(&grp_acl_type, sbuf.st_mode,
380                                                         S_IRGRP, S_IWGRP, S_IXGRP, fsp->is_directory);
381     other_access = map_unix_perms(&other_acl_type, sbuf.st_mode,
382                                                         S_IROTH, S_IWOTH, S_IXOTH, fsp->is_directory);
383
384     if(owner_access.mask)
385       init_sec_ace(&ace_list[num_acls++], &owner_sid, owner_acl_type,
386                    owner_access, 0);
387
388     if(group_access.mask)
389       init_sec_ace(&ace_list[num_acls++], &group_sid, grp_acl_type,
390                    group_access, 0);
391
392     if(other_access.mask)
393       init_sec_ace(&ace_list[num_acls++], &global_sid_World, other_acl_type,
394                    other_access, 0);
395
396     if(fsp->is_directory) {
397       /*
398        * For directory ACLs we also add in the inherited permissions
399        * ACE entries. These are the permissions a file would get when
400        * being created in the directory.
401        */
402       mode_t mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name);
403
404       owner_access = map_unix_perms(&owner_acl_type, mode,
405                             S_IRUSR, S_IWUSR, S_IXUSR, fsp->is_directory);
406       group_access = map_unix_perms(&grp_acl_type, mode,
407                             S_IRGRP, S_IWGRP, S_IXGRP, fsp->is_directory);
408       other_access = map_unix_perms(&other_acl_type, mode,
409                             S_IROTH, S_IWOTH, S_IXOTH, fsp->is_directory);
410
411       if(owner_access.mask)
412         init_sec_ace(&ace_list[num_acls++], &owner_sid, owner_acl_type,
413                      owner_access, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
414
415       if(group_access.mask)
416         init_sec_ace(&ace_list[num_acls++], &group_sid, grp_acl_type,
417                      group_access, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
418
419       if(other_access.mask)
420         init_sec_ace(&ace_list[num_acls++], &global_sid_World, other_acl_type,
421                      other_access, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
422     }
423
424     if(num_acls)
425       if((psa = make_sec_acl( ACL_REVISION, num_acls, ace_list)) == NULL) {
426         DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
427         return 0;
428       }
429   }
430
431   *ppdesc = make_standard_sec_desc( &owner_sid, &group_sid, psa, &sd_size);
432
433   if(!*ppdesc) {
434     DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
435     sd_size = 0;
436   }
437
438   free_sec_acl(&psa);
439
440   return sd_size;
441 }
442
443 /****************************************************************************
444  Reply to set a security descriptor on an fsp. security_info_sent is the
445  description of the following NT ACL.
446  This should be the only external function needed for the UNIX style set ACL.
447 ****************************************************************************/
448
449 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
450 {
451   connection_struct *conn = fsp->conn;
452   uid_t user = (uid_t)-1;
453   gid_t grp = (gid_t)-1;
454   mode_t perms = 0;
455   SMB_STRUCT_STAT sbuf;  
456   BOOL got_dacl = False;
457
458   /*
459    * Get the current state of the file.
460    */
461
462   if(fsp->is_directory || fsp->fd == -1) {
463     if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0)
464       return False;
465   } else {
466     if(conn->vfs_ops.fstat(fsp,fsp->fd,&sbuf) != 0)
467       return False;
468   }
469
470   /*
471    * Unpack the user/group/world id's and permissions.
472    */
473
474   if (!unpack_nt_permissions( &sbuf, &user, &grp, &perms, security_info_sent, psd, fsp->is_directory))
475     return False;
476
477   if (psd->dacl != NULL)
478     got_dacl = True;
479
480   /*
481    * Do we need to chown ?
482    */
483
484   if((user != (uid_t)-1 || grp != (uid_t)-1) && (sbuf.st_uid != user || sbuf.st_gid != grp)) {
485
486     DEBUG(3,("call_nt_transact_set_security_desc: chown %s. uid = %u, gid = %u.\n",
487           fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
488
489     if(vfs_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
490       DEBUG(3,("call_nt_transact_set_security_desc: chown %s, %u, %u failed. Error = %s.\n",
491             fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
492       return False;
493     }
494
495     /*
496      * Recheck the current state of the file, which may have changed.
497      * (suid/sgid bits, for instance)
498      */
499
500     if(fsp->is_directory) {
501       if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
502         return False;
503       }
504     } else {
505
506       int ret;
507     
508       if(fsp->fd == -1)
509         ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf);
510       else
511         ret = conn->vfs_ops.fstat(fsp,fsp->fd,&sbuf);
512   
513       if(ret != 0)
514         return False;
515     }
516   }
517
518   /*
519    * Only change security if we got a DACL.
520    */
521
522   if((security_info_sent & DACL_SECURITY_INFORMATION) && got_dacl) {
523
524     /*
525      * Check to see if we need to change anything.
526      * Enforce limits on modified bits *only*. Don't enforce masks
527          * on bits not changed by the user.
528      */
529
530     if(fsp->is_directory) {
531
532       perms &= (lp_dir_security_mask(SNUM(conn)) | sbuf.st_mode);
533       perms |= (lp_force_dir_security_mode(SNUM(conn)) & ( perms ^ sbuf.st_mode ));
534
535     } else {
536
537       perms &= (lp_security_mask(SNUM(conn)) | sbuf.st_mode); 
538       perms |= (lp_force_security_mode(SNUM(conn)) & ( perms ^ sbuf.st_mode ));
539
540     }
541
542     /*
543      * Preserve special bits.
544      */
545
546     perms |= (sbuf.st_mode & ~0777);
547
548     /*
549      * Do we need to chmod ?
550      */
551
552     if(sbuf.st_mode != perms) {
553
554       DEBUG(3,("call_nt_transact_set_security_desc: chmod %s. perms = 0%o.\n",
555             fsp->fsp_name, (unsigned int)perms ));
556
557       if(conn->vfs_ops.chmod(conn,dos_to_unix(fsp->fsp_name, False), perms) == -1) {
558         DEBUG(3,("call_nt_transact_set_security_desc: chmod %s, 0%o failed. Error = %s.\n",
559               fsp->fsp_name, (unsigned int)perms, strerror(errno) ));
560         return False;
561       }
562     }
563   }
564
565   return True;
566 }
567 #undef OLD_NTDOMAIN