Exemplo n.º 1
0
int
errorMapStart(const errormap * map, request_t * client_req, HttpReply * reply, const char *aclname, ERRMAPCB * callback, void *callback_data)
{
    char squid_error[100];
    int len = 0;
    const char *errorUrl;
    ErrorMapState *state;
    const char *tmp;
    http_status status;
    request_t *req;
    HttpHeaderPos hdrpos;
    HttpHeaderEntry *hdr;

    if (!client_req || !reply)
	return 0;

    status = reply->sline.status;

    tmp = httpHeaderGetStr(&reply->header, HDR_X_SQUID_ERROR);
    squid_error[0] = '\0';
    if (tmp) {
	xstrncpy(squid_error, tmp, sizeof(squid_error));
	len = strcspn(squid_error, " ");
    }
    squid_error[len] = '\0';
    errorUrl = getErrorMap(map, status, squid_error, aclname);
    if (!errorUrl)
	return 0;
    req = urlParse(urlMethodGetKnownByCode(METHOD_GET), (char *) errorUrl);
    if (!req) {
	debug(0, 0) ("errorMapStart: Invalid error URL '%s'\n", errorUrl);
	return 0;
    }
    req->urlgroup = xstrdup("error");

    state = cbdataAlloc(ErrorMapState);
    state->req = requestLink(req);
    state->e = storeCreateEntry(errorUrl, req->flags, req->method);
    state->sc = storeClientRegister(state->e, state);
    state->callback = callback;
    state->callback_data = callback_data;
    cbdataLock(callback_data);

    hdrpos = HttpHeaderInitPos;
    while ((hdr = httpHeaderGetEntry(&client_req->header, &hdrpos)) != NULL) {
	if (CBIT_TEST(client_headers, hdr->id))
	    httpHeaderAddClone(&req->header, hdr);
    }
    hdrpos = HttpHeaderInitPos;
    while ((hdr = httpHeaderGetEntry(&reply->header, &hdrpos)) != NULL) {
	if (CBIT_TEST(server_headers, hdr->id))
	    httpHeaderAddClone(&req->header, hdr);
    }
    httpHeaderPutInt(&req->header, HDR_X_ERROR_STATUS, (int) reply->sline.status);
    httpHeaderPutStr(&req->header, HDR_X_REQUEST_URI, urlCanonical(client_req));

    fwdStart(-1, state->e, req);
    storeClientRef(state->sc, state->e, 0, 0, SM_PAGE_SIZE, errorMapFetchHeaders, state);
    return 1;
}
/* returns true if connection should be "persistent" 
 * after processing this message */
