2007-09-25 20:17:24 +04:00
/*
* fs / cifs / cifsacl . c
*
2008-03-15 01:37:16 +03:00
* Copyright ( C ) International Business Machines Corp . , 2007 , 2008
2007-09-25 20:17:24 +04:00
* Author ( s ) : Steve French ( sfrench @ us . ibm . com )
*
* Contains the routines for mapping CIFS / NTFS ACLs
*
* This library is free software ; you can redistribute it and / or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation ; either version 2.1 of the License , or
* ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See
* the GNU Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
2007-09-25 23:53:44 +04:00
# include <linux/fs.h>
# include "cifspdu.h"
# include "cifsglob.h"
2007-10-03 22:22:19 +04:00
# include "cifsacl.h"
2007-09-25 23:53:44 +04:00
# include "cifsproto.h"
# include "cifs_debug.h"
2007-10-12 08:11:59 +04:00
# ifdef CONFIG_CIFS_EXPERIMENTAL
2007-10-16 22:40:37 +04:00
static struct cifs_wksid wksidarr [ NUM_WK_SIDS ] = {
2007-10-12 08:11:59 +04:00
{ { 1 , 0 , { 0 , 0 , 0 , 0 , 0 , 0 } , { 0 , 0 , 0 , 0 , 0 } } , " null user " } ,
{ { 1 , 1 , { 0 , 0 , 0 , 0 , 0 , 1 } , { 0 , 0 , 0 , 0 , 0 } } , " nobody " } ,
2007-10-17 01:35:39 +04:00
{ { 1 , 1 , { 0 , 0 , 0 , 0 , 0 , 5 } , { cpu_to_le32 ( 11 ) , 0 , 0 , 0 , 0 } } , " net-users " } ,
{ { 1 , 1 , { 0 , 0 , 0 , 0 , 0 , 5 } , { cpu_to_le32 ( 18 ) , 0 , 0 , 0 , 0 } } , " sys " } ,
{ { 1 , 2 , { 0 , 0 , 0 , 0 , 0 , 5 } , { cpu_to_le32 ( 32 ) , cpu_to_le32 ( 544 ) , 0 , 0 , 0 } } , " root " } ,
{ { 1 , 2 , { 0 , 0 , 0 , 0 , 0 , 5 } , { cpu_to_le32 ( 32 ) , cpu_to_le32 ( 545 ) , 0 , 0 , 0 } } , " users " } ,
2007-10-24 01:22:55 +04:00
{ { 1 , 2 , { 0 , 0 , 0 , 0 , 0 , 5 } , { cpu_to_le32 ( 32 ) , cpu_to_le32 ( 546 ) , 0 , 0 , 0 } } , " guest " } }
;
2007-10-12 08:11:59 +04:00
2007-09-25 20:17:24 +04:00
/* security id for everyone */
2007-10-30 07:45:14 +03:00
static const struct cifs_sid sid_everyone = {
1 , 1 , { 0 , 0 , 0 , 0 , 0 , 1 } , { 0 } } ;
2007-09-25 20:17:24 +04:00
/* group users */
2008-02-08 02:25:02 +03:00
static const struct cifs_sid sid_user = { 1 , 2 , { 0 , 0 , 0 , 0 , 0 , 5 } , { } } ;
2007-10-03 22:22:19 +04:00
2007-10-12 08:11:59 +04:00
int match_sid ( struct cifs_sid * ctsid )
{
int i , j ;
int num_subauth , num_sat , num_saw ;
struct cifs_sid * cwsid ;
if ( ! ctsid )
return ( - 1 ) ;
for ( i = 0 ; i < NUM_WK_SIDS ; + + i ) {
cwsid = & ( wksidarr [ i ] . cifssid ) ;
/* compare the revision */
if ( ctsid - > revision ! = cwsid - > revision )
continue ;
/* compare all of the six auth values */
for ( j = 0 ; j < 6 ; + + j ) {
if ( ctsid - > authority [ j ] ! = cwsid - > authority [ j ] )
break ;
}
if ( j < 6 )
continue ; /* all of the auth values did not match */
/* compare all of the subauth values if any */
2007-10-17 01:35:39 +04:00
num_sat = ctsid - > num_subauth ;
num_saw = cwsid - > num_subauth ;
2007-10-12 08:11:59 +04:00
num_subauth = num_sat < num_saw ? num_sat : num_saw ;
if ( num_subauth ) {
for ( j = 0 ; j < num_subauth ; + + j ) {
if ( ctsid - > sub_auth [ j ] ! = cwsid - > sub_auth [ j ] )
break ;
}
if ( j < num_subauth )
continue ; /* all sub_auth values do not match */
}
cFYI ( 1 , ( " matching sid: %s \n " , wksidarr [ i ] . sidname ) ) ;
return ( 0 ) ; /* sids compare/match */
}
cFYI ( 1 , ( " No matching sid " ) ) ;
return ( - 1 ) ;
}
2007-10-18 02:50:39 +04:00
/* if the two SIDs (roughly equivalent to a UUID for a user or group) are
the same returns 1 , if they do not match returns 0 */
2007-10-26 01:17:17 +04:00
int compare_sids ( const struct cifs_sid * ctsid , const struct cifs_sid * cwsid )
2007-10-12 08:11:59 +04:00
{
int i ;
int num_subauth , num_sat , num_saw ;
if ( ( ! ctsid ) | | ( ! cwsid ) )
2007-10-18 02:50:39 +04:00
return ( 0 ) ;
2007-10-12 08:11:59 +04:00
/* compare the revision */
if ( ctsid - > revision ! = cwsid - > revision )
2007-10-18 02:50:39 +04:00
return ( 0 ) ;
2007-10-12 08:11:59 +04:00
/* compare all of the six auth values */
for ( i = 0 ; i < 6 ; + + i ) {
if ( ctsid - > authority [ i ] ! = cwsid - > authority [ i ] )
2007-10-18 02:50:39 +04:00
return ( 0 ) ;
2007-10-12 08:11:59 +04:00
}
/* compare all of the subauth values if any */
2007-10-17 06:12:46 +04:00
num_sat = ctsid - > num_subauth ;
2007-10-17 06:48:17 +04:00
num_saw = cwsid - > num_subauth ;
2007-10-12 08:11:59 +04:00
num_subauth = num_sat < num_saw ? num_sat : num_saw ;
if ( num_subauth ) {
for ( i = 0 ; i < num_subauth ; + + i ) {
if ( ctsid - > sub_auth [ i ] ! = cwsid - > sub_auth [ i ] )
2007-10-18 02:50:39 +04:00
return ( 0 ) ;
2007-10-12 08:11:59 +04:00
}
}
2007-10-18 02:50:39 +04:00
return ( 1 ) ; /* sids compare/match */
2007-10-12 08:11:59 +04:00
}
2007-12-31 10:47:21 +03:00
/* copy ntsd, owner sid, and group sid from a security descriptor to another */
static void copy_sec_desc ( const struct cifs_ntsd * pntsd ,
struct cifs_ntsd * pnntsd , __u32 sidsoffset )
{
int i ;
struct cifs_sid * owner_sid_ptr , * group_sid_ptr ;
struct cifs_sid * nowner_sid_ptr , * ngroup_sid_ptr ;
/* copy security descriptor control portion */
pnntsd - > revision = pntsd - > revision ;
pnntsd - > type = pntsd - > type ;
pnntsd - > dacloffset = cpu_to_le32 ( sizeof ( struct cifs_ntsd ) ) ;
pnntsd - > sacloffset = 0 ;
pnntsd - > osidoffset = cpu_to_le32 ( sidsoffset ) ;
pnntsd - > gsidoffset = cpu_to_le32 ( sidsoffset + sizeof ( struct cifs_sid ) ) ;
/* copy owner sid */
owner_sid_ptr = ( struct cifs_sid * ) ( ( char * ) pntsd +
le32_to_cpu ( pntsd - > osidoffset ) ) ;
nowner_sid_ptr = ( struct cifs_sid * ) ( ( char * ) pnntsd + sidsoffset ) ;
nowner_sid_ptr - > revision = owner_sid_ptr - > revision ;
nowner_sid_ptr - > num_subauth = owner_sid_ptr - > num_subauth ;
for ( i = 0 ; i < 6 ; i + + )
nowner_sid_ptr - > authority [ i ] = owner_sid_ptr - > authority [ i ] ;
for ( i = 0 ; i < 5 ; i + + )
nowner_sid_ptr - > sub_auth [ i ] = owner_sid_ptr - > sub_auth [ i ] ;
/* copy group sid */
group_sid_ptr = ( struct cifs_sid * ) ( ( char * ) pntsd +
le32_to_cpu ( pntsd - > gsidoffset ) ) ;
ngroup_sid_ptr = ( struct cifs_sid * ) ( ( char * ) pnntsd + sidsoffset +
sizeof ( struct cifs_sid ) ) ;
ngroup_sid_ptr - > revision = group_sid_ptr - > revision ;
ngroup_sid_ptr - > num_subauth = group_sid_ptr - > num_subauth ;
for ( i = 0 ; i < 6 ; i + + )
ngroup_sid_ptr - > authority [ i ] = group_sid_ptr - > authority [ i ] ;
for ( i = 0 ; i < 5 ; i + + )
ngroup_sid_ptr - > sub_auth [ i ] =
cpu_to_le32 ( group_sid_ptr - > sub_auth [ i ] ) ;
return ;
}
2007-10-26 01:17:17 +04:00
/*
change posix mode to reflect permissions
pmode is the existing mode ( we only want to overwrite part of this
bits to set can be : S_IRWXU , S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007
*/
2007-12-05 11:24:38 +03:00
static void access_flags_to_mode ( __le32 ace_flags , int type , umode_t * pmode ,
2007-11-08 20:57:40 +03:00
umode_t * pbits_to_set )
2007-10-26 01:17:17 +04:00
{
2007-12-05 11:24:38 +03:00
__u32 flags = le32_to_cpu ( ace_flags ) ;
2007-11-08 20:57:40 +03:00
/* the order of ACEs is important. The canonical order is to begin with
2007-11-09 00:12:01 +03:00
DENY entries followed by ALLOW , otherwise an allow entry could be
2007-11-08 20:57:40 +03:00
encountered first , making the subsequent deny entry like " dead code "
2007-11-09 00:12:01 +03:00
which would be superflous since Windows stops when a match is made
2007-11-08 20:57:40 +03:00
for the operation you are trying to perform for your user */
/* For deny ACEs we change the mask so that subsequent allow access
control entries do not turn on the bits we are denying */
if ( type = = ACCESS_DENIED ) {
2008-02-08 02:25:02 +03:00
if ( flags & GENERIC_ALL )
2007-11-08 20:57:40 +03:00
* pbits_to_set & = ~ S_IRWXUGO ;
2008-02-08 02:25:02 +03:00
2007-12-05 11:24:38 +03:00
if ( ( flags & GENERIC_WRITE ) | |
( ( flags & FILE_WRITE_RIGHTS ) = = FILE_WRITE_RIGHTS ) )
2007-11-08 20:57:40 +03:00
* pbits_to_set & = ~ S_IWUGO ;
2007-12-05 11:24:38 +03:00
if ( ( flags & GENERIC_READ ) | |
( ( flags & FILE_READ_RIGHTS ) = = FILE_READ_RIGHTS ) )
2007-11-08 20:57:40 +03:00
* pbits_to_set & = ~ S_IRUGO ;
2007-12-05 11:24:38 +03:00
if ( ( flags & GENERIC_EXECUTE ) | |
( ( flags & FILE_EXEC_RIGHTS ) = = FILE_EXEC_RIGHTS ) )
2007-11-08 20:57:40 +03:00
* pbits_to_set & = ~ S_IXUGO ;
return ;
} else if ( type ! = ACCESS_ALLOWED ) {
cERROR ( 1 , ( " unknown access control type %d " , type ) ) ;
return ;
}
/* else ACCESS_ALLOWED type */
2007-10-26 01:17:17 +04:00
2007-12-05 11:24:38 +03:00
if ( flags & GENERIC_ALL ) {
2007-11-08 20:57:40 +03:00
* pmode | = ( S_IRWXUGO & ( * pbits_to_set ) ) ;
2008-02-12 23:32:36 +03:00
cFYI ( DBG2 , ( " all perms " ) ) ;
2007-10-26 08:32:43 +04:00
return ;
}
2007-12-05 11:24:38 +03:00
if ( ( flags & GENERIC_WRITE ) | |
( ( flags & FILE_WRITE_RIGHTS ) = = FILE_WRITE_RIGHTS ) )
2007-11-08 20:57:40 +03:00
* pmode | = ( S_IWUGO & ( * pbits_to_set ) ) ;
2007-12-05 11:24:38 +03:00
if ( ( flags & GENERIC_READ ) | |
( ( flags & FILE_READ_RIGHTS ) = = FILE_READ_RIGHTS ) )
2007-11-08 20:57:40 +03:00
* pmode | = ( S_IRUGO & ( * pbits_to_set ) ) ;
2007-12-05 11:24:38 +03:00
if ( ( flags & GENERIC_EXECUTE ) | |
( ( flags & FILE_EXEC_RIGHTS ) = = FILE_EXEC_RIGHTS ) )
2007-11-08 20:57:40 +03:00
* pmode | = ( S_IXUGO & ( * pbits_to_set ) ) ;
2007-10-26 01:17:17 +04:00
2008-02-12 23:32:36 +03:00
cFYI ( DBG2 , ( " access flags 0x%x mode now 0x%x " , flags , * pmode ) ) ;
2007-10-26 01:17:17 +04:00
return ;
}
2007-11-09 00:12:01 +03:00
/*
Generate access flags to reflect permissions mode is the existing mode .
This function is called for every ACE in the DACL whose SID matches
with either owner or group or everyone .
*/
static void mode_to_access_flags ( umode_t mode , umode_t bits_to_use ,
__u32 * pace_flags )
{
/* reset access mask */
* pace_flags = 0x0 ;
/* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */
mode & = bits_to_use ;
/* check for R/W/X UGO since we do not know whose flags
is this but we have cleared all the bits sans RWX for
either user or group or other as per bits_to_use */
if ( mode & S_IRUGO )
* pace_flags | = SET_FILE_READ_RIGHTS ;
if ( mode & S_IWUGO )
* pace_flags | = SET_FILE_WRITE_RIGHTS ;
if ( mode & S_IXUGO )
* pace_flags | = SET_FILE_EXEC_RIGHTS ;
2008-02-12 23:32:36 +03:00
cFYI ( DBG2 , ( " mode: 0x%x, access flags now 0x%x " , mode , * pace_flags ) ) ;
2007-11-09 00:12:01 +03:00
return ;
}
2008-03-29 06:09:18 +03:00
static __u16 fill_ace_for_sid ( struct cifs_ace * pntace ,
2007-12-31 10:47:21 +03:00
const struct cifs_sid * psid , __u64 nmode , umode_t bits )
{
int i ;
__u16 size = 0 ;
__u32 access_req = 0 ;
pntace - > type = ACCESS_ALLOWED ;
pntace - > flags = 0x0 ;
mode_to_access_flags ( nmode , bits , & access_req ) ;
if ( ! access_req )
access_req = SET_MINIMUM_RIGHTS ;
pntace - > access_req = cpu_to_le32 ( access_req ) ;
pntace - > sid . revision = psid - > revision ;
pntace - > sid . num_subauth = psid - > num_subauth ;
for ( i = 0 ; i < 6 ; i + + )
pntace - > sid . authority [ i ] = psid - > authority [ i ] ;
for ( i = 0 ; i < psid - > num_subauth ; i + + )
pntace - > sid . sub_auth [ i ] = psid - > sub_auth [ i ] ;
size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + ( psid - > num_subauth * 4 ) ;
pntace - > size = cpu_to_le16 ( size ) ;
return ( size ) ;
}
2007-10-12 08:11:59 +04:00
2007-10-31 07:54:42 +03:00
# ifdef CONFIG_CIFS_DEBUG2
static void dump_ace ( struct cifs_ace * pace , char * end_of_acl )
2007-10-03 22:22:19 +04:00
{
int num_subauth ;
/* validate that we do not go past end of acl */
2007-10-12 08:11:59 +04:00
2007-10-24 01:22:55 +04:00
if ( le16_to_cpu ( pace - > size ) < 16 ) {
cERROR ( 1 , ( " ACE too small, %d " , le16_to_cpu ( pace - > size ) ) ) ;
return ;
}
if ( end_of_acl < ( char * ) pace + le16_to_cpu ( pace - > size ) ) {
2007-10-03 22:22:19 +04:00
cERROR ( 1 , ( " ACL too small to parse ACE " ) ) ;
return ;
2007-10-24 01:22:55 +04:00
}
2007-10-03 22:22:19 +04:00
2007-10-24 01:22:55 +04:00
num_subauth = pace - > sid . num_subauth ;
2007-10-03 22:22:19 +04:00
if ( num_subauth ) {
2007-10-12 22:54:12 +04:00
int i ;
2007-10-24 01:22:55 +04:00
cFYI ( 1 , ( " ACE revision %d num_auth %d type %d flags %d size %d " ,
pace - > sid . revision , pace - > sid . num_subauth , pace - > type ,
2007-12-31 10:47:21 +03:00
pace - > flags , le16_to_cpu ( pace - > size ) ) ) ;
2007-10-03 23:43:19 +04:00
for ( i = 0 ; i < num_subauth ; + + i ) {
cFYI ( 1 , ( " ACE sub_auth[%d]: 0x%x " , i ,
2007-10-24 01:22:55 +04:00
le32_to_cpu ( pace - > sid . sub_auth [ i ] ) ) ) ;
2007-10-03 23:43:19 +04:00
}
/* BB add length check to make sure that we do not have huge
num auths and therefore go off the end */
}
return ;
}
2007-10-31 07:54:42 +03:00
# endif
2007-10-03 23:43:19 +04:00
2007-10-03 22:22:19 +04:00
2007-10-18 02:50:39 +04:00
static void parse_dacl ( struct cifs_acl * pdacl , char * end_of_acl ,
2007-10-26 08:32:43 +04:00
struct cifs_sid * pownersid , struct cifs_sid * pgrpsid ,
2007-10-26 01:17:17 +04:00
struct inode * inode )
2007-10-03 22:22:19 +04:00
{
int i ;
int num_aces = 0 ;
int acl_size ;
char * acl_base ;
struct cifs_ace * * ppace ;
/* BB need to add parm so we can store the SID BB */
2007-11-25 13:01:00 +03:00
if ( ! pdacl ) {
/* no DACL in the security descriptor, set
all the permissions for user / group / other */
inode - > i_mode | = S_IRWXUGO ;
return ;
}
2007-10-03 22:22:19 +04:00
/* validate that we do not go past end of acl */
2007-10-16 22:40:37 +04:00
if ( end_of_acl < ( char * ) pdacl + le16_to_cpu ( pdacl - > size ) ) {
2007-10-03 22:22:19 +04:00
cERROR ( 1 , ( " ACL too small to parse DACL " ) ) ;
return ;
}
2008-02-12 23:32:36 +03:00
cFYI ( DBG2 , ( " DACL revision %d size %d num aces %d " ,
2007-10-16 22:40:37 +04:00
le16_to_cpu ( pdacl - > revision ) , le16_to_cpu ( pdacl - > size ) ,
le32_to_cpu ( pdacl - > num_aces ) ) ) ;
2007-10-03 22:22:19 +04:00
2007-11-01 21:03:01 +03:00
/* reset rwx permissions for user/group/other.
Also , if num_aces is 0 i . e . DACL has no ACEs ,
user / group / other have no permissions */
inode - > i_mode & = ~ ( S_IRWXUGO ) ;
2007-10-03 22:22:19 +04:00
acl_base = ( char * ) pdacl ;
acl_size = sizeof ( struct cifs_acl ) ;
2007-10-17 06:12:46 +04:00
num_aces = le32_to_cpu ( pdacl - > num_aces ) ;
2007-10-03 22:22:19 +04:00
if ( num_aces > 0 ) {
2007-11-08 20:57:40 +03:00
umode_t user_mask = S_IRWXU ;
umode_t group_mask = S_IRWXG ;
umode_t other_mask = S_IRWXO ;
2007-10-03 22:22:19 +04:00
ppace = kmalloc ( num_aces * sizeof ( struct cifs_ace * ) ,
GFP_KERNEL ) ;
for ( i = 0 ; i < num_aces ; + + i ) {
2007-10-24 01:22:55 +04:00
ppace [ i ] = ( struct cifs_ace * ) ( acl_base + acl_size ) ;
2007-10-31 07:54:42 +03:00
# ifdef CONFIG_CIFS_DEBUG2
dump_ace ( ppace [ i ] , end_of_acl ) ;
# endif
2007-10-30 07:45:14 +03:00
if ( compare_sids ( & ( ppace [ i ] - > sid ) , pownersid ) )
access_flags_to_mode ( ppace [ i ] - > access_req ,
2007-11-08 20:57:40 +03:00
ppace [ i ] - > type ,
& ( inode - > i_mode ) ,
& user_mask ) ;
2007-10-30 07:45:14 +03:00
if ( compare_sids ( & ( ppace [ i ] - > sid ) , pgrpsid ) )
access_flags_to_mode ( ppace [ i ] - > access_req ,
2007-11-08 20:57:40 +03:00
ppace [ i ] - > type ,
& ( inode - > i_mode ) ,
& group_mask ) ;
2007-10-30 07:45:14 +03:00
if ( compare_sids ( & ( ppace [ i ] - > sid ) , & sid_everyone ) )
access_flags_to_mode ( ppace [ i ] - > access_req ,
2007-11-08 20:57:40 +03:00
ppace [ i ] - > type ,
& ( inode - > i_mode ) ,
& other_mask ) ;
2007-10-30 07:45:14 +03:00
2007-10-24 01:22:55 +04:00
/* memcpy((void *)(&(cifscred->aces[i])),
2007-10-03 23:43:19 +04:00
( void * ) ppace [ i ] ,
sizeof ( struct cifs_ace ) ) ; */
2007-10-03 22:22:19 +04:00
2007-10-24 01:22:55 +04:00
acl_base = ( char * ) ppace [ i ] ;
acl_size = le16_to_cpu ( ppace [ i ] - > size ) ;
2007-10-03 22:22:19 +04:00
}
kfree ( ppace ) ;
}
return ;
}
2007-09-25 20:17:24 +04:00
2007-12-31 10:47:21 +03:00
static int set_chmod_dacl ( struct cifs_acl * pndacl , struct cifs_sid * pownersid ,
struct cifs_sid * pgrpsid , __u64 nmode )
{
2008-03-29 06:09:18 +03:00
u16 size = 0 ;
2007-12-31 10:47:21 +03:00
struct cifs_acl * pnndacl ;
pnndacl = ( struct cifs_acl * ) ( ( char * ) pndacl + sizeof ( struct cifs_acl ) ) ;
size + = fill_ace_for_sid ( ( struct cifs_ace * ) ( ( char * ) pnndacl + size ) ,
pownersid , nmode , S_IRWXU ) ;
size + = fill_ace_for_sid ( ( struct cifs_ace * ) ( ( char * ) pnndacl + size ) ,
pgrpsid , nmode , S_IRWXG ) ;
size + = fill_ace_for_sid ( ( struct cifs_ace * ) ( ( char * ) pnndacl + size ) ,
& sid_everyone , nmode , S_IRWXO ) ;
pndacl - > size = cpu_to_le16 ( size + sizeof ( struct cifs_acl ) ) ;
2008-02-12 23:46:26 +03:00
pndacl - > num_aces = cpu_to_le32 ( 3 ) ;
2007-12-31 10:47:21 +03:00
return ( 0 ) ;
}
2007-09-25 20:17:24 +04:00
static int parse_sid ( struct cifs_sid * psid , char * end_of_acl )
{
/* BB need to add parm so we can store the SID BB */
2007-10-27 03:40:20 +04:00
/* validate that we do not go past end of ACL - sid must be at least 8
bytes long ( assuming no sub - auths - e . g . the null SID */
if ( end_of_acl < ( char * ) psid + 8 ) {
cERROR ( 1 , ( " ACL too small to parse SID %p " , psid ) ) ;
2007-09-25 20:17:24 +04:00
return - EINVAL ;
}
2007-10-03 22:22:19 +04:00
2007-10-16 22:40:37 +04:00
if ( psid - > num_subauth ) {
2007-09-25 20:17:24 +04:00
# ifdef CONFIG_CIFS_DEBUG2
2007-10-12 22:54:12 +04:00
int i ;
2007-10-24 01:22:55 +04:00
cFYI ( 1 , ( " SID revision %d num_auth %d " ,
psid - > revision , psid - > num_subauth ) ) ;
2007-09-25 20:17:24 +04:00
2007-10-16 22:40:37 +04:00
for ( i = 0 ; i < psid - > num_subauth ; i + + ) {
2007-10-03 22:22:19 +04:00
cFYI ( 1 , ( " SID sub_auth[%d]: 0x%x " , i ,
2007-10-12 08:11:59 +04:00
le32_to_cpu ( psid - > sub_auth [ i ] ) ) ) ;
2007-10-03 22:22:19 +04:00
}
2007-10-03 23:43:19 +04:00
/* BB add length check to make sure that we do not have huge
2007-10-03 22:22:19 +04:00
num auths and therefore go off the end */
2007-10-03 23:43:19 +04:00
cFYI ( 1 , ( " RID 0x%x " ,
2007-10-16 22:40:37 +04:00
le32_to_cpu ( psid - > sub_auth [ psid - > num_subauth - 1 ] ) ) ) ;
2007-09-25 20:17:24 +04:00
# endif
2007-10-03 22:22:19 +04:00
}
2007-09-25 20:17:24 +04:00
return 0 ;
}
2007-10-03 22:22:19 +04:00
2007-09-25 20:17:24 +04:00
/* Convert CIFS ACL to POSIX form */
2007-10-26 01:17:17 +04:00
static int parse_sec_desc ( struct cifs_ntsd * pntsd , int acl_len ,
struct inode * inode )
2007-09-25 20:17:24 +04:00
{
2007-10-03 22:22:19 +04:00
int rc ;
2007-09-25 20:17:24 +04:00
struct cifs_sid * owner_sid_ptr , * group_sid_ptr ;
struct cifs_acl * dacl_ptr ; /* no need for SACL ptr */
char * end_of_acl = ( ( char * ) pntsd ) + acl_len ;
2007-11-01 21:03:01 +03:00
__u32 dacloffset ;
2007-09-25 20:17:24 +04:00
2007-10-27 03:40:20 +04:00
if ( ( inode = = NULL ) | | ( pntsd = = NULL ) )
return - EIO ;
2007-09-25 20:17:24 +04:00
owner_sid_ptr = ( struct cifs_sid * ) ( ( char * ) pntsd +
2007-10-16 22:40:37 +04:00
le32_to_cpu ( pntsd - > osidoffset ) ) ;
2007-09-25 20:17:24 +04:00
group_sid_ptr = ( struct cifs_sid * ) ( ( char * ) pntsd +
2007-10-16 22:40:37 +04:00
le32_to_cpu ( pntsd - > gsidoffset ) ) ;
2007-11-01 21:03:01 +03:00
dacloffset = le32_to_cpu ( pntsd - > dacloffset ) ;
2007-11-06 00:46:10 +03:00
dacl_ptr = ( struct cifs_acl * ) ( ( char * ) pntsd + dacloffset ) ;
2008-02-12 23:32:36 +03:00
cFYI ( DBG2 , ( " revision %d type 0x%x ooffset 0x%x goffset 0x%x "
2007-09-25 20:17:24 +04:00
" sacloffset 0x%x dacloffset 0x%x " ,
2007-10-16 22:40:37 +04:00
pntsd - > revision , pntsd - > type , le32_to_cpu ( pntsd - > osidoffset ) ,
le32_to_cpu ( pntsd - > gsidoffset ) ,
2007-11-01 21:03:01 +03:00
le32_to_cpu ( pntsd - > sacloffset ) , dacloffset ) ) ;
2007-10-27 03:40:20 +04:00
/* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */
2007-09-25 20:17:24 +04:00
rc = parse_sid ( owner_sid_ptr , end_of_acl ) ;
if ( rc )
return rc ;
rc = parse_sid ( group_sid_ptr , end_of_acl ) ;
if ( rc )
return rc ;
2007-11-01 21:03:01 +03:00
if ( dacloffset )
parse_dacl ( dacl_ptr , end_of_acl , owner_sid_ptr ,
2007-11-06 00:46:10 +03:00
group_sid_ptr , inode ) ;
2007-11-01 21:03:01 +03:00
else
cFYI ( 1 , ( " no ACL " ) ) ; /* BB grant all or default perms? */
2007-10-03 22:22:19 +04:00
2007-09-25 20:17:24 +04:00
/* cifscred->uid = owner_sid_ptr->rid;
cifscred - > gid = group_sid_ptr - > rid ;
memcpy ( ( void * ) ( & ( cifscred - > osid ) ) , ( void * ) owner_sid_ptr ,
2007-10-26 01:17:17 +04:00
sizeof ( struct cifs_sid ) ) ;
2007-09-25 20:17:24 +04:00
memcpy ( ( void * ) ( & ( cifscred - > gsid ) ) , ( void * ) group_sid_ptr ,
2007-10-26 01:17:17 +04:00
sizeof ( struct cifs_sid ) ) ; */
2007-09-25 20:17:24 +04:00
2007-10-12 08:11:59 +04:00
2007-09-25 20:17:24 +04:00
return ( 0 ) ;
}
2007-10-27 03:40:20 +04:00
2007-12-31 10:47:21 +03:00
/* Convert permission bits from mode to equivalent CIFS ACL */
static int build_sec_desc ( struct cifs_ntsd * pntsd , struct cifs_ntsd * pnntsd ,
2008-04-10 00:55:31 +04:00
struct inode * inode , __u64 nmode )
2007-12-31 10:47:21 +03:00
{
int rc = 0 ;
__u32 dacloffset ;
__u32 ndacloffset ;
__u32 sidsoffset ;
struct cifs_sid * owner_sid_ptr , * group_sid_ptr ;
struct cifs_acl * dacl_ptr = NULL ; /* no need for SACL ptr */
struct cifs_acl * ndacl_ptr = NULL ; /* no need for SACL ptr */
if ( ( inode = = NULL ) | | ( pntsd = = NULL ) | | ( pnntsd = = NULL ) )
return ( - EIO ) ;
owner_sid_ptr = ( struct cifs_sid * ) ( ( char * ) pntsd +
le32_to_cpu ( pntsd - > osidoffset ) ) ;
group_sid_ptr = ( struct cifs_sid * ) ( ( char * ) pntsd +
le32_to_cpu ( pntsd - > gsidoffset ) ) ;
dacloffset = le32_to_cpu ( pntsd - > dacloffset ) ;
dacl_ptr = ( struct cifs_acl * ) ( ( char * ) pntsd + dacloffset ) ;
ndacloffset = sizeof ( struct cifs_ntsd ) ;
ndacl_ptr = ( struct cifs_acl * ) ( ( char * ) pnntsd + ndacloffset ) ;
ndacl_ptr - > revision = dacl_ptr - > revision ;
ndacl_ptr - > size = 0 ;
ndacl_ptr - > num_aces = 0 ;
rc = set_chmod_dacl ( ndacl_ptr , owner_sid_ptr , group_sid_ptr , nmode ) ;
sidsoffset = ndacloffset + le16_to_cpu ( ndacl_ptr - > size ) ;
/* copy security descriptor control portion and owner and group sid */
copy_sec_desc ( pntsd , pnntsd , sidsoffset ) ;
return ( rc ) ;
}
2007-11-01 21:03:01 +03:00
/* Retrieve an ACL from the server */
static struct cifs_ntsd * get_cifs_acl ( u32 * pacllen , struct inode * inode ,
2008-03-15 01:37:16 +03:00
const char * path , const __u16 * pfid )
2007-10-27 03:40:20 +04:00
{
2008-03-15 01:37:16 +03:00
struct cifsFileInfo * open_file = NULL ;
2008-04-29 04:06:05 +04:00
bool unlock_file = false ;
2007-10-27 03:40:20 +04:00
int xid ;
int rc = - EIO ;
__u16 fid ;
struct super_block * sb ;
struct cifs_sb_info * cifs_sb ;
struct cifs_ntsd * pntsd = NULL ;
cFYI ( 1 , ( " get mode from ACL for %s " , path ) ) ;
if ( inode = = NULL )
2007-11-01 21:03:01 +03:00
return NULL ;
2007-10-27 03:40:20 +04:00
xid = GetXid ( ) ;
2008-03-15 01:37:16 +03:00
if ( pfid = = NULL )
open_file = find_readable_file ( CIFS_I ( inode ) ) ;
else
fid = * pfid ;
2007-10-27 03:40:20 +04:00
sb = inode - > i_sb ;
if ( sb = = NULL ) {
FreeXid ( xid ) ;
2007-11-01 21:03:01 +03:00
return NULL ;
2007-10-27 03:40:20 +04:00
}
cifs_sb = CIFS_SB ( sb ) ;
if ( open_file ) {
2008-04-29 04:06:05 +04:00
unlock_file = true ;
2007-10-27 03:40:20 +04:00
fid = open_file - > netfid ;
2008-03-15 01:37:16 +03:00
} else if ( pfid = = NULL ) {
2008-04-29 04:06:05 +04:00
bool oplock = false ;
2007-10-27 03:40:20 +04:00
/* open file */
rc = CIFSSMBOpen ( xid , cifs_sb - > tcon , path , FILE_OPEN ,
2007-10-31 07:54:42 +03:00
READ_CONTROL , 0 , & fid , & oplock , NULL ,
2007-10-27 03:40:20 +04:00
cifs_sb - > local_nls , cifs_sb - > mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR ) ;
if ( rc ! = 0 ) {
cERROR ( 1 , ( " Unable to open file to get ACL " ) ) ;
FreeXid ( xid ) ;
2007-11-01 21:03:01 +03:00
return NULL ;
2007-10-27 03:40:20 +04:00
}
}
2007-11-01 21:03:01 +03:00
rc = CIFSSMBGetCIFSACL ( xid , cifs_sb - > tcon , fid , & pntsd , pacllen ) ;
cFYI ( 1 , ( " GetCIFSACL rc = %d ACL len %d " , rc , * pacllen ) ) ;
2008-04-29 04:06:05 +04:00
if ( unlock_file = = true ) /* find_readable_file increments ref count */
2007-10-27 03:40:20 +04:00
atomic_dec ( & open_file - > wrtPending ) ;
2008-03-15 01:37:16 +03:00
else if ( pfid = = NULL ) /* if opened above we have to close the handle */
2007-10-27 03:40:20 +04:00
CIFSSMBClose ( xid , cifs_sb - > tcon , fid ) ;
2008-03-15 01:37:16 +03:00
/* else handle was passed in by caller */
2007-10-27 03:40:20 +04:00
2007-11-01 21:03:01 +03:00
FreeXid ( xid ) ;
return pntsd ;
}
2007-12-31 10:47:21 +03:00
/* Set an ACL on the server */
static int set_cifs_acl ( struct cifs_ntsd * pnntsd , __u32 acllen ,
struct inode * inode , const char * path )
{
struct cifsFileInfo * open_file ;
2008-04-29 04:06:05 +04:00
bool unlock_file = false ;
2007-12-31 10:47:21 +03:00
int xid ;
int rc = - EIO ;
__u16 fid ;
struct super_block * sb ;
struct cifs_sb_info * cifs_sb ;
2008-02-12 23:32:36 +03:00
cFYI ( DBG2 , ( " set ACL for %s from mode 0x%x " , path , inode - > i_mode ) ) ;
2007-12-31 10:47:21 +03:00
if ( ! inode )
return ( rc ) ;
sb = inode - > i_sb ;
if ( sb = = NULL )
return ( rc ) ;
cifs_sb = CIFS_SB ( sb ) ;
xid = GetXid ( ) ;
open_file = find_readable_file ( CIFS_I ( inode ) ) ;
if ( open_file ) {
2008-04-29 04:06:05 +04:00
unlock_file = true ;
2007-12-31 10:47:21 +03:00
fid = open_file - > netfid ;
} else {
2008-04-29 04:06:05 +04:00
int oplock = 0 ;
2007-12-31 10:47:21 +03:00
/* open file */
rc = CIFSSMBOpen ( xid , cifs_sb - > tcon , path , FILE_OPEN ,
WRITE_DAC , 0 , & fid , & oplock , NULL ,
cifs_sb - > local_nls , cifs_sb - > mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR ) ;
if ( rc ! = 0 ) {
cERROR ( 1 , ( " Unable to open file to set ACL " ) ) ;
FreeXid ( xid ) ;
return ( rc ) ;
}
}
rc = CIFSSMBSetCIFSACL ( xid , cifs_sb - > tcon , fid , pnntsd , acllen ) ;
2008-02-12 23:32:36 +03:00
cFYI ( DBG2 , ( " SetCIFSACL rc = %d " , rc ) ) ;
2008-04-29 04:06:05 +04:00
if ( unlock_file )
2007-12-31 10:47:21 +03:00
atomic_dec ( & open_file - > wrtPending ) ;
else
CIFSSMBClose ( xid , cifs_sb - > tcon , fid ) ;
FreeXid ( xid ) ;
return ( rc ) ;
}
2007-11-01 21:03:01 +03:00
/* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */
2008-03-15 01:37:16 +03:00
void acl_to_uid_mode ( struct inode * inode , const char * path , const __u16 * pfid )
2007-11-01 21:03:01 +03:00
{
struct cifs_ntsd * pntsd = NULL ;
u32 acllen = 0 ;
int rc = 0 ;
2008-02-12 23:32:36 +03:00
cFYI ( DBG2 , ( " converting ACL to mode for %s " , path ) ) ;
2008-03-15 01:37:16 +03:00
pntsd = get_cifs_acl ( & acllen , inode , path , pfid ) ;
2007-11-01 21:03:01 +03:00
/* if we can retrieve the ACL, now parse Access Control Entries, ACEs */
if ( pntsd )
2007-10-27 03:40:20 +04:00
rc = parse_sec_desc ( pntsd , acllen , inode ) ;
2007-11-01 21:03:01 +03:00
if ( rc )
cFYI ( 1 , ( " parse sec desc failed rc = %d " , rc ) ) ;
2007-10-27 03:40:20 +04:00
kfree ( pntsd ) ;
return ;
}
2007-10-31 07:54:42 +03:00
2007-11-01 21:03:01 +03:00
/* Convert mode bits to an ACL so we can update the ACL on the server */
2007-12-31 10:47:21 +03:00
int mode_to_acl ( struct inode * inode , const char * path , __u64 nmode )
2007-10-31 07:54:42 +03:00
{
int rc = 0 ;
2008-04-10 00:55:31 +04:00
__u32 secdesclen = 0 ;
2007-12-31 10:47:21 +03:00
struct cifs_ntsd * pntsd = NULL ; /* acl obtained from server */
struct cifs_ntsd * pnntsd = NULL ; /* modified acl to be sent to server */
2007-10-31 07:54:42 +03:00
2008-02-12 23:32:36 +03:00
cFYI ( DBG2 , ( " set ACL from mode for %s " , path ) ) ;
2007-10-31 07:54:42 +03:00
/* Get the security descriptor */
2008-04-10 00:55:31 +04:00
pntsd = get_cifs_acl ( & secdesclen , inode , path , NULL ) ;
2007-10-31 07:54:42 +03:00
2007-12-31 10:47:21 +03:00
/* Add three ACEs for owner, group, everyone getting rid of
other ACEs as chmod disables ACEs and set the security descriptor */
2007-10-31 07:54:42 +03:00
2007-12-31 10:47:21 +03:00
if ( pntsd ) {
/* allocate memory for the smb header,
set security descriptor request security descriptor
parameters , and secuirty descriptor itself */
2007-10-31 07:54:42 +03:00
2008-04-10 00:55:31 +04:00
secdesclen = secdesclen < DEFSECDESCLEN ?
DEFSECDESCLEN : secdesclen ;
pnntsd = kmalloc ( secdesclen , GFP_KERNEL ) ;
2007-12-31 10:47:21 +03:00
if ( ! pnntsd ) {
cERROR ( 1 , ( " Unable to allocate security descriptor " ) ) ;
kfree ( pntsd ) ;
return ( - ENOMEM ) ;
}
2007-11-01 21:03:01 +03:00
2008-04-10 00:55:31 +04:00
rc = build_sec_desc ( pntsd , pnntsd , inode , nmode ) ;
2007-12-31 10:47:21 +03:00
2008-02-12 23:32:36 +03:00
cFYI ( DBG2 , ( " build_sec_desc rc: %d " , rc ) ) ;
2007-12-31 10:47:21 +03:00
if ( ! rc ) {
/* Set the security descriptor */
2008-04-10 00:55:31 +04:00
rc = set_cifs_acl ( pnntsd , secdesclen , inode , path ) ;
2008-02-12 23:32:36 +03:00
cFYI ( DBG2 , ( " set_cifs_acl rc: %d " , rc ) ) ;
2007-12-31 10:47:21 +03:00
}
kfree ( pnntsd ) ;
kfree ( pntsd ) ;
}
return ( rc ) ;
2007-10-31 07:54:42 +03:00
}
2007-10-12 08:11:59 +04:00
# endif /* CONFIG_CIFS_EXPERIMENTAL */