Annotation of libwww/Library/src/HTAABrow.c, revision 2.50

2.15      frystyk     1: /*                                                                  HTAABrow.c
2.32      frystyk     2: **     BROWSER SIDE ACCESS AUTHORIZATION MODULE
2.15      frystyk     3: **
2.19      frystyk     4: **     (c) COPYRIGHT MIT 1995.
2.15      frystyk     5: **     Please first read the full copyright statement in the file COPYRIGH.
2.50    ! kahan       6: **     @(#) $Id: HTAABrow.c,v 1.5 1998/12/15 12:50:20 cvs Exp $
2.1       luotonen    7: **
2.32      frystyk     8: **     Contains code for parsing challenges and creating credentials for 
2.36      frystyk     9: **     basic authentication schemes. See also the HTAAUtil module
2.32      frystyk    10: **     for how to handle other authentication schemes. You don't have to use
                     11: **     this code at all.
2.1       luotonen   12: **
                     13: ** AUTHORS:
                     14: **     AL      Ari Luotonen    luotonen@dxcern.cern.ch
2.32      frystyk    15: **     HFN     Henrik Frystyk
2.50    ! kahan      16: **      JKO     Jose Kahan      
2.1       luotonen   17: **
                     18: ** HISTORY:
2.5       luotonen   19: **     Oct 17  AL      Made corrections suggested by marca:
                     20: **                     Added  if (!realm->username) return NULL;
                     21: **                     Changed some ""s to NULLs.
2.33      frystyk    22: **                     Now doing HT_CALLOC() to init uuencode source;
2.5       luotonen   23: **                     otherwise HTUU_encode() reads uninitialized memory
                     24: **                     every now and then (not a real bug but not pretty).
                     25: **                     Corrected the formula for uuencode destination size.
2.32      frystyk    26: **     Feb 96 HFN      Rewritten to make it scheme independent and based on
                     27: **                     callback functions and an info structure
2.50    ! kahan      28: **      Nov 98 JKO      Added support for message digest authentication
2.1       luotonen   29: */
                     30: 
2.50    ! kahan      31: /* Portions of this code (as indicated) are derived from the Internet Draft
        !            32: ** draft-ietf-http-authentication-03 and are covered by the following
        !            33: ** copyright:
        !            34: 
        !            35: ** Copyright (C) The Internet Society (1998). All Rights Reserved.
        !            36: 
        !            37: ** This document and translations of it may be copied and furnished to
        !            38: ** others, and derivative works that comment on or otherwise explain it or
        !            39: ** assist in its implmentation may be prepared, copied, published and
        !            40: ** distributed, in whole or in part, without restriction of any kind,
        !            41: ** provided that the above copyright notice and this paragraph are included
        !            42: ** on all such copies and derivative works. However, this document itself
        !            43: ** may not be modified in any way, such as by removing the copyright notice
        !            44: ** or references to the Internet Society or other Internet organizations,
        !            45: ** except as needed for the purpose of developing Internet standards in
        !            46: ** which case the procedures for copyrights defined in the Internet
        !            47: ** Standards process must be followed, or as required to translate it into
        !            48: ** languages other than English.
        !            49: 
        !            50: ** The limited permissions granted above are perpetual and will not be
        !            51: ** revoked by the Internet Society or its successors or assigns.
        !            52: 
        !            53: ** This document and the information contained herein is provided on an "AS
        !            54: ** IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK
        !            55: ** FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
        !            56: ** LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT
        !            57: ** INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
        !            58: ** FITNESS FOR A PARTICULAR PURPOSE.
        !            59: **/
        !            60: 
2.17      frystyk    61: /* Library include files */
2.27      frystyk    62: #include "WWWLib.h"
                     63: #include "HTAAUtil.h"
2.50    ! kahan      64: #include "HTParse.h"
2.27      frystyk    65: #include "HTAABrow.h"                                   /* Implemented here */
2.50    ! kahan      66: #include "HTDigest.h"
2.1       luotonen   67: 
2.36      frystyk    68: #define BASIC_AUTH     "basic"
2.44      frystyk    69: #define DIGEST_AUTH    "digest"
2.36      frystyk    70: 
2.32      frystyk    71: typedef struct _HTBasic {                /* Basic challenge and credentials */
                     72:     char *     uid;
                     73:     char *     pw;
2.38      frystyk    74:     BOOL       retry;                      /* Should we ask the user again? */
2.40      frystyk    75:     BOOL       proxy;                               /* Proxy authentication */
2.32      frystyk    76: } HTBasic;
                     77: 
2.44      frystyk    78: typedef struct _HTDigest {              /* Digest challenge and credentials */
2.50    ! kahan      79:   /* digest info can be shared by one or more UT entries */
        !            80:     int         references;              
        !            81:   /* client authentication data */
2.44      frystyk    82:     char *     uid;
                     83:     char *     pw;
2.50    ! kahan      84:     char *      realm;
        !            85:     char *      cnonce;
        !            86:     long        nc;
        !            87:   /* server authentication data */
        !            88:     char *     nonce;
2.44      frystyk    89:     char *     opaque;
2.50    ! kahan      90:   /* session authentication data */
        !            91:     int         algorithm;
        !            92:     char *      qop;
2.44      frystyk    93:     BOOL       stale;
                     94:     BOOL       retry;                      /* Should we ask the user again? */
                     95:     BOOL       proxy;                               /* Proxy authentication */
                     96: } HTDigest;
                     97: 
2.50    ! kahan      98: #define HASHLEN 16
        !            99: typedef char HASH[HASHLEN+1];
        !           100: #define HASHHEXLEN 32
        !           101: typedef char HASHHEX[HASHHEXLEN+1];
        !           102: 
2.36      frystyk   103: /* ------------------------------------------------------------------------- */
                    104: 
