Ticket #8452: filezilla_3.9.0.6_http_proxy_digest.patch

File filezilla_3.9.0.6_http_proxy_digest.patch, 13.8 KB (added by Tommy Wu, 9 years ago)

patch for 3.9.0.6, use md5 related function from nettle

  • src/engine/proxy.cpp

    diff -Nur filezilla-3.9.0.6.orig/src/engine/proxy.cpp filezilla-3.9.0.6/src/engine/proxy.cpp
    old new  
    11#include <filezilla.h>
    22#include "proxy.h"
    33#include <errno.h>
     4#include <wx/tokenzr.h>
    45#include "ControlSocket.h"
    56#include <wx/sckaddr.h>
     7#include <nettle/md5.h>
    68
    79enum handshake_state
    810{
     
    2224    , m_pSocket(pSocket)
    2325    , m_pOwner(pOwner)
    2426{
     27    m_doAuth = 0;
    2528    m_pSocket->SetEventHandler(this);
    2629}
    2730
     
    3336    delete [] m_pRecvBuffer;
    3437}
    3538
     39static void AppendSubHex(unsigned char val, wxString& result)
     40{
     41   if (val > 9)
     42   {
     43      result.Append((char)('a'+val-10));
     44   }
     45   else
     46   {
     47      result.Append((char)('0'+val));
     48   }
     49}
     50
     51static void AppendHex(unsigned char val, wxString& result)
     52{
     53   AppendSubHex((val >> 4) & 0xF, result);
     54   AppendSubHex(val&0xF, result);
     55}
     56
     57static wxString ComputeMD5(const char *buf, unsigned int len)
     58{
     59    struct md5_ctx ctx;
     60    unsigned char signature[MD5_DIGEST_SIZE];
     61
     62    md5_init(&ctx);
     63    md5_update(&ctx, len, (const uint8_t *)buf);
     64    md5_digest(&ctx, MD5_DIGEST_SIZE, (uint8_t *)signature);
     65
     66    wxString result;
     67    for (int j = 0; j < (int)sizeof signature; j++)
     68    {
     69        AppendHex(signature[j], result);
     70    }
     71
     72    return result;
     73}
     74
    3675static wxString base64encode(const wxString& str)
    3776{
    3877    // Code shamelessly taken from wxWidgets and adopted to encode UTF-8 strings.
     
    89128    if (type == HTTP) {
    90129        m_handshakeState = http_wait;
    91130
    92         wxWX2MBbuf challenge;
    93         int challenge_len;
    94         if (!user.empty()) {
    95             challenge = base64encode(user + _T(":") + pass).mb_str(wxConvUTF8);
    96             challenge_len = strlen(challenge);
    97         }
    98         else {
    99             challenge = (size_t)0;
    100             challenge_len = 0;
    101         }
    102 
    103131        // Bit oversized, but be on the safe side
    104         m_pSendBuffer = new char[70 + strlen(host_raw) * 2 + 2*5 + challenge_len + 23];
     132        m_pSendBuffer = new char[70 + strlen(host_raw) * 2 + 2*5 + 23];
    105133
    106         if (!challenge) {
    107             m_sendBufferLen = sprintf(m_pSendBuffer, "CONNECT %s:%u HTTP/1.1\r\nHost: %s:%u\r\nUser-Agent: FileZilla\r\n\r\n",
    108                 (const char*)host_raw, port,
    109                 (const char*)host_raw, port);
    110         }
    111         else {
    112             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",
    113                 (const char*)host_raw, port,
    114                 (const char*)host_raw, port,
    115                 (const char*)challenge);
    116         }
     134        m_sendBufferLen = sprintf(m_pSendBuffer, "CONNECT %s:%u HTTP/1.1\r\nHost: %s:%u\r\nUser-Agent: FileZilla\r\n\r\n",
     135            (const char*)host_raw, port,
     136            (const char*)host_raw, port);
    117137
    118138        m_pRecvBuffer = new char[4096];
    119139        m_recvBufferLen = 4096;
     
    313333            if (!end)
    314334                continue;
    315335
     336            unsigned long int nContentLength = 0;
     337            bool bUseBasic = false;
     338            bool bUseDigest = false;
     339            wxString digestStr = _T("");
     340            wxString authStr = _T("");
     341            wxString header_str(m_pRecvBuffer, wxConvLocal, m_recvBufferPos);
     342            wxStringTokenizer tkz(header_str, wxT("\r\n"));
     343
     344            while (tkz.HasMoreTokens()) {
     345                wxString token = tkz.GetNextToken();
     346                if (token.Mid(0, 15).Lower() == _T("content-length:")) {
     347                    token.Mid(15).Trim(true).Trim(false).ToULong(&nContentLength);
     348                    continue;
     349                }
     350                if (token.Mid(0, 26).Lower() == _T("proxy-authenticate: basic ")) {
     351                    bUseBasic = true;
     352                    continue;
     353                }
     354                if (token.Mid(0, 27).Lower() == _T("proxy-authenticate: digest ")) {
     355                    authStr = token.Mid(27);
     356                    bUseDigest = true;
     357                    continue;
     358                }
     359            }
     360
     361            // read body
     362            unsigned long int nBodyLength = 0;
     363            char *pBody = new char[nContentLength];
     364            while (nBodyLength < nContentLength) {
     365                int read;
     366
     367                do_read = nContentLength - nBodyLength;
     368
     369                read = m_pSocket->Read(pBody + nBodyLength, do_read, error);
     370                if (read == -1) {
     371                    if (error != EAGAIN) {
     372                        delete [] pBody;
     373                        m_proxyState = noconn;
     374                        CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::close, error);
     375                        CSocketEventDispatcher::Get().SendEvent(evt);
     376                    }
     377                    else
     378                        m_can_read = false;
     379                    continue;
     380                }
     381                if (!read) {
     382                    delete [] pBody;
     383                    m_proxyState = noconn;
     384                    CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::close, ECONNABORTED);
     385                    CSocketEventDispatcher::Get().SendEvent(evt);
     386                    return;
     387                }
     388                if (m_pSendBuffer) {
     389                    delete [] pBody;
     390                    m_proxyState = noconn;
     391                    m_pOwner->LogMessage(MessageType::Debug_Warning, _T("Incoming data before requst fully sent"));
     392                    CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::close, ECONNABORTED);
     393                    CSocketEventDispatcher::Get().SendEvent(evt);
     394                    return;
     395                }
     396                nBodyLength += read;
     397            }
     398
    316399            end = strchr(m_pRecvBuffer, '\r');
    317400            wxASSERT(end);
    318401            *end = 0;
    319402            wxString reply(m_pRecvBuffer, wxConvUTF8);
    320403            m_pOwner->LogMessage(MessageType::Response, _("Proxy reply: %s"), reply);
    321404
     405            if (reply.Left(12) == _T("HTTP/1.1 401") || reply.Left(12) == _T("HTTP/1.1 407") ||
     406                reply.Left(12) == _T("HTTP/1.0 401") || reply.Left(12) == _T("HTTP/1.0 407")) {
     407                if (m_doAuth == 1) {
     408                    // need to try BASIC when DIGEST failed
     409                    bUseDigest = false;
     410                    bUseBasic = true;
     411                }
     412                if (m_doAuth == 2 || m_user == _T("")) {
     413                    delete [] pBody;
     414                    m_pOwner->LogMessage(MessageType::Debug_Warning, _("already m_doAuth (%d), or m_user is empty (%s)"), m_doAuth, m_user.c_str());
     415                    m_proxyState = noconn;
     416                    CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::close, ECONNRESET);
     417                    CSocketEventDispatcher::Get().SendEvent(evt);
     418                    return;
     419                }
     420
     421                if (bUseDigest) {
     422                    wxString realm = _T("");
     423                    wxString nonce = _T("");
     424                    wxString qop = _T("");
     425                    wxStringTokenizer tkz2(authStr, wxT(","));
     426                    while (tkz2.HasMoreTokens()) {
     427                        wxString token2 = tkz2.GetNextToken();
     428                        wxString name = _T("");
     429                        wxString value = _T("");
     430                        wxStringTokenizer tkz3(token2, wxT("="));
     431                        while (tkz3.HasMoreTokens()) {
     432                            wxString token3 = tkz3.GetNextToken();
     433                            if (name == _T("")) {
     434                                name = token3.Lower().Trim(true).Trim(false);
     435                                continue;
     436                            }
     437                            if (token3.Left(1) == _T("\""))
     438                                value = token3.Mid(1, token3.Len()-2).Trim(true).Trim(false);
     439                            else
     440                                value = token3.Trim(true).Trim(false);
     441                            break;
     442                        }
     443                        if (name == _T("realm"))
     444                            realm = value;
     445                        else if (name == _T("nonce"))
     446                            nonce = value;
     447                        else if (name == _T("qop"))
     448                            qop = value;
     449                    }
     450                    if (realm == _T("") || nonce == _T("")) {
     451                        delete [] pBody;
     452                        m_pOwner->LogMessage(MessageType::Debug_Warning, _T("no realm or nonce"));
     453                        m_proxyState = conn;
     454                        CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::connection, 0);
     455                        CSocketEventDispatcher::Get().SendEvent(evt);
     456                        return;
     457                    }
     458                    // HA1 = md5(username:realm:password)
     459                    // if qop = auth or no qop
     460                    // HA2 = md5(method:digestURI)
     461                    // if qop = auth-int
     462                    // HA2 = md5(method:digestURI:md5(body))
     463                    // if qop = auth or auth-int
     464                    // response = md5(HA1:nonce:nonceCount:clientNonce:qop:HA2)
     465                    // if no qop
     466                    // response = md5(HA1:nonce:HA2)
     467                    wxString ha1, ha2, response;
     468                    wxString s;
     469                    bool bAuth = false;
     470                    bool bAuthInt = false;
     471
     472                    if (qop != _T("")) {
     473                        wxStringTokenizer tkz4(qop, wxT(","));
     474                        while (tkz4.HasMoreTokens()) {
     475                            wxString token4 = tkz4.GetNextToken();
     476                            if (token4 == _T("auth"))
     477                                bAuth = true;
     478                            if (token4 == _T("auth-int"))
     479                                bAuthInt = true;
     480                        }
     481                    }
     482
     483                    s = wxString::Format(_T("%s:%s:%s"), m_user.c_str(), realm.c_str(), m_pass.c_str());
     484                    const wxWX2MBbuf s_raw = s.mb_str(wxConvUTF8);
     485                    ha1 = ComputeMD5((const char *)s_raw, strlen(s_raw));
     486
     487                    wxString uri = _T("");
     488                    if (qop == _T("") || bAuth) {
     489                        uri = wxString::Format(_T("%s:%u"), m_host.c_str(), m_port);
     490                        s = wxString::Format(_T("CONNECT:%s"), uri.c_str());
     491                        const wxWX2MBbuf s_raw = s.mb_str(wxConvUTF8);
     492                        ha2 = ComputeMD5((const char *)s_raw, strlen(s_raw));
     493                    }
     494                    else if (bAuthInt) {
     495                        uri = wxString::Format(_T("%s:%u"), m_host.c_str(), m_port);
     496                        wxString bodyMD5 = ComputeMD5((const char *)pBody, nBodyLength);
     497                        s = wxString::Format(_T("CONNECT:%s:%s"), uri.c_str(), bodyMD5.c_str());
     498                        const wxWX2MBbuf s_raw = s.mb_str(wxConvLocal);
     499                        ha2 = ComputeMD5((const char *)s_raw, strlen(s_raw));
     500                    }
     501                    else {
     502                        delete [] pBody;
     503                        m_pOwner->LogMessage(MessageType::Debug_Warning, _T("unknown qop: %s"), qop.c_str());
     504                        m_proxyState = conn;
     505                        CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::connection, 0);
     506                        CSocketEventDispatcher::Get().SendEvent(evt);
     507                        return;
     508                    }
     509
     510                    // we don't need body now
     511                    delete [] pBody;
     512                    if (bAuth || bAuthInt) {
     513                        wxString clientNonce = wxString::Format(_T("%04x%04x"), GetRandomNumber(0, 0xffff), GetRandomNumber(0, 0xffff));
     514
     515                        s = wxString::Format(_T("%s:%s:%s:%s:%s:%s"),
     516                            ha1.c_str(),
     517                            nonce.c_str(),
     518                            _T("00000001"),
     519                            clientNonce.c_str(),
     520                            bAuth ? _T("auth") : _T("auth-int"),
     521                            ha2.c_str());
     522                        const wxWX2MBbuf s_raw = s.mb_str(wxConvLocal);
     523                        response = ComputeMD5((const char *)s_raw, strlen(s_raw));
     524
     525                        digestStr = wxString::Format(_T("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\", qop=%s, nc=00000001, cnonce=\"%s\""),
     526                            m_user.c_str(),
     527                            realm.c_str(),
     528                            nonce.c_str(),
     529                            uri.c_str(),
     530                            response.c_str(),
     531                            bAuth ? _T("auth") : _T("auth-int"),
     532                            clientNonce.c_str());
     533                    }
     534                    else {
     535                        s = wxString::Format(_T("%s:%s:%s"),
     536                            ha1.c_str(),
     537                            nonce.c_str(),
     538                            ha2.c_str());
     539                        const wxWX2MBbuf s_raw = s.mb_str(wxConvLocal);
     540                        response = ComputeMD5((const char *)s_raw, strlen(s_raw));
     541
     542                        digestStr = wxString::Format(_T("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\""),
     543                            m_user.c_str(),
     544                            realm.c_str(),
     545                            nonce.c_str(),
     546                            uri.c_str(),
     547                            response.c_str());
     548                    }
     549
     550                    if (bUseBasic)
     551                        m_doAuth = 1;   // try BASIC later if DIGEST failed
     552                    else
     553                        m_doAuth = 2;
     554                    m_recvBufferPos = 0;
     555
     556                    const wxWX2MBbuf host_raw = m_host.mb_str(wxConvUTF8);
     557                    const wxWX2MBbuf digest_raw = digestStr.mb_str(wxConvLocal);
     558
     559                    int challenge_len = digestStr.Len();
     560
     561                    m_pOwner->LogMessage(MessageType::Status, _("Sending CONNECT with Digest Authorization"));
     562
     563                    // Bit oversized, but be on the safe side
     564                    m_pSendBuffer = new char[70 + m_host.Len() * 2 + 2*5 + challenge_len * 2 + 23 + 40];
     565
     566                    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",
     567                        (const char*)host_raw, m_port,
     568                        (const char*)host_raw, m_port,
     569                        (const char*)digest_raw);
     570
     571                    wxString host = m_pSocket->GetPeerIP();
     572                    unsigned int port = m_pSocket->GetRemotePort(error);
     573
     574                    m_pSocket->Close();
     575                    int res = m_pSocket->Connect(host, port);
     576                    // Treat success same as EINPROGRESS, we wait for connect notification in any case
     577                    if (res && res != EINPROGRESS) {
     578                        m_pOwner->LogMessage(MessageType::Debug_Warning, _T("connect error?"));
     579                        m_proxyState = conn;
     580                        CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::connection, 0);
     581                        CSocketEventDispatcher::Get().SendEvent(evt);
     582                        return;
     583                    }
     584                    return;
     585                }
     586
     587                if (bUseBasic) {
     588                    // we don't need body for this
     589                    delete [] pBody;
     590                    m_doAuth = 2;
     591                    m_recvBufferPos = 0;
     592
     593#if wxUSE_UNICODE
     594                    wxWX2MBbuf challenge;
     595#else
     596                    const wxWX2MBbuf challenge;
     597#endif
     598                    const wxWX2MBbuf host_raw = m_host.mb_str(wxConvUTF8);
     599                    int challenge_len;
     600
     601                    challenge = base64encode(m_user + _T(":") + m_pass).mb_str(wxConvUTF8);
     602                    challenge_len = strlen(challenge);
     603
     604                    m_pOwner->LogMessage(MessageType::Status, _("Sending CONNECT with Basic Authorization"));
     605                    // Bit oversized, but be on the safe side
     606                    m_pSendBuffer = new char[70 + strlen(host_raw) * 2 + 2*5 + challenge_len + 23];
     607
     608                    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",
     609                        (const char*)host_raw, m_port,
     610                        (const char*)host_raw, m_port,
     611                        (const char*)challenge);
     612
     613                    wxString host = m_pSocket->GetPeerIP();
     614                    unsigned int port = m_pSocket->GetRemotePort(error);
     615
     616                    m_pSocket->Close();
     617                    int res = m_pSocket->Connect(host, port);
     618                    // Treat success same as EINPROGRESS, we wait for connect notification in any case
     619                    if (res && res != EINPROGRESS) {
     620                        m_pOwner->LogMessage(MessageType::Debug_Warning, _T("connect error?"));
     621                        m_proxyState = conn;
     622                        CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::connection, 0);
     623                        CSocketEventDispatcher::Get().SendEvent(evt);
     624                        return;
     625                    }
     626                    return;
     627                }
     628                // other authentication method?
     629            }
     630            delete [] pBody;
     631
    322632            if (reply.Left(10) != _T("HTTP/1.1 2") && reply.Left(10) != _T("HTTP/1.0 2")) {
    323633                m_proxyState = noconn;
    324634                CSocketEvent *evt = new CSocketEvent(m_pEvtHandler, this, CSocketEvent::close, ECONNRESET);
  • src/engine/proxy.h

    diff -Nur filezilla-3.9.0.6.orig/src/engine/proxy.h filezilla-3.9.0.6/src/engine/proxy.h
    old new  
    6464    int m_recvBufferPos{};
    6565    int m_recvBufferLen{};
    6666
     67    int m_doAuth;
     68
    6769    void OnSocketEvent(CSocketEvent& event);
    6870    void OnReceive();
    6971    void OnSend();