Skip to content

Commit 00e8552

Browse files
yosuke-wolfsslejohnstown
authored andcommitted
Mask special bits from peer-supplied SCP receive mode
1 parent f2b4f43 commit 00e8552

3 files changed

Lines changed: 97 additions & 2 deletions

File tree

src/wolfscp.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,8 +1005,9 @@ static int GetScpFileMode(WOLFSSH* ssh, byte* buf, word32 bufSz,
10051005
}
10061006

10071007
if (ret == WS_SUCCESS) {
1008-
/* store file mode */
1009-
ssh->scpFileMode = mode;
1008+
/* store file mode, masking off setuid/setgid/sticky bits from the
1009+
* peer-supplied value to match the send path */
1010+
ssh->scpFileMode = mode & WOLFSSH_MODE_MASK;
10101011
/* eat trailing space */
10111012
if (bufSz >= (word32)(idx +1))
10121013
idx++;
@@ -1018,6 +1019,15 @@ static int GetScpFileMode(WOLFSSH* ssh, byte* buf, word32 bufSz,
10181019
}
10191020

10201021

1022+
#ifdef WOLFSSH_TEST_INTERNAL
1023+
int wolfSSH_TestScpGetFileMode(WOLFSSH* ssh, byte* buf, word32 bufSz,
1024+
word32* inOutIdx)
1025+
{
1026+
return GetScpFileMode(ssh, buf, bufSz, inOutIdx);
1027+
}
1028+
#endif /* WOLFSSH_TEST_INTERNAL */
1029+
1030+
10211031
/* Locates first space present in given string (buf) and sets inOutIdx
10221032
* to that offset.
10231033
*

tests/unit.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,6 +1222,81 @@ static int test_DoUserAuthBanner(void)
12221222
return result;
12231223
}
12241224

1225+
#if defined(WOLFSSH_TEST_INTERNAL) && defined(WOLFSSH_SCP)
1226+
/* Verify GetScpFileMode strips setuid/setgid/sticky bits from a peer-supplied
1227+
* SCP C/D record mode, matching the masking already done on the send path.
1228+
* The receive path cannot be exercised end-to-end because both peers mask the
1229+
* mode before transmitting, so this drives the parser directly. */
1230+
static int test_ScpGetFileMode(void)
1231+
{
1232+
WOLFSSH_CTX* ctx = NULL;
1233+
WOLFSSH* ssh = NULL;
1234+
static const char* hdrs[] = {
1235+
"C4755 0 f\n", /* setuid set */
1236+
"D2755 0 d\n", /* setgid set */
1237+
"D1755 0 d\n", /* sticky set */
1238+
"D7777 0 d\n", /* all special bits set */
1239+
"C0644 0 f\n" /* ordinary mode, unaffected */
1240+
};
1241+
static const int expected[] = { 0755, 0755, 0755, 0777, 0644 };
1242+
/* records the parser must reject */
1243+
static const char* badHdrs[] = {
1244+
"C8755 0 f\n", /* '8' is not an octal digit */
1245+
"X4755 0 f\n", /* prefix is neither 'C' nor 'D' */
1246+
"C75" /* shorter than the mode field */
1247+
};
1248+
int result = 0;
1249+
int ret;
1250+
int i;
1251+
word32 idx;
1252+
1253+
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
1254+
if (ctx == NULL)
1255+
return -420;
1256+
ssh = wolfSSH_new(ctx);
1257+
if (ssh == NULL) {
1258+
wolfSSH_CTX_free(ctx);
1259+
return -421;
1260+
}
1261+
1262+
for (i = 0; i < (int)(sizeof(hdrs) / sizeof(hdrs[0])); i++) {
1263+
idx = 0;
1264+
ssh->scpFileMode = 0;
1265+
ret = wolfSSH_TestScpGetFileMode(ssh, (byte*)hdrs[i],
1266+
(word32)WSTRLEN(hdrs[i]), &idx);
1267+
if (ret != WS_SUCCESS) {
1268+
result = -422;
1269+
break;
1270+
}
1271+
if (ssh->scpFileMode != expected[i]) {
1272+
result = -423;
1273+
break;
1274+
}
1275+
/* index advances past the 'C'/'D', four mode octets, and the
1276+
* trailing space */
1277+
if (idx != 6) {
1278+
result = -424;
1279+
break;
1280+
}
1281+
}
1282+
1283+
for (i = 0; result == 0 &&
1284+
i < (int)(sizeof(badHdrs) / sizeof(badHdrs[0])); i++) {
1285+
idx = 0;
1286+
ret = wolfSSH_TestScpGetFileMode(ssh, (byte*)badHdrs[i],
1287+
(word32)WSTRLEN(badHdrs[i]), &idx);
1288+
if (ret != WS_BAD_ARGUMENT) {
1289+
result = -425;
1290+
break;
1291+
}
1292+
}
1293+
1294+
wolfSSH_free(ssh);
1295+
wolfSSH_CTX_free(ctx);
1296+
return result;
1297+
}
1298+
#endif /* WOLFSSH_TEST_INTERNAL && WOLFSSH_SCP */
1299+
12251300
static int test_ChannelPutData(void)
12261301
{
12271302
WOLFSSH_CTX* ctx = NULL;
@@ -4850,6 +4925,12 @@ int wolfSSH_UnitTest(int argc, char** argv)
48504925
printf("ChannelPutData: %s\n", (unitResult == 0 ? "SUCCESS" : "FAILED"));
48514926
testResult = testResult || unitResult;
48524927

4928+
#if defined(WOLFSSH_TEST_INTERNAL) && defined(WOLFSSH_SCP)
4929+
unitResult = test_ScpGetFileMode();
4930+
printf("ScpGetFileMode: %s\n", (unitResult == 0 ? "SUCCESS" : "FAILED"));
4931+
testResult = testResult || unitResult;
4932+
#endif
4933+
48534934
unitResult = test_MsgHighwater();
48544935
printf("MsgHighwater: %s\n", (unitResult == 0 ? "SUCCESS" : "FAILED"));
48554936
testResult = testResult || unitResult;

wolfssh/internal.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,6 +1424,10 @@ enum WS_MessageIdLimits {
14241424
WOLFSSH_API int wolfSSH_TestSendUserAuthFailure(WOLFSSH* ssh,
14251425
byte partialSuccess);
14261426
WOLFSSH_API int wolfSSH_TestHighwaterCheck(WOLFSSH* ssh, byte side);
1427+
#ifdef WOLFSSH_SCP
1428+
WOLFSSH_API int wolfSSH_TestScpGetFileMode(WOLFSSH* ssh, byte* buf,
1429+
word32 bufSz, word32* inOutIdx);
1430+
#endif /* WOLFSSH_SCP */
14271431
#ifndef WOLFSSH_NO_DH
14281432
WOLFSSH_API int wolfSSH_TestKeyAgreeDh_client(WOLFSSH* ssh, byte hashId,
14291433
const byte* f, word32 fSz);

0 commit comments

Comments
 (0)