2 #include "auth/digest/auth_digest.h"
3 #include "auth/digest/User.h"
4 #include "auth/digest/UserRequest.h"
5 #include "auth/State.h"
8 #include "HttpRequest.h"
11 Auth::Digest::UserRequest::UserRequest() :
25 * Delete the digest request structure.
26 * Does NOT delete related AuthUser structures
28 Auth::Digest::UserRequest::~UserRequest()
30 assert(RefCountCount()==0);
43 authDigestNonceUnlink(nonce
);
47 Auth::Digest::UserRequest::authenticated() const
49 if (user() != NULL
&& user()->credentials() == Auth::Ok
)
55 /** log a digest user in
58 Auth::Digest::UserRequest::authenticate(HttpRequest
* request
, ConnStateData
* conn
, http_hdr_type type
)
64 /* if the check has corrupted the user, just return */
65 if (user() == NULL
|| user()->credentials() == Auth::Failed
) {
69 Auth::User::Pointer auth_user
= user();
71 Auth::Digest::User
*digest_user
= dynamic_cast<Auth::Digest::User
*>(auth_user
.getRaw());
72 assert(digest_user
!= NULL
);
74 Auth::Digest::UserRequest
*digest_request
= this;
76 /* do we have the HA1 */
77 if (!digest_user
->HA1created
) {
78 auth_user
->credentials(Auth::Pending
);
82 if (digest_request
->nonce
== NULL
) {
83 /* this isn't a nonce we issued */
84 auth_user
->credentials(Auth::Failed
);
88 DigestCalcHA1(digest_request
->algorithm
, NULL
, NULL
, NULL
,
89 authenticateDigestNonceNonceb64(digest_request
->nonce
),
90 digest_request
->cnonce
,
91 digest_user
->HA1
, SESSIONKEY
);
92 DigestCalcResponse(SESSIONKEY
, authenticateDigestNonceNonceb64(digest_request
->nonce
),
93 digest_request
->nc
, digest_request
->cnonce
, digest_request
->qop
,
94 RequestMethodStr(request
->method
), digest_request
->uri
, HA2
, Response
);
96 debugs(29, 9, "\nResponse = '" << digest_request
->response
<< "'\nsquid is = '" << Response
<< "'");
98 if (strcasecmp(digest_request
->response
, Response
) != 0) {
99 if (!digest_request
->flags
.helper_queried
) {
100 /* Query the helper in case the password has changed */
101 digest_request
->flags
.helper_queried
= 1;
102 auth_user
->credentials(Auth::Pending
);
106 if (static_cast<Auth::Digest::Config
*>(Auth::Config::Find("digest"))->PostWorkaround
&& request
->method
!= METHOD_GET
) {
107 /* Ugly workaround for certain very broken browsers using the
108 * wrong method to calculate the request-digest on POST request.
109 * This should be deleted once Digest authentication becomes more
110 * widespread and such broken browsers no longer are commonly
113 DigestCalcResponse(SESSIONKEY
, authenticateDigestNonceNonceb64(digest_request
->nonce
),
114 digest_request
->nc
, digest_request
->cnonce
, digest_request
->qop
,
115 RequestMethodStr(METHOD_GET
), digest_request
->uri
, HA2
, Response
);
117 if (strcasecmp(digest_request
->response
, Response
)) {
118 auth_user
->credentials(Auth::Failed
);
119 digest_request
->flags
.invalid_password
= 1;
120 digest_request
->setDenyMessage("Incorrect password");
123 const char *useragent
= request
->header
.getStr(HDR_USER_AGENT
);
125 static Ip::Address last_broken_addr
;
126 static int seen_broken_client
= 0;
128 if (!seen_broken_client
) {
129 last_broken_addr
.SetNoAddr();
130 seen_broken_client
= 1;
133 if (last_broken_addr
!= request
->client_addr
) {
134 debugs(29, DBG_IMPORTANT
, "Digest POST bug detected from " <<
135 request
->client_addr
<< " using '" <<
136 (useragent
? useragent
: "-") <<
137 "'. Please upgrade browser. See Bug #630 for details.");
139 last_broken_addr
= request
->client_addr
;
143 auth_user
->credentials(Auth::Failed
);
144 digest_request
->flags
.invalid_password
= 1;
145 digest_request
->setDenyMessage("Incorrect password");
149 /* check for stale nonce */
150 if (!authDigestNonceIsValid(digest_request
->nonce
, digest_request
->nc
)) {
151 debugs(29, 3, HERE
<< "user '" << auth_user
->username() << "' validated OK but nonce stale");
152 auth_user
->credentials(Auth::Failed
);
153 digest_request
->setDenyMessage("Stale nonce");
158 auth_user
->credentials(Auth::Ok
);
160 /* password was checked and did match */
161 debugs(29, 4, HERE
<< "user '" << auth_user
->username() << "' validated OK");
163 /* auth_user is now linked, we reset these values
164 * after external auth occurs anyway */
165 auth_user
->expiretime
= current_time
.tv_sec
;
170 Auth::Digest::UserRequest::module_direction()
172 if (user()->auth_type
!= Auth::AUTH_DIGEST
)
173 return Auth::CRED_ERROR
;
175 switch (user()->credentials()) {
178 return Auth::CRED_VALID
;
181 /* send new challenge */
182 return Auth::CRED_CHALLENGE
;
184 case Auth::Unchecked
:
186 return Auth::CRED_LOOKUP
;
189 return Auth::CRED_ERROR
;
194 Auth::Digest::UserRequest::addAuthenticationInfoHeader(HttpReply
* rep
, int accel
)
198 /* don't add to authentication error pages */
200 if ((!accel
&& rep
->sline
.status
== HTTP_PROXY_AUTHENTICATION_REQUIRED
)
201 || (accel
&& rep
->sline
.status
== HTTP_UNAUTHORIZED
))
204 type
= accel
? HDR_AUTHENTICATION_INFO
: HDR_PROXY_AUTHENTICATION_INFO
;
207 /* test for http/1.1 transfer chunked encoding */
212 if ((static_cast<Auth::Digest::Config
*>(Auth::Config::Find("digest"))->authenticateProgram
) && authDigestNonceLastRequest(nonce
)) {
213 flags
.authinfo_sent
= 1;
214 debugs(29, 9, HERE
<< "Sending type:" << type
<< " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce
) << "\"");
215 httpHeaderPutStrf(&rep
->header
, type
, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce
));
221 Auth::Digest::UserRequest::addAuthenticationInfoTrailer(HttpReply
* rep
, int accel
)
225 if (!auth_user_request
)
228 /* has the header already been send? */
229 if (flags
.authinfo_sent
)
232 /* don't add to authentication error pages */
233 if ((!accel
&& rep
->sline
.status
== HTTP_PROXY_AUTHENTICATION_REQUIRED
)
234 || (accel
&& rep
->sline
.status
== HTTP_UNAUTHORIZED
))
237 type
= accel
? HDR_AUTHENTICATION_INFO
: HDR_PROXY_AUTHENTICATION_INFO
;
239 if ((static_cast<Auth::Digest::Config
*>(digestScheme::GetInstance()->getConfig())->authenticate
) && authDigestNonceLastRequest(nonce
)) {
240 debugs(29, 9, HERE
<< "Sending type:" << type
<< " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce
) << "\"");
241 httpTrailerPutStrf(&rep
->header
, type
, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce
));
246 /* send the initial data to a digest authenticator module */
248 Auth::Digest::UserRequest::module_start(RH
* handler
, void *data
)
252 assert(user() != NULL
&& user()->auth_type
== Auth::AUTH_DIGEST
);
253 debugs(29, 9, HERE
<< "'\"" << user()->username() << "\":\"" << realm
<< "\"'");
255 if (static_cast<Auth::Digest::Config
*>(Auth::Config::Find("digest"))->authenticateProgram
== NULL
) {
256 debugs(29, DBG_CRITICAL
, "ERROR: No Digest authentication program configured.");
261 if (static_cast<Auth::Digest::Config
*>(Auth::Config::Find("digest"))->utf8
) {
263 latin1_to_utf8(userstr
, sizeof(userstr
), user()->username());
264 snprintf(buf
, 8192, "\"%s\":\"%s\"\n", userstr
, realm
);
266 snprintf(buf
, 8192, "\"%s\":\"%s\"\n", user()->username(), realm
);
269 helperSubmit(digestauthenticators
, buf
, Auth::Digest::UserRequest::HandleReply
,
270 new Auth::StateData(this, handler
, data
));
274 Auth::Digest::UserRequest::HandleReply(void *data
, char *reply
)
276 Auth::StateData
*replyData
= static_cast<Auth::StateData
*>(data
);
279 debugs(29, 9, HERE
<< "{" << (reply
? reply
: "<NULL>") << "}");
282 if ((t
= strchr(reply
, ' ')))
285 if (*reply
== '\0' || *reply
== '\n')
289 assert(replyData
->auth_user_request
!= NULL
);
290 Auth::UserRequest::Pointer auth_user_request
= replyData
->auth_user_request
;
292 if (reply
&& (strncasecmp(reply
, "ERR", 3) == 0)) {
293 /* allow this because the digest_request pointer is purely local */
294 Auth::Digest::UserRequest
*digest_request
= dynamic_cast<Auth::Digest::UserRequest
*>(auth_user_request
.getRaw());
295 assert(digest_request
);
297 digest_request
->user()->credentials(Auth::Failed
);
298 digest_request
->flags
.invalid_password
= 1;
301 digest_request
->setDenyMessage(t
);
303 /* allow this because the digest_request pointer is purely local */
304 Auth::Digest::User
*digest_user
= dynamic_cast<Auth::Digest::User
*>(auth_user_request
->user().getRaw());
305 assert(digest_user
!= NULL
);
307 CvtBin(reply
, digest_user
->HA1
);
308 digest_user
->HA1created
= 1;
311 if (cbdataReferenceValidDone(replyData
->data
, &cbdata
))
312 replyData
->handler(cbdata
, NULL
);