2.32      frystyk   105: /*
                    106: **     Create a protection template for the files
                    107: **     in the same directory as the given file
                    108: **     Returns a template matching docname, and other files in that directory.
                    109: **
                    110: **             E.g.  /foo/bar/x.html  =>  /foo/bar/ *
                    111: **                                                 ^
                    112: **                             Space only to prevent it from
                    113: **                             being a comment marker here,
                    114: **                             there really isn't any space.
2.1       luotonen  115: */
2.33      frystyk   116: PRIVATE char * make_template (const char * docname)
2.1       luotonen  117: {
2.39      frystyk   118:     char * tmplate = NULL;
2.32      frystyk   119:     if (docname) {
2.39      frystyk   120:        char * host = HTParse(docname, "", PARSE_ACCESS|PARSE_HOST|PARSE_PUNCTUATION);
                    121:        char * path = HTParse(docname, "", PARSE_PATH|PARSE_PUNCTUATION);
                    122:        char * slash = strrchr(path, '/');
                    123:        if (slash) {
2.47      frystyk   124: #if 0
2.39      frystyk   125:            if (*(slash+1)) {           
                    126:                strcpy(slash, "*");
                    127:                StrAllocCat(host, path);
                    128:            } else
2.43      frystyk   129:                StrAllocCat(host, "/*");
2.47      frystyk   130: #else
2.49      kahan     131:            if (*(slash+1)) {
                    132:                strcpy(slash + 1, "*");
2.47      frystyk   133:                StrAllocCat(host, path);
2.49      kahan     134:            } else {
                    135:                 StrAllocCat(host, path);
                    136:                 StrAllocCat(host, "*");
                    137:            }
2.47      frystyk   138: #endif
2.39      frystyk   139:        }
                    140:        HT_FREE(path);
                    141:        tmplate = host;
                    142:     } else
                    143:        StrAllocCopy(tmplate, "*");
2.32      frystyk   144:     if (AUTH_TRACE)
                    145:        HTTrace("Template.... Made template `%s' for file `%s'\n",
2.39      frystyk   146:                tmplate, docname ? docname : "<null>");
2.32      frystyk   147:     return tmplate;
2.1       luotonen  148: }
                    149: 
2.44      frystyk   150: /* ------------------------------------------------------------------------- */
                    151: /*                             Basic Authentication                         */
                    152: /* ------------------------------------------------------------------------- */
                    153: 
                    154: /*
                    155: **     Prompt the user for username and password.
                    156: **     Returns YES if user name was typed in, else NO
                    157: */
                    158: PRIVATE int prompt_user (HTRequest * request, const char * realm,
                    159:                         HTBasic * basic)
                    160: {
                    161:     HTAlertCallback * cbf = HTAlert_find(HT_A_USER_PW);
                    162:     if (request && cbf) {
                    163:        HTAlertPar * reply = HTAlert_newReply();
                    164:        int msg = basic->proxy ? HT_MSG_PROXY_UID : HT_MSG_UID;
                    165:        BOOL res = (*cbf)(request, HT_A_USER_PW, msg,
                    166:                          basic->uid, (char *) realm, reply);
                    167:        if (res) {
                    168:            HT_FREE(basic->uid);
                    169:            HT_FREE(basic->pw);
                    170:            basic->uid = HTAlert_replyMessage(reply);
                    171:            basic->pw = HTAlert_replySecret(reply);
                    172:        }
                    173:        HTAlert_deleteReply(reply);
                    174:        return res ? HT_OK : HT_ERROR;
                    175:     }
                    176:     return HT_OK;
                    177: }
                    178: 
                    179: PRIVATE HTBasic * HTBasic_new()
                    180: {
                    181:     HTBasic * me = NULL;
                    182:     if ((me = (HTBasic *) HT_CALLOC(1, sizeof(HTBasic))) == NULL)
                    183:        HT_OUTOFMEM("HTBasic_new");
                    184:     me->retry = YES;                          /* Ask the first time through */
                    185:     return me;
                    186: }
                    187: 
                    188: /*     HTBasic_delete
                    189: **     --------------
                    190: **     Deletes a "basic" information object
                    191: */
                    192: PUBLIC int HTBasic_delete (void * context)
                    193: {
                    194:     HTBasic * basic = (HTBasic *) context;
                    195:     if (basic) {
                    196:        HT_FREE(basic->uid);
                    197:        HT_FREE(basic->pw);
                    198:        HT_FREE(basic);
                    199:        return YES;
                    200:     }
                    201:     return NO;
                    202: }
                    203: 
2.32      frystyk   204: /*
                    205: **     Make basic authentication scheme credentials and register this
                    206: **     information in the request object as credentials. They will then
                    207: **     be included in the request header. An example is 
                    208: **
                    209: **             "Basic AkRDIhEF8sdEgs72F73bfaS=="
                    210: **
2.40      frystyk   211: **     The function can both create normal and proxy credentials
2.36      frystyk   212: **     Returns HT_OK or HT_ERROR
2.32      frystyk   213: */
2.36      frystyk   214: PRIVATE BOOL basic_credentials (HTRequest * request, HTBasic * basic)
2.32      frystyk   215: {
                    216:     if (request && basic) {
                    217:        char * cleartext = NULL;
                    218:        char * cipher = NULL;
                    219:        int cl_len = strlen(basic->uid ? basic->uid : "") +
2.42      frystyk   220:            strlen(basic->pw ? basic->pw : "") + 5;
2.37      frystyk   221:        int ci_len = 4 * (((cl_len+2)/3) + 1);
                    222:        if ((cleartext = (char *) HT_CALLOC(1, cl_len)) == NULL)
2.32      frystyk   223:            HT_OUTOFMEM("basic_credentials");
                    224:        *cleartext = '\0';
                    225:        if (basic->uid) strcpy(cleartext, basic->uid);
                    226:        strcat(cleartext, ":");
                    227:        if (basic->pw) strcat(cleartext, basic->pw);
2.37      frystyk   228:        if ((cipher = (char *) HT_CALLOC(1, ci_len + 3)) == NULL)
2.32      frystyk   229:            HT_OUTOFMEM("basic_credentials");
2.37      frystyk   230:        HTUU_encode((unsigned char *) cleartext, strlen(cleartext), cipher);
2.1       luotonen  231: 
2.32      frystyk   232:        /* Create the credentials and assign them to the request object */
                    233:        {
2.37      frystyk   234:            int cr_len = strlen("basic") + ci_len + 3;
2.32      frystyk   235:            char * cookie = (char *) HT_MALLOC(cr_len+1);
                    236:            if (!cookie) HT_OUTOFMEM("basic_credentials");
                    237:            strcpy(cookie, "Basic ");
                    238:            strcat(cookie, cipher);
2.37      frystyk   239:            if (AUTH_TRACE) HTTrace("Basic Cookie `%s\'\n", cookie);
2.40      frystyk   240: 
                    241:            /* Check whether it is proxy or normal credentials */
                    242:            if (basic->proxy)
                    243:                HTRequest_addCredentials(request, "Proxy-Authorization", cookie);
                    244:            else
                    245:                HTRequest_addCredentials(request, "Authorization", cookie);
                    246: 
2.32      frystyk   247:            HT_FREE(cookie);
2.1       luotonen  248:        }
2.32      frystyk   249:        HT_FREE(cleartext);
                    250:        HT_FREE(cipher);
2.36      frystyk   251:        return HT_OK;
2.32      frystyk   252:     }
2.36      frystyk   253:     return HT_ERROR;
2.1       luotonen  254: }
                    255: 
