extensions/auth/nsAuthSSPI.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/extensions/auth/nsAuthSSPI.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,667 @@
     1.4 +/* vim:set ts=4 sw=4 sts=4 et cindent: */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +//
    1.10 +// Negotiate Authentication Support Module
    1.11 +//
    1.12 +// Described by IETF Internet draft: draft-brezak-kerberos-http-00.txt
    1.13 +// (formerly draft-brezak-spnego-http-04.txt)
    1.14 +//
    1.15 +// Also described here:
    1.16 +// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/http-sso-1.asp
    1.17 +//
    1.18 +
    1.19 +#include "nsAuthSSPI.h"
    1.20 +#include "nsIServiceManager.h"
    1.21 +#include "nsIDNSService.h"
    1.22 +#include "nsIDNSRecord.h"
    1.23 +#include "nsNetCID.h"
    1.24 +#include "nsCOMPtr.h"
    1.25 +#include "nsICryptoHash.h"
    1.26 +#include "mozilla/Telemetry.h"
    1.27 +
    1.28 +#include <windows.h>
    1.29 +
    1.30 +#define SEC_SUCCESS(Status) ((Status) >= 0)
    1.31 +
    1.32 +#ifndef KERB_WRAP_NO_ENCRYPT
    1.33 +#define KERB_WRAP_NO_ENCRYPT 0x80000001
    1.34 +#endif
    1.35 +
    1.36 +#ifndef SECBUFFER_PADDING
    1.37 +#define SECBUFFER_PADDING 9
    1.38 +#endif
    1.39 +
    1.40 +#ifndef SECBUFFER_STREAM
    1.41 +#define SECBUFFER_STREAM 10
    1.42 +#endif
    1.43 +
    1.44 +//-----------------------------------------------------------------------------
    1.45 +
    1.46 +static const wchar_t *const pTypeName [] = {
    1.47 +    L"Kerberos",
    1.48 +    L"Negotiate",
    1.49 +    L"NTLM"
    1.50 +};
    1.51 +
    1.52 +#ifdef DEBUG
    1.53 +#define CASE_(_x) case _x: return # _x;
    1.54 +static const char *MapErrorCode(int rc)
    1.55 +{
    1.56 +    switch (rc) {
    1.57 +    CASE_(SEC_E_OK)
    1.58 +    CASE_(SEC_I_CONTINUE_NEEDED)
    1.59 +    CASE_(SEC_I_COMPLETE_NEEDED)
    1.60 +    CASE_(SEC_I_COMPLETE_AND_CONTINUE)
    1.61 +    CASE_(SEC_E_INCOMPLETE_MESSAGE)
    1.62 +    CASE_(SEC_I_INCOMPLETE_CREDENTIALS)
    1.63 +    CASE_(SEC_E_INVALID_HANDLE)
    1.64 +    CASE_(SEC_E_TARGET_UNKNOWN)
    1.65 +    CASE_(SEC_E_LOGON_DENIED)
    1.66 +    CASE_(SEC_E_INTERNAL_ERROR)
    1.67 +    CASE_(SEC_E_NO_CREDENTIALS)
    1.68 +    CASE_(SEC_E_NO_AUTHENTICATING_AUTHORITY)
    1.69 +    CASE_(SEC_E_INSUFFICIENT_MEMORY)
    1.70 +    CASE_(SEC_E_INVALID_TOKEN)
    1.71 +    }
    1.72 +    return "<unknown>";
    1.73 +}
    1.74 +#else
    1.75 +#define MapErrorCode(_rc) ""
    1.76 +#endif
    1.77 +
    1.78 +//-----------------------------------------------------------------------------
    1.79 +
    1.80 +static PSecurityFunctionTableW   sspi;
    1.81 +
    1.82 +static nsresult
    1.83 +InitSSPI()
    1.84 +{
    1.85 +    LOG(("  InitSSPI\n"));
    1.86 +
    1.87 +    sspi = InitSecurityInterfaceW();
    1.88 +    if (!sspi) {
    1.89 +        LOG(("InitSecurityInterfaceW failed"));
    1.90 +        return NS_ERROR_UNEXPECTED;
    1.91 +    }
    1.92 +
    1.93 +    return NS_OK;
    1.94 +}
    1.95 +
    1.96 +//-----------------------------------------------------------------------------
    1.97 +
    1.98 +static nsresult
    1.99 +MakeSN(const char *principal, nsCString &result)
   1.100 +{
   1.101 +    nsresult rv;
   1.102 +
   1.103 +    nsAutoCString buf(principal);
   1.104 +
   1.105 +    // The service name looks like "protocol@hostname", we need to map
   1.106 +    // this to a value that SSPI expects.  To be consistent with IE, we
   1.107 +    // need to map '@' to '/' and canonicalize the hostname.
   1.108 +    int32_t index = buf.FindChar('@');
   1.109 +    if (index == kNotFound)
   1.110 +        return NS_ERROR_UNEXPECTED;
   1.111 +    
   1.112 +    nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
   1.113 +    if (NS_FAILED(rv))
   1.114 +        return rv;
   1.115 +
   1.116 +    // This could be expensive if our DNS cache cannot satisfy the request.
   1.117 +    // However, we should have at least hit the OS resolver once prior to
   1.118 +    // reaching this code, so provided the OS resolver has this information
   1.119 +    // cached, we should not have to worry about blocking on this function call
   1.120 +    // for very long.  NOTE: because we ask for the canonical hostname, we
   1.121 +    // might end up requiring extra network activity in cases where the OS
   1.122 +    // resolver might not have enough information to satisfy the request from
   1.123 +    // its cache.  This is not an issue in versions of Windows up to WinXP.
   1.124 +    nsCOMPtr<nsIDNSRecord> record;
   1.125 +    rv = dns->Resolve(Substring(buf, index + 1),
   1.126 +                      nsIDNSService::RESOLVE_CANONICAL_NAME,
   1.127 +                      getter_AddRefs(record));
   1.128 +    if (NS_FAILED(rv))
   1.129 +        return rv;
   1.130 +
   1.131 +    nsAutoCString cname;
   1.132 +    rv = record->GetCanonicalName(cname);
   1.133 +    if (NS_SUCCEEDED(rv)) {
   1.134 +        result = StringHead(buf, index) + NS_LITERAL_CSTRING("/") + cname;
   1.135 +        LOG(("Using SPN of [%s]\n", result.get()));
   1.136 +    }
   1.137 +    return rv;
   1.138 +}
   1.139 +
   1.140 +//-----------------------------------------------------------------------------
   1.141 +
   1.142 +nsAuthSSPI::nsAuthSSPI(pType package)
   1.143 +    : mServiceFlags(REQ_DEFAULT)
   1.144 +    , mMaxTokenLen(0)
   1.145 +    , mPackage(package)
   1.146 +    , mCertDERData(nullptr)
   1.147 +    , mCertDERLength(0)
   1.148 +{
   1.149 +    memset(&mCred, 0, sizeof(mCred));
   1.150 +    memset(&mCtxt, 0, sizeof(mCtxt));
   1.151 +}
   1.152 +
   1.153 +nsAuthSSPI::~nsAuthSSPI()
   1.154 +{
   1.155 +    Reset();
   1.156 +
   1.157 +    if (mCred.dwLower || mCred.dwUpper) {
   1.158 +#ifdef __MINGW32__
   1.159 +        (sspi->FreeCredentialsHandle)(&mCred);
   1.160 +#else
   1.161 +        (sspi->FreeCredentialHandle)(&mCred);
   1.162 +#endif
   1.163 +        memset(&mCred, 0, sizeof(mCred));
   1.164 +    }
   1.165 +}
   1.166 +
   1.167 +void
   1.168 +nsAuthSSPI::Reset()
   1.169 +{
   1.170 +    mIsFirst = true;
   1.171 +
   1.172 +    if (mCertDERData){
   1.173 +        nsMemory::Free(mCertDERData);
   1.174 +        mCertDERData = nullptr;
   1.175 +        mCertDERLength = 0;   
   1.176 +    }
   1.177 +
   1.178 +    if (mCtxt.dwLower || mCtxt.dwUpper) {
   1.179 +        (sspi->DeleteSecurityContext)(&mCtxt);
   1.180 +        memset(&mCtxt, 0, sizeof(mCtxt));
   1.181 +    }
   1.182 +}
   1.183 +
   1.184 +NS_IMPL_ISUPPORTS(nsAuthSSPI, nsIAuthModule)
   1.185 +
   1.186 +NS_IMETHODIMP
   1.187 +nsAuthSSPI::Init(const char *serviceName,
   1.188 +                 uint32_t    serviceFlags,
   1.189 +                 const char16_t *domain,
   1.190 +                 const char16_t *username,
   1.191 +                 const char16_t *password)
   1.192 +{
   1.193 +    LOG(("  nsAuthSSPI::Init\n"));
   1.194 +
   1.195 +    mIsFirst = true;
   1.196 +    mCertDERLength = 0;
   1.197 +    mCertDERData = nullptr;
   1.198 +
   1.199 +    // The caller must supply a service name to be used. (For why we now require
   1.200 +    // a service name for NTLM, see bug 487872.)
   1.201 +    NS_ENSURE_TRUE(serviceName && *serviceName, NS_ERROR_INVALID_ARG);
   1.202 +
   1.203 +    nsresult rv;
   1.204 +
   1.205 +    // XXX lazy initialization like this assumes that we are single threaded
   1.206 +    if (!sspi) {
   1.207 +        rv = InitSSPI();
   1.208 +        if (NS_FAILED(rv))
   1.209 +            return rv;
   1.210 +    }
   1.211 +    SEC_WCHAR *package;
   1.212 +
   1.213 +    package = (SEC_WCHAR *) pTypeName[(int)mPackage];
   1.214 +
   1.215 +    if (mPackage == PACKAGE_TYPE_NTLM) {
   1.216 +        // (bug 535193) For NTLM, just use the uri host, do not do canonical host lookups.
   1.217 +        // The incoming serviceName is in the format: "protocol@hostname", SSPI expects
   1.218 +        // "<service class>/<hostname>", so swap the '@' for a '/'.
   1.219 +        mServiceName.Assign(serviceName);
   1.220 +        int32_t index = mServiceName.FindChar('@');
   1.221 +        if (index == kNotFound)
   1.222 +            return NS_ERROR_UNEXPECTED;
   1.223 +        mServiceName.Replace(index, 1, '/');
   1.224 +    }
   1.225 +    else {
   1.226 +        // Kerberos requires the canonical host, MakeSN takes care of this through a
   1.227 +        // DNS lookup.
   1.228 +        rv = MakeSN(serviceName, mServiceName);
   1.229 +        if (NS_FAILED(rv))
   1.230 +            return rv;
   1.231 +    }
   1.232 +
   1.233 +    mServiceFlags = serviceFlags;
   1.234 +
   1.235 +    SECURITY_STATUS rc;
   1.236 +
   1.237 +    PSecPkgInfoW pinfo;
   1.238 +    rc = (sspi->QuerySecurityPackageInfoW)(package, &pinfo);
   1.239 +    if (rc != SEC_E_OK) {
   1.240 +        LOG(("%s package not found\n", package));
   1.241 +        return NS_ERROR_UNEXPECTED;
   1.242 +    }
   1.243 +    mMaxTokenLen = pinfo->cbMaxToken;
   1.244 +    (sspi->FreeContextBuffer)(pinfo);
   1.245 +
   1.246 +    MS_TimeStamp useBefore;
   1.247 +
   1.248 +    SEC_WINNT_AUTH_IDENTITY_W ai;
   1.249 +    SEC_WINNT_AUTH_IDENTITY_W *pai = nullptr;
   1.250 +    
   1.251 +    // domain, username, and password will be null if nsHttpNTLMAuth's ChallengeReceived
   1.252 +    // returns false for identityInvalid. Use default credentials in this case by passing
   1.253 +    // null for pai.
   1.254 +    if (username && password) {
   1.255 +        // Keep a copy of these strings for the duration
   1.256 +        mUsername.Assign(username);
   1.257 +        mPassword.Assign(password);
   1.258 +        mDomain.Assign(domain);
   1.259 +        ai.Domain = reinterpret_cast<unsigned short*>(mDomain.BeginWriting());
   1.260 +        ai.DomainLength = mDomain.Length();
   1.261 +        ai.User = reinterpret_cast<unsigned short*>(mUsername.BeginWriting());
   1.262 +        ai.UserLength = mUsername.Length();
   1.263 +        ai.Password = reinterpret_cast<unsigned short*>(mPassword.BeginWriting());
   1.264 +        ai.PasswordLength = mPassword.Length();
   1.265 +        ai.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
   1.266 +        pai = &ai;
   1.267 +    }
   1.268 +
   1.269 +    rc = (sspi->AcquireCredentialsHandleW)(nullptr,
   1.270 +                                           package,
   1.271 +                                           SECPKG_CRED_OUTBOUND,
   1.272 +                                           nullptr,
   1.273 +                                           pai,
   1.274 +                                           nullptr,
   1.275 +                                           nullptr,
   1.276 +                                           &mCred,
   1.277 +                                           &useBefore);
   1.278 +    if (rc != SEC_E_OK)
   1.279 +        return NS_ERROR_UNEXPECTED;
   1.280 +
   1.281 +    static bool sTelemetrySent = false;
   1.282 +    if (!sTelemetrySent) {
   1.283 +        mozilla::Telemetry::Accumulate(
   1.284 +            mozilla::Telemetry::NTLM_MODULE_USED_2,
   1.285 +            serviceFlags & nsIAuthModule::REQ_PROXY_AUTH
   1.286 +                ? NTLM_MODULE_WIN_API_PROXY
   1.287 +                : NTLM_MODULE_WIN_API_DIRECT);
   1.288 +        sTelemetrySent = true;
   1.289 +    }
   1.290 +
   1.291 +    LOG(("AcquireCredentialsHandle() succeeded.\n"));
   1.292 +    return NS_OK;
   1.293 +}
   1.294 +
   1.295 +// The arguments inToken and inTokenLen are used to pass in the server
   1.296 +// certificate (when available) in the first call of the function. The
   1.297 +// second time these arguments hold an input token. 
   1.298 +NS_IMETHODIMP
   1.299 +nsAuthSSPI::GetNextToken(const void *inToken,
   1.300 +                         uint32_t    inTokenLen,
   1.301 +                         void      **outToken,
   1.302 +                         uint32_t   *outTokenLen)
   1.303 +{
   1.304 +    // String for end-point bindings.
   1.305 +    const char end_point[] = "tls-server-end-point:"; 
   1.306 +    const int end_point_length = sizeof(end_point) - 1;
   1.307 +    const int hash_size = 32;  // Size of a SHA256 hash.
   1.308 +    const int cbt_size = hash_size + end_point_length;
   1.309 +	
   1.310 +    SECURITY_STATUS rc;
   1.311 +    MS_TimeStamp ignored;
   1.312 +
   1.313 +    DWORD ctxAttr, ctxReq = 0;
   1.314 +    CtxtHandle *ctxIn;
   1.315 +    SecBufferDesc ibd, obd;
   1.316 +    // Optional second input buffer for the CBT (Channel Binding Token)
   1.317 +    SecBuffer ib[2], ob;
   1.318 +    // Pointer to the block of memory that stores the CBT
   1.319 +    char* sspi_cbt = nullptr;
   1.320 +    SEC_CHANNEL_BINDINGS pendpoint_binding;
   1.321 +
   1.322 +    LOG(("entering nsAuthSSPI::GetNextToken()\n"));
   1.323 +
   1.324 +    if (!mCred.dwLower && !mCred.dwUpper) {
   1.325 +        LOG(("nsAuthSSPI::GetNextToken(), not initialized. exiting."));
   1.326 +        return NS_ERROR_NOT_INITIALIZED;
   1.327 +    }
   1.328 +
   1.329 +    if (mServiceFlags & REQ_DELEGATE)
   1.330 +        ctxReq |= ISC_REQ_DELEGATE;
   1.331 +    if (mServiceFlags & REQ_MUTUAL_AUTH)
   1.332 +        ctxReq |= ISC_REQ_MUTUAL_AUTH;
   1.333 +
   1.334 +    if (inToken) {
   1.335 +        if (mIsFirst) {
   1.336 +            // First time if it comes with a token,
   1.337 +            // the token represents the server certificate.
   1.338 +            mIsFirst = false;
   1.339 +            mCertDERLength = inTokenLen;
   1.340 +            mCertDERData = nsMemory::Alloc(inTokenLen);
   1.341 +            if (!mCertDERData)
   1.342 +                return NS_ERROR_OUT_OF_MEMORY;
   1.343 +            memcpy(mCertDERData, inToken, inTokenLen);
   1.344 +
   1.345 +            // We are starting a new authentication sequence.  
   1.346 +            // If we have already initialized our
   1.347 +            // security context, then we're in trouble because it means that the
   1.348 +            // first sequence failed.  We need to bail or else we might end up in
   1.349 +            // an infinite loop.
   1.350 +            if (mCtxt.dwLower || mCtxt.dwUpper) {
   1.351 +                LOG(("Cannot restart authentication sequence!"));
   1.352 +                return NS_ERROR_UNEXPECTED;
   1.353 +            }
   1.354 +            ctxIn = nullptr;
   1.355 +            // The certificate needs to be erased before being passed 
   1.356 +            // to InitializeSecurityContextW().
   1.357 +            inToken = nullptr;
   1.358 +            inTokenLen = 0;
   1.359 +        } else {
   1.360 +            ibd.ulVersion = SECBUFFER_VERSION;
   1.361 +            ibd.cBuffers = 0;
   1.362 +            ibd.pBuffers = ib;
   1.363 +            
   1.364 +            // If we have stored a certificate, the Channel Binding Token
   1.365 +            // needs to be generated and sent in the first input buffer.
   1.366 +            if (mCertDERLength > 0) {
   1.367 +                // First we create a proper Endpoint Binding structure. 
   1.368 +                pendpoint_binding.dwInitiatorAddrType = 0;
   1.369 +                pendpoint_binding.cbInitiatorLength = 0;
   1.370 +                pendpoint_binding.dwInitiatorOffset = 0;
   1.371 +                pendpoint_binding.dwAcceptorAddrType = 0;
   1.372 +                pendpoint_binding.cbAcceptorLength = 0;
   1.373 +                pendpoint_binding.dwAcceptorOffset = 0;
   1.374 +                pendpoint_binding.cbApplicationDataLength = cbt_size;
   1.375 +                pendpoint_binding.dwApplicationDataOffset = 
   1.376 +                                            sizeof(SEC_CHANNEL_BINDINGS);
   1.377 +
   1.378 +                // Then add it to the array of sec buffers accordingly.
   1.379 +                ib[ibd.cBuffers].BufferType = SECBUFFER_CHANNEL_BINDINGS;
   1.380 +                ib[ibd.cBuffers].cbBuffer =
   1.381 +                        pendpoint_binding.cbApplicationDataLength
   1.382 +                        + pendpoint_binding.dwApplicationDataOffset;
   1.383 +          
   1.384 +                sspi_cbt = (char *) nsMemory::Alloc(ib[ibd.cBuffers].cbBuffer);
   1.385 +                if (!sspi_cbt){
   1.386 +                    return NS_ERROR_OUT_OF_MEMORY;
   1.387 +                }
   1.388 +
   1.389 +                // Helper to write in the memory block that stores the CBT
   1.390 +                char* sspi_cbt_ptr = sspi_cbt;
   1.391 +          
   1.392 +                ib[ibd.cBuffers].pvBuffer = sspi_cbt;
   1.393 +                ibd.cBuffers++;
   1.394 +
   1.395 +                memcpy(sspi_cbt_ptr, &pendpoint_binding,
   1.396 +                       pendpoint_binding.dwApplicationDataOffset);
   1.397 +                sspi_cbt_ptr += pendpoint_binding.dwApplicationDataOffset;
   1.398 +
   1.399 +                memcpy(sspi_cbt_ptr, end_point, end_point_length);
   1.400 +                sspi_cbt_ptr += end_point_length;
   1.401 +          
   1.402 +                // Start hashing. We are always doing SHA256, but depending
   1.403 +                // on the certificate, a different alogirthm might be needed.
   1.404 +                nsAutoCString hashString;
   1.405 +
   1.406 +                nsresult rv;
   1.407 +                nsCOMPtr<nsICryptoHash> crypto;
   1.408 +                crypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
   1.409 +                if (NS_SUCCEEDED(rv))
   1.410 +                    rv = crypto->Init(nsICryptoHash::SHA256);
   1.411 +                if (NS_SUCCEEDED(rv))
   1.412 +                    rv = crypto->Update((unsigned char*)mCertDERData, mCertDERLength);
   1.413 +                if (NS_SUCCEEDED(rv))
   1.414 +                    rv = crypto->Finish(false, hashString);
   1.415 +                if (NS_FAILED(rv)) {
   1.416 +                    nsMemory::Free(mCertDERData);
   1.417 +                    mCertDERData = nullptr;
   1.418 +                    mCertDERLength = 0;
   1.419 +                    nsMemory::Free(sspi_cbt);
   1.420 +                    return rv;
   1.421 +                }
   1.422 +          
   1.423 +                // Once the hash has been computed, we store it in memory right
   1.424 +                // after the Endpoint structure and the "tls-server-end-point:"
   1.425 +                // char array.
   1.426 +                memcpy(sspi_cbt_ptr, hashString.get(), hash_size);
   1.427 +          
   1.428 +                // Free memory used to store the server certificate
   1.429 +                nsMemory::Free(mCertDERData);
   1.430 +                mCertDERData = nullptr;
   1.431 +                mCertDERLength = 0;
   1.432 +            } // End of CBT computation.
   1.433 +
   1.434 +            // We always need this SECBUFFER.
   1.435 +            ib[ibd.cBuffers].BufferType = SECBUFFER_TOKEN;
   1.436 +            ib[ibd.cBuffers].cbBuffer = inTokenLen;
   1.437 +            ib[ibd.cBuffers].pvBuffer = (void *) inToken;
   1.438 +            ibd.cBuffers++;
   1.439 +            ctxIn = &mCtxt;
   1.440 +        }
   1.441 +    } else { // First time and without a token (no server certificate)
   1.442 +        // We are starting a new authentication sequence.  If we have already 
   1.443 +        // initialized our security context, then we're in trouble because it 
   1.444 +        // means that the first sequence failed.  We need to bail or else we 
   1.445 +        // might end up in an infinite loop.
   1.446 +        if (mCtxt.dwLower || mCtxt.dwUpper || mCertDERData || mCertDERLength) {
   1.447 +            LOG(("Cannot restart authentication sequence!"));
   1.448 +            return NS_ERROR_UNEXPECTED;
   1.449 +        }
   1.450 +        ctxIn = nullptr;
   1.451 +        mIsFirst = false;
   1.452 +    }
   1.453 +
   1.454 +    obd.ulVersion = SECBUFFER_VERSION;
   1.455 +    obd.cBuffers = 1;
   1.456 +    obd.pBuffers = &ob;
   1.457 +    ob.BufferType = SECBUFFER_TOKEN;
   1.458 +    ob.cbBuffer = mMaxTokenLen;
   1.459 +    ob.pvBuffer = nsMemory::Alloc(ob.cbBuffer);
   1.460 +    if (!ob.pvBuffer){
   1.461 +        if (sspi_cbt)
   1.462 +            nsMemory::Free(sspi_cbt);
   1.463 +        return NS_ERROR_OUT_OF_MEMORY;
   1.464 +    }
   1.465 +    memset(ob.pvBuffer, 0, ob.cbBuffer);
   1.466 +
   1.467 +    NS_ConvertUTF8toUTF16 wSN(mServiceName);
   1.468 +    SEC_WCHAR *sn = (SEC_WCHAR *) wSN.get();
   1.469 +
   1.470 +    rc = (sspi->InitializeSecurityContextW)(&mCred,
   1.471 +                                            ctxIn,
   1.472 +                                            sn,
   1.473 +                                            ctxReq,
   1.474 +                                            0,
   1.475 +                                            SECURITY_NATIVE_DREP,
   1.476 +                                            inToken ? &ibd : nullptr,
   1.477 +                                            0,
   1.478 +                                            &mCtxt,
   1.479 +                                            &obd,
   1.480 +                                            &ctxAttr,
   1.481 +                                            &ignored);
   1.482 +    if (rc == SEC_I_CONTINUE_NEEDED || rc == SEC_E_OK) {
   1.483 +
   1.484 +#ifdef PR_LOGGING
   1.485 +        if (rc == SEC_E_OK)
   1.486 +            LOG(("InitializeSecurityContext: succeeded.\n"));
   1.487 +        else
   1.488 +            LOG(("InitializeSecurityContext: continue.\n"));
   1.489 +#endif
   1.490 +        if (sspi_cbt)
   1.491 +            nsMemory::Free(sspi_cbt);
   1.492 +            
   1.493 +        if (!ob.cbBuffer) {
   1.494 +            nsMemory::Free(ob.pvBuffer);
   1.495 +            ob.pvBuffer = nullptr;
   1.496 +        }
   1.497 +        *outToken = ob.pvBuffer;
   1.498 +        *outTokenLen = ob.cbBuffer;
   1.499 +
   1.500 +        if (rc == SEC_E_OK)
   1.501 +            return NS_SUCCESS_AUTH_FINISHED;
   1.502 +
   1.503 +        return NS_OK;
   1.504 +    }
   1.505 +
   1.506 +    LOG(("InitializeSecurityContext failed [rc=%d:%s]\n", rc, MapErrorCode(rc)));
   1.507 +    Reset();
   1.508 +    nsMemory::Free(ob.pvBuffer);
   1.509 +    return NS_ERROR_FAILURE;
   1.510 +}
   1.511 +
   1.512 +NS_IMETHODIMP
   1.513 +nsAuthSSPI::Unwrap(const void *inToken,
   1.514 +                   uint32_t    inTokenLen,
   1.515 +                   void      **outToken,
   1.516 +                   uint32_t   *outTokenLen)
   1.517 +{
   1.518 +    SECURITY_STATUS rc;
   1.519 +    SecBufferDesc ibd;
   1.520 +    SecBuffer ib[2];
   1.521 +
   1.522 +    ibd.cBuffers = 2;
   1.523 +    ibd.pBuffers = ib;
   1.524 +    ibd.ulVersion = SECBUFFER_VERSION; 
   1.525 +
   1.526 +    // SSPI Buf
   1.527 +    ib[0].BufferType = SECBUFFER_STREAM;
   1.528 +    ib[0].cbBuffer = inTokenLen;
   1.529 +    ib[0].pvBuffer = nsMemory::Alloc(ib[0].cbBuffer);
   1.530 +    if (!ib[0].pvBuffer)
   1.531 +        return NS_ERROR_OUT_OF_MEMORY;
   1.532 +    
   1.533 +    memcpy(ib[0].pvBuffer, inToken, inTokenLen);
   1.534 +
   1.535 +    // app data
   1.536 +    ib[1].BufferType = SECBUFFER_DATA;
   1.537 +    ib[1].cbBuffer = 0;
   1.538 +    ib[1].pvBuffer = nullptr;
   1.539 +
   1.540 +    rc = (sspi->DecryptMessage)(
   1.541 +                                &mCtxt,
   1.542 +                                &ibd,
   1.543 +                                0, // no sequence numbers
   1.544 +                                nullptr
   1.545 +                                );
   1.546 +
   1.547 +    if (SEC_SUCCESS(rc)) {
   1.548 +        // check if ib[1].pvBuffer is really just ib[0].pvBuffer, in which
   1.549 +        // case we can let the caller free it. Otherwise, we need to
   1.550 +        // clone it, and free the original
   1.551 +        if (ib[0].pvBuffer == ib[1].pvBuffer) {
   1.552 +            *outToken = ib[1].pvBuffer;
   1.553 +        }
   1.554 +        else {
   1.555 +            *outToken = nsMemory::Clone(ib[1].pvBuffer, ib[1].cbBuffer);
   1.556 +            nsMemory::Free(ib[0].pvBuffer);
   1.557 +            if (!*outToken)
   1.558 +                return NS_ERROR_OUT_OF_MEMORY;
   1.559 +        }
   1.560 +        *outTokenLen = ib[1].cbBuffer;
   1.561 +    }
   1.562 +    else
   1.563 +        nsMemory::Free(ib[0].pvBuffer);
   1.564 +
   1.565 +    if (!SEC_SUCCESS(rc))
   1.566 +        return NS_ERROR_FAILURE;
   1.567 +
   1.568 +    return NS_OK;
   1.569 +}
   1.570 +
   1.571 +// utility class used to free memory on exit
   1.572 +class secBuffers
   1.573 +{
   1.574 +public:
   1.575 +
   1.576 +    SecBuffer ib[3];
   1.577 +
   1.578 +    secBuffers() { memset(&ib, 0, sizeof(ib)); }
   1.579 +
   1.580 +    ~secBuffers() 
   1.581 +    {
   1.582 +        if (ib[0].pvBuffer)
   1.583 +            nsMemory::Free(ib[0].pvBuffer);
   1.584 +
   1.585 +        if (ib[1].pvBuffer)
   1.586 +            nsMemory::Free(ib[1].pvBuffer);
   1.587 +
   1.588 +        if (ib[2].pvBuffer)
   1.589 +            nsMemory::Free(ib[2].pvBuffer);
   1.590 +    }
   1.591 +};
   1.592 +
   1.593 +NS_IMETHODIMP
   1.594 +nsAuthSSPI::Wrap(const void *inToken,
   1.595 +                 uint32_t    inTokenLen,
   1.596 +                 bool        confidential,
   1.597 +                 void      **outToken,
   1.598 +                 uint32_t   *outTokenLen)
   1.599 +{
   1.600 +    SECURITY_STATUS rc;
   1.601 +
   1.602 +    SecBufferDesc ibd;
   1.603 +    secBuffers bufs;
   1.604 +    SecPkgContext_Sizes sizes;
   1.605 +
   1.606 +    rc = (sspi->QueryContextAttributesW)(
   1.607 +         &mCtxt,
   1.608 +         SECPKG_ATTR_SIZES,
   1.609 +         &sizes);
   1.610 +
   1.611 +    if (!SEC_SUCCESS(rc))  
   1.612 +        return NS_ERROR_FAILURE;
   1.613 +    
   1.614 +    ibd.cBuffers = 3;
   1.615 +    ibd.pBuffers = bufs.ib;
   1.616 +    ibd.ulVersion = SECBUFFER_VERSION;
   1.617 +    
   1.618 +    // SSPI
   1.619 +    bufs.ib[0].cbBuffer = sizes.cbSecurityTrailer;
   1.620 +    bufs.ib[0].BufferType = SECBUFFER_TOKEN;
   1.621 +    bufs.ib[0].pvBuffer = nsMemory::Alloc(sizes.cbSecurityTrailer);
   1.622 +
   1.623 +    if (!bufs.ib[0].pvBuffer)
   1.624 +        return NS_ERROR_OUT_OF_MEMORY;
   1.625 +
   1.626 +    // APP Data
   1.627 +    bufs.ib[1].BufferType = SECBUFFER_DATA;
   1.628 +    bufs.ib[1].pvBuffer = nsMemory::Alloc(inTokenLen);
   1.629 +    bufs.ib[1].cbBuffer = inTokenLen;
   1.630 +    
   1.631 +    if (!bufs.ib[1].pvBuffer)
   1.632 +        return NS_ERROR_OUT_OF_MEMORY;
   1.633 +
   1.634 +    memcpy(bufs.ib[1].pvBuffer, inToken, inTokenLen);
   1.635 +
   1.636 +    // SSPI
   1.637 +    bufs.ib[2].BufferType = SECBUFFER_PADDING;
   1.638 +    bufs.ib[2].cbBuffer = sizes.cbBlockSize;
   1.639 +    bufs.ib[2].pvBuffer = nsMemory::Alloc(bufs.ib[2].cbBuffer);
   1.640 +
   1.641 +    if (!bufs.ib[2].pvBuffer)
   1.642 +        return NS_ERROR_OUT_OF_MEMORY;
   1.643 +
   1.644 +    rc = (sspi->EncryptMessage)(&mCtxt,
   1.645 +          confidential ? 0 : KERB_WRAP_NO_ENCRYPT,
   1.646 +         &ibd, 0);
   1.647 +
   1.648 +    if (SEC_SUCCESS(rc)) {
   1.649 +        int len  = bufs.ib[0].cbBuffer + bufs.ib[1].cbBuffer + bufs.ib[2].cbBuffer;
   1.650 +        char *p = (char *) nsMemory::Alloc(len);
   1.651 +
   1.652 +        if (!p)
   1.653 +            return NS_ERROR_OUT_OF_MEMORY;
   1.654 +				
   1.655 +        *outToken = (void *) p;
   1.656 +        *outTokenLen = len;
   1.657 +
   1.658 +        memcpy(p, bufs.ib[0].pvBuffer, bufs.ib[0].cbBuffer);
   1.659 +        p += bufs.ib[0].cbBuffer;
   1.660 +
   1.661 +        memcpy(p,bufs.ib[1].pvBuffer, bufs.ib[1].cbBuffer);
   1.662 +        p += bufs.ib[1].cbBuffer;
   1.663 +
   1.664 +        memcpy(p,bufs.ib[2].pvBuffer, bufs.ib[2].cbBuffer);
   1.665 +        
   1.666 +        return NS_OK;
   1.667 +    }
   1.668 +
   1.669 +    return NS_ERROR_FAILURE;
   1.670 +}

mercurial