Jan,
On Tuesday 17 November 2009, you wrote:
On Tue, Nov 17, 2009 at 4:13 PM, Alex Hermann alex@speakup.nl wrote:
Hello,
Why is the nonce expiry checked in post_auth instead of pre_auth? Now the expiry is checked after the username/password is checked against the DB. That seems a bit odd.
I moved the check to check_nonce (which is called from pre_auth) and it seems to work fine. Did I miss something? Security issue?
There are two major reasons for this:
The server sends back stale=true in digest credentials if the nonce has expired, but only if the credentials are otherwise valid (i.e. the username and the password are correct). The parameter stale=true indicates to the user agent that there is no need to ask the user for username and password again, it can just generate a new authorization header with ca> ched username and password and a new nonce string from the server.
The server can just as well generate a stale=true response immediately, independent of the credentials check. If later on a non-expired nonce arrives, it can do the credentials check and send a response without stale=true if necessary.
The second reason is that we need to accept credentials with old nonce string for ACK and CANCEL requests. Those two requests cannot be challenged (There is no reply for ACK and CANCEL must have the same CSeq as the request being canceled), thus we cannot ask the user agent to resubmit them again with a new nonce.
This reason is invalid because of the following existing code in pre_auth:
if ((_m->REQ_METHOD == METHOD_ACK) || (_m->REQ_METHOD == METHOD_CANCEL)) return AUTHORIZED;
On Tue, Nov 17, 2009 at 5:12 PM, Alex Hermann alex@speakup.nl wrote:
Jan,
On Tuesday 17 November 2009, you wrote:
On Tue, Nov 17, 2009 at 4:13 PM, Alex Hermann alex@speakup.nl wrote:
Hello,
Why is the nonce expiry checked in post_auth instead of pre_auth? Now the expiry is checked after the username/password is checked against the DB. That seems a bit odd.
I moved the check to check_nonce (which is called from pre_auth) and it seems to work fine. Did I miss something? Security issue?
There are two major reasons for this:
The server sends back stale=true in digest credentials if the nonce has expired, but only if the credentials are otherwise valid (i.e. the username and the password are correct). The parameter stale=true indicates to the user agent that there is no need to ask the user for username and password again, it can just generate a new authorization header with ca> ched username and password and a new nonce string from the server.
The server can just as well generate a stale=true response immediately, independent of the credentials check. If later on a non-expired nonce arrives, it can do the credentials check and send a response without stale=true if necessary.
How does the server know that the credentials are valid and it can thus send back stale=true? Note that you can only use that parameter if you verified that the username and password is valid (by verifying the response string). Here is relevant text from RFC2617:
stale A flag, indicating that the previous request from the client was rejected because the nonce value was stale. If stale is TRUE (case-insensitive), the client may wish to simply retry the request with a new encrypted response, without reprompting the user for a new username and password. The server should only set stale to TRUE if it receives a request for which the nonce is invalid but with a valid digest for that nonce (indicating that the client knows the correct username/password). If stale is FALSE, or anything other than TRUE, or the stale directive is not present, the username and/or password are invalid, and new values must be obtained.
In other words, you can move the expired nonce check to the beginning of the authentication process, but in that case you should make sure that you never send back stale=true.
The second reason is that we need to accept credentials with old nonce string for ACK and CANCEL requests. Those two requests cannot be challenged (There is no reply for ACK and CANCEL must have the same CSeq as the request being canceled), thus we cannot ask the user agent to resubmit them again with a new nonce.
This reason is invalid because of the following existing code in pre_auth:
if ((_m->REQ_METHOD == METHOD_ACK) || (_m->REQ_METHOD == METHOD_CANCEL)) return AUTHORIZED;
Good point, thanks.
-- Jan
On Tuesday 17 November 2009, Jan Janak wrote:
On Tue, Nov 17, 2009 at 5:12 PM, Alex Hermann alex@speakup.nl wrote:
Jan,
On Tuesday 17 November 2009, you wrote:
On Tue, Nov 17, 2009 at 4:13 PM, Alex Hermann alex@speakup.nl wrote:
The server can just as well generate a stale=true response immediately, independent of the credentials check. If later on a non-expired nonce arrives, it can do the credentials check and send a response without stale=true if necessary.
How does the server know that the credentials are valid and it can thus send back stale=true? Note that you can only use that parameter if you verified that the username and password is valid (by verifying the response string). Here is relevant text from RFC2617:
stale A flag, indicating that the previous request from the client was rejected because the nonce value was stale. If stale is TRUE (case-insensitive), the client may wish to simply retry the request with a new encrypted response, without reprompting the user for a new username and password. The server should only set stale to TRUE
^^^^^^
if it receives a request for which the nonce is invalid but with a valid digest for that nonce (indicating that the client knows the correct username/password). If stale is FALSE, or anything other than TRUE, or the stale directive is not present, the username and/or password are invalid, and new values must be obtained.
In other words, you can move the expired nonce check to the beginning of the authentication process, but in that case you should make sure that you never send back stale=true.
The text says _should_, which in normal RFC terms means that the implementation may choose not to do it if it has a good reason to do so rfc2119: " 3. SHOULD This word, or the adjective "RECOMMENDED", mean that there may exist valid reasons in particular circumstances to ignore a particular item, but the full implications must be understood and carefully weighed before choosing a different course. "
I think that halving the DB load on auth attempts is a good/valid reason, and I don't see how this could go wrong:
1) If the nonce is stale, stale=true is returned, the client tries again without prompting the user. If then the server decides the digest was invalid, it returns a response without stale=true and the client will prompt the user.
The server gets a little bit more traffic if there are a lot of clients configured with wrong credentials, otherwise, the traffic is the same but the DB load halves.
2) If the nonce is not stale: no changes to current behaviour.
On Tue, Nov 17, 2009 at 5:35 PM, Alex Hermann alex@speakup.nl wrote:
On Tuesday 17 November 2009, Jan Janak wrote:
On Tue, Nov 17, 2009 at 5:12 PM, Alex Hermann alex@speakup.nl wrote:
Jan,
On Tuesday 17 November 2009, you wrote:
On Tue, Nov 17, 2009 at 4:13 PM, Alex Hermann alex@speakup.nl wrote:
The server can just as well generate a stale=true response immediately, independent of the credentials check. If later on a non-expired nonce arrives, it can do the credentials check and send a response without stale=true if necessary.
How does the server know that the credentials are valid and it can thus send back stale=true? Note that you can only use that parameter if you verified that the username and password is valid (by verifying the response string). Here is relevant text from RFC2617:
stale A flag, indicating that the previous request from the client was rejected because the nonce value was stale. If stale is TRUE (case-insensitive), the client may wish to simply retry the request with a new encrypted response, without reprompting the user for a new username and password. The server should only set stale to TRUE
^^^^^^
if it receives a request for which the nonce is invalid but with a valid digest for that nonce (indicating that the client knows the correct username/password). If stale is FALSE, or anything other than TRUE, or the stale directive is not present, the username and/or password are invalid, and new values must be obtained.
In other words, you can move the expired nonce check to the beginning of the authentication process, but in that case you should make sure that you never send back stale=true.
The text says _should_, which in normal RFC terms means that the implementation may choose not to do it if it has a good reason to do so rfc2119: " 3. SHOULD This word, or the adjective "RECOMMENDED", mean that there may exist valid reasons in particular circumstances to ignore a particular item, but the full implications must be understood and carefully weighed before choosing a different course.
Note that RFC2119 only applies to capitalized "SHOULD", it does not apply to "should".
I think that halving the DB load on auth attempts is a good/valid reason, and I don't see how this could go wrong:
- If the nonce is stale, stale=true is returned, the client tries again
without prompting the user. If then the server decides the digest was invalid, it returns a response without stale=true and the client will prompt the user.
Or the user agent may just as well give up, report an error or try again later. You may end up having UAs that fail to authenticate from time to time, which can be worse than more DB load on the server.
I think that if you want to perform the stale nonce check early then disabling stale=true all together would be a safe bet.
The stale parameter is only useful for software based user agents that can interact with the user and prompt for password, most HW phones have the password preconfigured and there it does not matter if you send stale=true or not.
-- Jan
On Tuesday 17 November 2009 18:02:03 Jan Janak wrote:
- If the nonce is stale, stale=true is returned, the client tries again
without prompting the user. If then the server decides the digest was invalid, it returns a response without stale=true and the client will prompt the user.
Or the user agent may just as well give up, report an error or try again later. You may end up having UAs that fail to authenticate from time to time, which can be worse than more DB load on the server.
A nonce will(/should) only be reused by an UAC when it was accepted in an earlier request. So the credentials are highly likely to be correct.
An initial request has no credentials, so no stale=true is returned.
The only scenario of failure I can think of is this: the UAC reuses a nonce, it receives a stale=true response and concludes(*) the credentials are correct, so it resubmits with the new nonce. On the UAS however, the credentials are changed since the previous authenticated request so the resubmitted credentials with valid nonce are rejected without stale=true. Because of the earlier conclusion (*) the UAC doesn't ask the user for new credentials and stops. IMHO the UAC should only conclude the credentials are correct when it receives a non-failure code for the request.
I think this sequence of events is seldom and the behaviour of the UAC is so weird that I'm willing to take that risk.
I'll try to cook a patch that makes the nonce check before credentials check configurable and submit it properly.
I think that if you want to perform the stale nonce check early then disabling stale=true all together would be a safe bet.
That would make matters worse:
If an UAC sends valid credentials with an expired nonce and the UAS send back a 407 without stale=true, the UAC should give up or ask the user. Both of which are unwanted.