1 /*
2 SPDX-FileCopyrightText: 2003-2009 Sebastian Trueg <trueg@k3b.org>
3 SPDX-FileCopyrightText: 2009 Michal Malek <michalm@jabster.pl>
4 SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
5 SPDX-FileCopyrightText: 2009 Michal Malek <michalm@jabster.pl>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "k3bexternalbinpermissionmodel.h"
11 #include "k3bexternalbinmanager.h"
12 #include "k3bdefaultexternalprograms.h"
13 #include "k3bglobals.h"
14
15 #include <KLocalizedString>
16
17 #include <QDebug>
18 #include <QFile>
19 #include <QFileInfo>
20 #include <QList>
21 #include <QSet>
22
23 #include <sys/stat.h>
24
25
26 namespace {
27
shouldRunSuidRoot(const K3b::ExternalBin * bin)28 bool shouldRunSuidRoot( const K3b::ExternalBin* bin )
29 {
30 //
31 // Since kernel 2.6.8 older cdrecord versions are not able to use the SCSI subsystem when running suid root anymore
32 // So for we ignore the suid root issue with kernel >= 2.6.8 and cdrecord < 2.01.01a02
33 //
34 // Some kernel version 2.6.16.something again introduced a problem here. Since I do not know the exact version
35 // and a workaround was introduced in cdrecord 2.01.01a05 just use that version as the first for suid root.
36 //
37 // Seems as if cdrdao never had problems with suid root...
38 //
39
40 if( bin->name() == "cdrecord" ) {
41 return ( K3b::simpleKernelVersion() < K3b::Version( 2, 6, 8 ) ||
42 bin->version() >= K3b::Version( 2, 1, 1, "a05" ) ||
43 bin->hasFeature( "wodim" ) );
44 }
45 else if( bin->name() == "cdrdao" ) {
46 return true;
47 }
48 else if( bin->name() == "growisofs" ) {
49 //
50 // starting with 6.0 growiofs raises it's priority using nice(-20)
51 // BUT: newer kernels have ridiculously low default memorylocked resource limit, which prevents privileged
52 // users from starting growisofs 6.0 with "unable to anonymously mmap 33554432: Resource temporarily unavailable"
53 // error message. Until Andy releases a version including a workaround we simply never configure growisofs suid root
54 return false; // bin->version >= K3b::Version( 6, 0 );
55 }
56 else
57 return false;
58 }
59
60 } // namespace
61
62 namespace K3b {
63
64 class ExternalBinPermissionModel::Private
65 {
66 public:
Private(ExternalBinManager const & ebm)67 explicit Private(ExternalBinManager const& ebm) : externalBinManager(ebm) {}
68 ExternalBinManager const& externalBinManager;
69 QString burningGroup;
70 QList<const ExternalBin*> programs;
71 QSet<const ExternalBin*> selectedPrograms;
72
73 void buildProgramList();
74 bool getProgramInfo( const ExternalBin* program,
75 QString& owner, QString& group, QString& wantedGroup,
76 int& perm, int& wantedPerm ) const;
77 bool needChangePermissions( const ExternalBin* program ) const;
78 };
79
80
buildProgramList()81 void ExternalBinPermissionModel::Private::buildProgramList()
82 {
83 programs.clear();
84 const QMap<QString, ExternalProgram*>& map = externalBinManager.programs();
85 for( QMap<QString, ExternalProgram*>::const_iterator it = map.constBegin(); it != map.constEnd(); ++it ) {
86 if (it.key() == "cdrecord" ||
87 it.key() == "cdrdao" ||
88 it.key() == "growisofs") {
89 programs += it.value()->bins();
90 }
91 }
92 selectedPrograms = programs.toSet();
93 }
94
95
getProgramInfo(const ExternalBin * program,QString & owner,QString & group,QString & wantedGroup,int & perm,int & wantedPerm) const96 bool ExternalBinPermissionModel::Private::getProgramInfo( const ExternalBin* program,
97 QString& owner, QString& group, QString& wantedGroup,
98 int& perm, int& wantedPerm ) const
99 {
100 // we need the uid bit which is not supported by QFileInfo
101 struct stat s;
102 if( ::stat( QFile::encodeName(program->path()), &s ) == 0 ) {
103
104 QFileInfo fi( program->path() );
105 owner = fi.owner();
106 group = fi.group();
107 perm = s.st_mode & 0007777;
108
109 if( !burningGroup.isEmpty() && burningGroup != "root" )
110 wantedGroup = burningGroup;
111 else
112 wantedGroup = "root";
113
114 if( shouldRunSuidRoot( program ) ) {
115 if( wantedGroup != "root" )
116 wantedPerm = 0004710;
117 else
118 wantedPerm = 0004711;
119 }
120 else {
121 if( wantedGroup != "root" )
122 wantedPerm = 0000750;
123 else
124 wantedPerm = 0000755;
125 }
126
127 return true;
128 }
129 else {
130 qDebug() << "(ExternalBinPermissionModel) unable to stat " << program->path();
131 return false;
132 }
133 }
134
135
needChangePermissions(const ExternalBin * program) const136 bool ExternalBinPermissionModel::Private::needChangePermissions( const ExternalBin* program ) const
137 {
138 QString owner, group, wantedGroup;
139 int perm, wantedPerm;
140
141 if( getProgramInfo( program, owner, group, wantedGroup, perm, wantedPerm ) )
142 return( perm != wantedPerm || owner != "root" || group != wantedGroup );
143 else
144 return false;
145 }
146
147
ExternalBinPermissionModel(ExternalBinManager const & externalBinManager,QObject * parent)148 ExternalBinPermissionModel::ExternalBinPermissionModel(ExternalBinManager const& externalBinManager, QObject* parent)
149 :
150 QAbstractItemModel( parent ),
151 d( new Private( externalBinManager ) )
152 {
153 d->buildProgramList();
154 }
155
156
~ExternalBinPermissionModel()157 ExternalBinPermissionModel::~ExternalBinPermissionModel()
158 {
159 delete d;
160 }
161
162
selectedPrograms() const163 QList<HelperProgramItem> ExternalBinPermissionModel::selectedPrograms() const
164 {
165 QList<HelperProgramItem> selectedPrograms;
166 Q_FOREACH( const ExternalBin* program, d->selectedPrograms )
167 {
168 if( d->needChangePermissions( program ) )
169 selectedPrograms << HelperProgramItem( program->path(), shouldRunSuidRoot( program ) );
170 }
171 return selectedPrograms;
172 }
173
174
changesNeeded() const175 bool ExternalBinPermissionModel::changesNeeded() const
176 {
177 return !selectedPrograms().isEmpty();
178 }
179
180
searchPaths() const181 QStringList ExternalBinPermissionModel::searchPaths() const
182 {
183 return d->externalBinManager.searchPath();
184 }
185
186
burningGroup() const187 const QString& ExternalBinPermissionModel::burningGroup() const
188 {
189 return d->burningGroup;
190 }
191
192
programForIndex(const QModelIndex & index) const193 const ExternalBin* ExternalBinPermissionModel::programForIndex( const QModelIndex& index ) const
194 {
195 if( index.isValid() )
196 return static_cast<const ExternalBin*>( index.internalPointer() );
197 else
198 return 0;
199 }
200
201
indexForProgram(const ExternalBin * program) const202 QModelIndex ExternalBinPermissionModel::indexForProgram( const ExternalBin* program ) const
203 {
204 if( program != 0 && !d->programs.isEmpty() ) {
205 int row = d->programs.indexOf( program );
206 return createIndex( row, 0, const_cast<ExternalBin*>( program ) );
207 }
208 else
209 return QModelIndex();
210 }
211
212
data(const QModelIndex & index,int role) const213 QVariant ExternalBinPermissionModel::data( const QModelIndex& index, int role ) const
214 {
215 if( const ExternalBin* program = programForIndex( index ) ) {
216 if( role == Qt::DisplayRole ) {
217 if( index.column() == ProgramColumn ) {
218 return program->path();
219 } else {
220 QString owner, group, wantedGroup;
221 int perm, wantedPerm;
222
223 if( d->getProgramInfo( program, owner, group, wantedGroup, perm, wantedPerm ) ) {
224
225 if( index.column() == PermissionsColumn ) {
226 return QString(QString::number( perm, 8 ).rightJustified( 4, '0' ) + ' ' + owner + '.' + group);
227 } else if ( index.column() == NewPermissionsColumn ) {
228 if( perm != wantedPerm || owner != "root" || group != wantedGroup )
229 return QString("%1 root.%2").arg(wantedPerm,0,8).arg(wantedGroup);
230 else
231 return i18n("no change");
232 }
233 }
234 }
235 }
236 else if( role == Qt::CheckStateRole && index.column() == ProgramColumn && d->needChangePermissions( program ) ) {
237 if( d->selectedPrograms.contains( program ) )
238 return Qt::Checked;
239 else
240 return Qt::Unchecked;
241 }
242 }
243 return QVariant();
244 }
245
246
setData(const QModelIndex & index,const QVariant & value,int role)247 bool ExternalBinPermissionModel::setData( const QModelIndex& index, const QVariant& value, int role )
248 {
249 if( role == Qt::CheckStateRole ) {
250 if( const ExternalBin* program = programForIndex( index ) ) {
251 if( value.toInt() == Qt::Unchecked && d->selectedPrograms.contains( program ) ) {
252 d->selectedPrograms.remove( program );
253 emit dataChanged( index, index );
254 return true;
255 }
256 else if( value.toInt() == Qt::Checked && !d->selectedPrograms.contains( program ) ) {
257 d->selectedPrograms.insert( program );
258 emit dataChanged( index, index );
259 return true;
260 }
261 }
262 }
263 return false;
264 }
265
266
flags(const QModelIndex & index) const267 Qt::ItemFlags ExternalBinPermissionModel::flags( const QModelIndex& index ) const
268 {
269 if( const ExternalBin* program = programForIndex( index ) )
270 {
271 if( index.column() == ProgramColumn && d->needChangePermissions( program ) )
272 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
273 else
274 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
275 }
276 else
277 return 0;
278 }
279
280
headerData(int section,Qt::Orientation orientation,int role) const281 QVariant ExternalBinPermissionModel::headerData( int section, Qt::Orientation orientation, int role ) const
282 {
283 if( orientation == Qt::Horizontal && role == Qt::DisplayRole ) {
284 switch( section )
285 {
286 case ProgramColumn: return i18n( "Program" );
287 case PermissionsColumn: return i18n( "Permissions" );
288 case NewPermissionsColumn: return i18n( "New permissions" );
289 default: return QVariant();
290 }
291 }
292 else
293 return QVariant();
294 }
295
296
index(int row,int column,const QModelIndex & parent) const297 QModelIndex ExternalBinPermissionModel::index( int row, int column, const QModelIndex& parent ) const
298 {
299 if( hasIndex(row, column, parent) && !parent.isValid() ) {
300 const ExternalBin* program = d->programs.at( row );
301 if( program != 0 )
302 return createIndex( row, column, const_cast<ExternalBin*>( program ) );
303 else
304 return QModelIndex();
305 }
306 else
307 return QModelIndex();
308 }
309
310
parent(const QModelIndex & index) const311 QModelIndex ExternalBinPermissionModel::parent( const QModelIndex& index ) const
312 {
313 Q_UNUSED( index );
314 return QModelIndex();
315 }
316
317
rowCount(const QModelIndex & parent) const318 int ExternalBinPermissionModel::rowCount( const QModelIndex& parent ) const
319 {
320 if( !parent.isValid() )
321 return d->programs.size();
322 else
323 return 0;
324 }
325
326
columnCount(const QModelIndex & parent) const327 int ExternalBinPermissionModel::columnCount( const QModelIndex& parent ) const
328 {
329 Q_UNUSED( parent );
330 return NumColumns;
331 }
332
333
buddy(const QModelIndex & index) const334 QModelIndex ExternalBinPermissionModel::buddy( const QModelIndex& index ) const
335 {
336 if( programForIndex( index ) != 0 )
337 return ExternalBinPermissionModel::index( index.row(), ProgramColumn, index.parent() );
338 else
339 return index;
340 }
341
setBurningGroup(const QString & burningGroup)342 void ExternalBinPermissionModel::setBurningGroup( const QString& burningGroup )
343 {
344 if( burningGroup != d->burningGroup ) {
345 beginResetModel();
346 d->burningGroup = burningGroup;
347
348 // Remove from the selected list all programs
349 // whose permissions don't need to be changed anymore
350 for( QSet<const ExternalBin*>::iterator program = d->selectedPrograms.begin();
351 program != d->selectedPrograms.end(); )
352 {
353 if( !d->needChangePermissions( *program ) )
354 program = d->selectedPrograms.erase( program );
355 else
356 ++program;
357 }
358 endResetModel();
359 }
360 }
361
update()362 void ExternalBinPermissionModel::update()
363 {
364 beginResetModel();
365 d->buildProgramList();
366 d->selectedPrograms.intersect( d->programs.toSet() );
367 endResetModel();
368 }
369
370 } // namespace K3b
371
372
373