Skip to content

Commit b561815

Browse files
neunhoefjsteemann
andauthored
Get rid of getpwuid and friends to avoid nsswitch (arangodb#20447)
* Avoid nsswitch.conf for user and group lookup on Linux. * Fix compilation on Mac. * CHANGELOG. * Improve include situation. * Apply suggestions from code review Co-authored-by: Jan <jsteemann@users.noreply.github.com>
1 parent a43fcfa commit b561815

File tree

5 files changed

+142
-30
lines changed

5 files changed

+142
-30
lines changed

CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
devel
22
-----
33

4+
* No longer use getpwuid, getpwnam, getgrgid and getgrnam on Linux to
5+
avoid crashes due to /etc/nsswitch.conf on older Linux distributions.
6+
47
* Escape control characters (e.g. newlines) in the query strings that are
58
logged in an instance's audit log.
69

arangod/RestServer/PrivilegeFeature.cpp

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include "Basics/application-exit.h"
3030
#include "Basics/error.h"
31+
#include "Basics/FileUtils.h"
3132

3233
#ifdef TRI_HAVE_UNISTD_H
3334
#include <unistd.h>
@@ -136,21 +137,18 @@ void PrivilegeFeature::extractPrivileges() {
136137

137138
if (valid && gidNumber >= 0) {
138139
#ifdef ARANGODB_HAVE_GETGRGID
139-
group* g = getgrgid(gidNumber);
140-
141-
if (g == nullptr) {
140+
std::optional<gid_t> gid = FileUtils::findGroup(_gid);
141+
if (!gid) {
142142
LOG_TOPIC("3d53b", FATAL, arangodb::Logger::FIXME)
143143
<< "unknown numeric gid '" << _gid << "'";
144144
FATAL_ERROR_EXIT();
145145
}
146146
#endif
147147
} else {
148148
#ifdef ARANGODB_HAVE_GETGRNAM
149-
std::string name = _gid;
150-
group* g = getgrnam(name.c_str());
151-
152-
if (g != nullptr) {
153-
gidNumber = g->gr_gid;
149+
std::optional<gid_t> gid = FileUtils::findGroup(_gid);
150+
if (gid) {
151+
gidNumber = gid.value();
154152
} else {
155153
TRI_set_errno(TRI_ERROR_SYS_ERROR);
156154
LOG_TOPIC("20096", FATAL, arangodb::Logger::FIXME)
@@ -179,20 +177,18 @@ void PrivilegeFeature::extractPrivileges() {
179177

180178
if (valid) {
181179
#ifdef ARANGODB_HAVE_GETPWUID
182-
passwd* p = getpwuid(uidNumber);
183-
184-
if (p == nullptr) {
180+
std::optional<uid_t> uid = FileUtils::findUser(_uid);
181+
if (!uid) {
185182
LOG_TOPIC("09f8d", FATAL, arangodb::Logger::FIXME)
186183
<< "unknown numeric uid '" << _uid << "'";
187184
FATAL_ERROR_EXIT();
188185
}
189186
#endif
190187
} else {
191188
#ifdef ARANGODB_HAVE_GETPWNAM
192-
passwd* p = getpwnam(_uid.c_str());
193-
194-
if (p != nullptr) {
195-
uidNumber = p->pw_uid;
189+
std::optional<uid_t> uid = FileUtils::findUser(_uid);
190+
if (uid) {
191+
uidNumber = uid.value();
196192
} else {
197193
LOG_TOPIC("d54b7", FATAL, arangodb::Logger::FIXME)
198194
<< "cannot convert username '" << _uid << "' to numeric uid";
@@ -215,10 +211,10 @@ void PrivilegeFeature::dropPrivilegesPermanently() {
215211
defined(ARANGODB_HAVE_SETUID)
216212
// clear all supplementary groups
217213
if (!_gid.empty() && !_uid.empty()) {
218-
struct passwd* pwent = getpwuid(_numericUid);
214+
std::optional<std::string> name = FileUtils::findUserName(_numericUid);
219215

220-
if (pwent != nullptr) {
221-
initgroups(pwent->pw_name, _numericGid);
216+
if (name) {
217+
initgroups(name.value().c_str(), _numericGid);
222218
}
223219
}
224220
#endif

lib/Basics/FileUtils.cpp

Lines changed: 101 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333

3434
#include "FileUtils.h"
3535

36-
#include "Basics/operating-system.h"
3736
#include "Basics/process-utils.h"
37+
#include "Basics/NumberUtils.h"
3838

3939
#ifdef TRI_HAVE_DIRENT_H
4040
#include <dirent.h>
@@ -756,17 +756,17 @@ void makePathAbsolute(std::string& path) {
756756
}
757757
}
758758

759-
std::string slurpProgram(std::string const& program) {
759+
namespace {
760+
761+
std::string slurpProgramInternal(std::string const& program,
762+
std::vector<std::string> const& moreArgs) {
760763
ExternalProcess const* process;
761764
ExternalId external;
762765
ExternalProcessStatus res;
763766
std::string output;
764-
std::vector<std::string> moreArgs;
765767
std::vector<std::string> additionalEnv;
766768
char buf[1024];
767769

768-
moreArgs.push_back(std::string("version"));
769-
770770
TRI_CreateExternalProcess(program.c_str(), moreArgs, additionalEnv, true,
771771
&external);
772772
if (external._pid == TRI_INVALID_PROCESS_ID) {
@@ -804,4 +804,100 @@ std::string slurpProgram(std::string const& program) {
804804
return output;
805805
}
806806

807+
} // namespace
808+
809+
std::string slurpProgram(std::string const& program) {
810+
std::vector<std::string> moreArgs{std::string("version")};
811+
return slurpProgramInternal(program, moreArgs);
812+
}
813+
814+
#ifdef ARANGODB_HAVE_GETPWUID
815+
std::optional<uid_t> findUser(std::string const& nameOrId) noexcept {
816+
// We avoid getpwuid and getpwnam because they pose problems when
817+
// we build static binaries with glibc (because of /etc/nsswitch.conf).
818+
// However, we know that `id` exists for basically all Linux variants
819+
// and for Mac.
820+
try {
821+
std::vector<std::string> args{"-u", nameOrId};
822+
std::string output = slurpProgramInternal("/usr/bin/id", args);
823+
StringUtils::trimInPlace(output);
824+
bool valid = false;
825+
uid_t uidNumber = NumberUtils::atoi_positive<int>(
826+
output.data(), output.data() + output.size(), valid);
827+
if (valid) {
828+
return {uidNumber};
829+
}
830+
} catch (std::exception const&) {
831+
}
832+
return {std::nullopt};
833+
}
834+
835+
std::optional<std::string> findUserName(uid_t id) noexcept {
836+
#ifdef __APPLE__
837+
// For Mac we use the getpwuid function.
838+
struct passwd* pwent = getpwuid(id);
839+
if (pwent != nullptr) {
840+
return {std::string(pwent->pw_name)};
841+
}
842+
#else
843+
// For Linux (and other Unixes), we avoid this function because it
844+
// poses problems when we build static binaries with glibc (because of
845+
// /etc/nsswitch.conf).
846+
try {
847+
std::vector<std::string> args{"passwd", std::to_string(id)};
848+
std::string output = slurpProgramInternal("/usr/bin/getent", args);
849+
StringUtils::trimInPlace(output);
850+
auto parts = StringUtils::split(output, ':');
851+
if (parts.size() >= 1) {
852+
return {std::move(parts[0])};
853+
}
854+
} catch (std::exception const&) {
855+
}
856+
#endif
857+
return {std::nullopt};
858+
}
859+
#endif
860+
861+
#ifdef ARANGODB_HAVE_GETGRGID
862+
std::optional<gid_t> findGroup(std::string const& nameOrId) noexcept {
863+
#ifdef __APPLE__
864+
// For Mac we use the getgrgid and getgrnam functions.
865+
bool valid = false;
866+
int gidNumber = NumberUtils::atoi_positive<int>(
867+
nameOrId.data(), nameOrId.data() + nameOrId.size(), valid);
868+
869+
if (valid && gidNumber >= 0) {
870+
group* g = getgrgid(gidNumber);
871+
if (g != nullptr) {
872+
return {gidNumber};
873+
}
874+
} else {
875+
group* g = getgrnam(nameOrId.c_str());
876+
if (g != nullptr) {
877+
return {g->gr_gid};
878+
}
879+
}
880+
#else
881+
// For Linux (and other Unixes), we avoid these functions because they
882+
// pose problems when we build static binaries with glibc (because of
883+
// /etc/nsswitch.conf).
884+
try {
885+
std::vector<std::string> args{"group", nameOrId};
886+
std::string output = slurpProgramInternal("/usr/bin/getent", args);
887+
StringUtils::trimInPlace(output);
888+
auto parts = StringUtils::split(output, ':');
889+
if (parts.size() >= 3) {
890+
bool valid = false;
891+
uid_t gidNumber = NumberUtils::atoi_positive<int>(
892+
parts[2].data(), parts[2].data() + parts[2].size(), valid);
893+
if (valid) {
894+
return {gidNumber};
895+
}
896+
}
897+
} catch (std::exception const&) {
898+
}
899+
#endif
900+
return {std::nullopt};
901+
}
902+
#endif
807903
} // namespace arangodb::basics::FileUtils

lib/Basics/FileUtils.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,23 @@
2525

2626
#include <stddef.h>
2727
#include <functional>
28+
#include <optional>
2829
#include <string>
2930
#include <vector>
3031

3132
#include "Basics/Common.h"
3233
#include "Basics/FileResult.h"
3334
#include "Basics/FileResultString.h"
3435
#include "Basics/Result.h"
36+
#include "Basics/operating-system.h"
37+
38+
#ifdef ARANGODB_HAVE_GETGRGID
39+
#include <grp.h>
40+
#endif
41+
42+
#ifdef ARANGODB_HAVE_GETPWNAM
43+
#include <pwd.h>
44+
#endif
3545

3646
namespace arangodb::basics::FileUtils {
3747

@@ -154,4 +164,12 @@ std::string dirname(std::string const&);
154164
// returns the output of a program
155165
std::string slurpProgram(std::string const& program);
156166

167+
#ifdef ARANGODB_HAVE_GETPWUID
168+
std::optional<uid_t> findUser(std::string const& nameOrId) noexcept;
169+
std::optional<std::string> findUserName(uid_t id) noexcept;
170+
#endif
171+
#ifdef ARANGODB_HAVE_GETGRGID
172+
std::optional<gid_t> findGroup(std::string const& nameOrId) noexcept;
173+
#endif
174+
157175
} // namespace arangodb::basics::FileUtils

lib/Logger/LoggerFeature.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "LoggerFeature.h"
4040

4141
#include "ApplicationFeatures/ApplicationServer.h"
42+
#include "Basics/FileUtils.h"
4243
#include "Basics/NumberUtils.h"
4344
#include "Basics/StringUtils.h"
4445
#include "Basics/Thread.h"
@@ -655,20 +656,18 @@ void LoggerFeature::validateOptions(std::shared_ptr<ProgramOptions> options) {
655656

656657
if (valid && gidNumber >= 0) {
657658
#ifdef ARANGODB_HAVE_GETGRGID
658-
group* g = getgrgid(gidNumber);
659-
660-
if (g == nullptr) {
659+
std::optional<gid_t> gid = FileUtils::findGroup(_fileGroup);
660+
if (!gid) {
661661
LOG_TOPIC("174c2", FATAL, arangodb::Logger::FIXME)
662662
<< "unknown numeric gid '" << _fileGroup << "'";
663663
FATAL_ERROR_EXIT();
664664
}
665665
#endif
666666
} else {
667667
#ifdef ARANGODB_HAVE_GETGRNAM
668-
group* g = getgrnam(_fileGroup.c_str());
669-
670-
if (g != nullptr) {
671-
gidNumber = g->gr_gid;
668+
std::optional<gid_t> gid = FileUtils::findGroup(_fileGroup);
669+
if (gid) {
670+
gidNumber = gid.value();
672671
} else {
673672
TRI_set_errno(TRI_ERROR_SYS_ERROR);
674673
LOG_TOPIC("11a2c", FATAL, arangodb::Logger::FIXME)

0 commit comments

Comments
 (0)