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