Skip to content

Commit ca9c8b6

Browse files
committed
Reject password-change auth requests
- fail userauth when the request sets the password-change flag - do not invoke the userauth callback with the current password - parse the new-password field so the message is fully consumed - add negative unit test asserting USERAUTH_FAILURE and no callback Per RFC 4252 section 8, an expired password MUST NOT be used to authenticate; password changes remain unsupported. Issue: #1047 (6)
1 parent 0f6c386 commit ca9c8b6

2 files changed

Lines changed: 146 additions & 32 deletions

File tree

src/internal.c

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7444,51 +7444,59 @@ static int DoUserAuthRequestPassword(WOLFSSH* ssh, WS_UserAuthData* authData,
74447444

74457445
if (ret == WS_SUCCESS) {
74467446
if (pw->hasNewPassword) {
7447-
/* Skip the password change. Maybe error out since we aren't
7448-
* supporting password changes at this time. */
7447+
/* Password changes are not supported. Parse the new password
7448+
* field so the message is fully consumed, then reject the
7449+
* request rather than authenticating with the current password
7450+
* (RFC 4252 section 8: an expired password MUST NOT be used for
7451+
* authentication). The userauth callback is not called. */
74497452
ret = GetStringRef(&pw->newPasswordSz, &pw->newPassword,
74507453
buf, len, &begin);
7454+
if (ret == WS_SUCCESS) {
7455+
WLOG(WS_LOG_DEBUG,
7456+
"DUARPW: rejecting unsupported password change request");
7457+
authFailure = 1;
7458+
}
74517459
}
74527460
else {
74537461
pw->newPassword = NULL;
74547462
pw->newPasswordSz = 0;
7455-
}
74567463

7457-
if (ssh->ctx->userAuthCb != NULL) {
7458-
WLOG(WS_LOG_DEBUG, "DUARPW: Calling the userauth callback");
7459-
ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PASSWORD,
7460-
authData, ssh->userAuthCtx);
7461-
if (ret == WOLFSSH_USERAUTH_SUCCESS) {
7462-
WLOG(WS_LOG_DEBUG, "DUARPW: password check success");
7463-
ret = WS_SUCCESS;
7464-
}
7465-
else if (ret == WOLFSSH_USERAUTH_PARTIAL_SUCCESS) {
7466-
WLOG(WS_LOG_DEBUG, "DUARPW: password check partial success");
7467-
partialSuccess = 1;
7468-
ret = WS_SUCCESS;
7469-
}
7470-
else if (ret == WOLFSSH_USERAUTH_REJECTED) {
7471-
WLOG(WS_LOG_DEBUG, "DUARPW: password rejected");
7472-
#ifndef NO_FAILURE_ON_REJECTED
7464+
if (ssh->ctx->userAuthCb != NULL) {
7465+
WLOG(WS_LOG_DEBUG, "DUARPW: Calling the userauth callback");
7466+
ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PASSWORD,
7467+
authData, ssh->userAuthCtx);
7468+
if (ret == WOLFSSH_USERAUTH_SUCCESS) {
7469+
WLOG(WS_LOG_DEBUG, "DUARPW: password check success");
7470+
ret = WS_SUCCESS;
7471+
}
7472+
else if (ret == WOLFSSH_USERAUTH_PARTIAL_SUCCESS) {
7473+
WLOG(WS_LOG_DEBUG, "DUARPW: password check partial success");
7474+
partialSuccess = 1;
7475+
ret = WS_SUCCESS;
7476+
}
7477+
else if (ret == WOLFSSH_USERAUTH_REJECTED) {
7478+
WLOG(WS_LOG_DEBUG, "DUARPW: password rejected");
7479+
#ifndef NO_FAILURE_ON_REJECTED
7480+
authFailure = 1;
7481+
#endif
7482+
authRejected = 1;
7483+
ret = WS_USER_AUTH_E;
7484+
}
7485+
else if (ret == WOLFSSH_USERAUTH_WOULD_BLOCK) {
7486+
WLOG(WS_LOG_DEBUG, "DUARPW: userauth callback would block");
7487+
ret = WS_AUTH_PENDING;
7488+
}
7489+
else {
7490+
WLOG(WS_LOG_DEBUG, "DUARPW: password check failed, retry");
74737491
authFailure = 1;
7474-
#endif
7475-
authRejected = 1;
7476-
ret = WS_USER_AUTH_E;
7477-
}
7478-
else if (ret == WOLFSSH_USERAUTH_WOULD_BLOCK) {
7479-
WLOG(WS_LOG_DEBUG, "DUARPW: userauth callback would block");
7480-
ret = WS_AUTH_PENDING;
7492+
ret = WS_SUCCESS;
7493+
}
74817494
}
74827495
else {
7483-
WLOG(WS_LOG_DEBUG, "DUARPW: password check failed, retry");
7496+
WLOG(WS_LOG_DEBUG, "DUARPW: No user auth callback");
74847497
authFailure = 1;
7485-
ret = WS_SUCCESS;
74867498
}
74877499
}
7488-
else {
7489-
WLOG(WS_LOG_DEBUG, "DUARPW: No user auth callback");
7490-
authFailure = 1;
7491-
}
74927500
}
74937501

74947502
if (ret == WS_SUCCESS)

tests/unit.c

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2309,6 +2309,107 @@ static int test_DoUserAuthRequest_serviceName(void)
23092309
}
23102310

23112311

