Ticket #8452: filezilla_3.6.0_http_proxy_digest.patch

File filezilla_3.6.0_http_proxy_digest.patch, 13.5 KB (added by Tommy Wu, 8 years ago)

add digest support for http proxy

  • src/engine/proxy.cpp

    diff --strip-trailing-cr -Nur a/src/engine/proxy.cpp b/src/engine/proxy.cpp
    a b  
    11#include <filezilla.h>
    22#include "proxy.h"
    33#include <errno.h>
     4#include <wx/tokenzr.h>
     5#include <nettle/md5-compat.h>
    46#include "ControlSocket.h"
    57
    68enum handshake_state
     
    2527    m_pSendBuffer = 0;
    2628    m_pRecvBuffer = 0;
    2729
     30    m_doAuth = false;
     31
    2832    m_proxyType = unknown;
    2933
    3034    m_can_write = false;
     
    3943    delete [] m_pRecvBuffer;
    4044}
    4145
     46static void AppendSubHex(unsigned char val, wxString& result)
     47{
     48   if (val > 9)
     49   {
     50      result.Append((char)('a'+val-10));
     51   }
     52   else
     53   {
     54      result.Append((char)('0'+val));
     55   }
     56}
     57
     58static void AppendHex(unsigned char val, wxString& result)
     59{
     60   AppendSubHex((val >> 4) & 0xF, result);
     61   AppendSubHex(val&0xF, result);
     62}
     63
     64static wxString ComputeMD5(const char *buf, unsigned int len)
     65{
     66    MD5_CTX ctx;
     67    unsigned char signature[16];
     68
     69    MD5Init(&ctx);
     70    MD5Update(&ctx, (const unsigned char*)buf, len);
     71    MD5Final(signature, &ctx);
     72
     73    wxString result;
     74    for (int j = 0; j < (int)sizeof signature; j++)
     75    {
     76        AppendHex(signature[j], result);
     77    }
     78
     79    return result;
     80}
     81
    4282static wxString base64encode(const wxString& str)
    4383{
    4484    // Code shamelessly taken from wxWidgets and adopted to encode UTF-8 strings.
     
    96136    {
    97137        m_handshakeState = http_wait;
    98138
    99 #if wxUSE_UNICODE
    100         wxWX2MBbuf challenge;
    101 #else
    102         const wxWX2MBbuf challenge;
    103 #endif
    104         int challenge_len;
    105         if (user != _T(""))
    106         {
    107             challenge = base64encode(user + _T(":") + pass).mb_str(wxConvUTF8);
    108             challenge_len = strlen(challenge);
    109         }
    110         else
    111         {
    112             challenge = (size_t)0;
    113             challenge_len = 0;
    114         }
    115 
    116139        // Bit oversized, but be on the safe side
    117         m_pSendBuffer = new char[70 + strlen(host_raw) * 2 + 2*5 + challenge_len + 23];
     140        m_pSendBuffer = new char[70 + strlen(host_raw) * 2 + 2*5 + 23];
    118141
    119         if (!challenge)
    120         {
    121             m_sendBufferLen = sprintf(m_pSendBuffer, "CONNECT %s:%u HTTP/1.1\r\nHost: %s:%u\r\nUser-Agent: FileZilla\r\n\r\n",
    122                 (const char*)host_raw, port,
    123                 (const char*)host_raw, port);
    124         }
    125         else
    126         {
    127             m_sendBufferLen = sprintf(m_pSendBuffer, "CONNECT %s:%u HTTP/1.1\r\nHost: %s:%u\r\nProxy-Authorization: Basic %s\r\nUser-Agent: FileZilla\r\n\r\n",
    128                 (const char*)host_raw, port,
    129                 (const char*)host_raw, port,
    130                 (const char*)challenge);
    131         }
     142        m_sendBufferLen = sprintf(m_pSendBuffer, "CONNECT %s:%u HTTP/1.1\r\nHost: %s:%u\r\nUser-Agent: FileZilla\r\n\r\n",
     143            (const char*)host_raw, port,
     144            (const char*)host_raw, port);
    132145
    133146        m_pRecvBuffer = new char[4096];
    134147        m_recvBufferLen = 4096;
     
    302315            if (!end)
    303316                continue;
    304317
     318            unsigned long int nContentLength = 0;
     319            bool bUseBasic = false;
     320            bool bUseDigest = false;
     321            wxString digestStr = _T("");
     322            wxString authStr = _T("");
     323            wxString header_str(m_pRecvBuffer, wxConvLocal, m_recvBufferPos);
     324            wxStringTokenizer tkz(header_str, wxT("\r\n"));
     325
     326            while (tkz.HasMoreTokens())
     327            {
     328                wxString token = tkz.GetNextToken();
     329                if (token.Mid(0, 15).Lower() == _T("content-length:"))
     330                {
     331                    token.Mid(15).Trim(true).Trim(false).ToULong(&nContentLength);
     332                    continue;
     333                }
     334                if (token.Mid(0, 26).Lower() == _T("proxy-authenticate: basic "))
     335                {
     336                    bUseBasic = true;
     337                    continue;
     338                }
     339                if (token.Mid(0, 27).Lower() == _T("proxy-authenticate: digest "))
     340                {
     341                    authStr = token.Mid(27);
     342                    bUseDigest = true;
     343                    continue;
     344                }
     345            }
     346
     347            // read body
     348            unsigned long int nBodyLength = 0;
     349            char *pBody = new char[nContentLength];
     350            while (nBodyLength < nContentLength) {
     351                int read;
     352
     353                do_read = nContentLength - nBodyLength;
     354
     355                read = m_pSocket->Read(pBody + nBodyLength, do_read, error);
     356                if (read == -1)
     357                {
     358                    if (error != EAGAIN)
     359                    {
     360                        delete [] pBody;
     361                        m_proxyState = noconn;
     362                        CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::close, error);
     363                        CSocketEventDispatcher::Get().SendEvent(evt);
     364                    }
     365                    else
     366                        m_can_read = false;
     367                    continue;
     368                }
     369                if (!read)
     370                {
     371                    delete [] pBody;
     372                    m_proxyState = noconn;
     373                    CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::close, ECONNABORTED);
     374                    CSocketEventDispatcher::Get().SendEvent(evt);
     375                    return;
     376                }
     377                if (m_pSendBuffer)
     378                {
     379                    delete [] pBody;
     380                    m_proxyState = noconn;
     381                    m_pOwner->LogMessage(Debug_Warning, _T("Incoming data before requst fully sent"));
     382                    CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::close, ECONNABORTED);
     383                    CSocketEventDispatcher::Get().SendEvent(evt);
     384                    return;
     385                }
     386                nBodyLength += read;
     387            }
     388
    305389            end = strchr(m_pRecvBuffer, '\r');
    306390            wxASSERT(end);
    307391            *end = 0;
    308392            wxString reply(m_pRecvBuffer, wxConvUTF8);
    309393            m_pOwner->LogMessage(Response, _("Proxy reply: %s"), reply.c_str());
    310394
     395            if (reply.Left(12) == _T("HTTP/1.1 401") || reply.Left(12) == _T("HTTP/1.1 407") ||
     396                reply.Left(12) == _T("HTTP/1.0 401") || reply.Left(12) == _T("HTTP/1.0 407"))
     397            {
     398                if (m_doAuth || m_user == _T(""))
     399                {
     400                    delete [] pBody;
     401                    m_pOwner->LogMessage(Debug_Warning, _("already m_doAuth (%d), or m_user is empty (%s)"), m_doAuth, m_user.c_str());
     402                    m_proxyState = noconn;
     403                    CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::close, ECONNRESET);
     404                    CSocketEventDispatcher::Get().SendEvent(evt);
     405                    return;
     406                }
     407
     408                if (bUseDigest)
     409                {
     410                    wxString realm = _T("");
     411                    wxString nonce = _T("");
     412                    wxString qop = _T("");
     413                    wxStringTokenizer tkz2(authStr, wxT(","));
     414                    while (tkz2.HasMoreTokens())
     415                    {
     416                        wxString token2 = tkz2.GetNextToken();
     417                        wxString name = _T("");
     418                        wxString value = _T("");
     419                        wxStringTokenizer tkz3(token2, wxT("="));
     420                        while (tkz3.HasMoreTokens())
     421                        {
     422                            wxString token3 = tkz3.GetNextToken();
     423                            if (name == _T(""))
     424                            {
     425                                name = token3.Lower().Trim(true).Trim(false);
     426                                continue;
     427                            }
     428                            if (token3.Left(1) == _T("\""))
     429                                value = token3.Mid(1, token3.Len()-2).Trim(true).Trim(false);
     430                            else
     431                                value = token3.Trim(true).Trim(false);
     432                            break;
     433                        }
     434                        if (name == _T("realm"))
     435                            realm = value;
     436                        else if (name == _T("nonce"))
     437                            nonce = value;
     438                        else if (name == _T("qop"))
     439                            qop = value;
     440                    }
     441                    if (realm == _T("") || nonce == _T(""))
     442                    {
     443                        delete [] pBody;
     444                        m_pOwner->LogMessage(Debug_Warning, _T("no realm or nonce"));
     445                        m_proxyState = conn;
     446                        CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::connection, 0);
     447                        CSocketEventDispatcher::Get().SendEvent(evt);
     448                        return;
     449                    }
     450                    // HA1 = md5(username:realm:password)
     451                    // if qop = auth or no qop
     452                    // HA2 = md5(method:digestURI)
     453                    // if qop = auth-int
     454                    // HA2 = md5(method:digestURI:md5(body))
     455                    // if qop = auth or auth-int
     456                    // response = md5(HA1:nonce:nonceCount:clientNonce:qop:HA2)
     457                    // if no qop
     458                    // response = md5(HA1:nonce:HA2)
     459                    wxString ha1, ha2, response;
     460                    wxString s;
     461                    bool bAuth = false;
     462                    bool bAuthInt = false;
     463
     464                    if (qop != _T(""))
     465                    {
     466                        wxStringTokenizer tkz4(qop, wxT(","));
     467                        while (tkz4.HasMoreTokens())
     468                        {
     469                            wxString token4 = tkz4.GetNextToken();
     470                            if (token4 == _T("auth"))
     471                                bAuth = true;
     472                            if (token4 == _T("auth-int"))
     473                                bAuthInt = true;
     474                        }
     475                    }
     476
     477                    s = wxString::Format(_T("%s:%s:%s"), m_user.c_str(), realm.c_str(), m_pass.c_str());
     478                    const wxWX2MBbuf s_raw = s.mb_str(wxConvUTF8);
     479                    ha1 = ComputeMD5((const char *)s_raw, strlen(s_raw));
     480
     481                    wxString uri = _T("");
     482                    if (qop == _T("") || bAuth)
     483                    {
     484                        uri = wxString::Format(_T("%s:%u"), m_host.c_str(), m_port);
     485                        s = wxString::Format(_T("CONNECT:%s"), uri.c_str());
     486                        const wxWX2MBbuf s_raw = s.mb_str(wxConvUTF8);
     487                        ha2 = ComputeMD5((const char *)s_raw, strlen(s_raw));
     488                    }
     489                    else if (bAuthInt)
     490                    {
     491                        uri = wxString::Format(_T("%s:%u"), m_host.c_str(), m_port);
     492                        wxString bodyMD5 = ComputeMD5((const char *)pBody, nBodyLength);
     493                        s = wxString::Format(_T("CONNECT:%s:%s"), uri.c_str(), bodyMD5.c_str());
     494                        const wxWX2MBbuf s_raw = s.mb_str(wxConvLocal);
     495                        ha2 = ComputeMD5((const char *)s_raw, strlen(s_raw));
     496                    }
     497                    else
     498                    {
     499                        delete [] pBody;
     500                        m_pOwner->LogMessage(Debug_Warning, _T("unknown qop: %s"), qop.c_str());
     501                        m_proxyState = conn;
     502                        CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::connection, 0);
     503                        CSocketEventDispatcher::Get().SendEvent(evt);
     504                        return;
     505                    }
     506
     507                    // we don't need body now
     508                    delete [] pBody;
     509                    if (bAuth || bAuthInt)
     510                    {
     511                        wxString clientNonce = wxString::Format(_T("%04x%04x"), GetRandomNumber(0, 0xffff), GetRandomNumber(0, 0xffff));
     512
     513                        s = wxString::Format(_T("%s:%s:%s:%s:%s:%s"),
     514                            ha1.c_str(),
     515                            nonce.c_str(),
     516                            _T("00000001"),
     517                            clientNonce.c_str(),
     518                            bAuth ? _T("auth") : _T("auth-int"),
     519                            ha2.c_str());
     520                        const wxWX2MBbuf s_raw = s.mb_str(wxConvLocal);
     521                        response = ComputeMD5((const char *)s_raw, strlen(s_raw));
     522
     523                        digestStr = wxString::Format(_T("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\", qop=%s, nc=00000001, cnonce=\"%s\""),
     524                            m_user.c_str(),
     525                            realm.c_str(),
     526                            nonce.c_str(),
     527                            uri.c_str(),
     528                            response.c_str(),
     529                            bAuth ? _T("auth") : _T("auth-int"),
     530                            clientNonce.c_str());
     531                    }
     532                    else
     533                    {
     534                        s = wxString::Format(_T("%s:%s:%s"),
     535                            ha1.c_str(),
     536                            nonce.c_str(),
     537                            ha2.c_str());
     538                        const wxWX2MBbuf s_raw = s.mb_str(wxConvLocal);
     539                        response = ComputeMD5((const char *)s_raw, strlen(s_raw));
     540
     541                        digestStr = wxString::Format(_T("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\""),
     542                            m_user.c_str(),
     543                            realm.c_str(),
     544                            nonce.c_str(),
     545                            uri.c_str(),
     546                            response.c_str());
     547                    }
     548
     549                    m_doAuth = true;
     550                                m_recvBufferPos = 0;
     551
     552                    const wxWX2MBbuf host_raw = m_host.mb_str(wxConvUTF8);
     553                    const wxWX2MBbuf digest_raw = digestStr.mb_str(wxConvLocal);
     554
     555                    int challenge_len = digestStr.Len();
     556
     557                    m_pOwner->LogMessage(Status, _("Sending CONNECT with Digest Authorization"));
     558
     559                                // Bit oversized, but be on the safe side
     560                                m_pSendBuffer = new char[70 + m_host.Len() * 2 + 2*5 + challenge_len * 2 + 23 + 40];
     561
     562                    m_sendBufferLen = sprintf(m_pSendBuffer, "CONNECT %s:%u HTTP/1.1\r\nHost: %s:%u\r\nProxy-Authorization: %s\r\nUser-Agent: FileZilla\r\n\r\n",
     563                        (const char*)host_raw, m_port,
     564                        (const char*)host_raw, m_port,
     565                        (const char*)digest_raw);
     566
     567                    wxString host = m_pSocket->GetPeerIP();
     568                    unsigned int port = m_pSocket->GetRemotePort(error);
     569
     570                    m_pSocket->Close();
     571                    int res = m_pSocket->Connect(host, port);
     572                    // Treat success same as EINPROGRESS, we wait for connect notification in any case
     573                    if (res && res != EINPROGRESS)
     574                    {
     575                        m_pOwner->LogMessage(Debug_Warning, _T("connect error?"));
     576                        m_proxyState = conn;
     577                        CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::connection, 0);
     578                        CSocketEventDispatcher::Get().SendEvent(evt);
     579                        return;
     580                    }
     581                    return;
     582                }
     583
     584                if (bUseBasic)
     585                {
     586                    // we don't need body for this
     587                    delete [] pBody;
     588                    m_doAuth = true;
     589                                m_recvBufferPos = 0;
     590
     591#if wxUSE_UNICODE
     592                    wxWX2MBbuf challenge;
     593#else
     594                    const wxWX2MBbuf challenge;
     595#endif
     596                    const wxWX2MBbuf host_raw = m_host.mb_str(wxConvUTF8);
     597                    int challenge_len;
     598
     599                    challenge = base64encode(m_user + _T(":") + m_pass).mb_str(wxConvUTF8);
     600                    challenge_len = strlen(challenge);
     601
     602                    m_pOwner->LogMessage(Status, _("Sending CONNECT with Basic Authorization"));
     603                    // Bit oversized, but be on the safe side
     604                    m_pSendBuffer = new char[70 + strlen(host_raw) * 2 + 2*5 + challenge_len + 23];
     605
     606                    m_sendBufferLen = sprintf(m_pSendBuffer, "CONNECT %s:%u HTTP/1.1\r\nHost: %s:%u\r\nProxy-Authorization: Basic %s\r\nUser-Agent: FileZilla\r\n\r\n",
     607                        (const char*)host_raw, m_port,
     608                        (const char*)host_raw, m_port,
     609                        (const char*)challenge);
     610
     611                    wxString host = m_pSocket->GetPeerIP();
     612                    unsigned int port = m_pSocket->GetRemotePort(error);
     613
     614                    m_pSocket->Close();
     615                    int res = m_pSocket->Connect(host, port);
     616                    // Treat success same as EINPROGRESS, we wait for connect notification in any case
     617                    if (res && res != EINPROGRESS)
     618                    {
     619                        m_pOwner->LogMessage(Debug_Warning, _T("connect error?"));
     620                        m_proxyState = conn;
     621                        CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::connection, 0);
     622                        CSocketEventDispatcher::Get().SendEvent(evt);
     623                        return;
     624                    }
     625                    return;
     626                }
     627                // other authentication method?
     628            }
     629            delete [] pBody;
     630
    311631            if (reply.Left(10) != _T("HTTP/1.1 2") && reply.Left(10) != _T("HTTP/1.0 2"))
    312632            {
    313633                m_proxyState = noconn;
  • src/engine/proxy.h

    diff --strip-trailing-cr -Nur a/src/engine/proxy.h b/src/engine/proxy.h
    a b  
    6565    int m_recvBufferPos;
    6666    int m_recvBufferLen;
    6767
     68    bool m_doAuth;
     69
    6870    void OnSocketEvent(CSocketEvent& event);
    6971    void OnReceive();
    7072    void OnSend();