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

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

Webmaster