2.32      frystyk   256: /*     HTBasic_generate
                    257: **     ----------------
                    258: **     This function generates "basic" credentials for the challenge found in
                    259: **     the authentication information base for this request. The result is
                    260: **     stored as an association list in the request object.
                    261: **     This is a callback function for the AA handler.
                    262: */
2.45      frystyk   263: PUBLIC int HTBasic_generate (HTRequest * request, void * context, int mode)
2.32      frystyk   264: { 
2.36      frystyk   265:     HTBasic * basic = (HTBasic *) context;
2.45      frystyk   266:     BOOL proxy = mode==HT_NO_PROXY_ACCESS ? YES : NO;
2.36      frystyk   267:     if (request) {
                    268:        const char * realm = HTRequest_realm(request);
                    269: 
2.40      frystyk   270:        /*
2.46      frystyk   271:        **  If we were asked to explicitly ask the user again
                    272:        */
                    273:        if (mode == HT_REAUTH || mode == HT_PROXY_REAUTH)
                    274:            basic->retry = YES;
                    275: 
                    276:        /*
2.40      frystyk   277:        ** If we don't have a basic context then add a new one to the tree.
2.42      frystyk   278:        ** We use different trees for normal and proxy authentication
2.40      frystyk   279:        */
2.36      frystyk   280:        if (!basic) {
2.50    ! kahan     281:                basic = HTBasic_new();
2.42      frystyk   282:            if (proxy) {
                    283:                char * url = HTRequest_proxy(request);
                    284:                basic->proxy = YES;
                    285:                HTAA_updateNode(proxy, BASIC_AUTH, realm, url, basic);
                    286:            } else {
                    287:                char * url = HTAnchor_address((HTAnchor*)HTRequest_anchor(request));
                    288:                HTAA_updateNode(proxy, BASIC_AUTH, realm, url, basic);
                    289:                HT_FREE(url);
                    290:            }
2.36      frystyk   291:        }
                    292: 
2.32      frystyk   293:        /*
2.36      frystyk   294:        ** If we have a set of credentials (or the user provides a new set)
                    295:        ** then store it in the request object as the credentials
2.32      frystyk   296:        */
2.39      frystyk   297:        if ((basic->retry && prompt_user(request, realm, basic) == HT_OK) ||
                    298:            (!basic->retry && basic->uid)) {
2.38      frystyk   299:            basic->retry = NO;
2.36      frystyk   300:            return basic_credentials(request, basic);
2.48      frystyk   301:        } else {
                    302:            char * url = HTAnchor_address((HTAnchor*)HTRequest_anchor(request));
                    303:            HTAA_deleteNode(proxy, BASIC_AUTH, realm, url);
                    304:            HT_FREE(url);
2.37      frystyk   305:            return HT_ERROR;
2.48      frystyk   306:        }
2.1       luotonen  307:     }
2.36      frystyk   308:     return HT_OK;
2.1       luotonen  309: }
                    310: 
2.32      frystyk   311: /*     HTBasic_parse
                    312: **     -------------
                    313: **     This function parses the contents of a "basic" challenge 
                    314: **     and stores the challenge in our authentication information datebase.
                    315: **     We also store the realm in the request object which will help finding
                    316: **     the right set of credentials to generate.
                    317: **     The function is a callback function for the AA handler.
                    318: */
2.45      frystyk   319: PUBLIC int HTBasic_parse (HTRequest * request, HTResponse * response,
                    320:                          void * context, int status)
