1251877Speter/* Copyright 2009 Justin Erenkrantz and Greg Stein
2251877Speter *
3251877Speter * Licensed under the Apache License, Version 2.0 (the "License");
4251877Speter * you may not use this file except in compliance with the License.
5251877Speter * You may obtain a copy of the License at
6251877Speter *
7251877Speter *     http://www.apache.org/licenses/LICENSE-2.0
8251877Speter *
9251877Speter * Unless required by applicable law or agreed to in writing, software
10251877Speter * distributed under the License is distributed on an "AS IS" BASIS,
11251877Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12251877Speter * See the License for the specific language governing permissions and
13251877Speter * limitations under the License.
14251877Speter */
15251877Speter
16251877Speter/*** Digest authentication ***/
17251877Speter
18251877Speter#include <serf.h>
19251877Speter#include <serf_private.h>
20251877Speter#include <auth/auth.h>
21251877Speter
22251877Speter#include <apr.h>
23251877Speter#include <apr_base64.h>
24251877Speter#include <apr_strings.h>
25251877Speter#include <apr_uuid.h>
26251877Speter#include <apr_md5.h>
27251877Speter
28251877Speter/** Digest authentication, implements RFC 2617. **/
29251877Speter
30253895Speter/* TODO: add support for the domain attribute. This defines the protection
31253895Speter   space, so that serf can decide per URI if it should reuse the cached
32253895Speter   credentials for the server, or not. */
33253895Speter
34251877Speter/* Stores the context information related to Digest authentication.
35253895Speter   This information is stored in the per server cache in the serf context. */
36251877Spetertypedef struct digest_authn_info_t {
37251877Speter    /* nonce-count for digest authentication */
38251877Speter    unsigned int digest_nc;
39251877Speter
40251877Speter    const char *header;
41251877Speter
42251877Speter    const char *ha1;
43251877Speter
44251877Speter    const char *realm;
45251877Speter    const char *cnonce;
46251877Speter    const char *nonce;
47251877Speter    const char *opaque;
48251877Speter    const char *algorithm;
49251877Speter    const char *qop;
50251877Speter    const char *username;
51251877Speter
52251877Speter    apr_pool_t *pool;
53251877Speter} digest_authn_info_t;
54251877Speter
55251877Speterstatic char
56251877Speterint_to_hex(int v)
57251877Speter{
58251877Speter    return (v < 10) ? '0' + v : 'a' + (v - 10);
59251877Speter}
60251877Speter
61251877Speter/**
62251877Speter * Convert a string if ASCII characters HASHVAL to its hexadecimal
63251877Speter * representation.
64251877Speter *
65251877Speter * The returned string will be allocated in the POOL and be null-terminated.
66251877Speter */
67251877Speterstatic const char *
68251877Speterhex_encode(const unsigned char *hashval,
69251877Speter           apr_pool_t *pool)
70251877Speter{
71251877Speter    int i;
72251877Speter    char *hexval = apr_palloc(pool, (APR_MD5_DIGESTSIZE * 2) + 1);
73251877Speter    for (i = 0; i < APR_MD5_DIGESTSIZE; i++) {
74251877Speter        hexval[2 * i] = int_to_hex((hashval[i] >> 4) & 0xf);
75251877Speter        hexval[2 * i + 1] = int_to_hex(hashval[i] & 0xf);
76251877Speter    }
77251877Speter    hexval[APR_MD5_DIGESTSIZE * 2] = '\0';
78251877Speter    return hexval;
79251877Speter}
80251877Speter
81251877Speter/**
82251877Speter * Returns a 36-byte long string of random characters.
83251877Speter * UUIDs are formatted as: 00112233-4455-6677-8899-AABBCCDDEEFF.
84251877Speter *
85251877Speter * The returned string will be allocated in the POOL and be null-terminated.
86251877Speter */
87251877Speterstatic const char *
88251877Speterrandom_cnonce(apr_pool_t *pool)
89251877Speter{
90251877Speter    apr_uuid_t uuid;
91251877Speter    char *buf = apr_palloc(pool, APR_UUID_FORMATTED_LENGTH + 1);
92251877Speter
93251877Speter    apr_uuid_get(&uuid);
94251877Speter    apr_uuid_format(buf, &uuid);
95251877Speter
96251877Speter    return hex_encode((unsigned char*)buf, pool);
97251877Speter}
98251877Speter
99262339Speterstatic apr_status_t
100262339Speterbuild_digest_ha1(const char **out_ha1,
101262339Speter                 const char *username,
102251877Speter                 const char *password,
103251877Speter                 const char *realm_name,
104251877Speter                 apr_pool_t *pool)
105251877Speter{
106251877Speter    const char *tmp;
107251877Speter    unsigned char ha1[APR_MD5_DIGESTSIZE];
108251877Speter    apr_status_t status;
109251877Speter
110251877Speter    /* calculate ha1:
111251877Speter       MD5 hash of the combined user name, authentication realm and password */
112251877Speter    tmp = apr_psprintf(pool, "%s:%s:%s",
113251877Speter                       username,
114251877Speter                       realm_name,
115251877Speter                       password);
116251877Speter    status = apr_md5(ha1, tmp, strlen(tmp));
117262339Speter    if (status)
118262339Speter        return status;
119251877Speter
120262339Speter    *out_ha1 = hex_encode(ha1, pool);
121262339Speter
122262339Speter    return APR_SUCCESS;
123251877Speter}
124251877Speter
125262339Speterstatic apr_status_t
126262339Speterbuild_digest_ha2(const char **out_ha2,
127262339Speter                 const char *uri,
128251877Speter                 const char *method,
129251877Speter                 const char *qop,
130251877Speter                 apr_pool_t *pool)
131251877Speter{
132251877Speter    if (!qop || strcmp(qop, "auth") == 0) {
133251877Speter        const char *tmp;
134251877Speter        unsigned char ha2[APR_MD5_DIGESTSIZE];
135251877Speter        apr_status_t status;
136251877Speter
137251877Speter        /* calculate ha2:
138251877Speter           MD5 hash of the combined method and URI */
139251877Speter        tmp = apr_psprintf(pool, "%s:%s",
140251877Speter                           method,
141251877Speter                           uri);
142251877Speter        status = apr_md5(ha2, tmp, strlen(tmp));
143262339Speter        if (status)
144262339Speter            return status;
145251877Speter
146262339Speter        *out_ha2 = hex_encode(ha2, pool);
147262339Speter
148262339Speter        return APR_SUCCESS;
149251877Speter    } else {
150251877Speter        /* TODO: auth-int isn't supported! */
151262339Speter        return APR_ENOTIMPL;
152251877Speter    }
153251877Speter}
154251877Speter
155262339Speterstatic apr_status_t
156262339Speterbuild_auth_header(const char **out_header,
157262339Speter                  digest_authn_info_t *digest_info,
158251877Speter                  const char *path,
159251877Speter                  const char *method,
160251877Speter                  apr_pool_t *pool)
161251877Speter{
162251877Speter    char *hdr;
163251877Speter    const char *ha2;
164251877Speter    const char *response;
165251877Speter    unsigned char response_hdr[APR_MD5_DIGESTSIZE];
166251877Speter    const char *response_hdr_hex;
167251877Speter    apr_status_t status;
168251877Speter
169262339Speter    status = build_digest_ha2(&ha2, path, method, digest_info->qop, pool);
170262339Speter    if (status)
171262339Speter        return status;
172251877Speter
173251877Speter    hdr = apr_psprintf(pool,
174251877Speter                       "Digest realm=\"%s\","
175251877Speter                       " username=\"%s\","
176251877Speter                       " nonce=\"%s\","
177251877Speter                       " uri=\"%s\"",
178251877Speter                       digest_info->realm, digest_info->username,
179251877Speter                       digest_info->nonce,
180251877Speter                       path);
181251877Speter
182251877Speter    if (digest_info->qop) {
183251877Speter        if (! digest_info->cnonce)
184251877Speter            digest_info->cnonce = random_cnonce(digest_info->pool);
185251877Speter
186251877Speter        hdr = apr_psprintf(pool, "%s, nc=%08x, cnonce=\"%s\", qop=\"%s\"",
187251877Speter                           hdr,
188251877Speter                           digest_info->digest_nc,
189251877Speter                           digest_info->cnonce,
190251877Speter                           digest_info->qop);
191251877Speter
192251877Speter        /* Build the response header:
193251877Speter           MD5 hash of the combined HA1 result, server nonce (nonce),
194251877Speter           request counter (nc), client nonce (cnonce),
195251877Speter           quality of protection code (qop) and HA2 result. */
196251877Speter        response = apr_psprintf(pool, "%s:%s:%08x:%s:%s:%s",
197251877Speter                                digest_info->ha1, digest_info->nonce,
198251877Speter                                digest_info->digest_nc,
199251877Speter                                digest_info->cnonce, digest_info->qop, ha2);
200251877Speter    } else {
201251877Speter        /* Build the response header:
202251877Speter           MD5 hash of the combined HA1 result, server nonce (nonce)
203251877Speter           and HA2 result. */
204251877Speter        response = apr_psprintf(pool, "%s:%s:%s",
205251877Speter                                digest_info->ha1, digest_info->nonce, ha2);
206251877Speter    }
207251877Speter
208251877Speter    status = apr_md5(response_hdr, response, strlen(response));
209262339Speter    if (status)
210262339Speter        return status;
211262339Speter
212251877Speter    response_hdr_hex = hex_encode(response_hdr, pool);
213251877Speter
214251877Speter    hdr = apr_psprintf(pool, "%s, response=\"%s\"", hdr, response_hdr_hex);
215251877Speter
216251877Speter    if (digest_info->opaque) {
217251877Speter        hdr = apr_psprintf(pool, "%s, opaque=\"%s\"", hdr,
218251877Speter                           digest_info->opaque);
219251877Speter    }
220251877Speter    if (digest_info->algorithm) {
221251877Speter        hdr = apr_psprintf(pool, "%s, algorithm=\"%s\"", hdr,
222251877Speter                           digest_info->algorithm);
223251877Speter    }
224251877Speter
225262339Speter    *out_header = hdr;
226262339Speter
227262339Speter    return APR_SUCCESS;
228251877Speter}
229251877Speter
230251877Speterapr_status_t
231251877Speterserf__handle_digest_auth(int code,
232251877Speter                         serf_request_t *request,
233251877Speter                         serf_bucket_t *response,
234251877Speter                         const char *auth_hdr,
235251877Speter                         const char *auth_attr,
236251877Speter                         void *baton,
237251877Speter                         apr_pool_t *pool)
238251877Speter{
239251877Speter    char *attrs;
240251877Speter    char *nextkv;
241253895Speter    const char *realm, *realm_name = NULL;
242251877Speter    const char *nonce = NULL;
243251877Speter    const char *algorithm = NULL;
244251877Speter    const char *qop = NULL;
245251877Speter    const char *opaque = NULL;
246251877Speter    const char *key;
247251877Speter    serf_connection_t *conn = request->conn;
248251877Speter    serf_context_t *ctx = conn->ctx;
249253895Speter    serf__authn_info_t *authn_info;
250253895Speter    digest_authn_info_t *digest_info;
251251877Speter    apr_status_t status;
252251877Speter    apr_pool_t *cred_pool;
253251877Speter    char *username, *password;
254251877Speter
255251877Speter    /* Can't do Digest authentication if there's no callback to get
256251877Speter       username & password. */
257251877Speter    if (!ctx->cred_cb) {
258251877Speter        return SERF_ERROR_AUTHN_FAILED;
259251877Speter    }
260251877Speter
261253895Speter    if (code == 401) {
262253895Speter        authn_info = serf__get_authn_info_for_server(conn);
263253895Speter    } else {
264253895Speter        authn_info = &ctx->proxy_authn_info;
265253895Speter    }
266253895Speter    digest_info = authn_info->baton;
267253895Speter
268251877Speter    /* Need a copy cuz we're going to write NUL characters into the string.  */
269251877Speter    attrs = apr_pstrdup(pool, auth_attr);
270251877Speter
271251877Speter    /* We're expecting a list of key=value pairs, separated by a comma.
272251877Speter       Ex. realm="SVN Digest",
273251877Speter       nonce="f+zTl/leBAA=e371bd3070adfb47b21f5fc64ad8cc21adc371a5",
274251877Speter       algorithm=MD5, qop="auth" */
275251877Speter    for ( ; (key = apr_strtok(attrs, ",", &nextkv)) != NULL; attrs = NULL) {
276251877Speter        char *val;
277251877Speter
278251877Speter        val = strchr(key, '=');
279251877Speter        if (val == NULL)
280251877Speter            continue;
281251877Speter        *val++ = '\0';
282251877Speter
283251877Speter        /* skip leading spaces */
284251877Speter        while (*key && *key == ' ')
285251877Speter            key++;
286251877Speter
287251877Speter        /* If the value is quoted, then remove the quotes.  */
288251877Speter        if (*val == '"') {
289251877Speter            apr_size_t last = strlen(val) - 1;
290251877Speter
291251877Speter            if (val[last] == '"') {
292251877Speter                val[last] = '\0';
293251877Speter                val++;
294251877Speter            }
295251877Speter        }
296251877Speter
297251877Speter        if (strcmp(key, "realm") == 0)
298251877Speter            realm_name = val;
299251877Speter        else if (strcmp(key, "nonce") == 0)
300251877Speter            nonce = val;
301251877Speter        else if (strcmp(key, "algorithm") == 0)
302251877Speter            algorithm = val;
303251877Speter        else if (strcmp(key, "qop") == 0)
304251877Speter            qop = val;
305251877Speter        else if (strcmp(key, "opaque") == 0)
306251877Speter            opaque = val;
307251877Speter
308251877Speter        /* Ignore all unsupported attributes. */
309251877Speter    }
310251877Speter
311251877Speter    if (!realm_name) {
312251877Speter        return SERF_ERROR_AUTHN_MISSING_ATTRIBUTE;
313251877Speter    }
314251877Speter
315253895Speter    realm = serf__construct_realm(code == 401 ? HOST : PROXY,
316253895Speter                                  conn, realm_name,
317253895Speter                                  pool);
318251877Speter
319251877Speter    /* Ask the application for credentials */
320251877Speter    apr_pool_create(&cred_pool, pool);
321253895Speter    status = serf__provide_credentials(ctx,
322253895Speter                                       &username, &password,
323253895Speter                                       request, baton,
324253895Speter                                       code, authn_info->scheme->name,
325253895Speter                                       realm, cred_pool);
326251877Speter    if (status) {
327251877Speter        apr_pool_destroy(cred_pool);
328251877Speter        return status;
329251877Speter    }
330251877Speter
331251877Speter    digest_info->header = (code == 401) ? "Authorization" :
332251877Speter                                          "Proxy-Authorization";
333251877Speter
334253895Speter    /* Store the digest authentication parameters in the context cached for
335253895Speter       this server in the serf context, so we can use it to create the
336253895Speter       Authorization header when setting up requests on the same or different
337253895Speter       connections (e.g. in case of KeepAlive off on the server).
338253895Speter       TODO: we currently don't cache this info per realm, so each time a request
339253895Speter       'switches realms', we have to ask the application for new credentials. */
340251877Speter    digest_info->pool = conn->pool;
341251877Speter    digest_info->qop = apr_pstrdup(digest_info->pool, qop);
342251877Speter    digest_info->nonce = apr_pstrdup(digest_info->pool, nonce);
343251877Speter    digest_info->cnonce = NULL;
344251877Speter    digest_info->opaque = apr_pstrdup(digest_info->pool, opaque);
345251877Speter    digest_info->algorithm = apr_pstrdup(digest_info->pool, algorithm);
346251877Speter    digest_info->realm = apr_pstrdup(digest_info->pool, realm_name);
347251877Speter    digest_info->username = apr_pstrdup(digest_info->pool, username);
348251877Speter    digest_info->digest_nc++;
349251877Speter
350262339Speter    status = build_digest_ha1(&digest_info->ha1, username, password,
351262339Speter                              digest_info->realm, digest_info->pool);
352251877Speter
353251877Speter    apr_pool_destroy(cred_pool);
354251877Speter
355251877Speter    /* If the handshake is finished tell serf it can send as much requests as it
356251877Speter       likes. */
357251877Speter    serf_connection_set_max_outstanding_requests(conn, 0);
358251877Speter
359262339Speter    return status;
360251877Speter}
361251877Speter
362251877Speterapr_status_t
363251877Speterserf__init_digest(int code,
364251877Speter                  serf_context_t *ctx,
365251877Speter                  apr_pool_t *pool)
366251877Speter{
367251877Speter    return APR_SUCCESS;
368251877Speter}
369251877Speter
370251877Speterapr_status_t
371253895Speterserf__init_digest_connection(const serf__authn_scheme_t *scheme,
372253895Speter                             int code,
373251877Speter                             serf_connection_t *conn,
374251877Speter                             apr_pool_t *pool)
375251877Speter{
376253895Speter    serf_context_t *ctx = conn->ctx;
377253895Speter    serf__authn_info_t *authn_info;
378253895Speter
379251877Speter    if (code == 401) {
380253895Speter        authn_info = serf__get_authn_info_for_server(conn);
381251877Speter    } else {
382253895Speter        authn_info = &ctx->proxy_authn_info;
383251877Speter    }
384251877Speter
385253895Speter    if (!authn_info->baton) {
386253895Speter        authn_info->baton = apr_pcalloc(pool, sizeof(digest_authn_info_t));
387253895Speter    }
388253895Speter
389251877Speter    /* Make serf send the initial requests one by one */
390251877Speter    serf_connection_set_max_outstanding_requests(conn, 1);
391251877Speter
392251877Speter    return APR_SUCCESS;
393251877Speter}
394251877Speter
395251877Speterapr_status_t
396251877Speterserf__setup_request_digest_auth(peer_t peer,
397251877Speter                                int code,
398251877Speter                                serf_connection_t *conn,
399251877Speter                                serf_request_t *request,
400251877Speter                                const char *method,
401251877Speter                                const char *uri,
402251877Speter                                serf_bucket_t *hdrs_bkt)
403251877Speter{
404253895Speter    serf_context_t *ctx = conn->ctx;
405253895Speter    serf__authn_info_t *authn_info;
406253895Speter    digest_authn_info_t *digest_info;
407262339Speter    apr_status_t status;
408251877Speter
409253895Speter    if (peer == HOST) {
410253895Speter        authn_info = serf__get_authn_info_for_server(conn);
411253895Speter    } else {
412253895Speter        authn_info = &ctx->proxy_authn_info;
413253895Speter    }
414253895Speter    digest_info = authn_info->baton;
415253895Speter
416251877Speter    if (digest_info && digest_info->realm) {
417251877Speter        const char *value;
418253895Speter        const char *path;
419251877Speter
420251877Speter        /* TODO: per request pool? */
421251877Speter
422253895Speter        /* for request 'CONNECT serf.googlecode.com:443', the uri also should be
423253895Speter           serf.googlecode.com:443. apr_uri_parse can't handle this, so special
424253895Speter           case. */
425253895Speter        if (strcmp(method, "CONNECT") == 0)
426253895Speter            path = uri;
427253895Speter        else {
428253895Speter            apr_uri_t parsed_uri;
429251877Speter
430253895Speter            /* Extract path from uri. */
431253895Speter            status = apr_uri_parse(conn->pool, uri, &parsed_uri);
432253895Speter            if (status)
433253895Speter                return status;
434253895Speter
435253895Speter            path = parsed_uri.path;
436253895Speter        }
437253895Speter
438251877Speter        /* Build a new Authorization header. */
439251877Speter        digest_info->header = (peer == HOST) ? "Authorization" :
440251877Speter            "Proxy-Authorization";
441262339Speter        status = build_auth_header(&value, digest_info, path, method,
442262339Speter                                   conn->pool);
443262339Speter        if (status)
444262339Speter            return status;
445251877Speter
446251877Speter        serf_bucket_headers_setn(hdrs_bkt, digest_info->header,
447251877Speter                                 value);
448251877Speter        digest_info->digest_nc++;
449251877Speter
450251877Speter        /* Store the uri of this request on the serf_request_t object, to make
451251877Speter           it available when validating the Authentication-Info header of the
452251877Speter           matching response. */
453262339Speter        request->auth_baton = (void *)path;
454251877Speter    }
455251877Speter
456262339Speter    return APR_SUCCESS;
457251877Speter}
458251877Speter
459251877Speterapr_status_t
460262339Speterserf__validate_response_digest_auth(const serf__authn_scheme_t *scheme,
461262339Speter                                    peer_t peer,
462251877Speter                                    int code,
463251877Speter                                    serf_connection_t *conn,
464251877Speter                                    serf_request_t *request,
465251877Speter                                    serf_bucket_t *response,
466251877Speter                                    apr_pool_t *pool)
467251877Speter{
468251877Speter    const char *key;
469251877Speter    char *auth_attr;
470251877Speter    char *nextkv;
471251877Speter    const char *rspauth = NULL;
472251877Speter    const char *qop = NULL;
473251877Speter    const char *nc_str = NULL;
474251877Speter    serf_bucket_t *hdrs;
475253895Speter    serf_context_t *ctx = conn->ctx;
476262339Speter    apr_status_t status;
477251877Speter
478251877Speter    hdrs = serf_bucket_response_get_headers(response);
479251877Speter
480251877Speter    /* Need a copy cuz we're going to write NUL characters into the string.  */
481251877Speter    if (peer == HOST)
482251877Speter        auth_attr = apr_pstrdup(pool,
483251877Speter            serf_bucket_headers_get(hdrs, "Authentication-Info"));
484251877Speter    else
485251877Speter        auth_attr = apr_pstrdup(pool,
486251877Speter            serf_bucket_headers_get(hdrs, "Proxy-Authentication-Info"));
487251877Speter
488251877Speter    /* If there's no Authentication-Info header there's nothing to validate. */
489251877Speter    if (! auth_attr)
490251877Speter        return APR_SUCCESS;
491251877Speter
492251877Speter    /* We're expecting a list of key=value pairs, separated by a comma.
493251877Speter       Ex. rspauth="8a4b8451084b082be6b105e2b7975087",
494251877Speter       cnonce="346531653132652d303033392d3435", nc=00000007,
495251877Speter       qop=auth */
496251877Speter    for ( ; (key = apr_strtok(auth_attr, ",", &nextkv)) != NULL; auth_attr = NULL) {
497251877Speter        char *val;
498251877Speter
499251877Speter        val = strchr(key, '=');
500251877Speter        if (val == NULL)
501251877Speter            continue;
502251877Speter        *val++ = '\0';
503251877Speter
504251877Speter        /* skip leading spaces */
505251877Speter        while (*key && *key == ' ')
506251877Speter            key++;
507251877Speter
508251877Speter        /* If the value is quoted, then remove the quotes.  */
509251877Speter        if (*val == '"') {
510251877Speter            apr_size_t last = strlen(val) - 1;
511251877Speter
512251877Speter            if (val[last] == '"') {
513251877Speter                val[last] = '\0';
514251877Speter                val++;
515251877Speter            }
516251877Speter        }
517251877Speter
518251877Speter        if (strcmp(key, "rspauth") == 0)
519251877Speter            rspauth = val;
520251877Speter        else if (strcmp(key, "qop") == 0)
521251877Speter            qop = val;
522251877Speter        else if (strcmp(key, "nc") == 0)
523251877Speter            nc_str = val;
524251877Speter    }
525251877Speter
526251877Speter    if (rspauth) {
527251877Speter        const char *ha2, *tmp, *resp_hdr_hex;
528251877Speter        unsigned char resp_hdr[APR_MD5_DIGESTSIZE];
529251877Speter        const char *req_uri = request->auth_baton;
530253895Speter        serf__authn_info_t *authn_info;
531253895Speter        digest_authn_info_t *digest_info;
532251877Speter
533253895Speter        if (peer == HOST) {
534253895Speter            authn_info = serf__get_authn_info_for_server(conn);
535253895Speter        } else {
536253895Speter            authn_info = &ctx->proxy_authn_info;
537253895Speter        }
538253895Speter        digest_info = authn_info->baton;
539253895Speter
540262339Speter        status = build_digest_ha2(&ha2, req_uri, "", qop, pool);
541262339Speter        if (status)
542262339Speter            return status;
543262339Speter
544251877Speter        tmp = apr_psprintf(pool, "%s:%s:%s:%s:%s:%s",
545251877Speter                           digest_info->ha1, digest_info->nonce, nc_str,
546251877Speter                           digest_info->cnonce, digest_info->qop, ha2);
547251877Speter        apr_md5(resp_hdr, tmp, strlen(tmp));
548251877Speter        resp_hdr_hex =  hex_encode(resp_hdr, pool);
549251877Speter
550251877Speter        /* Incorrect response-digest in Authentication-Info header. */
551251877Speter        if (strcmp(rspauth, resp_hdr_hex) != 0) {
552251877Speter            return SERF_ERROR_AUTHN_FAILED;
553251877Speter        }
554251877Speter    }
555251877Speter
556251877Speter    return APR_SUCCESS;
557251877Speter}
558