int
httpMsgIsPersistent(http_version_t http_ver, const HttpHeader * hdr)
{
    if (httpHeaderHasConnDir(hdr, "close"))
	return 0;
#if WHEN_SQUID_IS_HTTP1_1
    if ((http_ver.major >= 1) && (http_ver.minor >= 1)) {
	/*
	 * for modern versions of HTTP: persistent unless there is
	 * a "Connection: close" header.
	 */
	return 1;
    } else {
#else
    {
#endif
	/*
	 * Persistent connections in Netscape 3.x are allegedly broken,
	 * return false if it is a browser connection.  If there is a
	 * VIA header, then we assume this is NOT a browser connection.
	 */
	const char *agent = httpHeaderGetStr(hdr, HDR_USER_AGENT);
	if (agent && !httpHeaderHas(hdr, HDR_VIA)) {
	    if (!strncasecmp(agent, "Mozilla/3.", 10))
		return 0;
	    if (!strncasecmp(agent, "Netscape/3.", 11))
		return 0;
	}
	/* for old versions of HTTP: persistent if has "keep-alive" */
	return httpHeaderHasConnDir(hdr, "keep-alive");
    }
}
Exemplo n.º 3
0
/* sync this routine when you update HttpReply struct */
static void
httpReplyHdrCacheInit(HttpReply * rep)
{
    const HttpHeader *hdr = &rep->header;
    const char *str;
    rep->content_length = httpHeaderGetSize(hdr, HDR_CONTENT_LENGTH);
    rep->date = httpHeaderGetTime(hdr, HDR_DATE);
    rep->last_modified = httpHeaderGetTime(hdr, HDR_LAST_MODIFIED);
    str = httpHeaderGetStr(hdr, HDR_CONTENT_TYPE);
    if (str)
	stringLimitInit(&rep->content_type, str, strcspn(str, ";\t "));
    else
	rep->content_type = StringNull;
    rep->cache_control = httpHeaderGetCc(hdr);
    rep->content_range = httpHeaderGetContRange(hdr);
    rep->keep_alive = httpMsgIsPersistent(rep->sline.version, &rep->header);
    /* be sure to set expires after date and cache-control */
    rep->expires = httpReplyHdrExpirationTime(rep);
}
Exemplo n.º 4
0
const char *
httpHeaderGetAuth(const HttpHeader * hdr, http_hdr_type id, const char *auth_scheme)
{
    const char *field;
    int l;
    assert(hdr && auth_scheme);
    field = httpHeaderGetStr(hdr, id);
    if (!field)			/* no authorization field */
	return NULL;
    l = strlen(auth_scheme);
    if (!l || strncasecmp(field, auth_scheme, l))	/* wrong scheme */
	return NULL;
    field += l;
    if (!xisspace(*field))	/* wrong scheme */
	return NULL;
    /* skip white space */
    field += xcountws(field);
    if (!*field)		/* no authorization cookie */
	return NULL;
    return base64_decode(field);
}
Exemplo n.º 5
0
void
locationRewriteStart(HttpReply * rep, clientHttpRequest * http, RH * handler, void *data)
{
    rewriteStateData *r = NULL;
    const char *location = httpHeaderGetStr(&rep->header, HDR_LOCATION);
    const char *urlgroup;
    char buf[8192];

    if (http->orig_request && http->orig_request->urlgroup)
	urlgroup = http->orig_request->urlgroup;
    else if (http->request && http->request->urlgroup)
	urlgroup = http->request->urlgroup;
    else
	urlgroup = NULL;

    assert(handler);
    if (!urlgroup || !*urlgroup)
	urlgroup = "-";
    debug(29, 5) ("locationRewriteStart: '%s'\n", location);
    if (Config.Program.location_rewrite.command == NULL) {
	handler(data, NULL);
	return;
    }
    if (Config.onoff.redirector_bypass && locrewriters->stats.queue_size) {
	/* Skip rewriter if there is one request queued */
	n_bypassed++;
	handler(data, NULL);
	return;
    }
    r = cbdataAlloc(rewriteStateData);
    r->orig_url = xstrdup(location);
    r->handler = handler;
    r->data = data;
    cbdataLock(r->data);
    snprintf(buf, 8192, "%s %s %s\n",
	r->orig_url, http->uri, urlgroup);
    helperSubmit(locrewriters, buf, locationRewriteHandleReply, r);
}
Exemplo n.º 6
0
char *
internalRedirectProcessURL(clientHttpRequest * req, rewritetoken * head)
{
    char *dev = NULL;
    size_t len = 0;
    debug(85, 5) ("internalRedirectProcessURL: start\n");
    for (; head != NULL; head = head->next) {
	const char *str = NULL;	/* string to append */
	size_t str_len = 0;
	int do_ulong = 0;
	unsigned long ulong = 0;
	const char *ulong_fmt = "%lu";
	debug(85, 5) ("internalRedirectProcessURL: token=%s str=%s urlEncode=%s\n",
	    tokenNames[head->type], head->str, head->urlEncode ? "true" : "false");
	switch (head->type) {
	case RFT_STRING:
	    str = head->str;
	    str_len = head->str_len;
	    break;
	case RFT_CLIENT_IPADDRESS:
	    str = inet_ntoa(req->conn->peer.sin_addr);
	    break;
	case RFT_LOCAL_IPADDRESS:
	    str = inet_ntoa(req->conn->me.sin_addr);
	    break;
	case RFT_LOCAL_PORT:
	    ulong = ntohs(req->conn->me.sin_port);
	    do_ulong = 1;
	    break;
	case RFT_EPOCH_SECONDS:
	    ulong = current_time.tv_sec;
	    do_ulong = 1;
	    break;
	case RFT_TIME_SUBSECONDS:
	    ulong = current_time.tv_usec / 1000;
	    do_ulong = 1;
	    ulong_fmt = "%03lu";
	    break;
	case RFT_USERNAME:
	    if (req->request->auth_user_request)
		str = authenticateUserUsername(req->request->auth_user_request->auth_user);

	    if (!str || !*str)
		str = req->conn->rfc931;

#ifdef USE_SSL
	    if ((!str || !*str) && req->conn != NULL)
		str = sslGetUserEmail(fd_table[req->conn->fd].ssl);
#endif

	    if (!str || !*str)
		str = req->request->extacl_user;

	    break;
	case RFT_USERLOGIN:
	    str = req->request->login;
	    break;
	case RFT_USERIDENT:
	    str = req->conn->rfc931;
	    break;
	case RFT_USERSSL:
#ifdef USE_SSL
	    if (req->conn != NULL)
		str = sslGetUserEmail(fd_table[req->conn->fd].ssl);
#endif
	    break;
	case RFT_EXTERNALACL_USER:
	    str = req->request->extacl_user;
	    break;
	case RFT_METHOD:
	    str = req->request->method->string;
	    break;
	case RFT_PROTOCOL:
	    str = ProtocolStr[req->request->protocol];
	    break;
	case RFT_URL:
	    str = req->uri;
	    break;
	case RFT_URLPATH:
	    str = req->request->urlpath.buf;
	    break;
	case RFT_URLHOST:
	    str = req->request->host;
	    break;
	case RFT_HDRHOST:
	    str = httpHeaderGetStr(&req->request->header, HDR_HOST);
	    break;
	case RFT_EXTERNALACL_LOGSTR:
	    str = req->request->extacl_log.buf;
	    break;
	default:
	    assert(0 && "Invalid rewrite token type");
	    break;
	}

	if (do_ulong) {
	    char tmpstr[12];
	    int nbytes = snprintf(tmpstr, 12, ulong_fmt, ulong);
	    assert(nbytes > 0);
	    dev = xreacat(dev, &len, tmpstr, nbytes);
	} else {
	    if ((str == NULL) || (*str == '\0'))
		str = "-";

	    if (str_len == 0)
		str_len = strlen(str);

	    if (head->urlEncode) {
		str = rfc1738_escape_part(str);
		str_len = strlen(str);
	    }
	    dev = xreacat(dev, &len, str, str_len);
	}
    }
    debug(85, 5) ("internalRedirectProcessURL: done: %s\n", dev);
    return dev;
}
Exemplo n.º 7
0
/* returns one of
 * AUTH_ACL_CHALLENGE,
 * AUTH_ACL_HELPER,
 * AUTH_ACL_CANNOT_AUTHENTICATE,
 * AUTH_AUTHENTICATED
 *
 * How to use: In your proxy-auth dependent acl code, use the following 
 * construct:
 * int rv;
 * if ((rv = AuthenticateAuthenticate()) != AUTH_AUTHENTICATED)
 *   return rv;
 * 
 * when this code is reached, the request/connection is authenticated.
 *
 * if you have non-acl code, but want to force authentication, you need a 
 * callback mechanism like the acl testing routines that will send a 40[1|7] to
 * the client when rv==AUTH_ACL_CHALLENGE, and will communicate with 
 * the authenticateStart routine for rv==AUTH_ACL_HELPER
 */
auth_acl_t
authenticateAuthenticate(auth_user_request_t ** auth_user_request, http_hdr_type headertype, request_t * request, ConnStateData * conn, struct in_addr src_addr)
{
    const char *proxy_auth;
    assert(headertype != 0);

    proxy_auth = httpHeaderGetStr(&request->header, headertype);

    /*
     * a note on proxy_auth logix here:
     * proxy_auth==NULL -> unauthenticated request || already
     * authenticated connection so we test for an authenticated
     * connection when we recieve no authentication header.
     */
    if (((proxy_auth == NULL) && (!authenticateUserAuthenticated(authTryGetUser(auth_user_request, conn, request))))
	|| (conn && conn->auth_type == AUTH_BROKEN)) {
	/* no header or authentication failed/got corrupted - restart */
	if (conn)
	    conn->auth_type = AUTH_UNKNOWN;
	debug(28, 4) ("authenticateAuthenticate: broken auth or no proxy_auth header. Requesting auth header.\n");
	/* something wrong with the AUTH credentials. Force a new attempt */
	if (conn && conn->auth_user_request) {
	    authenticateAuthUserRequestUnlock(conn->auth_user_request);
	    conn->auth_user_request = NULL;
	}
	if (*auth_user_request) {
	    /* unlock the ACL lock */
	    authenticateAuthUserRequestUnlock(*auth_user_request);
	    *auth_user_request = NULL;
	}
	return AUTH_ACL_CHALLENGE;
    }
#if 0
    /* 
     * Is this an already authenticated connection with a new auth header?
     * No check for function required in the if: its compulsory for conn based 
     * auth modules
     */
    if (proxy_auth && conn && conn->auth_user_request &&
	authenticateUserAuthenticated(conn->auth_user_request) &&
	strcmp(proxy_auth, authscheme_list[conn->auth_user_request->auth_user->auth_module - 1].authConnLastHeader(conn->auth_user_request))) {
	debug(28, 2) ("authenticateAuthenticate: DUPLICATE AUTH - authentication header on already authenticated connection!. AU %p, Current user '%s' proxy_auth %s\n", conn->auth_user_request, authenticateUserRequestUsername(conn->auth_user_request), proxy_auth);
	/* remove this request struct - the link is already authed and it can't be to 
	 * reauth.
	 */

	/* This should _only_ ever occur on the first pass through 
	 * authenticateAuthenticate 
	 */
	assert(*auth_user_request == NULL);
	/* unlock the conn lock on the auth_user_request */
	authenticateAuthUserRequestUnlock(conn->auth_user_request);
	/* mark the conn as non-authed. */
	conn->auth_user_request = NULL;
	/* Set the connection auth type */
	conn->auth_type = AUTH_UNKNOWN;
    }
#endif
    /* we have a proxy auth header and as far as we know this connection has
     * not had bungled connection oriented authentication happen on it. */
    debug(28, 9) ("authenticateAuthenticate: header %s.\n", proxy_auth ? proxy_auth : NULL);
    if (*auth_user_request == NULL) {
	debug(28, 9) ("authenticateAuthenticate: This is a new checklist test on FD:%d\n",
	    conn ? conn->fd : -1);
	if ((!request->auth_user_request)
	    && (!conn || conn->auth_type == AUTH_UNKNOWN)) {
	    /* beginning of a new request check */
	    debug(28, 4) ("authenticateAuthenticate: no connection authentication type\n");
	    if (!authenticateValidateUser(*auth_user_request =
		    authenticateGetAuthUser(proxy_auth))) {
		/* the decode might have left a username for logging, or a message to
		 * the user */
		if (authenticateUserRequestUsername(*auth_user_request)) {
		    /* lock the user for the request structure link */
		    authenticateAuthUserRequestLock(*auth_user_request);
		    request->auth_user_request = *auth_user_request;
		}
		/* unlock the ACL reference granted by ...GetAuthUser. */
		authenticateAuthUserRequestUnlock(*auth_user_request);
		*auth_user_request = NULL;
		return AUTH_ACL_CHALLENGE;
	    }
	    /* the user_request comes prelocked for the caller to GetAuthUser (us) */
	} else if (request->auth_user_request) {
	    *auth_user_request = request->auth_user_request;
	    /* lock the user request for this ACL processing */
	    authenticateAuthUserRequestLock(*auth_user_request);
	} else {
	    assert(conn);
	    if (conn->auth_user_request != NULL) {
		*auth_user_request = conn->auth_user_request;
		/* lock the user request for this ACL processing */
		authenticateAuthUserRequestLock(*auth_user_request);
	    } else {
		/* failed connection based authentication */
		debug(28, 4) ("authenticateAuthenticate: Auth user request %p conn-auth user request %p conn type %d authentication failed.\n",
		    *auth_user_request, conn->auth_user_request, conn->auth_type);
		authenticateAuthUserRequestUnlock(*auth_user_request);
		*auth_user_request = NULL;
		return AUTH_ACL_CHALLENGE;
	    }
	}
    }
    if (!authenticateUserAuthenticated(*auth_user_request)) {
	/* User not logged in. Log them in */
	authenticateAuthenticateUser(*auth_user_request, request,
	    conn, headertype);
	switch (authenticateDirection(*auth_user_request)) {
	case 1:
	    if (!request->auth_user_request) {
		/* lock the user for the request structure link */
		authenticateAuthUserRequestLock(*auth_user_request);
		request->auth_user_request = *auth_user_request;
	    }
	    /* fallthrough to -2 */
	case -2:
	    /* this ACL check is finished. Unlock. */
	    authenticateAuthUserRequestUnlock(*auth_user_request);
	    *auth_user_request = NULL;
	    return AUTH_ACL_CHALLENGE;
	case -1:
	    /* we are partway through authentication within squid,
	     * the *auth_user_request variables stores the auth_user_request
	     * for the callback to here - Do not Unlock */
	    return AUTH_ACL_HELPER;
	}
	/* on 0 the authentication is finished - fallthrough */
	/* See if user authentication failed for some reason */
	if (!authenticateUserAuthenticated(*auth_user_request)) {
	    if ((authenticateUserRequestUsername(*auth_user_request))) {
		if (!request->auth_user_request) {
		    /* lock the user for the request structure link */
		    authenticateAuthUserRequestLock(*auth_user_request);
		    request->auth_user_request = *auth_user_request;
		}
	    }
	    /* this ACL check is finished. Unlock. */
	    authenticateAuthUserRequestUnlock(*auth_user_request);
	    *auth_user_request = NULL;
	    return AUTH_ACL_CHALLENGE;
	}
    }
    /* copy username to request for logging on client-side */
    /* the credentials are correct at this point */
    if (!request->auth_user_request) {
	/* lock the user for the request structure link */
	authenticateAuthUserRequestLock(*auth_user_request);
	request->auth_user_request = *auth_user_request;
	authenticateAuthUserRequestSetIp(*auth_user_request, src_addr);
    }
    /* Unlock the request - we've authenticated it */
    authenticateAuthUserRequestUnlock(*auth_user_request);
    *auth_user_request = NULL;
    return AUTH_AUTHENTICATED;
}
Exemplo n.º 8
0
static void
authenticateNegotiateAuthenticateUser(auth_user_request_t * auth_user_request, request_t * request, ConnStateData * conn, http_hdr_type type)
{
    const char *proxy_auth, *blob;
    auth_user_t *auth_user;
    negotiate_request_t *negotiate_request;

    auth_user = auth_user_request->auth_user;
    assert(auth_user);
    assert(auth_user->auth_type == AUTH_NEGOTIATE);
    assert(auth_user->scheme_data != NULL);
    assert(auth_user_request->scheme_data != NULL);
    negotiate_request = auth_user_request->scheme_data;
    /* Check that we are in the client side, where we can generate
     * auth challenges */
    if (!conn) {
	negotiate_request->auth_state = AUTHENTICATE_STATE_FAILED;
	debug(29, 1) ("authenticateNegotiateAuthenticateUser: attempt to perform authentication without a connection!\n");
	return;
    }
    if (negotiate_request->waiting) {
	debug(29, 1) ("authenticateNegotiateAuthenticateUser: waiting for helper reply!\n");
	return;
    }
    if (negotiate_request->server_blob) {
	debug(29, 2) ("authenticateNegotiateAuthenticateUser: need to challenge client '%s'!\n", negotiate_request->server_blob);
	return;
    }
    /* get header */
    proxy_auth = httpHeaderGetStr(&request->header, type);
    blob = proxy_auth;
    while (xisspace(*blob) && *blob)
	blob++;
    while (!xisspace(*blob) && *blob)
	blob++;
    while (xisspace(*blob) && *blob)
	blob++;

    switch (negotiate_request->auth_state) {
    case AUTHENTICATE_STATE_NONE:
	/* we've received a negotiate request. pass to a helper */
	debug(29, 9) ("authenticateNegotiateAuthenticateUser: auth state negotiate none. %s\n", proxy_auth);
	negotiate_request->auth_state = AUTHENTICATE_STATE_INITIAL;
	safe_free(negotiate_request->client_blob);
	negotiate_request->client_blob = xstrdup(blob);
	conn->auth_type = AUTH_NEGOTIATE;
	conn->auth_user_request = auth_user_request;
	negotiate_request->conn = conn;
	/* and lock for the connection duration */
	debug(29, 9) ("authenticateNegotiateAuthenticateUser: Locking auth_user from the connection.\n");
	authenticateAuthUserRequestLock(auth_user_request);
	negotiate_request->request = requestLink(request);
	return;
	break;
    case AUTHENTICATE_STATE_INITIAL:
	debug(29, 1) ("authenticateNegotiateAuthenticateUser: need to ask helper!\n");
	return;
	break;
    case AUTHENTICATE_STATE_NEGOTIATE:
	/* we should have received a blob from the clien. pass it to the same 
	 * helper process */
	debug(29, 9) ("authenticateNegotiateAuthenticateUser: auth state challenge with header %s.\n", proxy_auth);
	/* do a cache lookup here. If it matches it's a successful negotiate 
	 * challenge - release the helper and use the existing auth_user 
	 * details. */
	safe_free(negotiate_request->client_blob);
	negotiate_request->client_blob = xstrdup(blob);
	if (negotiate_request->request)
	    requestUnlink(negotiate_request->request);
	negotiate_request->request = requestLink(request);
	return;
	break;
    case AUTHENTICATE_STATE_DONE:
	fatal("authenticateNegotiateAuthenticateUser: unexpect auth state DONE! Report a bug to the squid developers.\n");
	break;
    case AUTHENTICATE_STATE_FAILED:
	/* we've failed somewhere in authentication */
	debug(29, 9) ("authenticateNegotiateAuthenticateUser: auth state negotiate failed. %s\n", proxy_auth);
	return;
    }
    return;
}
Exemplo n.º 9
0
/* log a digest user in
 */
static void
authenticateDigestAuthenticateUser(auth_user_request_t * auth_user_request, request_t * request, ConnStateData * conn, http_hdr_type type)
{
    auth_user_t *auth_user;
    digest_request_h *digest_request;
    digest_user_h *digest_user;

    HASHHEX SESSIONKEY;
    HASHHEX HA2 = "";
    HASHHEX Response;

    assert(auth_user_request->auth_user != NULL);
    auth_user = auth_user_request->auth_user;

    assert(auth_user->scheme_data != NULL);
    digest_user = auth_user->scheme_data;

    digest_request = auth_user_request->scheme_data;
    assert(auth_user_request->scheme_data != NULL);
    /* if the check has corrupted the user, just return */
    if (digest_request->flags.credentials_ok == 3) {
	return;
    }
    /* do we have the HA1 */
    if (!digest_user->HA1created) {
	digest_request->flags.credentials_ok = 2;
	return;
    }
    if (digest_request->nonce == NULL) {
	/* this isn't a nonce we issued */
	digest_request->flags.credentials_ok = 3;
	return;
    }
    DigestCalcHA1(digest_request->algorithm, NULL, NULL, NULL,
	authenticateDigestNonceNonceb64(digest_request->nonce),
	digest_request->cnonce,
	digest_user->HA1, SESSIONKEY);
    DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
	digest_request->nc, digest_request->cnonce, digest_request->qop,
	RequestMethodStr[request->method], digest_request->uri, HA2, Response);

    debug(29, 9) ("\nResponse = '%s'\n"
	"squid is = '%s'\n", digest_request->response, Response);

    if (strcasecmp(digest_request->response, Response) != 0) {
	if (!digest_request->flags.helper_queried) {
	    /* Query the helper in case the password has changed */
	    digest_request->flags.helper_queried = 1;
	    digest_request->flags.credentials_ok = 2;
	    return;
	}
	if (digestConfig->PostWorkaround && request->method != METHOD_GET) {
	    /* Ugly workaround for certain very broken browsers using the
	     * wrong method to calculate the request-digest on POST request.
	     * This should be deleted once Digest authentication becomes more
	     * widespread and such broken browsers no longer are commonly
	     * used.
	     */
	    DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
		digest_request->nc, digest_request->cnonce, digest_request->qop,
		RequestMethodStr[METHOD_GET], digest_request->uri, HA2, Response);
	    if (strcasecmp(digest_request->response, Response)) {
		digest_request->flags.credentials_ok = 3;
		safe_free(auth_user_request->message);
		auth_user_request->message = xstrdup("Incorrect password");
		return;
	    } else {
		const char *useragent = httpHeaderGetStr(&request->header, HDR_USER_AGENT);
		static struct in_addr last_broken_addr;
		static int seen_broken_client = 0;

		if (!seen_broken_client) {
		    last_broken_addr = no_addr;
		    seen_broken_client = 1;
		}
		if (memcmp(&last_broken_addr, &request->client_addr, sizeof(last_broken_addr)) != 0) {
		    debug(29, 1) ("\nDigest POST bug detected from %s using '%s'. Please upgrade browser. See Bug #630 for details.\n", inet_ntoa(request->client_addr), useragent ? useragent : "-");
		    last_broken_addr = request->client_addr;
		}
	    }
	} else {
	    digest_request->flags.credentials_ok = 3;
	    safe_free(auth_user_request->message);
	    auth_user_request->message = xstrdup("Incorrect password");
	    return;
	}
    }
    /* check for stale nonce */
    if (!authDigestNonceIsValid(digest_request->nonce, digest_request->nc)) {
	debug(29, 3) ("authenticateDigestAuthenticateuser: user '%s' validated OK but nonce stale\n",
	    digest_user->username);
	digest_request->flags.nonce_stale = 1;
	digest_request->flags.credentials_ok = 3;
	safe_free(auth_user_request->message);
	auth_user_request->message = xstrdup("Stale nonce");
	return;
    }
    /* password was checked and did match */
    digest_request->flags.credentials_ok = 1;

    debug(29, 4) ("authenticateDigestAuthenticateuser: user '%s' validated OK\n",
	digest_user->username);

    /* auth_user is now linked, we reset these values
     * after external auth occurs anyway */
    auth_user->expiretime = current_time.tv_sec;
    return;
}