2.32      frystyk   321: {
2.45      frystyk   322:     HTAssocList * challenge = HTResponse_challenge(response);
2.38      frystyk   323:     HTBasic * basic = NULL;
2.40      frystyk   324:     BOOL proxy = status==HT_NO_PROXY_ACCESS ? YES : NO;
2.36      frystyk   325:     if (request && challenge) {
                    326:        char * p = HTAssocList_findObject(challenge, BASIC_AUTH);
                    327:        char * realm = HTNextField(&p);
                    328:        char * rm = HTNextField(&p);
2.38      frystyk   329: 
2.32      frystyk   330:        /*
2.36      frystyk   331:        ** If valid challenge then make a template for the resource and
                    332:        ** store this information in our authentication URL Tree
2.32      frystyk   333:        */
2.36      frystyk   334:        if (realm && !strcasecomp(realm, "realm") && rm) {
                    335:            if (AUTH_TRACE) HTTrace("Basic Parse. Realm `%s\' found\n", rm);
                    336:            HTRequest_setRealm(request, rm);
2.40      frystyk   337: 
                    338:            /*
                    339:            **  If we are in proxy mode then add the proxy - not the final URL
                    340:            */
                    341:            if (proxy) {
                    342:                char * url = HTRequest_proxy(request);
2.42      frystyk   343:                if (AUTH_TRACE) HTTrace("Basic Parse. Proxy authentication\n");
2.40      frystyk   344:                basic = (HTBasic *) HTAA_updateNode(proxy, BASIC_AUTH, rm,
                    345:                                                    url, NULL);
2.49      kahan     346:                /* if the previous authentication failed, then try again */
                    347:                if (HTRequest_AAretrys (request) > 1 
                    348:                    && status == HT_NO_ACCESS && basic)
                    349:                  basic->retry = YES;
2.40      frystyk   350:            } else {
                    351:                char * url = HTAnchor_address((HTAnchor *)
                    352:                                              HTRequest_anchor(request));
                    353:                char * tmplate = make_template(url);
                    354:                basic = (HTBasic *) HTAA_updateNode(proxy, BASIC_AUTH, rm,
                    355:                                                    tmplate, NULL);
2.49      kahan     356:                /* if the previous authentication failed, then try again */
                    357:                if (HTRequest_AAretrys (request) > 1 
                    358:                    && status == HT_NO_ACCESS && basic)
                    359:                  basic->retry = YES;
2.40      frystyk   360:                HT_FREE(url);
                    361:                HT_FREE(tmplate);
                    362:            }
2.1       luotonen  363:        }
2.38      frystyk   364: 
                    365:        /*
                    366:        ** For some reason the authentication failed so we have to ask the user
                    367:        ** if we should try again. It may be because the user typed the wrong
                    368:        ** user name and password
                    369:        */
2.49      kahan     370:        if (basic && basic->retry) {
2.38      frystyk   371:            HTAlertCallback * prompt = HTAlert_find(HT_A_CONFIRM);
2.40      frystyk   372: 
                    373:            /*
2.50    ! kahan     374:            ** Do we have a method registered for prompting the user whether
2.42      frystyk   375:            ** we should retry
2.40      frystyk   376:            */
2.38      frystyk   377:            if (prompt) {
2.40      frystyk   378:                int code = proxy ?
                    379:                    HT_MSG_RETRY_PROXY_AUTH : HT_MSG_RETRY_AUTHENTICATION;
                    380:                if ((*prompt)(request, HT_A_CONFIRM, code,
2.38      frystyk   381:                              NULL, NULL, NULL) != YES)
                    382:                    return HT_ERROR;
                    383:            }
                    384:        }
2.36      frystyk   385:        return HT_OK;
2.1       luotonen  386:     }
2.36      frystyk   387:     if (AUTH_TRACE) HTTrace("Auth........ No challenges found\n");
2.38      frystyk   388:     return HT_ERROR;
2.7       luotonen  389: }
2.44      frystyk   390: 
                    391: /* ------------------------------------------------------------------------- */
                    392: /*                             Digest Authentication                        */
                    393: /* ------------------------------------------------------------------------- */
                    394: 
                    395: /*
                    396: **     Prompt the user for username and password.
                    397: **     Returns YES if user name was typed in, else NO
                    398: */
                    399: PRIVATE int prompt_digest_user (HTRequest * request, const char * realm,
                    400:                                HTDigest * digest)
                    401: {
                    402:     HTAlertCallback * cbf = HTAlert_find(HT_A_USER_PW);
                    403:     if (request && cbf) {
                    404:        HTAlertPar * reply = HTAlert_newReply();
                    405:        int msg = digest->proxy ? HT_MSG_PROXY_UID : HT_MSG_UID;
                    406:        BOOL res = (*cbf)(request, HT_A_USER_PW, msg,
                    407:                          digest->uid, (char *) realm, reply);
                    408:        if (res) {
                    409:            HT_FREE(digest->uid);
                    410:            HT_FREE(digest->pw);
                    411:            digest->uid = HTAlert_replyMessage(reply);
                    412:            digest->pw = HTAlert_replySecret(reply);
                    413:        }
                    414:        HTAlert_deleteReply(reply);
                    415:        return res ? HT_OK : HT_ERROR;
                    416:     }
                    417:     return HT_OK;
                    418: }
                    419: 
                    420: PRIVATE HTDigest * HTDigest_new()
                    421: {
                    422:     HTDigest * me = NULL;
                    423:     if ((me = (HTDigest *) HT_CALLOC(1, sizeof(HTDigest))) == NULL)
                    424:        HT_OUTOFMEM("HTDigest_new");
2.50    ! kahan     425:     me->algorithm = HTDaMD5;                   /* use md5 as a default value */
2.44      frystyk   426:     me->retry = YES;                          /* Ask the first time through */
                    427:     return me;
                    428: }
                    429: 
                    430: /*     HTDigest_delete
                    431: **     --------------
                    432: **     Deletes a "digest" information object
                    433: **     A single object may be registered multiple places in the URL tree.
                    434: **     We keep a simple reference count on the object so that we know
                    435: **     when to delete the object.
                    436: */
                    437: PUBLIC int HTDigest_delete (void * context)
                    438: {
                    439:     HTDigest * digest = (HTDigest *) context;
                    440:     if (digest) {
                    441:        if (digest->references <= 0) {
                    442:            HT_FREE(digest->uid);
                    443:            HT_FREE(digest->pw);
2.50    ! kahan     444:            HT_FREE(digest->realm);
        !           445:            HT_FREE(digest->cnonce);
        !           446:            HT_FREE(digest->nonce);
2.44      frystyk   447:            HT_FREE(digest->opaque);
2.50    ! kahan     448:            HT_FREE(digest->qop);
2.44      frystyk   449:            HT_FREE(digest);
2.50    ! kahan     450:            return YES;
        !           451:        }
        !           452:        else
2.44      frystyk   453:            digest->references--;
2.50    ! kahan     454:     }
        !           455:     return NO;
        !           456: }
        !           457: 
        !           458: /*     HTDigest_reset
        !           459: **     --------------
        !           460: **      When digest authentication fails, we simulate a new digest by
        !           461: **      erasing the old one, but keeping the uid and the password. This is
        !           462: **      so that we can take into account the stale nonce protocol, without
        !           463: **      prompting the user for a new password.
        !           464: */
        !           465: 
        !           466: PRIVATE int HTDigest_reset (HTDigest *digest)
        !           467: {
        !           468:     if (digest) {
        !           469:        digest->nc = 0l;
        !           470:        digest->stale = 0;
        !           471:        digest->retry = YES;
        !           472:        HT_FREE(digest->cnonce);
        !           473:        HT_FREE(digest->nonce);
        !           474:        HT_FREE(digest->opaque);
        !           475:        HT_FREE(digest->qop);
2.44      frystyk   476:        return YES;
                    477:     }
2.50    ! kahan     478:     else
        !           479:        return NO;
        !           480: }
        !           481: 
        !           482: /*     HTDigest_refresh
        !           483: **     --------------
        !           484: **      This function updates the digest with whatever new 
        !           485: **     authentification information the server sent back.
        !           486: **      In theory, it should be called by an authentication after
        !           487: **     filter responsible for the mutual authenticati.
        !           488: */
        !           489: 
        !           490: PUBLIC int HTDigest_refresh (HTRequest *request, HTResponse *response,
        !           491:                             BOOL proxy, char *auth_info)
        !           492: {
        !           493:     char * realm = NULL;
        !           494:     char * value = NULL;
        !           495:     char * token = NULL;
        !           496:     
        !           497:     if (request && auth_info) {
        !           498:        HTDigest *digest;
        !           499:        char *url;
        !           500:        const char * realm = HTRequest_realm(request);
        !           501: 
        !           502:        if (AUTH_TRACE) HTTrace("Digest Update.. "
        !           503:                                "processing authentication-info");
        !           504: 
        !           505:        /* 
        !           506:        ** find the digest credentials 
        !           507:        */
        !           508:        if (proxy) {
        !           509:            url = HTRequest_proxy(request);
        !           510:            digest = (HTDigest *) HTAA_updateNode (proxy, DIGEST_AUTH, realm,
        !           511:                                                   url, NULL);
        !           512:        } else {
        !           513:            url = HTAnchor_address((HTAnchor *)
        !           514:                                   HTRequest_anchor(request));
        !           515:            digest = (HTDigest *) HTAA_updateNode (proxy, DIGEST_AUTH, realm, 
        !           516:                                                   url, NULL);
        !           517:        }
        !           518:        if (!digest) {
        !           519:            if (AUTH_TRACE) HTTrace("Digest Update.. "
        !           520:                                    "Error: received authentication-info "
        !           521:                                    "without having a local digest");   
        !           522:            return HT_ERROR;
        !           523:        }
        !           524: 
        !           525:        /*
        !           526:        **  Search through the set of parameters in the Authentication-info
        !           527:        **  header.
        !           528:        */
        !           529:        while ((token = HTNextField(&auth_info))) {
        !           530:            if (!strcasecomp(token, "nextnonce")) {
        !           531:                if ((value = HTNextField(&auth_info))) {
        !           532:                    HT_FREE (digest->nonce);
        !           533:                    StrAllocCopy(digest->nonce, value);
        !           534:                } else if (!strcasecomp(token, "qop")) {
        !           535:                    value = HTNextField(&auth_info);
        !           536:                    /* split, process  the qop, report errors */
        !           537:                } else if (!strcasecomp(token, "rspauth")) {
        !           538:                    value = HTNextField(&auth_info);
        !           539:                    /* process rspauth */
        !           540:                } else if (!strcasecomp(token, "cnonce")) {
        !           541:                    value = HTNextField (&auth_info);
        !           542:                    if (value && strcmp (digest->cnonce, value)) {
        !           543:                        /* print an alert?, bad cnonce? */
        !           544:                    }   
        !           545:                } else if (!strcasecomp(token, "nc")) {
        !           546:                    value = HTNextField(&auth_info);
        !           547:                    /* compare and printo some error? */
        !           548:                }
        !           549:            }   
        !           550:        }
        !           551:     }
        !           552: }
        !           553:     
        !           554: /*
        !           555: **    Simple function to add a parameter/value pair to a string
        !           556: **
        !           557: */
        !           558: 
        !           559: PRIVATE BOOL add_param (char ** dest, char *param, char * value, BOOL quoted)
        !           560: {
        !           561:     char *tmp = *dest;
        !           562: 
        !           563:     if (!param || *param == '\0' || !value || *value == '\0')
        !           564:        return NO;
        !           565: 
        !           566:     /* if there was a previous parameter, we add the next one in the
        !           567:        following line */
        !           568:     if (tmp) 
        !           569:        StrAllocCat(tmp, ",");
        !           570: 
        !           571:     /* copy the new parameter and value */
        !           572:     StrAllocCat(tmp, param);
        !           573:     StrAllocCat(tmp, "=");
        !           574:     if (quoted) {
        !           575:     StrAllocCat(tmp, "\"");
        !           576:     StrAllocCat(tmp, value);
        !           577:     StrAllocCat(tmp, "\"");
        !           578:     } else
        !           579:        StrAllocCat(tmp, value);
        !           580:     *dest = tmp;
        !           581: 
        !           582:     return YES;
        !           583: }
        !           584: 
        !           585: /*
        !           586: **  Code derived from draft-ietf-http-authentication-03 starts here
        !           587: */
        !           588: 
        !           589: PRIVATE void CvtHex (HASH Bin, HASHHEX Hex)
        !           590: {
        !           591:     unsigned short i;
        !           592:     unsigned char j;
        !           593: 
        !           594:     for (i = 0; i < HASHLEN; i++) {
        !           595:        j = (Bin[i] >> 4) & 0xf;
        !           596:        if (j <= 9)
        !           597:            Hex[i*2] = (j + '0');
        !           598:        else
        !           599:            Hex[i*2] = (j + 'a' - 10);
        !           600:        j = Bin[i] & 0xf;
        !           601:        if (j <= 9)
        !           602:            Hex[i*2+1] = (j + '0');
        !           603:        else
        !           604:            Hex[i*2+1] = (j + 'a' - 10);
        !           605:   }
        !           606:     Hex[HASHHEXLEN] = '\0';
        !           607: }
        !           608: 
        !           609: /* calculate H(A1) as per spec */
        !           610: PRIVATE void DigestCalcHA1 (int algorithm, char * pszAlg, char * pszUserName,
        !           611:                            char * pszRealm, char * pszPassword,
        !           612:                            char * pszNonce, char * pszCNonce,
        !           613:                            HASHHEX SessionKey)
        !           614: {
        !           615:     HTDigestContext MdCtx;
        !           616:     HASH HA1;
        !           617: 
        !           618:     HTDigest_init (&MdCtx, algorithm);
        !           619:     HTDigest_update (&MdCtx, pszUserName, strlen(pszUserName));
        !           620:     HTDigest_update (&MdCtx, ":", 1);
        !           621:     HTDigest_update (&MdCtx, pszRealm, strlen(pszRealm));
        !           622:     HTDigest_update (&MdCtx, ":", 1);
        !           623:     HTDigest_update (&MdCtx, pszPassword, strlen(pszPassword));
        !           624:     HTDigest_final (HA1, &MdCtx);
        !           625:     if (strcasecmp (pszAlg, "md5-sess") == 0) {
        !           626:        HTDigest_init (&MdCtx, algorithm);
        !           627:        HTDigest_update (&MdCtx, HA1, strlen (HA1));
        !           628:        HTDigest_update (&MdCtx, ":", 1);
        !           629:        HTDigest_update (&MdCtx, pszNonce, strlen(pszNonce));
        !           630:        HTDigest_update (&MdCtx, ":", 1);
        !           631:        HTDigest_update (&MdCtx, pszCNonce, strlen(pszCNonce));
        !           632:        HTDigest_final (HA1, &MdCtx);
        !           633:     }
        !           634:     CvtHex (HA1, SessionKey);
2.44      frystyk   635: }
                    636: 
