Skip to content

Commit ad92855

Browse files
committed
Daemon user creation for Alpine and Illumos and general user creation improvements
1 parent a4ecafa commit ad92855

File tree

6 files changed

+164
-47
lines changed

6 files changed

+164
-47
lines changed

cmake/detect_system.cmake

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -82,19 +82,70 @@ check_cxx_source_compiles("
8282
}"
8383
HAVE_ABI_CXA_THROW)
8484

85+
if (NOT DEFINED USERADD_PATH)
86+
find_program(USERADD_PATH useradd PATHS /usr/sbin /bin NO_DEFAULT_PATH)
87+
if (USERADD_PATH)
88+
message(STATUS "Looking for useradd - found at ${USERADD_PATH}")
89+
else()
90+
message(STATUS "Looking for useradd - not found")
91+
endif()
92+
endif()
93+
94+
if (NOT DEFINED GROUPADD_PATH)
95+
find_program(GROUPADD_PATH groupadd PATHS /usr/sbin /bin NO_DEFAULT_PATH)
96+
if (GROUPADD_PATH)
97+
message(STATUS "Looking for groupadd - found at ${GROUPADD_PATH}")
98+
else()
99+
message(STATUS "Looking for groupadd - not found")
100+
endif()
101+
endif()
85102

86-
find_program(USERADD_PATH useradd PATHS /usr/sbin NO_DEFAULT_PATH)
87-
if (USERADD_PATH)
88-
set(HAVE_USERADD ON CACHE INTERNAL "Whether platform has useradd command")
89-
else()
90-
set(HAVE_USERADD OFF CACHE INTERNAL "Whether platform has useradd command")
103+
if (NOT DEFINED PW_PATH)
104+
find_program(PW_PATH pw PATHS /usr/sbin NO_DEFAULT_PATH)
105+
if (PW_PATH)
106+
message(STATUS "Looking for pw - found at ${PW_PATH}")
107+
else()
108+
message(STATUS "Looking for pw - not found")
109+
endif()
91110
endif()
92111

93-
find_program(PW_PATH pw PATHS /usr/sbin NO_DEFAULT_PATH)
94-
if (PW_PATH)
95-
set(HAVE_PW ON CACHE INTERNAL "Whether platform has pw command")
96-
else()
97-
set(HAVE_PW OFF CACHE INTERNAL "Whether platform has pw command")
112+
# Alpine Linux needs special treatment
113+
114+
if (NOT DEFINED IS_ALPINE_LINUX)
115+
if(EXISTS "/etc/os-release")
116+
file(STRINGS "/etc/os-release" OS_RELEASE_CONTENTS)
117+
foreach(line IN LISTS OS_RELEASE_CONTENTS)
118+
if(line MATCHES "^ID=alpine$")
119+
set(IS_ALPINE_LINUX TRUE CACHE INTERNAL "whether this is Alpine Linux")
120+
message(STATUS "This is Alpine Linux")
121+
break()
122+
endif()
123+
endforeach()
124+
endif()
125+
if (NOT DEFINED IS_ALPINE_LINUX)
126+
set(IS_ALPINE_LINUX FALSE CACHE INTERNAL "whether this is Alpine Linux")
127+
endif()
128+
endif()
129+
130+
if(IS_ALPINE_LINUX)
131+
if (NOT DEFINED ADDUSER_PATH)
132+
find_program(ADDUSER_PATH adduser PATHS /usr/sbin NO_DEFAULT_PATH)
133+
if (ADDUSER_PATH)
134+
message(STATUS "Looking for adduser - found at ${ADDUSER_PATH}")
135+
else()
136+
message(STATUS "Looking for adduser - not found")
137+
endif()
138+
endif()
139+
140+
if (NOT DEFINED ADDGROUP_PATH)
141+
find_program(ADDGROUP_PATH addgroup PATHS /usr/sbin NO_DEFAULT_PATH)
142+
if (ADDGROUP_PATH)
143+
message(STATUS "Looking for addgroup - found at ${ADDGROUP_PATH}")
144+
else()
145+
message(STATUS "Looking for addgroup - not found")
146+
endif()
147+
endif()
148+
98149
endif()
99150

100151
if (WSDDN_WITH_SYSTEMD STREQUAL "yes" OR WSDDN_WITH_SYSTEMD STREQUAL "auto" AND NOT DEFINED CACHE{HAVE_SYSTEMD})