2312+
/* userauth callback that records whether it was invoked. Returns SUCCESS so
2313+
* that, if it were ever reached for a password-change request, the request
2314+
* would be (incorrectly) authenticated - making a missed rejection visible. */
2315+
static int s_pwChangeCbCalled = 0;
2316+
static int UnitAuthAlwaysSucceed(byte authType, WS_UserAuthData* authData,
2317+
void* ctx)
2318+
{
2319+
(void)authData;
2320+
(void)ctx;
2321+
if (authType == WOLFSSH_USERAUTH_PASSWORD) {
2322+
s_pwChangeCbCalled = 1;
2323+
}
2324+
return WOLFSSH_USERAUTH_SUCCESS;
2325+
}
2326+
2327+
/* Verify DoUserAuthRequest rejects a password request that sets the
2328+
* password-change flag (RFC 4252 Section 8: an expired password MUST NOT be
2329+
* used for authentication). The request is otherwise well-formed and the
2330+
* userauth callback would return SUCCESS, so a missing rejection would let the
2331+
* old password authenticate. Asserts:
2332+
* 1. ret == WS_SUCCESS (connection stays open for retry)
2333+
* 2. the userauth callback is never invoked
2334+
* 3. exactly one packet is sent and it is SSH_MSG_USERAUTH_FAILURE
2335+
* 4. *idx == len (the new-password field is fully consumed) */
2336+
static int test_DoUserAuthRequest_rejectsPasswordChange(void)
2337+
{
2338+
WOLFSSH_CTX* ctx = NULL;
2339+
WOLFSSH* ssh = NULL;
2340+
int result = 0;
2341+
int ret;
2342+
int capMsgId;
2343+
byte buf[128];
2344+
word32 len = 0, idx = 0;
2345+
2346+
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
2347+
if (ctx == NULL)
2348+
return -660;
2349+
wolfSSH_SetIOSend(ctx, CaptureIoSendAuthSvc);
2350+
wolfSSH_SetUserAuth(ctx, UnitAuthAlwaysSucceed);
2351+
2352+
ssh = wolfSSH_new(ctx);
2353+
if (ssh == NULL) {
2354+
result = -661;
2355+
goto out;
2356+
}
2357+
2358+
s_pwChangeCbCalled = 0;
2359+
s_authSvcCaptureSz = 0;
2360+
s_authSvcSendCount = 0;
2361+
WMEMSET(s_authSvcCapture, 0, sizeof(s_authSvcCapture));
2362+
2363+
/* username: "user" */
2364+
buf[len++] = 0; buf[len++] = 0; buf[len++] = 0; buf[len++] = 4;
2365+
WMEMCPY(buf + len, "user", 4); len += 4;
2366+
/* service name: "ssh-connection" */
2367+
buf[len++] = 0; buf[len++] = 0; buf[len++] = 0; buf[len++] = 14;
2368+
WMEMCPY(buf + len, "ssh-connection", 14); len += 14;
2369+
/* auth method: "password" */
2370+
buf[len++] = 0; buf[len++] = 0; buf[len++] = 0; buf[len++] = 8;
2371+
WMEMCPY(buf + len, "password", 8); len += 8;
2372+
/* password-change flag: TRUE */
2373+
buf[len++] = 1;
2374+
/* current password: "oldpass" */
2375+
buf[len++] = 0; buf[len++] = 0; buf[len++] = 0; buf[len++] = 7;
2376+
WMEMCPY(buf + len, "oldpass", 7); len += 7;
2377+
/* new password: "newpass" */
2378+
buf[len++] = 0; buf[len++] = 0; buf[len++] = 0; buf[len++] = 7;
2379+
WMEMCPY(buf + len, "newpass", 7); len += 7;
2380+
2381+
ret = wolfSSH_TestDoUserAuthRequest(ssh, buf, len, &idx);
2382+
2383+
if (ret != WS_SUCCESS) {
2384+
result = -662;
2385+
goto out;
2386+
}
2387+
if (s_pwChangeCbCalled) {
2388+
/* The callback must not run for a password-change request. */
2389+
result = -663;
2390+
goto out;
2391+
}
2392+
if (s_authSvcSendCount != 1) {
2393+
result = -664;
2394+
goto out;
2395+
}
2396+
capMsgId = CaptureMsgId(s_authSvcCapture, s_authSvcCaptureSz);
2397+
if (capMsgId != MSGID_USERAUTH_FAILURE) {
2398+
result = -665;
2399+
goto out;
2400+
}
2401+
if (idx != len) {
2402+
result = -666;
2403+
goto out;
2404+
}
2405+
2406+
out:
2407+
wolfSSH_free(ssh);
2408+
wolfSSH_CTX_free(ctx);
2409+
return result;
2410+
}
2411+
2412+
23122413
/* userAuthTypesCb that advertises no methods (returns mask 0). Mirrors a
23132414
* wolfsshd configuration with both PasswordAuthentication no and
23142415
* PubkeyAuthentication no. */
@@ -5518,6 +5619,11 @@ int wolfSSH_UnitTest(int argc, char** argv)
55185619
(unitResult == 0 ? "SUCCESS" : "FAILED"));
55195620
testResult = testResult || unitResult;
55205621

5622+
unitResult = test_DoUserAuthRequest_rejectsPasswordChange();
5623+
printf("DoUserAuthRequest_rejectsPasswordChange: %s\n",
5624+
(unitResult == 0 ? "SUCCESS" : "FAILED"));
5625+
testResult = testResult || unitResult;
5626+
55215627
unitResult = test_SendUserAuthFailure_emptyMethods();
55225628
printf("SendUserAuthFailure_emptyMethods: %s\n",
55235629
(unitResult == 0 ? "SUCCESS" : "FAILED"));

0 commit comments

Comments
 (0)