PageRenderTime 36ms CodeModel.GetById 13ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 0ms

/mordor/http/negotiate.cpp

http://github.com/mozy/mordor
C++ | 156 lines | 134 code | 19 blank | 3 comment | 12 complexity | d90516eb1f596e985e8eead3edf57170 MD5 | raw file
  1// Copyright (c) 2009 - Mozy, Inc.
  2
  3#include "negotiate.h"
  4
  5#include "http.h"
  6#include "mordor/log.h"
  7#include "mordor/string.h"
  8
  9#pragma comment(lib, "secur32.lib")
 10
 11namespace Mordor {
 12namespace HTTP {
 13
 14static Logger::ptr g_log = Log::lookup("mordor:http:negotiate");
 15
 16NegotiateAuth::NegotiateAuth(const std::string &username,
 17                             const std::string &password)
 18    : m_username(toUtf16(username)),
 19      m_password(toUtf16(password))
 20{
 21    SecInvalidateHandle(&m_creds);
 22    SecInvalidateHandle(&m_secCtx);
 23    size_t pos = m_username.find(L'\\');
 24    if (pos != std::wstring::npos) {
 25        m_domain = m_username.substr(0, pos);
 26        m_username = m_username.substr(pos + 1);
 27    }
 28}
 29
 30NegotiateAuth::~NegotiateAuth()
 31{
 32    if (SecIsValidHandle(&m_creds)) {
 33        FreeCredentialHandle(&m_creds);
 34        SecInvalidateHandle(&m_creds);
 35    }
 36    if (SecIsValidHandle(&m_secCtx)) {
 37        FreeCredentialHandle(&m_secCtx);
 38        SecInvalidateHandle(&m_secCtx);
 39    }
 40}
 41
 42bool
 43NegotiateAuth::authorize(const AuthParams &challenge, AuthParams &authorization,
 44    const URI &uri)
 45{
 46    SECURITY_STATUS status;
 47    std::wstring packageW = toUtf16(challenge.scheme);
 48    std::string param = challenge.param;
 49
 50    std::string outboundBuffer;
 51    SecBufferDesc outboundBufferDesc;
 52    SecBuffer outboundSecBuffer;
 53    TimeStamp lifetime;
 54    ULONG contextAttributes;
 55
 56    outboundBuffer.resize(4096);
 57    outboundBufferDesc.ulVersion = 0;
 58    outboundBufferDesc.cBuffers = 1;
 59    outboundBufferDesc.pBuffers = &outboundSecBuffer;
 60    outboundSecBuffer.BufferType = SECBUFFER_TOKEN;
 61    outboundSecBuffer.pvBuffer = &outboundBuffer[0];
 62    outboundSecBuffer.cbBuffer = (unsigned long)outboundBuffer.size();
 63
 64    if (param.empty()) {
 65        // No response from server; we're starting a new session
 66        if (SecIsValidHandle(&m_creds))
 67            return false;
 68
 69        SEC_WINNT_AUTH_IDENTITY_W id;
 70        id.User = (unsigned short *)m_username.c_str();
 71        id.UserLength = (unsigned long)m_username.size();
 72        id.Domain = (unsigned short *)m_domain.c_str();
 73        id.DomainLength = (unsigned long)m_domain.size();
 74        id.Password = (unsigned short *)m_password.c_str();
 75        id.PasswordLength = (unsigned long)m_password.size();
 76        id.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
 77        status = AcquireCredentialsHandleW(NULL,
 78            (wchar_t *)packageW.c_str(),
 79            SECPKG_CRED_OUTBOUND,
 80            NULL,
 81            m_username.empty() ? NULL : &id,
 82            NULL,
 83            NULL,
 84            &m_creds,
 85            &lifetime);
 86        MORDOR_LOG_TRACE(g_log) << "AcquireCredentialsHandleW("
 87            << challenge.scheme << ", " << toUtf8(m_username) << "): ("
 88            << status << ")";
 89        if (!SUCCEEDED(status))
 90            MORDOR_THROW_EXCEPTION_FROM_ERROR_API(status, "AcquireCredentialsHandleW");
 91
 92        status = InitializeSecurityContextW(
 93            &m_creds,
 94            NULL,
 95            (wchar_t *)toUtf16(uri.toString()).c_str(),
 96            ISC_REQ_CONFIDENTIALITY,
 97            0,
 98            SECURITY_NATIVE_DREP,
 99            NULL,
100            0,
101            &m_secCtx,
102            &outboundBufferDesc,
103            &contextAttributes,
104            &lifetime);
105        MORDOR_LOG_TRACE(g_log) << "InitializeSecurityContextW("
106            << uri << ", {0}):  {" << outboundSecBuffer.cbBuffer << "} ("
107            << status << ")";
108    } else {
109        // Prepare the response from the server
110        std::string inboundBuffer = base64decode(param);
111        SecBufferDesc inboundBufferDesc;
112        SecBuffer inboundSecBuffer;
113
114        inboundBufferDesc.ulVersion = 0;
115        inboundBufferDesc.cBuffers = 1;
116        inboundBufferDesc.pBuffers = &inboundSecBuffer;
117        inboundSecBuffer.BufferType = SECBUFFER_TOKEN;
118        inboundSecBuffer.pvBuffer = &inboundBuffer[0];
119        inboundSecBuffer.cbBuffer = (unsigned long)inboundBuffer.size();
120
121        status = InitializeSecurityContextW(
122            &m_creds,
123            &m_secCtx,
124            (wchar_t *)toUtf16(uri.toString()).c_str(),
125            ISC_REQ_CONFIDENTIALITY,
126            0,
127            SECURITY_NATIVE_DREP,
128            &inboundBufferDesc,
129            0,
130            &m_secCtx,
131            &outboundBufferDesc,
132            &contextAttributes,
133            &lifetime);
134        MORDOR_LOG_TRACE(g_log) << "InitializeSecurityContextW("
135            << uri << ", {" << inboundSecBuffer.cbBuffer << "}):  {"
136            << outboundSecBuffer.cbBuffer << "} (" << status << ")";
137    }
138
139    if (status == SEC_I_COMPLETE_NEEDED ||
140        status == SEC_I_COMPLETE_AND_CONTINUE) {
141        status = CompleteAuthToken(&m_secCtx, &outboundBufferDesc);
142        MORDOR_LOG_TRACE(g_log) << "CompleteAuthToken(): {"
143            << outboundSecBuffer.cbBuffer << "} (" << status << ")";
144    }
145
146    if (!SUCCEEDED(status))
147        MORDOR_THROW_EXCEPTION_FROM_ERROR(status);
148
149    outboundBuffer.resize(outboundSecBuffer.cbBuffer);
150    authorization.scheme = challenge.scheme;
151    authorization.param = base64encode(outboundBuffer);
152    authorization.parameters.clear();
153    return true;
154}
155
156}}