File indexing completed on 2024-05-05 04:51:37

0001 /*
0002     SPDX-FileCopyrightText: 2003-2009 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-FileCopyrightText: 2009 Michal Malek <michalm@jabster.pl>
0004     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
0005     SPDX-FileCopyrightText: 2009 Michal Malek <michalm@jabster.pl>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "k3bexternalbinpermissionmodel.h"
0011 #include "k3bexternalbinmanager.h"
0012 #include "k3bdefaultexternalprograms.h"
0013 #include "k3bglobals.h"
0014 
0015 #include <KLocalizedString>
0016 
0017 #include <QDebug>
0018 #include <QFile>
0019 #include <QFileInfo>
0020 #include <QList>
0021 #include <QSet>
0022 
0023 #include <sys/stat.h>
0024 
0025 
0026 namespace {
0027     
0028 bool shouldRunSuidRoot( const K3b::ExternalBin* bin )
0029 {
0030     //
0031     // Since kernel 2.6.8 older cdrecord versions are not able to use the SCSI subsystem when running suid root anymore
0032     // So for we ignore the suid root issue with kernel >= 2.6.8 and cdrecord < 2.01.01a02
0033     //
0034     // Some kernel version 2.6.16.something again introduced a problem here. Since I do not know the exact version
0035     // and a workaround was introduced in cdrecord 2.01.01a05 just use that version as the first for suid root.
0036     //
0037     // Seems as if cdrdao never had problems with suid root...
0038     //
0039 
0040     if( bin->name() == "cdrecord" ) {
0041         return ( K3b::simpleKernelVersion() < K3b::Version( 2, 6, 8 ) ||
0042                  bin->version() >= K3b::Version( 2, 1, 1, "a05" ) ||
0043                  bin->hasFeature( "wodim" ) );
0044     }
0045     else if( bin->name() == "cdrdao" ) {
0046         return true;
0047     }
0048     else if( bin->name() == "growisofs" ) {
0049         //
0050         // starting with 6.0 growiofs raises it's priority using nice(-20)
0051         // BUT: newer kernels have ridiculously low default memorylocked resource limit, which prevents privileged
0052         // users from starting growisofs 6.0 with "unable to anonymously mmap 33554432: Resource temporarily unavailable"
0053         // error message. Until Andy releases a version including a workaround we simply never configure growisofs suid root
0054         return false; // bin->version >= K3b::Version( 6, 0 );
0055     }
0056     else
0057         return false;
0058 }
0059 
0060 } // namespace
0061 
0062 namespace K3b {
0063 
0064 class ExternalBinPermissionModel::Private
0065 {
0066 public:
0067     explicit Private(ExternalBinManager const& ebm) : externalBinManager(ebm) {}
0068     ExternalBinManager const& externalBinManager;
0069     QString burningGroup;
0070     QList<const ExternalBin*> programs;
0071     QSet<const ExternalBin*> selectedPrograms;
0072 
0073     void buildProgramList();
0074     bool getProgramInfo( const ExternalBin* program,
0075                          QString& owner, QString& group, QString& wantedGroup,
0076                          int& perm, int& wantedPerm ) const;
0077     bool needChangePermissions( const ExternalBin* program ) const;
0078 };
0079 
0080 
0081 void ExternalBinPermissionModel::Private::buildProgramList()
0082 {
0083     programs.clear();
0084     const QMap<QString, ExternalProgram*>& map = externalBinManager.programs();
0085     for( QMap<QString, ExternalProgram*>::const_iterator it = map.constBegin(); it != map.constEnd(); ++it ) {
0086         if (it.key() == "cdrecord" ||
0087             it.key() == "cdrdao" ||
0088             it.key() == "growisofs") {
0089             programs += it.value()->bins();
0090         }
0091     }
0092     selectedPrograms = QSet<const ExternalBin *>(programs.begin(), programs.end());
0093 }
0094 
0095 
0096 bool ExternalBinPermissionModel::Private::getProgramInfo( const ExternalBin* program,
0097                                                   QString& owner, QString& group, QString& wantedGroup,
0098                                                   int& perm, int& wantedPerm ) const
0099 {
0100     // we need the uid bit which is not supported by QFileInfo
0101     struct stat s;
0102     if( ::stat( QFile::encodeName(program->path()), &s ) == 0 ) {
0103 
0104         QFileInfo fi( program->path() );
0105         owner = fi.owner();
0106         group = fi.group();
0107         perm = s.st_mode & 0007777;
0108 
0109         if( !burningGroup.isEmpty() && burningGroup != "root" )
0110             wantedGroup = burningGroup;
0111         else
0112             wantedGroup = "root";
0113 
0114         if( shouldRunSuidRoot( program ) ) {
0115             if( wantedGroup != "root" )
0116                 wantedPerm = 0004710;
0117             else
0118                 wantedPerm = 0004711;
0119         }
0120         else {
0121             if( wantedGroup != "root" )
0122                 wantedPerm = 0000750;
0123             else
0124                 wantedPerm = 0000755;
0125         }
0126 
0127         return true;
0128     }
0129     else {
0130         qDebug() << "(ExternalBinPermissionModel) unable to stat " << program->path();
0131         return false;
0132     }
0133 }
0134 
0135 
0136 bool ExternalBinPermissionModel::Private::needChangePermissions( const ExternalBin* program ) const
0137 {
0138     QString owner, group, wantedGroup;
0139     int perm, wantedPerm;
0140 
0141     if( getProgramInfo( program, owner, group, wantedGroup, perm, wantedPerm ) )
0142         return( perm != wantedPerm || owner != "root" || group != wantedGroup );
0143     else
0144         return false;
0145 }
0146 
0147 
0148 ExternalBinPermissionModel::ExternalBinPermissionModel(ExternalBinManager const& externalBinManager, QObject* parent)
0149 :
0150     QAbstractItemModel( parent ),
0151     d( new Private( externalBinManager ) )
0152 {
0153     d->buildProgramList();
0154 }
0155 
0156 
0157 ExternalBinPermissionModel::~ExternalBinPermissionModel()
0158 {
0159     delete d;
0160 }
0161 
0162 
0163 QList<HelperProgramItem> ExternalBinPermissionModel::selectedPrograms() const
0164 {
0165     QList<HelperProgramItem> selectedPrograms;
0166     Q_FOREACH( const ExternalBin* program, d->selectedPrograms )
0167     {
0168         if( d->needChangePermissions( program ) )
0169             selectedPrograms << HelperProgramItem( program->path(), shouldRunSuidRoot( program ) );
0170     }
0171     return selectedPrograms;
0172 }
0173 
0174 
0175 bool ExternalBinPermissionModel::changesNeeded() const
0176 {
0177     return !selectedPrograms().isEmpty();
0178 }
0179 
0180 
0181 QStringList ExternalBinPermissionModel::searchPaths() const
0182 {
0183     return d->externalBinManager.searchPath();
0184 }
0185 
0186 
0187 const QString& ExternalBinPermissionModel::burningGroup() const
0188 {
0189     return d->burningGroup;
0190 }
0191 
0192 
0193 const ExternalBin* ExternalBinPermissionModel::programForIndex( const QModelIndex& index ) const
0194 {
0195     if( index.isValid() )
0196         return static_cast<const ExternalBin*>( index.internalPointer() );
0197     else
0198         return 0;
0199 }
0200 
0201 
0202 QModelIndex ExternalBinPermissionModel::indexForProgram( const ExternalBin* program ) const
0203 {
0204     if( program != 0 && !d->programs.isEmpty() ) {
0205         int row = d->programs.indexOf( program );
0206         return createIndex( row, 0, const_cast<ExternalBin*>( program ) );
0207     }
0208     else
0209         return QModelIndex();
0210 }
0211 
0212 
0213 QVariant ExternalBinPermissionModel::data( const QModelIndex& index, int role ) const
0214 {
0215     if( const ExternalBin* program = programForIndex( index ) ) {
0216         if( role == Qt::DisplayRole ) {
0217             if( index.column() == ProgramColumn ) {
0218                 return program->path();
0219             } else {
0220                 QString owner, group, wantedGroup;
0221                 int perm, wantedPerm;
0222 
0223                 if( d->getProgramInfo( program, owner, group, wantedGroup, perm, wantedPerm ) ) {
0224 
0225                     if( index.column() == PermissionsColumn ) {
0226                         return QString(QString::number( perm, 8 ).rightJustified( 4, '0' ) + ' ' + owner + '.' + group);
0227                     } else if ( index.column() == NewPermissionsColumn ) {
0228                         if( perm != wantedPerm || owner != "root" || group != wantedGroup )
0229                             return QString("%1 root.%2").arg(wantedPerm,0,8).arg(wantedGroup);
0230                         else
0231                             return i18n("no change");
0232                     }
0233                 }
0234             }
0235         }
0236         else if( role == Qt::CheckStateRole && index.column() == ProgramColumn && d->needChangePermissions( program ) ) {
0237             if( d->selectedPrograms.contains( program ) )
0238                 return Qt::Checked;
0239             else
0240                 return Qt::Unchecked;
0241         }
0242     }
0243     return QVariant();
0244 }
0245 
0246 
0247 bool ExternalBinPermissionModel::setData( const QModelIndex& index, const QVariant& value, int role )
0248 {
0249     if( role == Qt::CheckStateRole ) {
0250         if( const ExternalBin* program = programForIndex( index ) ) {
0251             if( value.toInt() == Qt::Unchecked && d->selectedPrograms.contains( program ) ) {
0252                 d->selectedPrograms.remove( program );
0253                 emit dataChanged( index, index );
0254                 return true;
0255             }
0256             else if( value.toInt() == Qt::Checked && !d->selectedPrograms.contains( program ) ) {
0257                 d->selectedPrograms.insert( program );
0258                 emit dataChanged( index, index );
0259                 return true;
0260             }
0261         }
0262     }
0263     return false;
0264 }
0265 
0266 
0267 Qt::ItemFlags ExternalBinPermissionModel::flags( const QModelIndex& index ) const
0268 {
0269     if( const ExternalBin* program = programForIndex( index ) )
0270     {
0271         if( index.column() == ProgramColumn && d->needChangePermissions( program ) )
0272             return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
0273         else
0274             return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
0275     }
0276     else
0277         return Qt::ItemFlags();
0278 }
0279 
0280 
0281 QVariant ExternalBinPermissionModel::headerData( int section, Qt::Orientation orientation, int role ) const
0282 {
0283     if( orientation == Qt::Horizontal && role == Qt::DisplayRole ) {
0284         switch( section )
0285         {
0286             case ProgramColumn: return i18n( "Program" );
0287             case PermissionsColumn: return i18n( "Permissions" );
0288             case NewPermissionsColumn: return i18n( "New permissions" );
0289             default: return QVariant();
0290         }
0291     }
0292     else
0293         return QVariant();
0294 }
0295 
0296 
0297 QModelIndex ExternalBinPermissionModel::index( int row, int column, const QModelIndex& parent ) const
0298 {
0299     if( hasIndex(row, column, parent) && !parent.isValid() ) {
0300         const ExternalBin* program = d->programs.at( row );
0301         if( program != 0 )
0302             return createIndex( row, column, const_cast<ExternalBin*>( program ) );
0303         else
0304             return QModelIndex();
0305     }
0306     else
0307         return QModelIndex();
0308 }
0309 
0310 
0311 QModelIndex ExternalBinPermissionModel::parent( const QModelIndex& index ) const
0312 {
0313     Q_UNUSED( index );
0314     return QModelIndex();
0315 }
0316 
0317 
0318 int ExternalBinPermissionModel::rowCount( const QModelIndex& parent ) const
0319 {
0320     if( !parent.isValid() )
0321         return d->programs.size();
0322     else
0323         return 0;
0324 }
0325 
0326 
0327 int ExternalBinPermissionModel::columnCount( const QModelIndex& parent ) const
0328 {
0329     Q_UNUSED( parent );
0330     return NumColumns;
0331 }
0332 
0333 
0334 QModelIndex ExternalBinPermissionModel::buddy( const QModelIndex& index ) const
0335 {
0336     if( programForIndex( index ) != 0 )
0337         return ExternalBinPermissionModel::index( index.row(), ProgramColumn, index.parent() );
0338     else
0339         return index;
0340 }
0341 
0342 void ExternalBinPermissionModel::setBurningGroup( const QString& burningGroup )
0343 {
0344     if( burningGroup != d->burningGroup ) {
0345         beginResetModel();
0346         d->burningGroup = burningGroup;
0347         
0348         // Remove from the selected list all programs
0349         // whose permissions don't need to be changed anymore
0350         for( QSet<const ExternalBin*>::iterator program = d->selectedPrograms.begin();
0351              program != d->selectedPrograms.end(); )
0352         {
0353             if( !d->needChangePermissions( *program ) )
0354                 program = d->selectedPrograms.erase( program );
0355             else
0356                 ++program;
0357         }
0358         endResetModel();
0359     }
0360 }
0361 
0362 void ExternalBinPermissionModel::update()
0363 {
0364     beginResetModel();
0365     d->buildProgramList();
0366     d->selectedPrograms.intersect( QSet<const ExternalBin *>(d->programs.begin(), d->programs.end() ));
0367 
0368     endResetModel();
0369 }
0370 
0371 } // namespace K3b
0372 
0373 #include "moc_k3bexternalbinpermissionmodel.cpp"