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