Skip to content

Commit da908a0

Browse files
wolfsshd: make the supplementary-group drop reliable and tested
1 parent b8e2fd3 commit da908a0

3 files changed

Lines changed: 315 additions & 66 deletions

File tree

apps/wolfsshd/auth.c

Lines changed: 99 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,16 @@
9393
#endif
9494

9595
#if defined(WOLFSSHD_UNIT_TEST) && !defined(_WIN32)
96+
/* Adapts the platform setgroups(2) to a fixed prototype so unit tests can
97+
* swap in a stub regardless of the size argument's native type (int on
98+
* macOS, size_t on Linux). */
99+
static int wsshd_setgroups_default(int size, const WGID_T* list)
100+
{
101+
return setgroups(size, list);
102+
}
96103
int (*wsshd_setregid_cb)(WGID_T, WGID_T) = setregid;
97104
int (*wsshd_setreuid_cb)(WUID_T, WUID_T) = setreuid;
105+
int (*wsshd_setgroups_cb)(int, const WGID_T*) = wsshd_setgroups_default;
98106
#endif
99107

100108
struct WOLFSSHD_AUTH {
@@ -1626,7 +1634,7 @@ static int RequestAuthentication(WS_UserAuthData* authData,
16261634
ret = WOLFSSH_USERAUTH_REJECTED;
16271635
}
16281636
else {
1629-
#ifdef WIN32
1637+
#ifdef _WIN32
16301638
/* Still need to get users token on Windows */
16311639
wolfSSH_Log(WS_LOG_INFO,
16321640
"[SSHD] Relying on CA for public key check");
@@ -1945,7 +1953,7 @@ int wolfSSHD_AuthRaisePermissions(WOLFSSHD_AUTH* auth)
19451953
int ret = 0;
19461954

19471955
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Attempting to raise permissions level");
1948-
#ifndef WIN32
1956+
#ifndef _WIN32
19491957
if (auth) {
19501958
if (setegid(auth->sGid) != 0) {
19511959
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error raising gid");
@@ -1970,7 +1978,7 @@ int wolfSSHD_AuthRaisePermissions(WOLFSSHD_AUTH* auth)
19701978
int wolfSSHD_AuthReducePermissionsUser(WOLFSSHD_AUTH* auth, WUID_T uid,
19711979
WGID_T gid)
19721980
{
1973-
#ifndef WIN32
1981+
#ifndef _WIN32
19741982
#ifdef WOLFSSHD_UNIT_TEST
19751983
if (wsshd_setregid_cb(gid, gid) != 0) {
19761984
#else
@@ -2005,7 +2013,7 @@ int wolfSSHD_AuthReducePermissions(WOLFSSHD_AUTH* auth)
20052013
}
20062014

20072015
flag = wolfSSHD_ConfigGetPrivilegeSeparation(auth->conf);
2008-
#ifndef WIN32
2016+
#ifndef _WIN32
20092017
if (flag == WOLFSSHD_PRIV_SEPARAT || flag == WOLFSSHD_PRIV_SANDBOX) {
20102018
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Lowering permissions level");
20112019

@@ -2023,13 +2031,25 @@ int wolfSSHD_AuthReducePermissions(WOLFSSHD_AUTH* auth)
20232031
return ret;
20242032
}
20252033

2026-
#ifndef WIN32
2034+
#ifndef _WIN32
20272035
#if defined(__OSX__) || defined( __APPLE__)
20282036
#define WGETGROUPLIST(x,y,z,w) getgrouplist((x),(y),(int*)(z),(w))
20292037
#else
20302038
#define WGETGROUPLIST(x,y,z,w) getgrouplist((x),(y),(z),(w))
20312039
#endif
20322040

2041+
#if defined(WOLFSSHD_UNIT_TEST) && !defined(_WIN32)
2042+
/* Adapts getgrouplist(3) to a fixed prototype so unit tests can swap in a
2043+
* stub, hiding the macOS int/gid_t argument difference behind WGETGROUPLIST. */
2044+
static int wsshd_getgrouplist_default(const char* usr, WGID_T grp,
2045+
WGID_T* groups, int* ngroups)
2046+
{
2047+
return WGETGROUPLIST(usr, grp, groups, ngroups);
2048+
}
2049+
int (*wsshd_getgrouplist_cb)(const char*, WGID_T, WGID_T*, int*)
2050+
= wsshd_getgrouplist_default;
2051+
#endif
2052+
20332053
/* Initial guess and upper bound for the number of groups a user can be in.
20342054
* getgrouplist cannot be reliably sized with a NULL probe (macOS returns
20352055
* success with size 0 and, when the buffer is too small, echoes the input size
@@ -2042,39 +2062,19 @@ int wolfSSHD_AuthReducePermissions(WOLFSSHD_AUTH* auth)
20422062
#define WOLFSSHD_GROUP_LIST_MAX 65536
20432063
#endif
20442064

2045-
/* frees a group name array previously built by wolfSSHD_GetUserGroupNames */
2046-
WOLFSSHD_STATIC void wolfSSHD_FreeUserGroupNames(void* heap, char** names,
2047-
word32 count)
2048-
{
2049-
word32 i;
2050-
2051-
if (names != NULL) {
2052-
for (i = 0; i < count; i++) {
2053-
WFREE(names[i], heap, DYNTYPE_SSHD);
2054-
}
2055-
WFREE(names, heap, DYNTYPE_SSHD);
2056-
}
2057-
}
2058-
2059-
/* Builds the list of group names the user belongs to, primary plus
2060-
* supplementary, so Match Group can be evaluated against the full set. On
2061-
* success sets *outNames to an owned array of owned names and *outCount to the
2062-
* entry count; the caller frees them with wolfSSHD_FreeUserGroupNames. Returns
2063-
* WS_SUCCESS on success, leaving *outNames NULL on failure. */
2064-
WOLFSSHD_STATIC int wolfSSHD_GetUserGroupNames(void* heap, const char* usr,
2065-
WGID_T primaryGid, char*** outNames, word32* outCount)
2065+
/* Resolves the user's full gid list into an owned buffer via grow-and-retry
2066+
* sizing, since a NULL-size probe is unreliable (macOS reports size 0). Sets
2067+
* *outList (caller frees) and *outCount; returns WS_SUCCESS. */
2068+
static int wolfSSHD_GetUserGroupList(void* heap, const char* usr,
2069+
WGID_T primaryGid, gid_t** outList, int* outCount)
20662070
{
20672071
int ret = WS_SUCCESS;
20682072
int grpListSz = 0;
20692073
int allocSz;
20702074
int res;
2071-
int i;
20722075
gid_t* grpList = NULL;
2073-
char** names = NULL;
2074-
struct group* g;
2075-
word32 count = 0;
20762076

2077-
*outNames = NULL;
2077+
*outList = NULL;
20782078
*outCount = 0;
20792079

20802080
#if defined(__QNX__) || defined(__QNXNTO__)
@@ -2092,7 +2092,11 @@ WOLFSSHD_STATIC int wolfSSHD_GetUserGroupNames(void* heap, const char* usr,
20922092
}
20932093
if (ret == WS_SUCCESS) {
20942094
grpListSz = allocSz;
2095+
#ifdef WOLFSSHD_UNIT_TEST
2096+
res = wsshd_getgrouplist_cb(usr, primaryGid, grpList, &grpListSz);
2097+
#else
20952098
res = WGETGROUPLIST(usr, primaryGid, grpList, &grpListSz);
2099+
#endif
20962100
if (res != 0) {
20972101
ret = WS_FATAL_ERROR;
20982102
}
@@ -2111,7 +2115,11 @@ WOLFSSHD_STATIC int wolfSSHD_GetUserGroupNames(void* heap, const char* usr,
21112115
}
21122116

21132117
grpListSz = allocSz;
2118+
#ifdef WOLFSSHD_UNIT_TEST
2119+
res = wsshd_getgrouplist_cb(usr, primaryGid, grpList, &grpListSz);
2120+
#else
21142121
res = WGETGROUPLIST(usr, primaryGid, grpList, &grpListSz);
2122+
#endif
21152123
if (res < 0) {
21162124
/* buffer too small: discard, grow, and retry up to the cap */
21172125
WFREE(grpList, heap, DYNTYPE_SSHD);
@@ -2128,6 +2136,51 @@ WOLFSSHD_STATIC int wolfSSHD_GetUserGroupNames(void* heap, const char* usr,
21282136
}
21292137
#endif
21302138

2139+
if (ret == WS_SUCCESS) {
2140+
*outList = grpList;
2141+
*outCount = grpListSz;
2142+
}
2143+
else if (grpList != NULL) {
2144+
WFREE(grpList, heap, DYNTYPE_SSHD);
2145+
}
2146+
2147+
return ret;
2148+
}
2149+
2150+
/* frees a group name array previously built by wolfSSHD_GetUserGroupNames */
2151+
WOLFSSHD_STATIC void wolfSSHD_FreeUserGroupNames(void* heap, char** names,
2152+
word32 count)
2153+
{
2154+
word32 i;
2155+
2156+
if (names != NULL) {
2157+
for (i = 0; i < count; i++) {
2158+
WFREE(names[i], heap, DYNTYPE_SSHD);
2159+
}
2160+
WFREE(names, heap, DYNTYPE_SSHD);
2161+
}
2162+
}
2163+
2164+
/* Builds the owned list of group names the user belongs to (primary plus
2165+
* supplementary) for Match Group evaluation; freed with
2166+
* wolfSSHD_FreeUserGroupNames. Returns WS_SUCCESS, *outNames NULL on failure. */
2167+
WOLFSSHD_STATIC int wolfSSHD_GetUserGroupNames(void* heap, const char* usr,
2168+
WGID_T primaryGid, char*** outNames, word32* outCount)
2169+
{
2170+
int ret;
2171+
int grpListSz = 0;
2172+
int i;
2173+
gid_t* grpList = NULL;
2174+
char** names = NULL;
2175+
struct group* g;
2176+
word32 count = 0;
2177+
2178+
*outNames = NULL;
2179+
*outCount = 0;
2180+
2181+
ret = wolfSSHD_GetUserGroupList(heap, usr, primaryGid, &grpList,
2182+
&grpListSz);
2183+
21312184
if (ret == WS_SUCCESS) {
21322185
names = (char**)WMALLOC(sizeof(char*) * grpListSz, heap, DYNTYPE_SSHD);
21332186
if (names == NULL) {
@@ -2167,52 +2220,32 @@ WOLFSSHD_STATIC int wolfSSHD_GetUserGroupNames(void* heap, const char* usr,
21672220

21682221
return ret;
21692222
}
2170-
#endif /* WIN32 */
2223+
#endif /* _WIN32 */
21712224

21722225
/* sets the extended groups the user is in, returns WS_SUCCESS on success */
21732226
int wolfSSHD_AuthSetGroups(const WOLFSSHD_AUTH* auth, const char* usr,
21742227
WGID_T gid)
21752228
{
21762229
int ret = WS_SUCCESS;
2177-
#ifndef WIN32
2230+
#ifndef _WIN32
21782231
int grpListSz = 0;
21792232
gid_t* grpList = NULL;
21802233

2181-
#if defined(__QNX__) || defined(__QNXNTO__)
2182-
/* QNX does not support getting the exact group list size ahead of time,
2183-
only the max group list size */
2184-
grpListSz = sysconf( _SC_NGROUPS_MAX );
2234+
/* resolve the full group list with portable grow-and-retry sizing, then
2235+
* apply it; a NULL-size probe is unreliable and would skip the drop. */
2236+
ret = wolfSSHD_GetUserGroupList(auth->heap, usr, gid, &grpList, &grpListSz);
2237+
if (ret == WS_SUCCESS) {
2238+
#ifdef WOLFSSHD_UNIT_TEST
2239+
if (wsshd_setgroups_cb(grpListSz, grpList) == -1) {
21852240
#else
2186-
/* should return -1 if grpListSz is smaller than actual groups */
2187-
if (WGETGROUPLIST(usr, gid, NULL, &grpListSz) == -1)
2241+
if (setgroups(grpListSz, grpList) == -1) {
21882242
#endif
2189-
{
2190-
grpList = (gid_t*)WMALLOC(sizeof(gid_t) * grpListSz, auth->heap,
2191-
DYNTYPE_SSHD);
2192-
if (grpList == NULL) {
2193-
ret = WS_MEMORY_E;
2194-
}
2195-
else {
2196-
int res;
2197-
2198-
res = WGETGROUPLIST(usr, gid, grpList, &grpListSz);
2199-
#if defined(__QNX__) || defined(__QNXNTO__)
2200-
if (res != 0) {
2201-
ret = WS_FATAL_ERROR;
2202-
}
2203-
#else
2204-
if (res != grpListSz) {
2205-
ret = WS_FATAL_ERROR;
2206-
}
2207-
#endif
2208-
2209-
if (ret == WS_SUCCESS &&
2210-
setgroups(grpListSz, grpList) == -1) {
2211-
ret = WS_FATAL_ERROR;
2212-
}
2213-
WFREE(grpList, auth->heap, DYNTYPE_SSHD);
2243+
ret = WS_FATAL_ERROR;
22142244
}
22152245
}
2246+
if (grpList != NULL) {
2247+
WFREE(grpList, auth->heap, DYNTYPE_SSHD);
2248+
}
22162249
#else
22172250
WOLFSSH_UNUSED(auth);
22182251
WOLFSSH_UNUSED(usr);
@@ -2247,7 +2280,7 @@ WOLFSSHD_CONFIG* wolfSSHD_AuthGetUserConf(const WOLFSSHD_AUTH* auth,
22472280

22482281
if (auth != NULL) {
22492282
if (usr != NULL) {
2250-
#ifdef WIN32
2283+
#ifdef _WIN32
22512284
/* LogonUserEx(): group lookup is not implemented on Windows, so
22522285
* Match Group directives do not apply here */
22532286
#else
@@ -2271,7 +2304,7 @@ WOLFSSHD_CONFIG* wolfSSHD_AuthGetUserConf(const WOLFSSHD_AUTH* auth,
22712304
ret = wolfSSHD_GetUserConf(auth->conf, usr, (const char**)grpNames,
22722305
grpCount, host, localAdr, localPort, RDomain, adr);
22732306

2274-
#ifndef WIN32
2307+
#ifndef _WIN32
22752308
wolfSSHD_FreeUserGroupNames(auth->heap, grpNames, grpCount);
22762309
#endif
22772310
}

apps/wolfsshd/auth.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ int wolfSSHD_OpenSecureFile(const char* path, WUID_T ownerUid,
9090
#ifndef _WIN32
9191
extern int (*wsshd_setregid_cb)(WGID_T, WGID_T);
9292
extern int (*wsshd_setreuid_cb)(WUID_T, WUID_T);
93+
extern int (*wsshd_setgroups_cb)(int, const WGID_T*);
94+
extern int (*wsshd_getgrouplist_cb)(const char*, WGID_T, WGID_T*, int*);
9395
int wolfSSHD_GetUserGroupNames(void* heap, const char* usr, WGID_T primaryGid,
9496
char*** outNames, word32* outCount);
9597
void wolfSSHD_FreeUserGroupNames(void* heap, char** names, word32 count);

0 commit comments

Comments
 (0)