2.50    ! kahan     637: /* calculate request-digest/response-digest as per HTTP Digest spec */
        !           638: PRIVATE void DigestCalcResponse (
        !           639:     int    algorithm,      /* message digest algorithm */
        !           640:     HASHHEX HA1,           /* H(A1) */
        !           641:     char * pszNonce,       /* nonce from server */
        !           642:     char * pszNonceCount,  /* 8 hex digits */
        !           643:     char * pszCNonce,      /* client nonce */
        !           644:     char * pszQop,         /* qop-value: "", "auth", "auth-int" */
        !           645:     char * pszMethod,      /* method from the request */
        !           646:     char * pszDigestUri,   /* requested URL */
        !           647:     char * HEntity,        /* H(entity body) if qop="auth-int" */
        !           648:     char * Response        /* request-digest or response-digest */
        !           649:     )
        !           650: {
        !           651:     HTDigestContext MdCtx;
        !           652:     HASH HA2;
        !           653:     HASH RespHash;
        !           654:     HASHHEX HA2Hex;
        !           655: 
        !           656:     /* calculate H(A2) */
        !           657: 
        !           658:     HTDigest_init (&MdCtx, algorithm);
        !           659:     HTDigest_update (&MdCtx, pszMethod, strlen(pszMethod));
        !           660:     HTDigest_update (&MdCtx, ":", 1);
        !           661:     HTDigest_update (&MdCtx, pszDigestUri, strlen(pszDigestUri));
        !           662:     if (pszQop && strcasecmp (pszQop, "auth-int") == 0) {
        !           663:        HTDigest_update (&MdCtx, ":", 1);
        !           664:        HTDigest_update (&MdCtx, HEntity, HASHHEXLEN);
        !           665:     }
        !           666:     HTDigest_final (HA2, &MdCtx);
        !           667:     CvtHex (HA2, HA2Hex);
        !           668: 
        !           669:     /* calculate response */
        !           670:     HTDigest_init (&MdCtx, algorithm);
        !           671:     HTDigest_update (&MdCtx, HA1, HASHHEXLEN);
        !           672:     HTDigest_update (&MdCtx, ":", 1);
        !           673:     HTDigest_update (&MdCtx, pszNonce, strlen(pszNonce));
        !           674:     HTDigest_update (&MdCtx, ":", 1);
        !           675:     if (pszQop && *pszQop) {
        !           676:        HTDigest_update (&MdCtx, pszNonceCount, strlen(pszNonceCount));
        !           677:        HTDigest_update (&MdCtx, ":", 1);
        !           678:        HTDigest_update (&MdCtx, pszCNonce, strlen(pszCNonce));
        !           679:        HTDigest_update (&MdCtx, ":", 1);
        !           680:        HTDigest_update (&MdCtx, pszQop, strlen(pszQop));
        !           681:        HTDigest_update (&MdCtx, ":", 1);
        !           682:     }
        !           683:     HTDigest_update (&MdCtx, HA2Hex, HASHHEXLEN);
        !           684:     HTDigest_final (RespHash, &MdCtx);
        !           685:     CvtHex (RespHash, Response);
        !           686: }      
        !           687: 
        !           688: /*
        !           689: **  Code derived from draft-ietf-http-authentication-03 ends here
        !           690: */
        !           691: 