src/app_state.cpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,24 @@ void AppState::init() {
6969
if (getuid() == 0) {
7070

7171
if (!m_currentCommandLine.runAs) {
72-
#if CAN_CREATE_USERS
7372
WSDLOG_DEBUG("Running as root but no account to run under is specified in configuration. Using {}", WSDDN_DEFAULT_USER_NAME);
7473
auto pwd = ptl::Passwd::getByName(WSDDN_DEFAULT_USER_NAME);
7574
if (pwd) {
7675
m_currentCommandLine.runAs = Identity(pwd->pw_uid, pwd->pw_gid);
7776
} else {
78-
WSDLOG_INFO("User {} does not exist, creating", WSDDN_DEFAULT_USER_NAME);
77+
WSDLOG_INFO("User {} does not exist, trying to create", WSDDN_DEFAULT_USER_NAME);
7978
m_currentCommandLine.runAs = Identity::createDaemonUser(WSDDN_DEFAULT_USER_NAME);
79+
80+
if (!m_currentCommandLine.runAs) {
81+
WSDLOG_INFO("User creation is not supported on this platform");
82+
WSDLOG_CRITICAL("Running network service as a root is extremely insecure and is not allowed.\n"
83+
"Please use one of the following approaches: \n"
84+
" * pass `--user username` command line option to specify which account to run network code under\n"
85+
" * start " WSDDN_PROGNAME " under a non-root account (if using systemd consider DynamicUser approach)"
86+
);
87+
exit(EXIT_FAILURE);
88+
}
8089
}
81-
#else
82-
WSDLOG_CRITICAL("Running network service as a root is extremely insecure and is not allowed.\n"
83-
"Please use one of the following approaches: \n"
84-
" * pass `--user username` command line option to specify which account to run network code under\n"
85-
" * start " WSDDN_PROGNAME " under a non-root account (if using systemd consider DynamicUser approach)"
86-
);
87-
exit(EXIT_FAILURE);
88-
#endif
8990
}
9091
if (!m_currentCommandLine.chrootDir) {
9192
WSDLOG_DEBUG("Running as root but no chroot specified in configuration. Using {}", WSDDN_DEFAULT_CHROOT_DIR);

src/sys_config.h.in

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,26 @@
1414
#cmakedefine01 HAVE_CXXABI_H
1515
#cmakedefine01 HAVE_ABI_CXA_THROW
1616
#cmakedefine01 HAVE_SYSTEMD
17-
#cmakedefine01 HAVE_USERADD
18-
#cmakedefine01 HAVE_PW
17+
#cmakedefine01 IS_ALPINE_LINUX
1918

2019
#cmakedefine LIBSYSTEMD_SO "${LIBSYSTEMD_SO}"
2120

21+
#cmakedefine USERADD_PATH "${USERADD_PATH}"
22+
#cmakedefine GROUPADD_PATH "${GROUPADD_PATH}"
23+
#cmakedefine PW_PATH "${PW_PATH}"
24+
#cmakedefine ADDUSER_PATH "${ADDUSER_PATH}"
25+
#cmakedefine ADDGROUP_PATH "${ADDGROUP_PATH}"
2226

2327
#if (defined(__APPLE__) && defined(__MACH__))
2428
#define WSDDN_PLATFORM_APPLE 1
2529
#define ADMIN_GROUP_NAME "admin"
2630
#define WSDDN_DEFAULT_USER_NAME "_wsddn"
2731
#define WSDDN_DEFAULT_CHROOT_DIR "/var/empty"
2832
#define WSDDN_BUNDLE_IDENTIFIER "@WSDDN_BUNDLE_IDENTIFIER@"
33+
#elif defined(__FreeBSD__)
34+
#define WSDDN_PLATFORM_APPLE 0
35+
#define WSDDN_DEFAULT_USER_NAME "wsddn"
36+
#define WSDDN_DEFAULT_CHROOT_DIR "/var/empty"
2937
#elif defined(__OpenBSD__)
3038
#define WSDDN_PLATFORM_APPLE 0
3139
#define WSDDN_DEFAULT_USER_NAME "_wsddn"
@@ -34,6 +42,10 @@
3442
#define WSDDN_PLATFORM_APPLE 0
3543
#define WSDDN_DEFAULT_USER_NAME "_wsddn"
3644
#define WSDDN_DEFAULT_CHROOT_DIR "/var/chroot/wsddn"
45+
#elif defined(__sun) || defined(__HAIKU__)
46+
#define WSDDN_PLATFORM_APPLE 0
47+
#define WSDDN_DEFAULT_USER_NAME "wsddn"
48+
#define WSDDN_DEFAULT_CHROOT_DIR "/var/empty"
3749
#else
3850
#define WSDDN_PLATFORM_APPLE 0
3951
#define WSDDN_DEFAULT_USER_NAME "wsddn"
@@ -47,9 +59,6 @@
4759
#define HAVE_OS_LOG WSDDN_PLATFORM_APPLE
4860

4961

50-
#define CAN_CREATE_USERS HAVE_APPLE_USER_CREATION || HAVE_USERADD || HAVE_PW
51-
52-
5362
#define WSDDN_VERSION "@WSDDN_VERSION@"
5463
#define WSDDN_PROGNAME "$<TARGET_FILE_NAME:wsddn>"
5564

src/sys_util.cpp

Lines changed: 75 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,45 +10,104 @@ const char * OsLogHandle::s_category = "main";
1010

1111
#endif
1212

13-
#if HAVE_USERADD || HAVE_PW
13+
#if !HAVE_APPLE_USER_CREATION
1414

15-
auto Identity::createDaemonUser(const sys_string & name) -> Identity {
15+
static auto runCreateDaemonUserCommands([[maybe_unused]] const sys_string & name) -> bool {
16+
17+
#if defined(__linux__) && defined(USERADD_PATH)
18+
19+
(void)run({USERADD_PATH, "-r", "-d", WSDDN_DEFAULT_CHROOT_DIR, "-s", "/bin/false", name.c_str()});
20+
return true;
21+
22+
#elif defined(__linux__) && defined(IS_ALPINE_LINUX) && defined(ADDUSER_PATH) && defined(ADDGROUP_PATH)
23+
24+
//The second addgroup instead of -G for adduser is necessary since for some reason -G doesn't
25+
//modify /etc/group when run from here
26+
(void)run({ADDGROUP_PATH, "-S", name.c_str()});
27+
(void)run({ADDUSER_PATH, "-S", "-D", "-H", "-h", "/var/empty", "-s", "/sbin/nologin", "-g", name.c_str(), name.c_str()});
28+
(void)run({ADDGROUP_PATH, name.c_str(), name.c_str()});
29+
return true;
30+
31+
#elif (defined(__OpenBSD__) || defined(__NetBSD__)) && defined(USERADD_PATH)
1632

17-
#if HAVE_USERADD
18-
#ifdef __linux__
19-
sys_string command = S("useradd -r -d " WSDDN_DEFAULT_CHROOT_DIR " -s /bin/false '") + name + S("'");
20-
#else
21-
sys_string command = S("useradd -L daemon -g =uid -d " WSDDN_DEFAULT_CHROOT_DIR " -s /sbin/nologin -c \"WS-Discovery Daemon\" '") + name + S("'");
2233
createMissingDirs(WSDDN_DEFAULT_CHROOT_DIR, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP, Identity::admin());
34+
(void)run({USERADD_PATH, "-L", "daemon", "-g", "=uid", "-d", WSDDN_DEFAULT_CHROOT_DIR, "-s", "/sbin/nologin", "-c", "WS-Discovery Daemon", name.c_str()});
35+
return true;
36+
37+
#elif defined(__HAIKU__) && defined(USERADD_PATH) && defined(GROUPADD_PATH)
38+
39+
(void)run({GROUPADD_PATH, name.c_str()});
40+
(void)run({USERADD_PATH, "-g", name.c_str(), "-d", WSDDN_DEFAULT_CHROOT_DIR, "-s", "/bin/false", "-n", "WS-Discovery Daemon", name.c_str()});
41+
return true;
42+
43+
#elif defined(__FreeBSD__) && defined(PW_PATH)
44+
45+
(void)run({PW_PATH, "adduser", name.c_str(), "-d", WSDDN_DEFAULT_CHROOT_DIR, "-s", "/usr/sbin/nologin", "-c", "WS-Discovery Daemon User"});
46+
return true;
47+
48+
#elif defined(__sun) && defined(USERADD_PATH) && defined(GROUPADD_PATH)
49+
50+
(void)run({GROUPADD_PATH, name.c_str()});
51+
(void)run({USERADD_PATH, "-g", name.c_str(), "-d", WSDDN_DEFAULT_CHROOT_DIR, "-s", "/bin/false", "-c", "WS-Discovery Daemon User", name.c_str()});
52+
return true;
53+
54+
#else
55+
56+
return false;
57+
2358
#endif
24-
#elif HAVE_PW
25-
sys_string command = S("pw adduser '") + name + S("' -d " WSDDN_DEFAULT_CHROOT_DIR " -s /bin/false -c \"WS-Discovery Daemon User\"");
59+
}
60+
61+
auto Identity::createDaemonUser(const sys_string & name) -> std::optional<Identity> {
62+
63+
if (!runCreateDaemonUserCommands(name))
64+
return {};
65+
auto pwd = ptl::Passwd::getByName(name);
66+
if (!pwd)
67+
throw std::runtime_error(fmt::format("unable to create user {}", name));
68+
return Identity(pwd->pw_uid, pwd->pw_gid);
69+
}
70+
2671
#endif
27-
(void)!system(command.c_str());
28-
auto pwd = ptl::Passwd::getByName(name);
29-
if (!pwd)
30-
throw std::runtime_error(fmt::format("unable to create user {}", name));
31-
return Identity(pwd->pw_uid, pwd->pw_gid);
32-
}
3372

73+
int run(const ptl::StringRefArray & args) {
74+
ptl::SpawnAttr spawnAttr;
75+
#ifndef __HAIKU__
76+
spawnAttr.setFlags(POSIX_SPAWN_SETSIGDEF);
77+
auto sigs = ptl::SignalSet::all();
78+
sigs.del(SIGKILL);
79+
sigs.del(SIGSTOP);
80+
spawnAttr.setSigDefault(sigs);
3481
#endif
82+
83+
auto proc = spawn(args, ptl::SpawnSettings().attr(spawnAttr).usePath());
84+
85+
auto stat = proc.wait().value();
86+
if (WIFEXITED(stat))
87+
return WEXITSTATUS(stat);
88+
if (WIFSIGNALED(stat))
89+
return 128+WTERMSIG(stat);
90+
throw std::runtime_error(fmt::format("`{} finished with status 0x{:X}`", args, stat));
91+
}
3592

3693
void shell(const ptl::StringRefArray & args, bool suppressStdErr, std::function<void (const ptl::FileDescriptor & fd)> reader) {
3794
auto [read, write] = ptl::Pipe::create();
3895
ptl::SpawnAttr spawnAttr;
96+
#ifndef __HAIKU__
3997
spawnAttr.setFlags(POSIX_SPAWN_SETSIGDEF);
4098
auto sigs = ptl::SignalSet::all();
4199
sigs.del(SIGKILL);
42100
sigs.del(SIGSTOP);
43101
spawnAttr.setSigDefault(sigs);
102+
#endif
44103

45104
ptl::SpawnFileActions act;
46105
act.addDuplicateTo(write, stdout);
47106
act.addClose(read);
48107
if (suppressStdErr) {
49108
act.addOpen(stderr, "/dev/null", O_WRONLY, 0);
50109
}
51-
auto proc = spawn(args, ptl::SpawnSettings().fileActions(act).usePath());
110+
auto proc = spawn(args, ptl::SpawnSettings().attr(spawnAttr).fileActions(act).usePath());
52111
write.close();
53112

54113
reader(read);

src/sys_util.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,7 @@ class Identity {
3131
#endif
3232
}
3333

34-
#if CAN_CREATE_USERS
35-
36-
static auto createDaemonUser(const sys_string & name) -> Identity;
37-
38-
#endif
34+
static auto createDaemonUser(const sys_string & name) -> std::optional<Identity>;
3935

4036
void setMyIdentity() const {
4137
ptl::setGroups({});
@@ -202,6 +198,7 @@ class LineReader {
202198
Sink m_sink;
203199
};
204200

201+
int run(const ptl::StringRefArray & args);
205202
void shell(const ptl::StringRefArray & args, bool suppressStdErr, std::function<void (const ptl::FileDescriptor & fd)> reader);
206203

207204

src/sys_util_mac.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ static auto createRecordWithUniqueId(const cf_ptr<ODNodeRef> & localNode,
201201
return {record, idValue};
202202
}
203203

204-
auto Identity::createDaemonUser(const sys_string & name) -> Identity {
204+
auto Identity::createDaemonUser(const sys_string & name) -> std::optional<Identity> {
205205

206206
cf_ptr<CFErrorRef> err;
207207

0 commit comments

Comments
 (0)