File indexing completed on 2024-05-12 03:55:01
0001 /* 0002 KUser - represent a user/account 0003 0004 SPDX-FileCopyrightText: 2002 Tim Jansen <tim@tjansen.de> 0005 SPDX-FileCopyrightText: 2014 Alex Richardson <arichardson.kde@gmail.com> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "config-util.h" 0011 #include "kcoreaddons_debug.h" 0012 #include "kuser.h" 0013 0014 #include <QFileInfo> 0015 0016 #include <cerrno> 0017 #include <grp.h> 0018 #include <pwd.h> 0019 #include <stdlib.h> 0020 #include <unistd.h> 0021 0022 #include <algorithm> // std::find 0023 #include <functional> // std::function 0024 0025 #if defined(__BIONIC__) && __ANDROID_API__ < 26 0026 static inline struct passwd *getpwent() 0027 { 0028 return nullptr; 0029 } 0030 inline void setpwent() 0031 { 0032 } 0033 static inline void setgrent() 0034 { 0035 } 0036 static inline struct group *getgrent() 0037 { 0038 return nullptr; 0039 } 0040 inline void endpwent() 0041 { 0042 } 0043 static inline void endgrent() 0044 { 0045 } 0046 #endif 0047 0048 // Only define os_pw_size() if it's going to be used 0049 #if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) 0050 static int os_pw_size() // hint for size of passwd struct 0051 { 0052 const int size_max = sysconf(_SC_GETPW_R_SIZE_MAX); 0053 if (size_max != -1) { 0054 return size_max; 0055 } 0056 return 1024; 0057 } 0058 #endif 0059 0060 // Only define os_gr_size() if it's going to be used 0061 #if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID) && (__ANDROID_API__ >= 24)) 0062 static int os_gr_size() // hint for size of group struct 0063 { 0064 const int size_max = sysconf(_SC_GETGR_R_SIZE_MAX); 0065 if (size_max != -1) { 0066 return size_max; 0067 } 0068 return 1024; 0069 } 0070 #endif 0071 0072 class KUserPrivate : public QSharedData 0073 { 0074 public: 0075 uid_t uid = uid_t(-1); 0076 gid_t gid = gid_t(-1); 0077 QString loginName; 0078 QString homeDir, shell; 0079 QMap<KUser::UserProperty, QVariant> properties; 0080 0081 KUserPrivate() 0082 { 0083 } 0084 KUserPrivate(const char *name) 0085 { 0086 if (!name) { 0087 fillPasswd(nullptr); 0088 } else { 0089 struct passwd *pw = nullptr; 0090 #if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) 0091 static const int bufsize = os_pw_size(); 0092 QVarLengthArray<char, 1024> buf(bufsize); 0093 struct passwd entry; 0094 getpwnam_r(name, &entry, buf.data(), buf.size(), &pw); 0095 #else 0096 pw = getpwnam(name); // not thread-safe! 0097 #endif 0098 fillPasswd(pw); 0099 } 0100 } 0101 KUserPrivate(K_UID uid) 0102 { 0103 struct passwd *pw = nullptr; 0104 #if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) 0105 static const int bufsize = os_pw_size(); 0106 QVarLengthArray<char, 1024> buf(bufsize); 0107 struct passwd entry; 0108 getpwuid_r(uid, &entry, buf.data(), buf.size(), &pw); 0109 #else 0110 pw = getpwuid(uid); // not thread-safe! 0111 #endif 0112 fillPasswd(pw); 0113 } 0114 KUserPrivate(const passwd *p) 0115 { 0116 fillPasswd(p); 0117 } 0118 0119 void fillPasswd(const passwd *p) 0120 { 0121 if (p) { 0122 #ifndef __BIONIC__ 0123 QString gecos = QString::fromLocal8Bit(p->pw_gecos); 0124 #else 0125 QString gecos = QString(); 0126 #endif 0127 QStringList gecosList = gecos.split(QLatin1Char(',')); 0128 // fill up the list, should be at least 4 entries 0129 while (gecosList.size() < 4) { 0130 gecosList << QString(); 0131 } 0132 0133 uid = p->pw_uid; 0134 gid = p->pw_gid; 0135 loginName = QString::fromLocal8Bit(p->pw_name); 0136 properties[KUser::FullName] = QVariant(gecosList[0]); 0137 properties[KUser::RoomNumber] = QVariant(gecosList[1]); 0138 properties[KUser::WorkPhone] = QVariant(gecosList[2]); 0139 properties[KUser::HomePhone] = QVariant(gecosList[3]); 0140 if (uid == ::getuid() && uid == ::geteuid()) { 0141 homeDir = QFile::decodeName(qgetenv("HOME")); 0142 } 0143 if (homeDir.isEmpty()) { 0144 homeDir = QFile::decodeName(p->pw_dir); 0145 } 0146 shell = QString::fromLocal8Bit(p->pw_shell); 0147 } 0148 } 0149 }; 0150 0151 KUser::KUser(UIDMode mode) 0152 { 0153 uid_t _uid = ::getuid(); 0154 uid_t _euid; 0155 if (mode == UseEffectiveUID && (_euid = ::geteuid()) != _uid) { 0156 d = new KUserPrivate(_euid); 0157 } else { 0158 d = new KUserPrivate(qgetenv("LOGNAME").constData()); 0159 if (d->uid != _uid) { 0160 d = new KUserPrivate(qgetenv("USER").constData()); 0161 if (d->uid != _uid) { 0162 d = new KUserPrivate(_uid); 0163 } 0164 } 0165 } 0166 } 0167 0168 KUser::KUser(K_UID _uid) 0169 : d(new KUserPrivate(_uid)) 0170 { 0171 } 0172 0173 KUser::KUser(KUserId _uid) 0174 : d(new KUserPrivate(_uid.nativeId())) 0175 { 0176 } 0177 0178 KUser::KUser(const QString &name) 0179 : d(new KUserPrivate(name.toLocal8Bit().data())) 0180 { 0181 } 0182 0183 KUser::KUser(const char *name) 0184 : d(new KUserPrivate(name)) 0185 { 0186 } 0187 0188 KUser::KUser(const passwd *p) 0189 : d(new KUserPrivate(p)) 0190 { 0191 } 0192 0193 KUser::KUser(const KUser &user) 0194 : d(user.d) 0195 { 0196 } 0197 0198 KUser &KUser::operator=(const KUser &user) 0199 { 0200 d = user.d; 0201 return *this; 0202 } 0203 0204 bool KUser::operator==(const KUser &user) const 0205 { 0206 return isValid() && (d->uid == user.d->uid); 0207 } 0208 0209 bool KUser::isValid() const 0210 { 0211 return d->uid != uid_t(-1); 0212 } 0213 0214 KUserId KUser::userId() const 0215 { 0216 return KUserId(d->uid); 0217 } 0218 0219 KGroupId KUser::groupId() const 0220 { 0221 return KGroupId(d->gid); 0222 } 0223 0224 bool KUser::isSuperUser() const 0225 { 0226 return d->uid == 0; 0227 } 0228 0229 QString KUser::loginName() const 0230 { 0231 return d->loginName; 0232 } 0233 0234 QString KUser::homeDir() const 0235 { 0236 return d->homeDir; 0237 } 0238 0239 QString KUser::faceIconPath() const 0240 { 0241 QString pathToFaceIcon; 0242 if (!d->loginName.isEmpty()) { 0243 pathToFaceIcon = QStringLiteral(ACCOUNTS_SERVICE_ICON_DIR) + QLatin1Char('/') + d->loginName; 0244 } 0245 0246 if (QFile::exists(pathToFaceIcon)) { 0247 return pathToFaceIcon; 0248 } 0249 0250 pathToFaceIcon = homeDir() + QLatin1Char('/') + QLatin1String(".face.icon"); 0251 0252 if (QFileInfo(pathToFaceIcon).isReadable()) { 0253 return pathToFaceIcon; 0254 } 0255 0256 return QString(); 0257 } 0258 0259 QString KUser::shell() const 0260 { 0261 return d->shell; 0262 } 0263 0264 template<class Func> 0265 static void listGroupsForUser(const char *name, gid_t gid, uint maxCount, Func handleNextGroup) 0266 { 0267 if (Q_UNLIKELY(maxCount == 0)) { 0268 return; 0269 } 0270 uint found = 0; 0271 #if HAVE_GETGROUPLIST 0272 QVarLengthArray<gid_t, 100> gid_buffer; 0273 gid_buffer.resize(100); 0274 int numGroups = gid_buffer.size(); 0275 int result = getgrouplist(name, gid, gid_buffer.data(), &numGroups); 0276 if (result < 0 && uint(numGroups) < maxCount) { 0277 // getgrouplist returns -1 if the buffer was too small to store all entries, the required size is in numGroups 0278 qCDebug(KCOREADDONS_DEBUG) << "Buffer was too small: " << gid_buffer.size() << ", need" << numGroups; 0279 gid_buffer.resize(numGroups); 0280 numGroups = gid_buffer.size(); 0281 getgrouplist(name, gid, gid_buffer.data(), &numGroups); 0282 } 0283 for (int i = 0; i < numGroups && found < maxCount; ++i) { 0284 struct group *g = getgrgid(gid_buffer[i]); // ### not threadsafe 0285 // should never be null, but better be safe than crash 0286 if (g) { 0287 found++; 0288 handleNextGroup(g); 0289 } 0290 } 0291 #else 0292 // fall back to getgrent() and reading gr->gr_mem 0293 // This is slower than getgrouplist, but works as well 0294 // add the current gid, this is often not part of g->gr_mem (e.g. build.kde.org or my openSuSE 13.1 system) 0295 struct group *g = getgrgid(gid); // ### not threadsafe 0296 if (g) { 0297 handleNextGroup(g); 0298 found++; 0299 if (found >= maxCount) { 0300 return; 0301 } 0302 } 0303 0304 static const auto groupContainsUser = [](struct group *g, const char *name) -> bool { 0305 for (char **user = g->gr_mem; *user; user++) { 0306 if (strcmp(name, *user) == 0) { 0307 return true; 0308 } 0309 } 0310 return false; 0311 }; 0312 0313 setgrent(); 0314 while ((g = getgrent())) { 0315 // don't add the current gid again 0316 if (g->gr_gid != gid && groupContainsUser(g, name)) { 0317 handleNextGroup(g); 0318 found++; 0319 if (found >= maxCount) { 0320 break; 0321 } 0322 } 0323 } 0324 endgrent(); 0325 #endif 0326 } 0327 0328 QList<KUserGroup> KUser::groups(uint maxCount) const 0329 { 0330 QList<KUserGroup> result; 0331 listGroupsForUser(d->loginName.toLocal8Bit().constData(), d->gid, maxCount, [&](const group *g) { 0332 result.append(KUserGroup(g)); 0333 }); 0334 return result; 0335 } 0336 0337 QStringList KUser::groupNames(uint maxCount) const 0338 { 0339 QStringList result; 0340 listGroupsForUser(d->loginName.toLocal8Bit().constData(), d->gid, maxCount, [&](const group *g) { 0341 result.append(QString::fromLocal8Bit(g->gr_name)); 0342 }); 0343 return result; 0344 } 0345 0346 QVariant KUser::property(UserProperty which) const 0347 { 0348 return d->properties.value(which); 0349 } 0350 0351 QList<KUser> KUser::allUsers(uint maxCount) 0352 { 0353 QList<KUser> result; 0354 0355 passwd *p; 0356 setpwent(); 0357 0358 for (uint i = 0; i < maxCount && (p = getpwent()); ++i) { 0359 result.append(KUser(p)); 0360 } 0361 0362 endpwent(); 0363 0364 return result; 0365 } 0366 0367 QStringList KUser::allUserNames(uint maxCount) 0368 { 0369 QStringList result; 0370 0371 passwd *p; 0372 setpwent(); 0373 0374 for (uint i = 0; i < maxCount && (p = getpwent()); ++i) { 0375 result.append(QString::fromLocal8Bit(p->pw_name)); 0376 } 0377 0378 endpwent(); 0379 return result; 0380 } 0381 0382 KUser::~KUser() 0383 { 0384 } 0385 0386 class KUserGroupPrivate : public QSharedData 0387 { 0388 public: 0389 gid_t gid = gid_t(-1); 0390 QString name; 0391 0392 KUserGroupPrivate() 0393 { 0394 } 0395 KUserGroupPrivate(const char *_name) 0396 { 0397 fillGroup(_name ? ::getgrnam(_name) : nullptr); 0398 } 0399 KUserGroupPrivate(K_GID gid) 0400 { 0401 struct group *gr = nullptr; 0402 #if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID) && (__ANDROID_API__ >= 24)) 0403 static const int bufsize = os_gr_size(); 0404 QVarLengthArray<char, 1024> buf(bufsize); 0405 struct group entry; 0406 // Some large systems have more members than the POSIX max size 0407 // Loop over by doubling the buffer size (upper limit 250k) 0408 for (int size = bufsize; size < 256000; size += size) { 0409 buf.resize(size); 0410 // ERANGE indicates that the buffer was too small 0411 if (!getgrgid_r(gid, &entry, buf.data(), buf.size(), &gr) || errno != ERANGE) { 0412 break; 0413 } 0414 } 0415 #else 0416 gr = getgrgid(gid); // not thread-safe! 0417 #endif 0418 fillGroup(gr); 0419 } 0420 KUserGroupPrivate(const ::group *p) 0421 { 0422 fillGroup(p); 0423 } 0424 0425 void fillGroup(const ::group *p) 0426 { 0427 if (p) { 0428 gid = p->gr_gid; 0429 name = QString::fromLocal8Bit(p->gr_name); 0430 } 0431 } 0432 }; 0433 0434 KUserGroup::KUserGroup(KUser::UIDMode mode) 0435 : d(new KUserGroupPrivate(KUser(mode).groupId().nativeId())) 0436 { 0437 } 0438 0439 KUserGroup::KUserGroup(K_GID _gid) 0440 : d(new KUserGroupPrivate(_gid)) 0441 { 0442 } 0443 0444 KUserGroup::KUserGroup(KGroupId _gid) 0445 : d(new KUserGroupPrivate(_gid.nativeId())) 0446 { 0447 } 0448 0449 KUserGroup::KUserGroup(const QString &_name) 0450 : d(new KUserGroupPrivate(_name.toLocal8Bit().data())) 0451 { 0452 } 0453 0454 KUserGroup::KUserGroup(const char *_name) 0455 : d(new KUserGroupPrivate(_name)) 0456 { 0457 } 0458 0459 KUserGroup::KUserGroup(const ::group *g) 0460 : d(new KUserGroupPrivate(g)) 0461 { 0462 } 0463 0464 KUserGroup::KUserGroup(const KUserGroup &group) 0465 : d(group.d) 0466 { 0467 } 0468 0469 KUserGroup &KUserGroup::operator=(const KUserGroup &group) 0470 { 0471 d = group.d; 0472 return *this; 0473 } 0474 0475 bool KUserGroup::operator==(const KUserGroup &group) const 0476 { 0477 return isValid() && (d->gid == group.d->gid); 0478 } 0479 0480 bool KUserGroup::isValid() const 0481 { 0482 return d->gid != gid_t(-1); 0483 } 0484 0485 KGroupId KUserGroup::groupId() const 0486 { 0487 return KGroupId(d->gid); 0488 } 0489 0490 QString KUserGroup::name() const 0491 { 0492 return d->name; 0493 } 0494 0495 static void listGroupMembers(gid_t gid, uint maxCount, std::function<void(passwd *)> handleNextGroupUser) 0496 { 0497 if (maxCount == 0) { 0498 return; 0499 } 0500 struct group *g = getgrgid(gid); // ### not threadsafe 0501 if (!g) { 0502 return; 0503 } 0504 uint found = 0; 0505 QVarLengthArray<uid_t> addedUsers; 0506 struct passwd *p = nullptr; 0507 for (char **user = g->gr_mem; *user; user++) { 0508 if ((p = getpwnam(*user))) { // ### not threadsafe 0509 addedUsers.append(p->pw_uid); 0510 handleNextGroupUser(p); 0511 found++; 0512 if (found >= maxCount) { 0513 break; 0514 } 0515 } 0516 } 0517 0518 // gr_mem doesn't contain users where the primary group == gid -> we have to iterate over all users 0519 setpwent(); 0520 while ((p = getpwent()) && found < maxCount) { 0521 if (p->pw_gid != gid) { 0522 continue; // only need primary gid since otherwise gr_mem already contains this user 0523 } 0524 // make sure we don't list a user twice 0525 if (std::find(addedUsers.cbegin(), addedUsers.cend(), p->pw_uid) == addedUsers.cend()) { 0526 handleNextGroupUser(p); 0527 found++; 0528 } 0529 } 0530 endpwent(); 0531 } 0532 0533 QList<KUser> KUserGroup::users(uint maxCount) const 0534 { 0535 QList<KUser> result; 0536 listGroupMembers(d->gid, maxCount, [&](const passwd *p) { 0537 result.append(KUser(p)); 0538 }); 0539 return result; 0540 } 0541 0542 QStringList KUserGroup::userNames(uint maxCount) const 0543 { 0544 QStringList result; 0545 listGroupMembers(d->gid, maxCount, [&](const passwd *p) { 0546 result.append(QString::fromLocal8Bit(p->pw_name)); 0547 }); 0548 return result; 0549 } 0550 0551 QList<KUserGroup> KUserGroup::allGroups(uint maxCount) 0552 { 0553 QList<KUserGroup> result; 0554 0555 ::group *g; 0556 setgrent(); 0557 0558 for (uint i = 0; i < maxCount && (g = getgrent()); ++i) { 0559 result.append(KUserGroup(g)); 0560 } 0561 0562 endgrent(); 0563 0564 return result; 0565 } 0566 0567 QStringList KUserGroup::allGroupNames(uint maxCount) 0568 { 0569 QStringList result; 0570 0571 ::group *g; 0572 setgrent(); 0573 0574 for (uint i = 0; i < maxCount && (g = getgrent()); ++i) { 0575 result.append(QString::fromLocal8Bit(g->gr_name)); 0576 } 0577 0578 endgrent(); 0579 0580 return result; 0581 } 0582 0583 KUserGroup::~KUserGroup() 0584 { 0585 } 0586 0587 KUserId KUserId::fromName(const QString &name) 0588 { 0589 if (name.isEmpty()) { 0590 return KUserId(); 0591 } 0592 QByteArray name8Bit = name.toLocal8Bit(); 0593 struct passwd *p = ::getpwnam(name8Bit.constData()); 0594 if (!p) { 0595 qCWarning(KCOREADDONS_DEBUG, "Failed to lookup user %s: %s", name8Bit.constData(), strerror(errno)); 0596 return KUserId(); 0597 } 0598 return KUserId(p->pw_uid); 0599 } 0600 0601 KGroupId KGroupId::fromName(const QString &name) 0602 { 0603 if (name.isEmpty()) { 0604 return KGroupId(); 0605 } 0606 QByteArray name8Bit = name.toLocal8Bit(); 0607 struct group *g = ::getgrnam(name8Bit.constData()); 0608 if (!g) { 0609 qCWarning(KCOREADDONS_DEBUG, "Failed to lookup group %s: %s", name8Bit.constData(), strerror(errno)); 0610 return KGroupId(); 0611 } 0612 return KGroupId(g->gr_gid); 0613 } 0614 0615 KUserId KUserId::currentUserId() 0616 { 0617 return KUserId(getuid()); 0618 } 0619 0620 KUserId KUserId::currentEffectiveUserId() 0621 { 0622 return KUserId(geteuid()); 0623 } 0624 0625 KGroupId KGroupId::currentGroupId() 0626 { 0627 return KGroupId(getgid()); 0628 } 0629 0630 KGroupId KGroupId::currentEffectiveGroupId() 0631 { 0632 return KGroupId(getegid()); 0633 }