2.44      frystyk   692: /*
                    693: **     Make digest authentication scheme credentials and register this
                    694: **     information in the request object as credentials. They will then
2.50    ! kahan     695: **     be included in the request header. An example is
        !           696: **
        !           697: **                 "Digest nonce:cnonce:blahblahblhah:digest-response"
        !           698: **
2.44      frystyk   699: **     The function can both create normal and proxy credentials
                    700: **     Returns HT_OK or HT_ERROR
                    701: */
2.50    ! kahan     702: 
2.44      frystyk   703: PRIVATE BOOL digest_credentials (HTRequest * request, HTDigest * digest)
                    704: {
2.50    ! kahan     705:     if (request && digest && digest->realm)
        !           706:     {
        !           707:         char * realm = (char *) digest->realm;
        !           708:        char * uri;
        !           709:        char * method = (char *) HTMethod_name (HTRequest_method (request));
        !           710:        char * cleartext = NULL;
        !           711:        char nc[9];
        !           712:        HASHHEX HA1;
        !           713:         HASHHEX HA2;
        !           714:        HASHHEX response;
2.44      frystyk   715: 
2.50    ! kahan     716:        /* @@ maybe optimize all my reallocs by preallocating the memory */
2.44      frystyk   717: 
2.50    ! kahan     718:        if (digest->proxy)
        !           719:            uri = HTRequest_proxy(request);
        !           720:        else
        !           721:            uri = HTAnchor_address( (HTAnchor*)HTRequest_anchor(request));
        !           722: 
        !           723:        /* increment the nonce counter */
        !           724:        digest->nc++;
        !           725:        sprintf (nc, "%08lx", digest->nc);
        !           726:        add_param (&cleartext, "username", digest->uid, YES);
        !           727:        add_param (&cleartext, "realm", realm, YES);
        !           728:        add_param (&cleartext, "nonce", digest->nonce, YES);
        !           729:        add_param (&cleartext, "uri", uri, YES);
        !           730:        /* @@@  no support for auth-int yet */
        !           731:        if (digest->qop) {
        !           732:            add_param (&cleartext, "qop", "auth", NO);
        !           733:            add_param (&cleartext, "nc", nc, NO);
        !           734:            add_param (&cleartext, "cnonce", digest->cnonce, YES);
        !           735:        }
        !           736:        /* compute the response digest */
        !           737:        /* @@@ md5 hard coded, change it to something from the answer, 
        !           738:           md5-sess, etc */
        !           739:        DigestCalcHA1 (digest->algorithm, "md5", digest->uid, realm, digest->pw, digest->nonce,
        !           740:                       digest->cnonce, HA1);
        !           741:        DigestCalcResponse (digest->algorithm, HA1, digest->nonce, nc, digest->cnonce,
        !           742:                            digest->qop, method, uri, HA2, response);
        !           743:        add_param (&cleartext, "response", response, NO);
        !           744:        add_param (&cleartext, "opaque", digest->opaque, NO);
2.44      frystyk   745: 
                    746:        /* Create the credentials and assign them to the request object */
                    747:        {
2.50    ! kahan     748:            int cr_len = strlen ("Digest") + strlen (cleartext) + 3;
2.44      frystyk   749:            char * cookie = (char *) HT_MALLOC(cr_len+1);
                    750:            if (!cookie) HT_OUTOFMEM("digest_credentials");
                    751:            strcpy(cookie, "Digest ");
2.50    ! kahan     752:            strcat (cookie, cleartext);
2.44      frystyk   753:            if (AUTH_TRACE) HTTrace("Digest Cookie `%s\'\n", cookie);
                    754: 
                    755:            /* Check whether it is proxy or normal credentials */
                    756:            if (digest->proxy)
2.50    ! kahan     757:                HTRequest_addCredentials(request, "Proxy-Authorization",
        !           758:                                         cookie);
2.44      frystyk   759:            else
                    760:                HTRequest_addCredentials(request, "Authorization", cookie);
                    761: 
                    762:            HT_FREE(cookie);
                    763:        }
                    764:        HT_FREE(cleartext);
                    765:        return HT_OK;
                    766:     }
                    767:     return HT_ERROR;
                    768: }
                    769: 
                    770: /*     HTDigest_generate
                    771: **     ----------------
                    772: **     This function generates "digest" credentials for the challenge found in
                    773: **     the authentication information base for this request. The result is
                    774: **     stored as an association list in the request object.
                    775: **     This is a callback function for the AA handler.
                    776: */
