File indexing completed on 2023-09-24 04:08:33
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 2005-2007 Till Adam <adam@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 // $Id: kacl.cpp 424977 2005-06-13 15:13:22Z tilladam $ 0008 0009 #include <config-kiocore.h> 0010 0011 #include "kacl.h" 0012 0013 #if HAVE_POSIX_ACL 0014 #include "../aclhelpers_p.h" 0015 #endif 0016 0017 #include <QDataStream> 0018 #include <QHash> 0019 #include <QString> 0020 0021 #include "kiocoredebug.h" 0022 0023 #if HAVE_POSIX_ACL 0024 using namespace KIO; 0025 #endif 0026 0027 class Q_DECL_HIDDEN KACL::KACLPrivate 0028 { 0029 public: 0030 KACLPrivate() 0031 #if HAVE_POSIX_ACL 0032 : m_acl(nullptr) 0033 #endif 0034 { 0035 } 0036 #if HAVE_POSIX_ACL 0037 explicit KACLPrivate(acl_t acl) 0038 : m_acl(acl) 0039 { 0040 } 0041 #endif 0042 #if HAVE_POSIX_ACL 0043 ~KACLPrivate() 0044 { 0045 if (m_acl) { 0046 acl_free(m_acl); 0047 } 0048 } 0049 #endif 0050 // helpers 0051 #if HAVE_POSIX_ACL 0052 bool setMaskPermissions(unsigned short v); 0053 QString getUserName(uid_t uid) const; 0054 QString getGroupName(gid_t gid) const; 0055 bool setAllUsersOrGroups(const QList<QPair<QString, unsigned short>> &list, acl_tag_t type); 0056 bool setNamedUserOrGroupPermissions(const QString &name, unsigned short permissions, acl_tag_t type); 0057 0058 acl_t m_acl; 0059 mutable QHash<uid_t, QString> m_usercache; 0060 mutable QHash<gid_t, QString> m_groupcache; 0061 #endif 0062 }; 0063 0064 KACL::KACL(const QString &aclString) 0065 : d(new KACLPrivate) 0066 { 0067 setACL(aclString); 0068 } 0069 0070 KACL::KACL(mode_t basePermissions) 0071 #if HAVE_POSIX_ACL 0072 : d(new KACLPrivate(ACLPortability::acl_from_mode(basePermissions))) 0073 #else 0074 : d(new KACLPrivate) 0075 #endif 0076 { 0077 #if !HAVE_POSIX_ACL 0078 Q_UNUSED(basePermissions); 0079 #endif 0080 } 0081 0082 KACL::KACL() 0083 : d(new KACLPrivate) 0084 { 0085 } 0086 0087 KACL::KACL(const KACL &rhs) 0088 : d(new KACLPrivate) 0089 { 0090 setACL(rhs.asString()); 0091 } 0092 0093 KACL::~KACL() = default; 0094 0095 KACL &KACL::operator=(const KACL &rhs) 0096 { 0097 if (this != &rhs) { 0098 setACL(rhs.asString()); 0099 } 0100 return *this; 0101 } 0102 0103 bool KACL::operator==(const KACL &rhs) const 0104 { 0105 #if HAVE_POSIX_ACL 0106 return (ACLPortability::acl_cmp(d->m_acl, rhs.d->m_acl) == 0); 0107 #else 0108 Q_UNUSED(rhs); 0109 return true; 0110 #endif 0111 } 0112 0113 bool KACL::operator!=(const KACL &rhs) const 0114 { 0115 return !operator==(rhs); 0116 } 0117 0118 bool KACL::isValid() const 0119 { 0120 bool valid = false; 0121 #if HAVE_POSIX_ACL 0122 if (d->m_acl) { 0123 valid = (acl_valid(d->m_acl) == 0); 0124 } 0125 #endif 0126 return valid; 0127 } 0128 0129 bool KACL::isExtended() const 0130 { 0131 #if HAVE_POSIX_ACL 0132 return (ACLPortability::acl_equiv_mode(d->m_acl, nullptr) != 0); 0133 #else 0134 return false; 0135 #endif 0136 } 0137 0138 #if HAVE_POSIX_ACL 0139 static acl_entry_t entryForTag(acl_t acl, acl_tag_t tag) 0140 { 0141 acl_entry_t entry; 0142 int ret = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); 0143 while (ret == 1) { 0144 acl_tag_t currentTag; 0145 acl_get_tag_type(entry, ¤tTag); 0146 if (currentTag == tag) { 0147 return entry; 0148 } 0149 ret = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry); 0150 } 0151 return nullptr; 0152 } 0153 0154 static unsigned short entryToPermissions(acl_entry_t entry) 0155 { 0156 if (entry == nullptr) { 0157 return 0; 0158 } 0159 acl_permset_t permset; 0160 if (acl_get_permset(entry, &permset) != 0) { 0161 return 0; 0162 } 0163 return (ACLPortability::acl_get_perm(permset, ACL_READ) << 2 | ACLPortability::acl_get_perm(permset, ACL_WRITE) << 1 0164 | ACLPortability::acl_get_perm(permset, ACL_EXECUTE)); 0165 } 0166 0167 static void permissionsToEntry(acl_entry_t entry, unsigned short v) 0168 { 0169 if (entry == nullptr) { 0170 return; 0171 } 0172 acl_permset_t permset; 0173 if (acl_get_permset(entry, &permset) != 0) { 0174 return; 0175 } 0176 acl_clear_perms(permset); 0177 if (v & 4) { 0178 acl_add_perm(permset, ACL_READ); 0179 } 0180 if (v & 2) { 0181 acl_add_perm(permset, ACL_WRITE); 0182 } 0183 if (v & 1) { 0184 acl_add_perm(permset, ACL_EXECUTE); 0185 } 0186 } 0187 0188 static int getUidForName(const QString &name) 0189 { 0190 struct passwd *user = getpwnam(name.toLocal8Bit().constData()); 0191 if (user) { 0192 return user->pw_uid; 0193 } else { 0194 return -1; 0195 } 0196 } 0197 0198 static int getGidForName(const QString &name) 0199 { 0200 struct group *group = getgrnam(name.toLocal8Bit().constData()); 0201 if (group) { 0202 return group->gr_gid; 0203 } else { 0204 return -1; 0205 } 0206 } 0207 #endif 0208 // ------------------ begin API implementation ------------ 0209 0210 unsigned short KACL::ownerPermissions() const 0211 { 0212 #if HAVE_POSIX_ACL 0213 return entryToPermissions(entryForTag(d->m_acl, ACL_USER_OBJ)); 0214 #else 0215 return 0; 0216 #endif 0217 } 0218 0219 bool KACL::setOwnerPermissions(unsigned short v) 0220 { 0221 #if HAVE_POSIX_ACL 0222 permissionsToEntry(entryForTag(d->m_acl, ACL_USER_OBJ), v); 0223 #else 0224 Q_UNUSED(v); 0225 #endif 0226 return true; 0227 } 0228 0229 unsigned short KACL::owningGroupPermissions() const 0230 { 0231 #if HAVE_POSIX_ACL 0232 return entryToPermissions(entryForTag(d->m_acl, ACL_GROUP_OBJ)); 0233 #else 0234 return 0; 0235 #endif 0236 } 0237 0238 bool KACL::setOwningGroupPermissions(unsigned short v) 0239 { 0240 #if HAVE_POSIX_ACL 0241 permissionsToEntry(entryForTag(d->m_acl, ACL_GROUP_OBJ), v); 0242 #else 0243 Q_UNUSED(v); 0244 #endif 0245 return true; 0246 } 0247 0248 unsigned short KACL::othersPermissions() const 0249 { 0250 #if HAVE_POSIX_ACL 0251 return entryToPermissions(entryForTag(d->m_acl, ACL_OTHER)); 0252 #else 0253 return 0; 0254 #endif 0255 } 0256 0257 bool KACL::setOthersPermissions(unsigned short v) 0258 { 0259 #if HAVE_POSIX_ACL 0260 permissionsToEntry(entryForTag(d->m_acl, ACL_OTHER), v); 0261 #else 0262 Q_UNUSED(v); 0263 #endif 0264 return true; 0265 } 0266 0267 mode_t KACL::basePermissions() const 0268 { 0269 mode_t perms(0); 0270 #if HAVE_POSIX_ACL 0271 if (ownerPermissions() & ACL_READ) { 0272 perms |= S_IRUSR; 0273 } 0274 if (ownerPermissions() & ACL_WRITE) { 0275 perms |= S_IWUSR; 0276 } 0277 if (ownerPermissions() & ACL_EXECUTE) { 0278 perms |= S_IXUSR; 0279 } 0280 if (owningGroupPermissions() & ACL_READ) { 0281 perms |= S_IRGRP; 0282 } 0283 if (owningGroupPermissions() & ACL_WRITE) { 0284 perms |= S_IWGRP; 0285 } 0286 if (owningGroupPermissions() & ACL_EXECUTE) { 0287 perms |= S_IXGRP; 0288 } 0289 if (othersPermissions() & ACL_READ) { 0290 perms |= S_IROTH; 0291 } 0292 if (othersPermissions() & ACL_WRITE) { 0293 perms |= S_IWOTH; 0294 } 0295 if (othersPermissions() & ACL_EXECUTE) { 0296 perms |= S_IXOTH; 0297 } 0298 #endif 0299 return perms; 0300 } 0301 0302 unsigned short KACL::maskPermissions(bool &exists) const 0303 { 0304 exists = true; 0305 #if HAVE_POSIX_ACL 0306 acl_entry_t entry = entryForTag(d->m_acl, ACL_MASK); 0307 if (entry == nullptr) { 0308 exists = false; 0309 return 0; 0310 } 0311 return entryToPermissions(entry); 0312 #else 0313 return 0; 0314 #endif 0315 } 0316 0317 #if HAVE_POSIX_ACL 0318 bool KACL::KACLPrivate::setMaskPermissions(unsigned short v) 0319 { 0320 acl_entry_t entry = entryForTag(m_acl, ACL_MASK); 0321 if (entry == nullptr) { 0322 acl_create_entry(&m_acl, &entry); 0323 acl_set_tag_type(entry, ACL_MASK); 0324 } 0325 permissionsToEntry(entry, v); 0326 return true; 0327 } 0328 #endif 0329 0330 bool KACL::setMaskPermissions(unsigned short v) 0331 { 0332 #if HAVE_POSIX_ACL 0333 return d->setMaskPermissions(v); 0334 #else 0335 Q_UNUSED(v); 0336 return true; 0337 #endif 0338 } 0339 0340 #if HAVE_POSIX_ACL 0341 using unique_ptr_acl_free = std::unique_ptr<void, int (*)(void *)>; 0342 #endif 0343 0344 /************************** 0345 * Deal with named users * 0346 **************************/ 0347 unsigned short KACL::namedUserPermissions(const QString &name, bool *exists) const 0348 { 0349 #if HAVE_POSIX_ACL 0350 acl_entry_t entry; 0351 *exists = false; 0352 int ret = acl_get_entry(d->m_acl, ACL_FIRST_ENTRY, &entry); 0353 while (ret == 1) { 0354 acl_tag_t currentTag; 0355 acl_get_tag_type(entry, ¤tTag); 0356 if (currentTag == ACL_USER) { 0357 const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free); 0358 const uid_t id = *(static_cast<uid_t *>(idptr.get())); 0359 if (d->getUserName(id) == name) { 0360 *exists = true; 0361 return entryToPermissions(entry); 0362 } 0363 } 0364 ret = acl_get_entry(d->m_acl, ACL_NEXT_ENTRY, &entry); 0365 } 0366 #else 0367 Q_UNUSED(name); 0368 Q_UNUSED(exists); 0369 #endif 0370 return 0; 0371 } 0372 0373 #if HAVE_POSIX_ACL 0374 bool KACL::KACLPrivate::setNamedUserOrGroupPermissions(const QString &name, unsigned short permissions, acl_tag_t type) 0375 { 0376 bool allIsWell = true; 0377 acl_t newACL = acl_dup(m_acl); 0378 acl_entry_t entry; 0379 bool createdNewEntry = false; 0380 bool found = false; 0381 int ret = acl_get_entry(newACL, ACL_FIRST_ENTRY, &entry); 0382 while (ret == 1) { 0383 acl_tag_t currentTag; 0384 acl_get_tag_type(entry, ¤tTag); 0385 if (currentTag == type) { 0386 const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free); 0387 const int id = *(static_cast<int *>(idptr.get())); // We assume that sizeof(uid_t) == sizeof(gid_t) 0388 const QString entryName = type == ACL_USER ? getUserName(id) : getGroupName(id); 0389 if (entryName == name) { 0390 // found him, update 0391 permissionsToEntry(entry, permissions); 0392 found = true; 0393 break; 0394 } 0395 } 0396 ret = acl_get_entry(newACL, ACL_NEXT_ENTRY, &entry); 0397 } 0398 if (!found) { 0399 acl_create_entry(&newACL, &entry); 0400 acl_set_tag_type(entry, type); 0401 int id = type == ACL_USER ? getUidForName(name) : getGidForName(name); 0402 if (id == -1 || acl_set_qualifier(entry, &id) != 0) { 0403 acl_delete_entry(newACL, entry); 0404 allIsWell = false; 0405 } else { 0406 permissionsToEntry(entry, permissions); 0407 createdNewEntry = true; 0408 } 0409 } 0410 if (allIsWell && createdNewEntry) { 0411 // 23.1.1 of 1003.1e states that as soon as there is a named user or 0412 // named group entry, there needs to be a mask entry as well, so add 0413 // one, if the user hasn't explicitly set one. 0414 if (entryForTag(newACL, ACL_MASK) == nullptr) { 0415 acl_calc_mask(&newACL); 0416 } 0417 } 0418 0419 if (!allIsWell || acl_valid(newACL) != 0) { 0420 acl_free(newACL); 0421 allIsWell = false; 0422 } else { 0423 acl_free(m_acl); 0424 m_acl = newACL; 0425 } 0426 return allIsWell; 0427 } 0428 #endif 0429 0430 bool KACL::setNamedUserPermissions(const QString &name, unsigned short permissions) 0431 { 0432 #if HAVE_POSIX_ACL 0433 return d->setNamedUserOrGroupPermissions(name, permissions, ACL_USER); 0434 #else 0435 Q_UNUSED(name); 0436 Q_UNUSED(permissions); 0437 return true; 0438 #endif 0439 } 0440 0441 ACLUserPermissionsList KACL::allUserPermissions() const 0442 { 0443 ACLUserPermissionsList list; 0444 #if HAVE_POSIX_ACL 0445 acl_entry_t entry; 0446 int ret = acl_get_entry(d->m_acl, ACL_FIRST_ENTRY, &entry); 0447 while (ret == 1) { 0448 acl_tag_t currentTag; 0449 acl_get_tag_type(entry, ¤tTag); 0450 if (currentTag == ACL_USER) { 0451 const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free); 0452 const uid_t id = *(static_cast<uid_t *>(idptr.get())); 0453 QString name = d->getUserName(id); 0454 unsigned short permissions = entryToPermissions(entry); 0455 ACLUserPermissions pair = qMakePair(name, permissions); 0456 list.append(pair); 0457 } 0458 ret = acl_get_entry(d->m_acl, ACL_NEXT_ENTRY, &entry); 0459 } 0460 #endif 0461 return list; 0462 } 0463 0464 #if HAVE_POSIX_ACL 0465 bool KACL::KACLPrivate::setAllUsersOrGroups(const QList<QPair<QString, unsigned short>> &list, acl_tag_t type) 0466 { 0467 bool allIsWell = true; 0468 bool atLeastOneUserOrGroup = false; 0469 0470 // make working copy, in case something goes wrong 0471 acl_t newACL = acl_dup(m_acl); 0472 acl_entry_t entry; 0473 0474 // clear user entries 0475 int ret = acl_get_entry(newACL, ACL_FIRST_ENTRY, &entry); 0476 while (ret == 1) { 0477 acl_tag_t currentTag; 0478 acl_get_tag_type(entry, ¤tTag); 0479 if (currentTag == type) { 0480 acl_delete_entry(newACL, entry); 0481 // we have to start from the beginning, the iterator is 0482 // invalidated, on deletion 0483 ret = acl_get_entry(newACL, ACL_FIRST_ENTRY, &entry); 0484 } else { 0485 ret = acl_get_entry(newACL, ACL_NEXT_ENTRY, &entry); 0486 } 0487 } 0488 0489 // now add the entries from the list 0490 for (const auto &[name, userId] : list) { 0491 acl_create_entry(&newACL, &entry); 0492 acl_set_tag_type(entry, type); 0493 int id = type == ACL_USER ? getUidForName(name) : getGidForName(name); 0494 if (id == -1 || acl_set_qualifier(entry, &id) != 0) { 0495 // user or group doesn't exist => error 0496 acl_delete_entry(newACL, entry); 0497 allIsWell = false; 0498 break; 0499 } else { 0500 permissionsToEntry(entry, userId); 0501 atLeastOneUserOrGroup = true; 0502 } 0503 } 0504 0505 if (allIsWell && atLeastOneUserOrGroup) { 0506 // 23.1.1 of 1003.1e states that as soon as there is a named user or 0507 // named group entry, there needs to be a mask entry as well, so add 0508 // one, if the user hasn't explicitly set one. 0509 if (entryForTag(newACL, ACL_MASK) == nullptr) { 0510 acl_calc_mask(&newACL); 0511 } 0512 } 0513 if (allIsWell && (acl_valid(newACL) == 0)) { 0514 acl_free(m_acl); 0515 m_acl = newACL; 0516 } else { 0517 acl_free(newACL); 0518 } 0519 return allIsWell; 0520 } 0521 #endif 0522 0523 bool KACL::setAllUserPermissions(const ACLUserPermissionsList &users) 0524 { 0525 #if HAVE_POSIX_ACL 0526 return d->setAllUsersOrGroups(users, ACL_USER); 0527 #else 0528 Q_UNUSED(users); 0529 return true; 0530 #endif 0531 } 0532 0533 /************************** 0534 * Deal with named groups * 0535 **************************/ 0536 0537 unsigned short KACL::namedGroupPermissions(const QString &name, bool *exists) const 0538 { 0539 *exists = false; 0540 #if HAVE_POSIX_ACL 0541 acl_entry_t entry; 0542 int ret = acl_get_entry(d->m_acl, ACL_FIRST_ENTRY, &entry); 0543 while (ret == 1) { 0544 acl_tag_t currentTag; 0545 acl_get_tag_type(entry, ¤tTag); 0546 if (currentTag == ACL_GROUP) { 0547 const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free); 0548 const gid_t id = *(static_cast<gid_t *>(idptr.get())); 0549 if (d->getGroupName(id) == name) { 0550 *exists = true; 0551 return entryToPermissions(entry); 0552 } 0553 } 0554 ret = acl_get_entry(d->m_acl, ACL_NEXT_ENTRY, &entry); 0555 } 0556 #else 0557 Q_UNUSED(name); 0558 #endif 0559 return 0; 0560 } 0561 0562 bool KACL::setNamedGroupPermissions(const QString &name, unsigned short permissions) 0563 { 0564 #if HAVE_POSIX_ACL 0565 return d->setNamedUserOrGroupPermissions(name, permissions, ACL_GROUP); 0566 #else 0567 Q_UNUSED(name); 0568 Q_UNUSED(permissions); 0569 return true; 0570 #endif 0571 } 0572 0573 ACLGroupPermissionsList KACL::allGroupPermissions() const 0574 { 0575 ACLGroupPermissionsList list; 0576 #if HAVE_POSIX_ACL 0577 acl_entry_t entry; 0578 int ret = acl_get_entry(d->m_acl, ACL_FIRST_ENTRY, &entry); 0579 while (ret == 1) { 0580 acl_tag_t currentTag; 0581 acl_get_tag_type(entry, ¤tTag); 0582 if (currentTag == ACL_GROUP) { 0583 const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free); 0584 const gid_t id = *(static_cast<gid_t *>(idptr.get())); 0585 QString name = d->getGroupName(id); 0586 unsigned short permissions = entryToPermissions(entry); 0587 ACLGroupPermissions pair = qMakePair(name, permissions); 0588 list.append(pair); 0589 } 0590 ret = acl_get_entry(d->m_acl, ACL_NEXT_ENTRY, &entry); 0591 } 0592 #endif 0593 return list; 0594 } 0595 0596 bool KACL::setAllGroupPermissions(const ACLGroupPermissionsList &groups) 0597 { 0598 #if HAVE_POSIX_ACL 0599 return d->setAllUsersOrGroups(groups, ACL_GROUP); 0600 #else 0601 Q_UNUSED(groups); 0602 return true; 0603 #endif 0604 } 0605 0606 /************************** 0607 * from and to string * 0608 **************************/ 0609 0610 bool KACL::setACL(const QString &aclStr) 0611 { 0612 bool ret = false; 0613 #if HAVE_POSIX_ACL 0614 acl_t temp = acl_from_text(aclStr.toLatin1().constData()); 0615 if (acl_valid(temp) != 0) { 0616 // TODO errno is set, what to do with it here? 0617 acl_free(temp); 0618 } else { 0619 if (d->m_acl) { 0620 acl_free(d->m_acl); 0621 } 0622 d->m_acl = temp; 0623 ret = true; 0624 } 0625 #else 0626 Q_UNUSED(aclStr); 0627 #endif 0628 return ret; 0629 } 0630 0631 QString KACL::asString() const 0632 { 0633 #if HAVE_POSIX_ACL 0634 ssize_t size = 0; 0635 char *txt = acl_to_text(d->m_acl, &size); 0636 const QString ret = QString::fromLatin1(txt, size); 0637 acl_free(txt); 0638 return ret; 0639 #else 0640 return QString(); 0641 #endif 0642 } 0643 0644 // helpers 0645 0646 #if HAVE_POSIX_ACL 0647 QString KACL::KACLPrivate::getUserName(uid_t uid) const 0648 { 0649 auto it = m_usercache.find(uid); 0650 if (it == m_usercache.end()) { 0651 struct passwd *user = getpwuid(uid); 0652 if (user) { 0653 it = m_usercache.insert(uid, QString::fromLatin1(user->pw_name)); 0654 } else { 0655 return QString::number(uid); 0656 } 0657 } 0658 return *it; 0659 } 0660 0661 QString KACL::KACLPrivate::getGroupName(gid_t gid) const 0662 { 0663 auto it = m_groupcache.find(gid); 0664 if (it == m_groupcache.end()) { 0665 struct group *grp = getgrgid(gid); 0666 if (grp) { 0667 it = m_groupcache.insert(gid, QString::fromLatin1(grp->gr_name)); 0668 } else { 0669 return QString::number(gid); 0670 } 0671 } 0672 return *it; 0673 } 0674 #endif 0675 0676 void KACL::virtual_hook(int, void *) 0677 { 0678 /*BASE::virtual_hook( id, data );*/ 0679 } 0680 0681 QDataStream &operator<<(QDataStream &s, const KACL &a) 0682 { 0683 s << a.asString(); 0684 return s; 0685 } 0686 0687 QDataStream &operator>>(QDataStream &s, KACL &a) 0688 { 0689 QString str; 0690 s >> str; 0691 a.setACL(str); 0692 return s; 0693 }