diff options
Diffstat (limited to 'tdeio/tdeio/kacl.cpp')
-rw-r--r-- | tdeio/tdeio/kacl.cpp | 682 |
1 files changed, 682 insertions, 0 deletions
diff --git a/tdeio/tdeio/kacl.cpp b/tdeio/tdeio/kacl.cpp new file mode 100644 index 000000000..432a50d31 --- /dev/null +++ b/tdeio/tdeio/kacl.cpp @@ -0,0 +1,682 @@ +/* This file is part of the KDE project + Copyright (C) 2005 Till Adam <adam@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +// $Id: kacl.cpp 424977 2005-06-13 15:13:22Z tilladam $ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> +#include <sys/stat.h> +#ifdef USE_POSIX_ACL +#ifdef HAVE_NON_POSIX_ACL_EXTENSIONS +#include <acl/libacl.h> +#else +#include <posixacladdons.h> +#endif +#endif +#include <tqintdict.h> + +#include <kdebug.h> + +#include "kacl.h" + + +#ifdef USE_POSIX_ACL +static void printACL( acl_t acl, const TQString &comment ); +static TQString aclAsString(const acl_t acl); +#endif + +class KACL::KACLPrivate { +public: + KACLPrivate() : m_acl( 0 ) { init(); } +#ifdef USE_POSIX_ACL + KACLPrivate( acl_t acl ) + : m_acl( acl ) { init(); } + ~KACLPrivate() { if ( m_acl ) acl_free( m_acl ); } +#endif + void init() { + m_usercache.setAutoDelete( true ); + m_groupcache.setAutoDelete( true ); + } + // helpers +#ifdef USE_POSIX_ACL + bool setMaskPermissions( unsigned short v ); + TQString getUserName( uid_t uid ) const; + TQString getGroupName( gid_t gid ) const; + bool setAllUsersOrGroups( const TQValueList< QPair<TQString, unsigned short> > &list, acl_tag_t type ); + bool setNamedUserOrGroupPermissions( const TQString& name, unsigned short permissions, acl_tag_t type ); + + acl_t m_acl; +#else + int m_acl; +#endif + mutable TQIntDict<TQString> m_usercache; + mutable TQIntDict<TQString> m_groupcache; +}; + +KACL::KACL( const TQString &aclString ) + : d( new KACLPrivate ) +{ + setACL( aclString ); +} + +KACL::KACL( mode_t basePermissions ) +#ifdef USE_POSIX_ACL + : d( new KACLPrivate( acl_from_mode( basePermissions ) ) ) +#else + : d( new KACLPrivate ) +#endif +{ +#ifndef USE_POSIX_ACL + Q_UNUSED( basePermissions ); +#endif +} + +KACL::KACL() + : d( new KACLPrivate ) +{ +} + +KACL::KACL( const KACL& rhs ) + : d( new KACLPrivate ) +{ + setACL( rhs.asString() ); +} + +KACL::~KACL() +{ + delete d; +} + +bool KACL::operator==( const KACL& rhs ) const { +#ifdef USE_POSIX_ACL + return ( acl_cmp( d->m_acl, rhs.d->m_acl ) == 0 ); +#else + Q_UNUSED( rhs ); + return true; +#endif +} + +bool KACL::isValid() const +{ + bool valid = false; +#ifdef USE_POSIX_ACL + if ( d->m_acl ) { + valid = ( acl_valid( d->m_acl ) == 0 ); + } +#endif + return valid; +} + +bool KACL::isExtended() const +{ +#ifdef USE_POSIX_ACL + return ( acl_equiv_mode( d->m_acl, NULL ) != 0 ); +#else + return false; +#endif +} + +#ifdef USE_POSIX_ACL +static acl_entry_t entryForTag( acl_t acl, acl_tag_t tag ) +{ + acl_entry_t entry; + int ret = acl_get_entry( acl, ACL_FIRST_ENTRY, &entry ); + while ( ret == 1 ) { + acl_tag_t currentTag; + acl_get_tag_type( entry, ¤tTag ); + if ( currentTag == tag ) + return entry; + ret = acl_get_entry( acl, ACL_NEXT_ENTRY, &entry ); + } + return 0; +} + +static unsigned short entryToPermissions( acl_entry_t entry ) +{ + if ( entry == 0 ) return 0; + acl_permset_t permset; + if ( acl_get_permset( entry, &permset ) != 0 ) return 0; + return( acl_get_perm( permset, ACL_READ ) << 2 | + acl_get_perm( permset, ACL_WRITE ) << 1 | + acl_get_perm( permset, ACL_EXECUTE ) ); +} + +static void permissionsToEntry( acl_entry_t entry, unsigned short v ) +{ + if ( entry == 0 ) return; + acl_permset_t permset; + if ( acl_get_permset( entry, &permset ) != 0 ) return; + acl_clear_perms( permset ); + if ( v & 4 ) acl_add_perm( permset, ACL_READ ); + if ( v & 2 ) acl_add_perm( permset, ACL_WRITE ); + if ( v & 1 ) acl_add_perm( permset, ACL_EXECUTE ); +} + +static void printACL( acl_t acl, const TQString &comment ) +{ + kdDebug() << comment << aclAsString( acl ) << endl; +} + +static int getUidForName( const TQString& name ) +{ + struct passwd *user = getpwnam( name.latin1() ); + if ( user ) + return user->pw_uid; + else + return -1; +} + +static int getGidForName( const TQString& name ) +{ + struct group *group = getgrnam( name.latin1() ); + if ( group ) + return group->gr_gid; + else + return -1; +} +#endif +// ------------------ begin API implementation ------------ + +unsigned short KACL::ownerPermissions() const +{ +#ifdef USE_POSIX_ACL + return entryToPermissions( entryForTag( d->m_acl, ACL_USER_OBJ ) ); +#else + return 0; +#endif +} + +bool KACL::setOwnerPermissions( unsigned short v ) +{ +#ifdef USE_POSIX_ACL + permissionsToEntry( entryForTag( d->m_acl, ACL_USER_OBJ ), v ); +#else + Q_UNUSED( v ); +#endif + return true; +} + +unsigned short KACL::owningGroupPermissions() const +{ +#ifdef USE_POSIX_ACL + return entryToPermissions( entryForTag( d->m_acl, ACL_GROUP_OBJ ) ); +#else + return 0; +#endif +} + +bool KACL::setOwningGroupPermissions( unsigned short v ) +{ +#ifdef USE_POSIX_ACL + permissionsToEntry( entryForTag( d->m_acl, ACL_GROUP_OBJ ), v ); +#else + Q_UNUSED( v ); +#endif + return true; +} + +unsigned short KACL::othersPermissions() const +{ +#ifdef USE_POSIX_ACL + return entryToPermissions( entryForTag( d->m_acl, ACL_OTHER ) ); +#else + return 0; +#endif +} + +bool KACL::setOthersPermissions( unsigned short v ) +{ +#ifdef USE_POSIX_ACL + permissionsToEntry( entryForTag( d->m_acl, ACL_OTHER ), v ); +#else + Q_UNUSED( v ); +#endif + return true; +} + +mode_t KACL::basePermissions() const +{ + mode_t perms( 0 ); +#ifdef USE_POSIX_ACL + if ( ownerPermissions() & ACL_READ ) perms |= S_IRUSR; + if ( ownerPermissions() & ACL_WRITE ) perms |= S_IWUSR; + if ( ownerPermissions() & ACL_EXECUTE ) perms |= S_IXUSR; + if ( owningGroupPermissions() & ACL_READ ) perms |= S_IRGRP; + if ( owningGroupPermissions() & ACL_WRITE ) perms |= S_IWGRP; + if ( owningGroupPermissions() & ACL_EXECUTE ) perms |= S_IXGRP; + if ( othersPermissions() & ACL_READ ) perms |= S_IROTH; + if ( othersPermissions() & ACL_WRITE ) perms |= S_IWOTH; + if ( othersPermissions() & ACL_EXECUTE ) perms |= S_IXOTH; +#endif + return perms; +} + +unsigned short KACL::maskPermissions( bool &exists ) const +{ + exists = true; +#ifdef USE_POSIX_ACL + acl_entry_t entry = entryForTag( d->m_acl, ACL_MASK ); + if ( entry == 0 ) { + exists = false; + return 0; + } + return entryToPermissions( entry ); +#else + return 0; +#endif +} + +#ifdef USE_POSIX_ACL +bool KACL::KACLPrivate::setMaskPermissions( unsigned short v ) +{ + acl_entry_t entry = entryForTag( m_acl, ACL_MASK ); + if ( entry == 0 ) { + acl_create_entry( &m_acl, &entry ); + acl_set_tag_type( entry, ACL_MASK ); + } + permissionsToEntry( entry, v ); + return true; +} +#endif + +bool KACL::setMaskPermissions( unsigned short v ) +{ +#ifdef USE_POSIX_ACL + return d->setMaskPermissions( v ); +#else + Q_UNUSED( v ); + return true; +#endif +} + +/************************** + * Deal with named users * + **************************/ +unsigned short KACL::namedUserPermissions( const TQString& name, bool *exists ) const +{ +#ifdef USE_POSIX_ACL + acl_entry_t entry; + uid_t id; + *exists = false; + int ret = acl_get_entry( d->m_acl, ACL_FIRST_ENTRY, &entry ); + while ( ret == 1 ) { + acl_tag_t currentTag; + acl_get_tag_type( entry, ¤tTag ); + if ( currentTag == ACL_USER ) { + id = *( (uid_t*) acl_get_qualifier( entry ) ); + if ( d->getUserName( id ) == name ) { + *exists = true; + return entryToPermissions( entry ); + } + } + ret = acl_get_entry( d->m_acl, ACL_NEXT_ENTRY, &entry ); + } +#else + Q_UNUSED( name ); + Q_UNUSED( exists ); +#endif + return 0; +} + +#ifdef USE_POSIX_ACL +bool KACL::KACLPrivate::setNamedUserOrGroupPermissions( const TQString& name, unsigned short permissions, acl_tag_t type ) +{ + bool allIsWell = true; + acl_t newACL = acl_dup( m_acl ); + acl_entry_t entry; + bool createdNewEntry = false; + bool found = false; + int ret = acl_get_entry( newACL, ACL_FIRST_ENTRY, &entry ); + while ( ret == 1 ) { + acl_tag_t currentTag; + acl_get_tag_type( entry, ¤tTag ); + if ( currentTag == type ) { + int id = * (int*)acl_get_qualifier( entry ); + const TQString entryName = type == ACL_USER? getUserName( id ): getGroupName( id ); + if ( entryName == name ) { + // found him, update + permissionsToEntry( entry, permissions ); + found = true; + break; + } + } + ret = acl_get_entry( newACL, ACL_NEXT_ENTRY, &entry ); + } + if ( !found ) { + acl_create_entry( &newACL, &entry ); + acl_set_tag_type( entry, type ); + int id = type == ACL_USER? getUidForName( name ): getGidForName( name ); + if ( id == -1 || acl_set_qualifier( entry, &id ) != 0 ) { + acl_delete_entry( newACL, entry ); + allIsWell = false; + } else { + permissionsToEntry( entry, permissions ); + createdNewEntry = true; + } + } + if ( allIsWell && createdNewEntry ) { + // 23.1.1 of 1003.1e states that as soon as there is a named user or + // named group entry, there needs to be a mask entry as well, so add + // one, if the user hasn't explicitely set one. + if ( entryForTag( newACL, ACL_MASK ) == 0 ) { + acl_calc_mask( &newACL ); + } + } + + if ( !allIsWell || acl_valid( newACL ) != 0 ) { + acl_free( newACL ); + allIsWell = false; + } else { + acl_free( m_acl ); + m_acl = newACL; + } + return allIsWell; +} +#endif + +bool KACL::setNamedUserPermissions( const TQString& name, unsigned short permissions ) +{ +#ifdef USE_POSIX_ACL + return d->setNamedUserOrGroupPermissions( name, permissions, ACL_USER ); +#else + Q_UNUSED( name ); + Q_UNUSED( permissions ); + return true; +#endif +} + +ACLUserPermissionsList KACL::allUserPermissions() const +{ + ACLUserPermissionsList list; +#ifdef USE_POSIX_ACL + acl_entry_t entry; + uid_t id; + int ret = acl_get_entry( d->m_acl, ACL_FIRST_ENTRY, &entry ); + while ( ret == 1 ) { + acl_tag_t currentTag; + acl_get_tag_type( entry, ¤tTag ); + if ( currentTag == ACL_USER ) { + id = *( (uid_t*) acl_get_qualifier( entry ) ); + TQString name = d->getUserName( id ); + unsigned short permissions = entryToPermissions( entry ); + ACLUserPermissions pair = qMakePair( name, permissions ); + list.append( pair ); + } + ret = acl_get_entry( d->m_acl, ACL_NEXT_ENTRY, &entry ); + } +#endif + return list; +} + +#ifdef USE_POSIX_ACL +bool KACL::KACLPrivate::setAllUsersOrGroups( const TQValueList< QPair<TQString, unsigned short> > &list, acl_tag_t type ) +{ + bool allIsWell = true; + bool atLeastOneUserOrGroup = false; + + // make working copy, in case something goes wrong + acl_t newACL = acl_dup( m_acl ); + acl_entry_t entry; + +//printACL( newACL, "Before cleaning: " ); + // clear user entries + int ret = acl_get_entry( newACL, ACL_FIRST_ENTRY, &entry ); + while ( ret == 1 ) { + acl_tag_t currentTag; + acl_get_tag_type( entry, ¤tTag ); + if ( currentTag == type ) { + acl_delete_entry( newACL, entry ); + // we have to start from the beginning, the iterator is + // invalidated, on deletion + ret = acl_get_entry( newACL, ACL_FIRST_ENTRY, &entry ); + } else { + ret = acl_get_entry( newACL, ACL_NEXT_ENTRY, &entry ); + } + } +//printACL( newACL, "After cleaning out entries: " ); + + // now add the entries from the list + TQValueList< QPair<TQString, unsigned short> >::const_iterator it = list.constBegin(); + while ( it != list.constEnd() ) { + acl_create_entry( &newACL, &entry ); + acl_set_tag_type( entry, type ); + int id = type == ACL_USER? getUidForName( (*it).first):getGidForName( (*it).first ); + if ( id == -1 || acl_set_qualifier( entry, &id ) != 0 ) { + // user or group doesn't exist => error + acl_delete_entry( newACL, entry ); + allIsWell = false; + break; + } else { + permissionsToEntry( entry, (*it).second ); + atLeastOneUserOrGroup = true; + } + ++it; + } +//printACL( newACL, "After adding entries: " ); + if ( allIsWell && atLeastOneUserOrGroup ) { + // 23.1.1 of 1003.1e states that as soon as there is a named user or + // named group entry, there needs to be a mask entry as well, so add + // one, if the user hasn't explicitely set one. + if ( entryForTag( newACL, ACL_MASK ) == 0 ) { + acl_calc_mask( &newACL ); + } + } + if ( allIsWell && ( acl_valid( newACL ) == 0 ) ) { + acl_free( m_acl ); + m_acl = newACL; + } else { + acl_free( newACL ); + } + return allIsWell; +} +#endif + +bool KACL::setAllUserPermissions( const ACLUserPermissionsList &users ) +{ +#ifdef USE_POSIX_ACL + return d->setAllUsersOrGroups( users, ACL_USER ); +#else + Q_UNUSED( users ); + return true; +#endif +} + + +/************************** + * Deal with named groups * + **************************/ + +unsigned short KACL::namedGroupPermissions( const TQString& name, bool *exists ) const +{ + *exists = false; +#ifdef USE_POSIX_ACL + acl_entry_t entry; + gid_t id; + int ret = acl_get_entry( d->m_acl, ACL_FIRST_ENTRY, &entry ); + while ( ret == 1 ) { + acl_tag_t currentTag; + acl_get_tag_type( entry, ¤tTag ); + if ( currentTag == ACL_GROUP ) { + id = *( (gid_t*) acl_get_qualifier( entry ) ); + if ( d->getGroupName( id ) == name ) { + *exists = true; + return entryToPermissions( entry ); + } + } + ret = acl_get_entry( d->m_acl, ACL_NEXT_ENTRY, &entry ); + } +#else + Q_UNUSED( name ); +#endif + return 0; +} + +bool KACL::setNamedGroupPermissions( const TQString& name, unsigned short permissions ) +{ +#ifdef USE_POSIX_ACL + return d->setNamedUserOrGroupPermissions( name, permissions, ACL_GROUP ); +#else + Q_UNUSED( name ); + Q_UNUSED( permissions ); + return true; +#endif +} + + +ACLGroupPermissionsList KACL::allGroupPermissions() const +{ + ACLGroupPermissionsList list; +#ifdef USE_POSIX_ACL + acl_entry_t entry; + gid_t id; + int ret = acl_get_entry( d->m_acl, ACL_FIRST_ENTRY, &entry ); + while ( ret == 1 ) { + acl_tag_t currentTag; + acl_get_tag_type( entry, ¤tTag ); + if ( currentTag == ACL_GROUP ) { + id = *( (gid_t*) acl_get_qualifier( entry ) ); + TQString name = d->getGroupName( id ); + unsigned short permissions = entryToPermissions( entry ); + ACLGroupPermissions pair = qMakePair( name, permissions ); + list.append( pair ); + } + ret = acl_get_entry( d->m_acl, ACL_NEXT_ENTRY, &entry ); + } +#endif + return list; +} + +bool KACL::setAllGroupPermissions( const ACLGroupPermissionsList &groups ) +{ +#ifdef USE_POSIX_ACL + return d->setAllUsersOrGroups( groups, ACL_GROUP ); +#else + Q_UNUSED( groups ); + return true; +#endif +} + +/************************** + * from and to string * + **************************/ + +bool KACL::setACL( const TQString &aclStr ) +{ + bool ret = false; +#ifdef USE_POSIX_ACL + if ( aclStr.isEmpty() ) + return false; + + acl_t temp = acl_from_text( aclStr.latin1() ); + if ( acl_valid( temp ) != 0 ) { + // TODO errno is set, what to do with it here? + acl_free( temp ); + } else { + if ( d->m_acl ) + acl_free( d->m_acl ); + d->m_acl = temp; + ret = true; + } +#else + Q_UNUSED( aclStr ); +#endif + return ret; +} + +TQString KACL::asString() const +{ +#ifdef USE_POSIX_ACL + return aclAsString( d->m_acl ); +#else + return TQString::null; +#endif +} + + +// helpers + +#ifdef USE_POSIX_ACL +TQString KACL::KACLPrivate::getUserName( uid_t uid ) const +{ + TQString *temp; + temp = m_usercache.find( uid ); + if ( !temp ) { + struct passwd *user = getpwuid( uid ); + if ( user ) { + m_usercache.insert( uid, new TQString(TQString::fromLatin1(user->pw_name)) ); + return TQString::fromLatin1( user->pw_name ); + } + else + return TQString::number( uid ); + } + else + return *temp; +} + + +TQString KACL::KACLPrivate::getGroupName( gid_t gid ) const +{ + TQString *temp; + temp = m_groupcache.find( gid ); + if ( !temp ) { + struct group *grp = getgrgid( gid ); + if ( grp ) { + m_groupcache.insert( gid, new TQString(TQString::fromLatin1(grp->gr_name)) ); + return TQString::fromLatin1( grp->gr_name ); + } + else + return TQString::number( gid ); + } + else + return *temp; +} + +static TQString aclAsString(const acl_t acl) +{ + char *aclString = acl_to_text( acl, 0 ); + TQString ret = TQString::fromLatin1( aclString ); + acl_free( (void*)aclString ); + return ret; +} + + +#endif + +void KACL::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + +TQDataStream & operator<< ( TQDataStream & s, const KACL & a ) +{ + s << a.asString(); + return s; +} + +TQDataStream & operator>> ( TQDataStream & s, KACL & a ) +{ + TQString str; + s >> str; + a.setACL( str ); + return s; +} + +// vim:set ts=8 sw=4: |