2.45      frystyk   777: PUBLIC int HTDigest_generate (HTRequest * request, void * context, int mode)
2.44      frystyk   778: { 
                    779:     HTDigest * digest = (HTDigest *) context;
2.45      frystyk   780:     BOOL proxy = mode==HT_NO_PROXY_ACCESS ? YES : NO;
2.44      frystyk   781:     if (request) {
                    782:        const char * realm = HTRequest_realm(request);
2.46      frystyk   783: 
                    784:        /*
                    785:        **  If we were asked to explicitly ask the user again
                    786:        */
                    787:        if (mode == HT_REAUTH || mode == HT_PROXY_REAUTH)
                    788:            digest->retry = YES;
2.44      frystyk   789: 
                    790:        /*
                    791:        ** If we don't have a digest context then add a new one to the tree.
                    792:        ** We use different trees for normal and proxy authentication
                    793:        */
                    794:        if (!digest) {
2.50    ! kahan     795:            digest = HTDigest_new();
2.44      frystyk   796:            if (proxy) {
                    797:                char * url = HTRequest_proxy(request);
                    798:                digest->proxy = YES;
                    799:                HTAA_updateNode(proxy, DIGEST_AUTH, realm, url, digest);
                    800:            } else {
                    801:                char * url = HTAnchor_address((HTAnchor*)HTRequest_anchor(request));
                    802:                HTAA_updateNode(proxy, DIGEST_AUTH, realm, url, digest);
                    803:                HT_FREE(url);
                    804:            }
                    805:        }
                    806: 
                    807:        /*
                    808:        ** If we have a set of credentials (or the user provides a new set)
                    809:        ** then store it in the request object as the credentials
                    810:        */
2.50    ! kahan     811:        if ((digest->retry && 
2.44      frystyk   812:             prompt_digest_user(request, realm, digest) == HT_OK) ||
                    813:            (!digest->retry && digest->uid)) {
2.50    ! kahan     814:        /* @@@ here we should generate a new cnonce value */
        !           815:            digest->cnonce = "012345678";
2.44      frystyk   816:            digest->retry = NO;
                    817:            return digest_credentials(request, digest);
2.50    ! kahan     818:        } else {
        !           819:            char * url = HTAnchor_address((HTAnchor*)HTRequest_anchor(request));
        !           820:            if (proxy)
        !           821:                HTAA_deleteNode(proxy, DIGEST_AUTH, realm, url);
        !           822:            else
        !           823:                HTAA_deleteNode(proxy, DIGEST_AUTH, realm, url);
        !           824:            HT_FREE(url);
2.44      frystyk   825:            return HT_ERROR;
2.50    ! kahan     826:        }
2.44      frystyk   827:     }
                    828:     return HT_OK;
                    829: }
                    830: 
                    831: /*     HTDigest_parse
                    832: **     -------------
                    833: **     This function parses the contents of a "digest" challenge 
                    834: **     and stores the challenge in our authentication information datebase.
                    835: **     We also store the realm in the request object which will help finding
                    836: **     the right set of credentials to generate.
                    837: **     The function is a callback function for the AA handler.
                    838: */
2.45      frystyk   839: PUBLIC int HTDigest_parse (HTRequest * request, HTResponse * response,
                    840:                           void * context, int status)
