kacl.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2005 Till Adam <adam@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017    Boston, MA 02110-1301, USA.
00018 */
00019 // $Id: kacl.cpp 424977 2005-06-13 15:13:22Z tilladam $
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif
00024 
00025 #include <sys/types.h>
00026 #include <pwd.h>
00027 #include <grp.h>
00028 #include <sys/stat.h>
00029 #ifdef USE_POSIX_ACL
00030 #ifdef HAVE_NON_POSIX_ACL_EXTENSIONS
00031 #include <acl/libacl.h>
00032 #else
00033 #include <posixacladdons.h>
00034 #endif
00035 #endif
00036 #include <qintdict.h>
00037 
00038 #include <kdebug.h>
00039 
00040 #include "kacl.h"
00041 
00042 
00043 #ifdef USE_POSIX_ACL
00044 static void printACL( acl_t acl, const QString &comment );
00045 static QString aclAsString(const acl_t acl);
00046 #endif
00047 
00048 class KACL::KACLPrivate {
00049 public:
00050     KACLPrivate() : m_acl( 0 ) { init(); }
00051 #ifdef USE_POSIX_ACL
00052     KACLPrivate( acl_t acl )
00053         : m_acl( acl ) { init(); }
00054     ~KACLPrivate() { if ( m_acl ) acl_free( m_acl ); }
00055 #endif
00056     void init() {
00057         m_usercache.setAutoDelete( true );
00058         m_groupcache.setAutoDelete( true );
00059     }
00060     // helpers
00061 #ifdef USE_POSIX_ACL
00062     bool setMaskPermissions( unsigned short v );
00063     QString getUserName( uid_t uid ) const;
00064     QString getGroupName( gid_t gid ) const;
00065     bool setAllUsersOrGroups( const QValueList< QPair<QString, unsigned short> > &list, acl_tag_t type );
00066     bool setNamedUserOrGroupPermissions( const QString& name, unsigned short permissions, acl_tag_t type );
00067 
00068     acl_t m_acl;
00069 #else
00070     int m_acl;
00071 #endif
00072     mutable QIntDict<QString> m_usercache;
00073     mutable QIntDict<QString> m_groupcache;
00074 };
00075 
00076 KACL::KACL( const QString &aclString )
00077     : d( new KACLPrivate )
00078 {
00079     setACL( aclString );
00080 }
00081 
00082 KACL::KACL( mode_t basePermissions )
00083 #ifdef USE_POSIX_ACL
00084     : d( new KACLPrivate( acl_from_mode( basePermissions ) ) )
00085 #else
00086     : d( new KACLPrivate )
00087 #endif
00088 {
00089 #ifndef USE_POSIX_ACL
00090     Q_UNUSED( basePermissions );
00091 #endif
00092 }
00093 
00094 KACL::KACL()
00095     : d( new KACLPrivate )
00096 {
00097 }
00098 
00099 KACL::KACL( const KACL& rhs )
00100     : d( new KACLPrivate )
00101 {
00102     setACL( rhs.asString() );
00103 }
00104 
00105 KACL::~KACL()
00106 {
00107     delete d;
00108 }
00109 
00110 bool KACL::operator==( const KACL& rhs ) const {
00111 #ifdef USE_POSIX_ACL
00112     return ( acl_cmp( d->m_acl, rhs.d->m_acl ) == 0 );
00113 #else
00114     Q_UNUSED( rhs );
00115     return true;
00116 #endif
00117 }
00118 
00119 bool KACL::isValid() const
00120 {
00121     bool valid = false;
00122 #ifdef USE_POSIX_ACL
00123     if ( d->m_acl ) {
00124         valid = ( acl_valid( d->m_acl ) == 0 );
00125     }
00126 #endif
00127     return valid;
00128 }
00129 
00130 bool KACL::isExtended() const
00131 {
00132 #ifdef USE_POSIX_ACL
00133     return ( acl_equiv_mode( d->m_acl, NULL ) != 0 );
00134 #else
00135     return false;
00136 #endif
00137 }
00138 
00139 #ifdef USE_POSIX_ACL
00140 static acl_entry_t entryForTag( acl_t acl, acl_tag_t tag )
00141 {
00142     acl_entry_t entry;
00143     int ret = acl_get_entry( acl, ACL_FIRST_ENTRY, &entry );
00144     while ( ret == 1 ) {
00145         acl_tag_t currentTag;
00146         acl_get_tag_type( entry, &currentTag );
00147         if ( currentTag == tag )
00148             return entry;
00149         ret = acl_get_entry( acl, ACL_NEXT_ENTRY, &entry );
00150     }
00151     return 0;
00152 }
00153 
00154 static unsigned short entryToPermissions( acl_entry_t entry )
00155 {
00156     if ( entry == 0 ) return 0;
00157     acl_permset_t permset;
00158     if ( acl_get_permset( entry, &permset ) != 0 ) return 0;
00159     return( acl_get_perm( permset, ACL_READ ) << 2 |
00160             acl_get_perm( permset, ACL_WRITE ) << 1 |
00161             acl_get_perm( permset, ACL_EXECUTE ) );
00162 }
00163 
00164 static void permissionsToEntry( acl_entry_t entry, unsigned short v )
00165 {
00166     if ( entry == 0 ) return;
00167     acl_permset_t permset;
00168     if ( acl_get_permset( entry, &permset ) != 0 ) return;
00169     acl_clear_perms( permset );
00170     if ( v & 4 ) acl_add_perm( permset, ACL_READ );
00171     if ( v & 2 ) acl_add_perm( permset, ACL_WRITE );
00172     if ( v & 1 ) acl_add_perm( permset, ACL_EXECUTE );
00173 }
00174 
00175 static void printACL( acl_t acl, const QString &comment )
00176 {
00177     kdDebug() << comment << aclAsString( acl ) << endl;
00178 }
00179 
00180 static int getUidForName( const QString& name )
00181 {
00182     struct passwd *user = getpwnam( name.latin1() );
00183     if ( user )
00184         return user->pw_uid;
00185     else
00186         return -1;
00187 }
00188 
00189 static int getGidForName( const QString& name )
00190 {
00191     struct group *group = getgrnam( name.latin1() );
00192     if ( group )
00193         return group->gr_gid;
00194     else
00195         return -1;
00196 }
00197 #endif
00198 // ------------------ begin API implementation ------------
00199 
00200 unsigned short KACL::ownerPermissions() const
00201 {
00202 #ifdef USE_POSIX_ACL
00203     return entryToPermissions( entryForTag( d->m_acl, ACL_USER_OBJ ) );
00204 #else
00205     return 0;
00206 #endif
00207 }
00208 
00209 bool KACL::setOwnerPermissions( unsigned short v )
00210 {
00211 #ifdef USE_POSIX_ACL
00212     permissionsToEntry( entryForTag( d->m_acl, ACL_USER_OBJ ), v );
00213 #else
00214     Q_UNUSED( v );
00215 #endif
00216     return true;
00217 }
00218 
00219 unsigned short KACL::owningGroupPermissions() const
00220 {
00221 #ifdef USE_POSIX_ACL
00222     return entryToPermissions( entryForTag( d->m_acl, ACL_GROUP_OBJ ) );
00223 #else
00224     return 0;
00225 #endif
00226 }
00227 
00228 bool KACL::setOwningGroupPermissions( unsigned short v )
00229 {
00230 #ifdef USE_POSIX_ACL
00231     permissionsToEntry( entryForTag( d->m_acl, ACL_GROUP_OBJ ), v );
00232 #else
00233     Q_UNUSED( v );
00234 #endif
00235     return true;
00236 }
00237 
00238 unsigned short KACL::othersPermissions() const
00239 {
00240 #ifdef USE_POSIX_ACL
00241     return entryToPermissions( entryForTag( d->m_acl, ACL_OTHER ) );
00242 #else
00243     return 0;
00244 #endif
00245 }
00246 
00247 bool KACL::setOthersPermissions( unsigned short v )
00248 {
00249 #ifdef USE_POSIX_ACL
00250     permissionsToEntry( entryForTag( d->m_acl, ACL_OTHER ), v );
00251 #else
00252     Q_UNUSED( v );
00253 #endif
00254     return true;
00255 }
00256 
00257 mode_t KACL::basePermissions() const
00258 {
00259     mode_t perms( 0 );
00260 #ifdef USE_POSIX_ACL
00261     if ( ownerPermissions() & ACL_READ ) perms |= S_IRUSR;
00262     if ( ownerPermissions() & ACL_WRITE ) perms |= S_IWUSR;
00263     if ( ownerPermissions() & ACL_EXECUTE ) perms |= S_IXUSR;
00264     if ( owningGroupPermissions() & ACL_READ ) perms |= S_IRGRP;
00265     if ( owningGroupPermissions() & ACL_WRITE ) perms |= S_IWGRP;
00266     if ( owningGroupPermissions() & ACL_EXECUTE ) perms |= S_IXGRP;
00267     if ( othersPermissions() & ACL_READ ) perms |= S_IROTH;
00268     if ( othersPermissions() & ACL_WRITE ) perms |= S_IWOTH;
00269     if ( othersPermissions() & ACL_EXECUTE ) perms |= S_IXOTH;
00270 #endif
00271    return perms;
00272 }
00273 
00274 unsigned short KACL::maskPermissions( bool &exists ) const
00275 {
00276     exists = true;
00277 #ifdef USE_POSIX_ACL
00278     acl_entry_t entry = entryForTag( d->m_acl, ACL_MASK );
00279     if ( entry == 0 ) {
00280         exists = false;
00281         return 0;
00282     }
00283     return entryToPermissions( entry );
00284 #else
00285     return 0;
00286 #endif
00287 }
00288 
00289 #ifdef USE_POSIX_ACL
00290 bool KACL::KACLPrivate::setMaskPermissions( unsigned short v )
00291 {
00292     permissionsToEntry( entryForTag( m_acl, ACL_MASK ), v );
00293     return true;
00294 }
00295 #endif
00296 
00297 bool KACL::setMaskPermissions( unsigned short v )
00298 {
00299 #ifdef USE_POSIX_ACL
00300     return d->setMaskPermissions( v );
00301 #else
00302     Q_UNUSED( v );
00303     return true;
00304 #endif
00305 }
00306 
00307 /**************************
00308  * Deal with named users  *
00309  **************************/
00310 unsigned short KACL::namedUserPermissions( const QString& name, bool *exists ) const
00311 {
00312 #ifdef USE_POSIX_ACL
00313     acl_entry_t entry;
00314     uid_t id;
00315     *exists = false;
00316     int ret = acl_get_entry( d->m_acl, ACL_FIRST_ENTRY, &entry );
00317     while ( ret == 1 ) {
00318         acl_tag_t currentTag;
00319         acl_get_tag_type( entry, &currentTag );
00320         if ( currentTag ==  ACL_USER ) {
00321             id = *( (uid_t*) acl_get_qualifier( entry ) );
00322             if ( d->getUserName( id ) == name ) {
00323                 *exists = true;
00324                 return entryToPermissions( entry );
00325             }
00326         }
00327         ret = acl_get_entry( d->m_acl, ACL_NEXT_ENTRY, &entry );
00328     }
00329 #else
00330     Q_UNUSED( name );
00331     Q_UNUSED( exists );
00332 #endif
00333     return 0;
00334 }
00335 
00336 #ifdef USE_POSIX_ACL
00337 bool KACL::KACLPrivate::setNamedUserOrGroupPermissions( const QString& name, unsigned short permissions, acl_tag_t type )
00338 {
00339     bool allIsWell = true;
00340     acl_t newACL = acl_dup( m_acl );
00341     acl_entry_t entry;
00342     bool createdNewEntry = false;
00343     bool found = false;
00344     int ret = acl_get_entry( newACL, ACL_FIRST_ENTRY, &entry );
00345     while ( ret == 1 ) {
00346         acl_tag_t currentTag;
00347         acl_get_tag_type( entry, &currentTag );
00348         if ( currentTag == type ) {
00349             int id = * (int*)acl_get_qualifier( entry );
00350             const QString entryName = type == ACL_USER? getUserName( id ): getGroupName( id );
00351             if ( entryName == name ) {
00352               // found him, update
00353               permissionsToEntry( entry, permissions );
00354               found = true;
00355               break;
00356             }
00357         }
00358         ret = acl_get_entry( newACL, ACL_NEXT_ENTRY, &entry );
00359     }
00360     if ( !found ) {
00361         acl_create_entry( &newACL, &entry );
00362         acl_set_tag_type(  entry, type );
00363         int id = type == ACL_USER? getUidForName( name ): getGidForName( name );
00364         if ( id == -1 ||  acl_set_qualifier( entry, &id ) != 0 ) {
00365             acl_delete_entry( newACL, entry );
00366             allIsWell = false;
00367         } else {
00368             permissionsToEntry( entry, permissions );
00369             createdNewEntry = true;
00370         }
00371     }
00372     if ( allIsWell && createdNewEntry ) {
00373         // 23.1.1 of 1003.1e states that as soon as there is a named user or
00374         // named group entry, there needs to be a mask entry as well, so add
00375         // one.
00376         setMaskPermissions( acl_calc_mask( &newACL ) );
00377     }
00378 
00379     if ( !allIsWell || acl_valid( newACL ) != 0 ) {
00380         acl_free( newACL );
00381         allIsWell = false;
00382     } else {
00383         acl_free( m_acl );
00384         m_acl = newACL;
00385     }
00386     return allIsWell;
00387 }
00388 #endif
00389 
00390 bool KACL::setNamedUserPermissions( const QString& name, unsigned short permissions )
00391 {
00392 #ifdef USE_POSIX_ACL
00393     return d->setNamedUserOrGroupPermissions( name, permissions, ACL_USER );
00394 #else
00395     Q_UNUSED( name );
00396     Q_UNUSED( permissions );
00397     return true;
00398 #endif
00399 }
00400 
00401 ACLUserPermissionsList KACL::allUserPermissions() const
00402 {
00403     ACLUserPermissionsList list;
00404 #ifdef USE_POSIX_ACL
00405     acl_entry_t entry;
00406     uid_t id;
00407     int ret = acl_get_entry( d->m_acl, ACL_FIRST_ENTRY, &entry );
00408     while ( ret == 1 ) {
00409         acl_tag_t currentTag;
00410         acl_get_tag_type( entry, &currentTag );
00411         if ( currentTag ==  ACL_USER ) {
00412             id = *( (uid_t*) acl_get_qualifier( entry ) );
00413             QString name = d->getUserName( id );
00414             unsigned short permissions = entryToPermissions( entry );
00415             ACLUserPermissions pair = qMakePair( name, permissions );
00416             list.append( pair );
00417         }
00418         ret = acl_get_entry( d->m_acl, ACL_NEXT_ENTRY, &entry );
00419     }
00420 #endif
00421     return list;
00422 }
00423 
00424 #ifdef USE_POSIX_ACL
00425 bool KACL::KACLPrivate::setAllUsersOrGroups( const QValueList< QPair<QString, unsigned short> > &list, acl_tag_t type )
00426 {
00427     bool allIsWell = true;
00428     bool atLeastOneUserOrGroup = false;
00429 
00430     // make working copy, in case something goes wrong
00431     acl_t newACL = acl_dup( m_acl );
00432     acl_entry_t entry;
00433 
00434 //printACL( newACL, "Before cleaning: " );
00435     // clear user entries
00436     int ret = acl_get_entry( newACL, ACL_FIRST_ENTRY, &entry );
00437     while ( ret == 1 ) {
00438         acl_tag_t currentTag;
00439         acl_get_tag_type( entry, &currentTag );
00440         if ( currentTag ==  type ) {
00441             acl_delete_entry( newACL, entry );
00442             // we have to start from the beginning, the iterator is
00443             // invalidated, on deletion
00444             ret = acl_get_entry( newACL, ACL_FIRST_ENTRY, &entry );
00445         } else {
00446             ret = acl_get_entry( newACL, ACL_NEXT_ENTRY, &entry );
00447         }
00448     }
00449 //printACL( newACL, "After cleaning out entries: " );
00450 
00451     // now add the entries from the list
00452     QValueList< QPair<QString, unsigned short> >::const_iterator it = list.constBegin();
00453     while ( it != list.constEnd() ) {
00454         acl_create_entry( &newACL, &entry );
00455         acl_set_tag_type( entry, type );
00456         int id = type == ACL_USER? getUidForName( (*it).first):getGidForName( (*it).first );
00457         if ( id == -1 || acl_set_qualifier( entry, &id ) != 0 ) {
00458             // user or group doesn't exist => error
00459             acl_delete_entry( newACL, entry );
00460             allIsWell = false;
00461             break;
00462         } else {
00463             permissionsToEntry( entry, (*it).second );
00464             atLeastOneUserOrGroup = true;
00465         }
00466         ++it;
00467     }
00468 //printACL( newACL, "After adding entries: " );
00469     if ( allIsWell && atLeastOneUserOrGroup ) {
00470         // 23.1.1 of 1003.1e states that as soon as there is a named user or
00471         // named group entry, there needs to be a mask entry as well, so add
00472         // one.
00473         setMaskPermissions( acl_calc_mask( &newACL ) );
00474     }
00475     if ( allIsWell && ( acl_valid( newACL ) == 0 ) ) {
00476         acl_free( m_acl );
00477         m_acl = newACL;
00478     } else {
00479         acl_free( newACL );
00480     }
00481     return allIsWell;
00482 }
00483 #endif
00484 
00485 bool KACL::setAllUserPermissions( const ACLUserPermissionsList &users )
00486 {
00487 #ifdef USE_POSIX_ACL
00488     return d->setAllUsersOrGroups( users, ACL_USER );
00489 #else
00490     Q_UNUSED( users );
00491     return true;
00492 #endif
00493 }
00494 
00495 
00496 /**************************
00497  * Deal with named groups  *
00498  **************************/
00499 
00500 unsigned short KACL::namedGroupPermissions( const QString& name, bool *exists ) const
00501 {
00502     *exists = false;
00503 #ifdef USE_POSIX_ACL
00504     acl_entry_t entry;
00505     gid_t id;
00506     int ret = acl_get_entry( d->m_acl, ACL_FIRST_ENTRY, &entry );
00507     while ( ret == 1 ) {
00508         acl_tag_t currentTag;
00509         acl_get_tag_type( entry, &currentTag );
00510         if ( currentTag ==  ACL_GROUP ) {
00511             id = *( (gid_t*) acl_get_qualifier( entry ) );
00512             if ( d->getGroupName( id ) == name ) {
00513                 *exists = true;
00514                 return entryToPermissions( entry );
00515             }
00516         }
00517         ret = acl_get_entry( d->m_acl, ACL_NEXT_ENTRY, &entry );
00518     }
00519 #else
00520     Q_UNUSED( name );
00521 #endif
00522     return 0;
00523 }
00524 
00525 bool KACL::setNamedGroupPermissions( const QString& name, unsigned short permissions )
00526 {
00527 #ifdef USE_POSIX_ACL
00528     return d->setNamedUserOrGroupPermissions( name, permissions, ACL_GROUP );
00529 #else
00530     Q_UNUSED( name );
00531     Q_UNUSED( permissions );
00532     return true;
00533 #endif
00534 }
00535 
00536 
00537 ACLGroupPermissionsList KACL::allGroupPermissions() const
00538 {
00539     ACLGroupPermissionsList list;
00540 #ifdef USE_POSIX_ACL
00541     acl_entry_t entry;
00542     gid_t id;
00543     int ret = acl_get_entry( d->m_acl, ACL_FIRST_ENTRY, &entry );
00544     while ( ret == 1 ) {
00545         acl_tag_t currentTag;
00546         acl_get_tag_type( entry, &currentTag );
00547         if ( currentTag ==  ACL_GROUP ) {
00548             id = *( (gid_t*) acl_get_qualifier( entry ) );
00549             QString name = d->getGroupName( id );
00550             unsigned short permissions = entryToPermissions( entry );
00551             ACLGroupPermissions pair = qMakePair( name, permissions );
00552             list.append( pair );
00553         }
00554         ret = acl_get_entry( d->m_acl, ACL_NEXT_ENTRY, &entry );
00555     }
00556 #endif
00557     return list;
00558 }
00559 
00560 bool KACL::setAllGroupPermissions( const ACLGroupPermissionsList &groups )
00561 {
00562 #ifdef USE_POSIX_ACL
00563     return d->setAllUsersOrGroups( groups, ACL_GROUP );
00564 #else
00565     Q_UNUSED( groups );
00566     return true;
00567 #endif
00568 }
00569 
00570 /**************************
00571  * from and to string     *
00572  **************************/
00573 
00574 bool KACL::setACL( const QString &aclStr )
00575 {
00576     bool ret = false;
00577 #ifdef USE_POSIX_ACL
00578     if ( aclStr.isEmpty() )
00579         return false;
00580 
00581     acl_t temp = acl_from_text( aclStr.latin1() );
00582     if ( acl_valid( temp ) != 0 ) {
00583         // TODO errno is set, what to do with it here?
00584         acl_free( temp );
00585     } else {
00586         if ( d->m_acl )
00587             acl_free( d->m_acl );
00588         d->m_acl = temp;
00589         ret = true;
00590     }
00591 #else
00592     Q_UNUSED( aclStr );
00593 #endif
00594     return ret;
00595 }
00596 
00597 QString KACL::asString() const
00598 {
00599 #ifdef USE_POSIX_ACL
00600     return aclAsString( d->m_acl );
00601 #else
00602     return QString::null;
00603 #endif
00604 }
00605 
00606 
00607 // helpers
00608 
00609 #ifdef USE_POSIX_ACL
00610 QString KACL::KACLPrivate::getUserName( uid_t uid ) const
00611 {
00612     QString *temp;
00613     temp = m_usercache.find( uid );
00614     if ( !temp ) {
00615         struct passwd *user = getpwuid( uid );
00616         if ( user ) {
00617             m_usercache.insert( uid, new QString(QString::fromLatin1(user->pw_name)) );
00618             return QString::fromLatin1( user->pw_name );
00619         }
00620         else
00621             return QString::number( uid );
00622     }
00623     else
00624         return *temp;
00625 }
00626 
00627 
00628 QString KACL::KACLPrivate::getGroupName( gid_t gid ) const
00629 {
00630     QString *temp;
00631     temp = m_groupcache.find( gid );
00632     if ( !temp ) {
00633         struct group *grp = getgrgid( gid );
00634         if ( grp ) {
00635             m_groupcache.insert( gid, new QString(QString::fromLatin1(grp->gr_name)) );
00636             return QString::fromLatin1( grp->gr_name );
00637         }
00638         else
00639             return QString::number( gid );
00640     }
00641     else
00642         return *temp;
00643 }
00644 
00645 static QString aclAsString(const acl_t acl)
00646 {
00647     char *aclString = acl_to_text( acl, 0 );
00648     QString ret = QString::fromLatin1( aclString );
00649     acl_free( (void*)aclString );
00650     return ret;
00651 }
00652 
00653 
00654 #endif
00655 
00656 void KACL::virtual_hook( int, void* )
00657 { /*BASE::virtual_hook( id, data );*/ }
00658 
00659 QDataStream & operator<< ( QDataStream & s, const KACL & a )
00660 {
00661     s << a.asString();
00662     return s;
00663 }
00664 
00665 QDataStream & operator>> ( QDataStream & s, KACL & a )
00666 {
00667     QString str;
00668     s >> str;
00669     a.setACL( str );
00670     return s;
00671 }
00672 
00673 // vim:set ts=8 sw=4:
KDE Home | KDE Accessibility Home | Description of Access Keys