2.44      frystyk   841: {
2.45      frystyk   842:     HTAssocList * challenge = HTResponse_challenge(response);
2.44      frystyk   843:     HTDigest * digest = NULL;    
                    844:     BOOL proxy = status==HT_NO_PROXY_ACCESS ? YES : NO;
                    845:     if (request && challenge) {
                    846:        char * p = HTAssocList_findObject(challenge, DIGEST_AUTH);
                    847:        char * realm =  HTNextField(&p);
2.50    ! kahan     848:        char * rm    =  HTNextField(&p);
        !           849:        char * value = NULL;
2.44      frystyk   850:        char * token = NULL;
                    851:        char * uris = NULL;
                    852: 
                    853:        /*
2.50    ! kahan     854:        ** If valid challenge then make a template for the resource and
        !           855:        ** store this information in our authentication URL Tree
2.44      frystyk   856:        */
2.50    ! kahan     857:        if (realm && !strcasecomp(realm, "realm") && rm) {
        !           858:            if (AUTH_TRACE) HTTrace("Digest Parse. Realm `%s\' found\n", rm);
        !           859:            HTRequest_setRealm(request, rm);
        !           860: 
        !           861:            /*
        !           862:            **  If we are in proxy mode then add the proxy - not the final URL
        !           863:            */
        !           864:            if (proxy) {
        !           865:                char * url = HTRequest_proxy(request);
        !           866:                if (AUTH_TRACE) HTTrace("Digest Parse. Proxy authentication\n");
        !           867:                digest = (HTDigest *) HTAA_updateNode(proxy, DIGEST_AUTH, rm,
        !           868:                                                      url, NULL);
        !           869:                /* if the previous authentication failed, then try again */
        !           870:                if (HTRequest_AAretrys (request) > 1 
        !           871:                    && status == HT_NO_ACCESS && digest)
        !           872:                  digest->retry = YES;
        !           873:            } else {
        !           874:                char * url = HTAnchor_address((HTAnchor *)
        !           875:                                              HTRequest_anchor(request));
        !           876:                char * tmplate = make_template(url);
        !           877:                digest = (HTDigest *) HTAA_updateNode(proxy, DIGEST_AUTH, rm,
        !           878:                                                      tmplate, NULL);
        !           879:                /* if the previous authentication failed, then try again */
        !           880:                if (HTRequest_AAretrys (request) > 1 
        !           881:                    && status == HT_NO_ACCESS && digest)
        !           882:                  digest->retry = YES;
        !           883:                HT_FREE(tmplate);
        !           884:                HT_FREE(url);
        !           885:            }
        !           886:        } else {
        !           887:            if (AUTH_TRACE) HTTrace("Digest Parse. Missing or incomplete realm\n");
        !           888:            return HT_ERROR;
2.44      frystyk   889:        }
2.50    ! kahan     890: 
        !           891: 
        !           892:        /* if we get here it's because there's no digest */
        !           893:        /* we parse the digest parameters from the challenge */
        !           894: 
        !           895:        if (digest) {
        !           896:            /* it's an old digest, so we clean all in it except for the
        !           897:               uid and the password, hoping that the server send back
        !           898:               that data */
        !           899:            HTDigest_reset (digest);
        !           900:        } else {
        !           901:            /* it's a brand new digest */
2.44      frystyk   902:            digest = HTDigest_new();
2.50    ! kahan     903:            StrAllocCopy (digest->realm, rm);
        !           904:        }
2.44      frystyk   905: 
                    906:        /*
                    907:        **  Search through the set of parameters in the digest header.
                    908:        **  If valid challenge then make a template for the resource and
                    909:        **  store this information in our authentication URL Tree
                    910:        */
                    911:        while ((token = HTNextField(&p))) {
                    912:            if (!strcasecomp(token, "domain")) {
                    913:                if ((value = HTNextField(&p)))
                    914:                    uris = value;
2.50    ! kahan     915:            } else if (!strcasecomp(token, "nonce")) {
2.44      frystyk   916:                if ((value = HTNextField(&p)))
2.50    ! kahan     917:                    StrAllocCopy(digest->nonce, value);
2.44      frystyk   918:            } else if (!strcasecomp(token, "opaque")) {
                    919:                if ((value = HTNextField(&p)))
                    920:                    StrAllocCopy(digest->opaque, value);
2.50    ! kahan     921:            } else if (!strcasecomp(token, "qop")) {
        !           922:                /* split the qop */
        !           923:                if ((value = HTNextField(&p)))
        !           924:                    StrAllocCopy(digest->qop, value);
2.44      frystyk   925:            } else if (!strcasecomp(token, "stale")) {
2.50    ! kahan     926:                if ((value = HTNextField(&p)) && !strcasecomp(value, "true")) {
        !           927:                    /* only true if we already had a digest with uid and pw info */
        !           928:                    if (digest->uid && digest->pw) {
        !           929:                        digest->stale = YES;            
        !           930:                        digest->retry = NO;
        !           931:                    }
        !           932:                }
2.44      frystyk   933:            } else if (!strcasecomp(token, "algorithm")) {
2.50    ! kahan     934:                if ((value = HTNextField(&p)) && strcasecomp(value, "md5")) {
2.44      frystyk   935:                    /*
                    936:                    **  We only support MD5 for the moment
                    937:                    */
2.50    ! kahan     938:                    if (AUTH_TRACE) HTTrace("Digest Parse Unknown "
        !           939:                                            "algorithm `%s\'\n", value);
2.44      frystyk   940:                    HTDigest_delete(digest);
                    941:                    return HT_ERROR;
2.50    ! kahan     942:                } else
        !           943:                    digest->algorithm = HTDaMD5;
        !           944:            }
        !           945:        }
        !           946:        
        !           947:        if (digest->stale)
        !           948:            return HT_OK;
        !           949:        else if (digest->uid || digest->pw) {
        !           950:            /*
        !           951:            ** For some reason there was no stale nonce header and the
        !           952:            ** authentication failed so we have to ask the user if we should
        !           953:            ** try again. It may be because the user typed the wrong user name
        !           954:            ** and password
        !           955:            */
        !           956:            HTAlertCallback * prompt = HTAlert_find(HT_A_CONFIRM);
        !           957: 
        !           958:            /*
        !           959:            ** Do we have a method registered for prompting the user whether
        !           960:            ** we should retry
        !           961:            */
        !           962:            if (prompt) {
        !           963:                int code = proxy ?
        !           964:                    HT_MSG_RETRY_PROXY_AUTH : HT_MSG_RETRY_AUTHENTICATION;
        !           965:                if ((*prompt)(request, HT_A_CONFIRM, code,
        !           966:                              NULL, NULL, NULL) != YES)
        !           967:                    return HT_ERROR;
        !           968:                return HT_OK;
2.44      frystyk   969:            }
2.50    ! kahan     970:            return HT_ERROR;
2.44      frystyk   971:        }
                    972: 
                    973:        /*
2.50    ! kahan     974:        ** It's the first time we go this way, so we check the domain field to 
        !           975:        ** create the digest node entries for each URI.
2.44      frystyk   976:        */
                    977:        if (!uris) {
                    978:            if (proxy) {
2.50    ! kahan     979:                /* we ignore the domain */
2.44      frystyk   980:                char * location = HTRequest_proxy(request);
                    981:                if (AUTH_TRACE) HTTrace("Digest Parse Proxy authentication\n");
2.50    ! kahan     982:                HTAA_updateNode(proxy, DIGEST_AUTH, rm, location, digest);
2.44      frystyk   983:            } else {
                    984:                char * url = HTAnchor_address((HTAnchor *) HTRequest_anchor(request));
                    985:                char * tmplate = make_template(url);
2.50    ! kahan     986:                HTAA_updateNode(proxy, DIGEST_AUTH, rm, tmplate, digest);
2.44      frystyk   987:                HT_FREE(url);
                    988:                HT_FREE(tmplate);
                    989:            }
                    990:        } else {
2.50    ! kahan     991:            char * base_url =
        !           992:                HTAnchor_address((HTAnchor *) HTRequest_anchor(request));
        !           993:            char * domain_url;
        !           994:            char * full_url;
        !           995: 
        !           996:            while ((domain_url = HTNextField (&uris))) {
        !           997:                /* complete the URL if it's an absolute one */
        !           998:                full_url = HTParse (domain_url, base_url, PARSE_ALL);
        !           999:                digest->references++;
        !          1000:                if (proxy) {
        !          1001:                    if (AUTH_TRACE) HTTrace("Digest Parse Proxy authentication\n");
        !          1002:                    HTAA_updateNode(proxy, DIGEST_AUTH, rm, full_url, digest);
        !          1003:                } else {
        !          1004:                    char * tmplate = make_template(full_url);
        !          1005:                    HTAA_updateNode (proxy, DIGEST_AUTH, rm, tmplate, digest);
        !          1006:                    HT_FREE (tmplate);
        !          1007:                }
        !          1008:                HT_FREE (full_url);
2.44      frystyk  1009:            }
2.50    ! kahan    1010:            HT_FREE (base_url);
        !          1011:            HT_FREE (uris);
2.44      frystyk  1012:        }
                   1013:        return HT_OK;
2.50    ! kahan    1014:     }  
2.44      frystyk  1015:     if (AUTH_TRACE) HTTrace("Auth........ No challenges found\n");
                   1016:     return HT_ERROR;
                   1017: }
2.50    ! kahan    1018: 
        !          1019: 
        !          1020: 
        !          1021: 
        !          1022: 
        !          1023: 
        !          1024: 
        !          1025: 
        !          1026: 
        !          1027: 
        !          1028: 
        !          1029: 
        !          1030: 
2.44      frystyk  1031: 

Webmaster