1 /* vim:set ts=4 sw=4 sts=4 et cindent: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 //
7 // Negotiate Authentication Support Module
8 //
9 // Described by IETF Internet draft: draft-brezak-kerberos-http-00.txt
10 // (formerly draft-brezak-spnego-http-04.txt)
11 //
12 // Also described here:
13 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/http-sso-1.asp
14 //
15 
16 #include "nsAuthSSPI.h"
17 #include "nsIServiceManager.h"
18 #include "nsIDNSService.h"
19 #include "nsIDNSRecord.h"
20 #include "nsNetCID.h"
21 #include "nsCOMPtr.h"
22 #include "nsICryptoHash.h"
23 #include "mozilla/Telemetry.h"
24 
25 #include <windows.h>
26 
27 #define SEC_SUCCESS(Status) ((Status) >= 0)
28 
29 #ifndef KERB_WRAP_NO_ENCRYPT
30 #define KERB_WRAP_NO_ENCRYPT 0x80000001
31 #endif
32 
33 #ifndef SECBUFFER_PADDING
34 #define SECBUFFER_PADDING 9
35 #endif
36 
37 #ifndef SECBUFFER_STREAM
38 #define SECBUFFER_STREAM 10
39 #endif
40 
41 //-----------------------------------------------------------------------------
42 
43 static const wchar_t *const pTypeName [] = {
44     L"Kerberos",
45     L"Negotiate",
46     L"NTLM"
47 };
48 
49 #ifdef DEBUG
50 #define CASE_(_x) case _x: return # _x;
MapErrorCode(int rc)51 static const char *MapErrorCode(int rc)
52 {
53     switch (rc) {
54     CASE_(SEC_E_OK)
55     CASE_(SEC_I_CONTINUE_NEEDED)
56     CASE_(SEC_I_COMPLETE_NEEDED)
57     CASE_(SEC_I_COMPLETE_AND_CONTINUE)
58     CASE_(SEC_E_INCOMPLETE_MESSAGE)
59     CASE_(SEC_I_INCOMPLETE_CREDENTIALS)
60     CASE_(SEC_E_INVALID_HANDLE)
61     CASE_(SEC_E_TARGET_UNKNOWN)
62     CASE_(SEC_E_LOGON_DENIED)
63     CASE_(SEC_E_INTERNAL_ERROR)
64     CASE_(SEC_E_NO_CREDENTIALS)
65     CASE_(SEC_E_NO_AUTHENTICATING_AUTHORITY)
66     CASE_(SEC_E_INSUFFICIENT_MEMORY)
67     CASE_(SEC_E_INVALID_TOKEN)
68     }
69     return "<unknown>";
70 }
71 #else
72 #define MapErrorCode(_rc) ""
73 #endif
74 
75 //-----------------------------------------------------------------------------
76 
77 static PSecurityFunctionTableW   sspi;
78 
79 static nsresult
InitSSPI()80 InitSSPI()
81 {
82     LOG(("  InitSSPI\n"));
83 
84     sspi = InitSecurityInterfaceW();
85     if (!sspi) {
86         LOG(("InitSecurityInterfaceW failed"));
87         return NS_ERROR_UNEXPECTED;
88     }
89 
90     return NS_OK;
91 }
92 
93 //-----------------------------------------------------------------------------
94 
95 static nsresult
MakeSN(const char * principal,nsCString & result)96 MakeSN(const char *principal, nsCString &result)
97 {
98     nsresult rv;
99 
100     nsAutoCString buf(principal);
101 
102     // The service name looks like "protocol@hostname", we need to map
103     // this to a value that SSPI expects.  To be consistent with IE, we
104     // need to map '@' to '/' and canonicalize the hostname.
105     int32_t index = buf.FindChar('@');
106     if (index == kNotFound)
107         return NS_ERROR_UNEXPECTED;
108 
109     nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
110     if (NS_FAILED(rv))
111         return rv;
112 
113     // This could be expensive if our DNS cache cannot satisfy the request.
114     // However, we should have at least hit the OS resolver once prior to
115     // reaching this code, so provided the OS resolver has this information
116     // cached, we should not have to worry about blocking on this function call
117     // for very long.  NOTE: because we ask for the canonical hostname, we
118     // might end up requiring extra network activity in cases where the OS
119     // resolver might not have enough information to satisfy the request from
120     // its cache.  This is not an issue in versions of Windows up to WinXP.
121     nsCOMPtr<nsIDNSRecord> record;
122     rv = dns->Resolve(Substring(buf, index + 1),
123                       nsIDNSService::RESOLVE_CANONICAL_NAME,
124                       getter_AddRefs(record));
125     if (NS_FAILED(rv))
126         return rv;
127 
128     nsAutoCString cname;
129     rv = record->GetCanonicalName(cname);
130     if (NS_SUCCEEDED(rv)) {
131         result = StringHead(buf, index) + NS_LITERAL_CSTRING("/") + cname;
132         LOG(("Using SPN of [%s]\n", result.get()));
133     }
134     return rv;
135 }
136 
137 //-----------------------------------------------------------------------------
138 
nsAuthSSPI(pType package)139 nsAuthSSPI::nsAuthSSPI(pType package)
140     : mServiceFlags(REQ_DEFAULT)
141     , mMaxTokenLen(0)
142     , mPackage(package)
143     , mCertDERData(nullptr)
144     , mCertDERLength(0)
145 {
146     memset(&mCred, 0, sizeof(mCred));
147     memset(&mCtxt, 0, sizeof(mCtxt));
148 }
149 
~nsAuthSSPI()150 nsAuthSSPI::~nsAuthSSPI()
151 {
152     Reset();
153 
154     if (mCred.dwLower || mCred.dwUpper) {
155 #ifdef __MINGW32__
156         (sspi->FreeCredentialsHandle)(&mCred);
157 #else
158         (sspi->FreeCredentialHandle)(&mCred);
159 #endif
160         memset(&mCred, 0, sizeof(mCred));
161     }
162 }
163 
164 void
Reset()165 nsAuthSSPI::Reset()
166 {
167     mIsFirst = true;
168 
169     if (mCertDERData){
170         free(mCertDERData);
171         mCertDERData = nullptr;
172         mCertDERLength = 0;
173     }
174 
175     if (mCtxt.dwLower || mCtxt.dwUpper) {
176         (sspi->DeleteSecurityContext)(&mCtxt);
177         memset(&mCtxt, 0, sizeof(mCtxt));
178     }
179 }
180 
NS_IMPL_ISUPPORTS(nsAuthSSPI,nsIAuthModule)181 NS_IMPL_ISUPPORTS(nsAuthSSPI, nsIAuthModule)
182 
183 NS_IMETHODIMP
184 nsAuthSSPI::Init(const char *serviceName,
185                  uint32_t    serviceFlags,
186                  const char16_t *domain,
187                  const char16_t *username,
188                  const char16_t *password)
189 {
190     LOG(("  nsAuthSSPI::Init\n"));
191 
192     mIsFirst = true;
193     mCertDERLength = 0;
194     mCertDERData = nullptr;
195 
196     // The caller must supply a service name to be used. (For why we now require
197     // a service name for NTLM, see bug 487872.)
198     NS_ENSURE_TRUE(serviceName && *serviceName, NS_ERROR_INVALID_ARG);
199 
200     nsresult rv;
201 
202     // XXX lazy initialization like this assumes that we are single threaded
203     if (!sspi) {
204         rv = InitSSPI();
205         if (NS_FAILED(rv))
206             return rv;
207     }
208     SEC_WCHAR *package;
209 
210     package = (SEC_WCHAR *) pTypeName[(int)mPackage];
211 
212     if (mPackage == PACKAGE_TYPE_NTLM) {
213         // (bug 535193) For NTLM, just use the uri host, do not do canonical host lookups.
214         // The incoming serviceName is in the format: "protocol@hostname", SSPI expects
215         // "<service class>/<hostname>", so swap the '@' for a '/'.
216         mServiceName.Assign(serviceName);
217         int32_t index = mServiceName.FindChar('@');
218         if (index == kNotFound)
219             return NS_ERROR_UNEXPECTED;
220         mServiceName.Replace(index, 1, '/');
221     }
222     else {
223         // Kerberos requires the canonical host, MakeSN takes care of this through a
224         // DNS lookup.
225         rv = MakeSN(serviceName, mServiceName);
226         if (NS_FAILED(rv))
227             return rv;
228     }
229 
230     mServiceFlags = serviceFlags;
231 
232     SECURITY_STATUS rc;
233 
234     PSecPkgInfoW pinfo;
235     rc = (sspi->QuerySecurityPackageInfoW)(package, &pinfo);
236     if (rc != SEC_E_OK) {
237         LOG(("%s package not found\n", package));
238         return NS_ERROR_UNEXPECTED;
239     }
240     mMaxTokenLen = pinfo->cbMaxToken;
241     (sspi->FreeContextBuffer)(pinfo);
242 
243     MS_TimeStamp useBefore;
244 
245     SEC_WINNT_AUTH_IDENTITY_W ai;
246     SEC_WINNT_AUTH_IDENTITY_W *pai = nullptr;
247 
248     // domain, username, and password will be null if nsHttpNTLMAuth's ChallengeReceived
249     // returns false for identityInvalid. Use default credentials in this case by passing
250     // null for pai.
251     if (username && password) {
252         // Keep a copy of these strings for the duration
253         mUsername.Assign(username);
254         mPassword.Assign(password);
255         mDomain.Assign(domain);
256         ai.Domain = reinterpret_cast<unsigned short*>(mDomain.BeginWriting());
257         ai.DomainLength = mDomain.Length();
258         ai.User = reinterpret_cast<unsigned short*>(mUsername.BeginWriting());
259         ai.UserLength = mUsername.Length();
260         ai.Password = reinterpret_cast<unsigned short*>(mPassword.BeginWriting());
261         ai.PasswordLength = mPassword.Length();
262         ai.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
263         pai = &ai;
264     }
265 
266     rc = (sspi->AcquireCredentialsHandleW)(nullptr,
267                                            package,
268                                            SECPKG_CRED_OUTBOUND,
269                                            nullptr,
270                                            pai,
271                                            nullptr,
272                                            nullptr,
273                                            &mCred,
274                                            &useBefore);
275     if (rc != SEC_E_OK)
276         return NS_ERROR_UNEXPECTED;
277 
278     static bool sTelemetrySent = false;
279     if (!sTelemetrySent) {
280         mozilla::Telemetry::Accumulate(
281             mozilla::Telemetry::NTLM_MODULE_USED_2,
282             serviceFlags & nsIAuthModule::REQ_PROXY_AUTH
283                 ? NTLM_MODULE_WIN_API_PROXY
284                 : NTLM_MODULE_WIN_API_DIRECT);
285         sTelemetrySent = true;
286     }
287 
288     LOG(("AcquireCredentialsHandle() succeeded.\n"));
289     return NS_OK;
290 }
291 
292 // The arguments inToken and inTokenLen are used to pass in the server
293 // certificate (when available) in the first call of the function. The
294 // second time these arguments hold an input token.
295 NS_IMETHODIMP
GetNextToken(const void * inToken,uint32_t inTokenLen,void ** outToken,uint32_t * outTokenLen)296 nsAuthSSPI::GetNextToken(const void *inToken,
297                          uint32_t    inTokenLen,
298                          void      **outToken,
299                          uint32_t   *outTokenLen)
300 {
301     // String for end-point bindings.
302     const char end_point[] = "tls-server-end-point:";
303     const int end_point_length = sizeof(end_point) - 1;
304     const int hash_size = 32;  // Size of a SHA256 hash.
305     const int cbt_size = hash_size + end_point_length;
306 
307     SECURITY_STATUS rc;
308     MS_TimeStamp ignored;
309 
310     DWORD ctxAttr, ctxReq = 0;
311     CtxtHandle *ctxIn;
312     SecBufferDesc ibd, obd;
313     // Optional second input buffer for the CBT (Channel Binding Token)
314     SecBuffer ib[2], ob;
315     // Pointer to the block of memory that stores the CBT
316     char* sspi_cbt = nullptr;
317     SEC_CHANNEL_BINDINGS pendpoint_binding;
318 
319     LOG(("entering nsAuthSSPI::GetNextToken()\n"));
320 
321     if (!mCred.dwLower && !mCred.dwUpper) {
322         LOG(("nsAuthSSPI::GetNextToken(), not initialized. exiting."));
323         return NS_ERROR_NOT_INITIALIZED;
324     }
325 
326     if (mServiceFlags & REQ_DELEGATE)
327         ctxReq |= ISC_REQ_DELEGATE;
328     if (mServiceFlags & REQ_MUTUAL_AUTH)
329         ctxReq |= ISC_REQ_MUTUAL_AUTH;
330 
331     if (inToken) {
332         if (mIsFirst) {
333             // First time if it comes with a token,
334             // the token represents the server certificate.
335             mIsFirst = false;
336             mCertDERLength = inTokenLen;
337             mCertDERData = moz_xmalloc(inTokenLen);
338             if (!mCertDERData)
339                 return NS_ERROR_OUT_OF_MEMORY;
340             memcpy(mCertDERData, inToken, inTokenLen);
341 
342             // We are starting a new authentication sequence.
343             // If we have already initialized our
344             // security context, then we're in trouble because it means that the
345             // first sequence failed.  We need to bail or else we might end up in
346             // an infinite loop.
347             if (mCtxt.dwLower || mCtxt.dwUpper) {
348                 LOG(("Cannot restart authentication sequence!"));
349                 return NS_ERROR_UNEXPECTED;
350             }
351             ctxIn = nullptr;
352             // The certificate needs to be erased before being passed
353             // to InitializeSecurityContextW().
354             inToken = nullptr;
355             inTokenLen = 0;
356         } else {
357             ibd.ulVersion = SECBUFFER_VERSION;
358             ibd.cBuffers = 0;
359             ibd.pBuffers = ib;
360 
361             // If we have stored a certificate, the Channel Binding Token
362             // needs to be generated and sent in the first input buffer.
363             if (mCertDERLength > 0) {
364                 // First we create a proper Endpoint Binding structure.
365                 pendpoint_binding.dwInitiatorAddrType = 0;
366                 pendpoint_binding.cbInitiatorLength = 0;
367                 pendpoint_binding.dwInitiatorOffset = 0;
368                 pendpoint_binding.dwAcceptorAddrType = 0;
369                 pendpoint_binding.cbAcceptorLength = 0;
370                 pendpoint_binding.dwAcceptorOffset = 0;
371                 pendpoint_binding.cbApplicationDataLength = cbt_size;
372                 pendpoint_binding.dwApplicationDataOffset =
373                                             sizeof(SEC_CHANNEL_BINDINGS);
374 
375                 // Then add it to the array of sec buffers accordingly.
376                 ib[ibd.cBuffers].BufferType = SECBUFFER_CHANNEL_BINDINGS;
377                 ib[ibd.cBuffers].cbBuffer =
378                         pendpoint_binding.cbApplicationDataLength
379                         + pendpoint_binding.dwApplicationDataOffset;
380 
381                 sspi_cbt = (char *) moz_xmalloc(ib[ibd.cBuffers].cbBuffer);
382                 if (!sspi_cbt){
383                     return NS_ERROR_OUT_OF_MEMORY;
384                 }
385 
386                 // Helper to write in the memory block that stores the CBT
387                 char* sspi_cbt_ptr = sspi_cbt;
388 
389                 ib[ibd.cBuffers].pvBuffer = sspi_cbt;
390                 ibd.cBuffers++;
391 
392                 memcpy(sspi_cbt_ptr, &pendpoint_binding,
393                        pendpoint_binding.dwApplicationDataOffset);
394                 sspi_cbt_ptr += pendpoint_binding.dwApplicationDataOffset;
395 
396                 memcpy(sspi_cbt_ptr, end_point, end_point_length);
397                 sspi_cbt_ptr += end_point_length;
398 
399                 // Start hashing. We are always doing SHA256, but depending
400                 // on the certificate, a different alogirthm might be needed.
401                 nsAutoCString hashString;
402 
403                 nsresult rv;
404                 nsCOMPtr<nsICryptoHash> crypto;
405                 crypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
406                 if (NS_SUCCEEDED(rv))
407                     rv = crypto->Init(nsICryptoHash::SHA256);
408                 if (NS_SUCCEEDED(rv))
409                     rv = crypto->Update((unsigned char*)mCertDERData, mCertDERLength);
410                 if (NS_SUCCEEDED(rv))
411                     rv = crypto->Finish(false, hashString);
412                 if (NS_FAILED(rv)) {
413                     free(mCertDERData);
414                     mCertDERData = nullptr;
415                     mCertDERLength = 0;
416                     free(sspi_cbt);
417                     return rv;
418                 }
419 
420                 // Once the hash has been computed, we store it in memory right
421                 // after the Endpoint structure and the "tls-server-end-point:"
422                 // char array.
423                 memcpy(sspi_cbt_ptr, hashString.get(), hash_size);
424 
425                 // Free memory used to store the server certificate
426                 free(mCertDERData);
427                 mCertDERData = nullptr;
428                 mCertDERLength = 0;
429             } // End of CBT computation.
430 
431             // We always need this SECBUFFER.
432             ib[ibd.cBuffers].BufferType = SECBUFFER_TOKEN;
433             ib[ibd.cBuffers].cbBuffer = inTokenLen;
434             ib[ibd.cBuffers].pvBuffer = (void *) inToken;
435             ibd.cBuffers++;
436             ctxIn = &mCtxt;
437         }
438     } else { // First time and without a token (no server certificate)
439         // We are starting a new authentication sequence.  If we have already
440         // initialized our security context, then we're in trouble because it
441         // means that the first sequence failed.  We need to bail or else we
442         // might end up in an infinite loop.
443         if (mCtxt.dwLower || mCtxt.dwUpper || mCertDERData || mCertDERLength) {
444             LOG(("Cannot restart authentication sequence!"));
445             return NS_ERROR_UNEXPECTED;
446         }
447         ctxIn = nullptr;
448         mIsFirst = false;
449     }
450 
451     obd.ulVersion = SECBUFFER_VERSION;
452     obd.cBuffers = 1;
453     obd.pBuffers = &ob;
454     ob.BufferType = SECBUFFER_TOKEN;
455     ob.cbBuffer = mMaxTokenLen;
456     ob.pvBuffer = moz_xmalloc(ob.cbBuffer);
457     if (!ob.pvBuffer){
458         if (sspi_cbt)
459             free(sspi_cbt);
460         return NS_ERROR_OUT_OF_MEMORY;
461     }
462     memset(ob.pvBuffer, 0, ob.cbBuffer);
463 
464     NS_ConvertUTF8toUTF16 wSN(mServiceName);
465     SEC_WCHAR *sn = (SEC_WCHAR *) wSN.get();
466 
467     rc = (sspi->InitializeSecurityContextW)(&mCred,
468                                             ctxIn,
469                                             sn,
470                                             ctxReq,
471                                             0,
472                                             SECURITY_NATIVE_DREP,
473                                             inToken ? &ibd : nullptr,
474                                             0,
475                                             &mCtxt,
476                                             &obd,
477                                             &ctxAttr,
478                                             &ignored);
479     if (rc == SEC_I_CONTINUE_NEEDED || rc == SEC_E_OK) {
480 
481         if (rc == SEC_E_OK)
482             LOG(("InitializeSecurityContext: succeeded.\n"));
483         else
484             LOG(("InitializeSecurityContext: continue.\n"));
485 
486         if (sspi_cbt)
487             free(sspi_cbt);
488 
489         if (!ob.cbBuffer) {
490             free(ob.pvBuffer);
491             ob.pvBuffer = nullptr;
492         }
493         *outToken = ob.pvBuffer;
494         *outTokenLen = ob.cbBuffer;
495 
496         if (rc == SEC_E_OK)
497             return NS_SUCCESS_AUTH_FINISHED;
498 
499         return NS_OK;
500     }
501 
502     LOG(("InitializeSecurityContext failed [rc=%d:%s]\n", rc, MapErrorCode(rc)));
503     Reset();
504     free(ob.pvBuffer);
505     return NS_ERROR_FAILURE;
506 }
507 
508 NS_IMETHODIMP
Unwrap(const void * inToken,uint32_t inTokenLen,void ** outToken,uint32_t * outTokenLen)509 nsAuthSSPI::Unwrap(const void *inToken,
510                    uint32_t    inTokenLen,
511                    void      **outToken,
512                    uint32_t   *outTokenLen)
513 {
514     SECURITY_STATUS rc;
515     SecBufferDesc ibd;
516     SecBuffer ib[2];
517 
518     ibd.cBuffers = 2;
519     ibd.pBuffers = ib;
520     ibd.ulVersion = SECBUFFER_VERSION;
521 
522     // SSPI Buf
523     ib[0].BufferType = SECBUFFER_STREAM;
524     ib[0].cbBuffer = inTokenLen;
525     ib[0].pvBuffer = moz_xmalloc(ib[0].cbBuffer);
526     if (!ib[0].pvBuffer)
527         return NS_ERROR_OUT_OF_MEMORY;
528 
529     memcpy(ib[0].pvBuffer, inToken, inTokenLen);
530 
531     // app data
532     ib[1].BufferType = SECBUFFER_DATA;
533     ib[1].cbBuffer = 0;
534     ib[1].pvBuffer = nullptr;
535 
536     rc = (sspi->DecryptMessage)(
537                                 &mCtxt,
538                                 &ibd,
539                                 0, // no sequence numbers
540                                 nullptr
541                                 );
542 
543     if (SEC_SUCCESS(rc)) {
544         // check if ib[1].pvBuffer is really just ib[0].pvBuffer, in which
545         // case we can let the caller free it. Otherwise, we need to
546         // clone it, and free the original
547         if (ib[0].pvBuffer == ib[1].pvBuffer) {
548             *outToken = ib[1].pvBuffer;
549         }
550         else {
551             *outToken = nsMemory::Clone(ib[1].pvBuffer, ib[1].cbBuffer);
552             free(ib[0].pvBuffer);
553             if (!*outToken)
554                 return NS_ERROR_OUT_OF_MEMORY;
555         }
556         *outTokenLen = ib[1].cbBuffer;
557     }
558     else
559         free(ib[0].pvBuffer);
560 
561     if (!SEC_SUCCESS(rc))
562         return NS_ERROR_FAILURE;
563 
564     return NS_OK;
565 }
566 
567 // utility class used to free memory on exit
568 class secBuffers
569 {
570 public:
571 
572     SecBuffer ib[3];
573 
secBuffers()574     secBuffers() { memset(&ib, 0, sizeof(ib)); }
575 
~secBuffers()576     ~secBuffers()
577     {
578         if (ib[0].pvBuffer)
579             free(ib[0].pvBuffer);
580 
581         if (ib[1].pvBuffer)
582             free(ib[1].pvBuffer);
583 
584         if (ib[2].pvBuffer)
585             free(ib[2].pvBuffer);
586     }
587 };
588 
589 NS_IMETHODIMP
Wrap(const void * inToken,uint32_t inTokenLen,bool confidential,void ** outToken,uint32_t * outTokenLen)590 nsAuthSSPI::Wrap(const void *inToken,
591                  uint32_t    inTokenLen,
592                  bool        confidential,
593                  void      **outToken,
594                  uint32_t   *outTokenLen)
595 {
596     SECURITY_STATUS rc;
597 
598     SecBufferDesc ibd;
599     secBuffers bufs;
600     SecPkgContext_Sizes sizes;
601 
602     rc = (sspi->QueryContextAttributesW)(
603          &mCtxt,
604          SECPKG_ATTR_SIZES,
605          &sizes);
606 
607     if (!SEC_SUCCESS(rc))
608         return NS_ERROR_FAILURE;
609 
610     ibd.cBuffers = 3;
611     ibd.pBuffers = bufs.ib;
612     ibd.ulVersion = SECBUFFER_VERSION;
613 
614     // SSPI
615     bufs.ib[0].cbBuffer = sizes.cbSecurityTrailer;
616     bufs.ib[0].BufferType = SECBUFFER_TOKEN;
617     bufs.ib[0].pvBuffer = moz_xmalloc(sizes.cbSecurityTrailer);
618 
619     if (!bufs.ib[0].pvBuffer)
620         return NS_ERROR_OUT_OF_MEMORY;
621 
622     // APP Data
623     bufs.ib[1].BufferType = SECBUFFER_DATA;
624     bufs.ib[1].pvBuffer = moz_xmalloc(inTokenLen);
625     bufs.ib[1].cbBuffer = inTokenLen;
626 
627     if (!bufs.ib[1].pvBuffer)
628         return NS_ERROR_OUT_OF_MEMORY;
629 
630     memcpy(bufs.ib[1].pvBuffer, inToken, inTokenLen);
631 
632     // SSPI
633     bufs.ib[2].BufferType = SECBUFFER_PADDING;
634     bufs.ib[2].cbBuffer = sizes.cbBlockSize;
635     bufs.ib[2].pvBuffer = moz_xmalloc(bufs.ib[2].cbBuffer);
636 
637     if (!bufs.ib[2].pvBuffer)
638         return NS_ERROR_OUT_OF_MEMORY;
639 
640     rc = (sspi->EncryptMessage)(&mCtxt,
641           confidential ? 0 : KERB_WRAP_NO_ENCRYPT,
642          &ibd, 0);
643 
644     if (SEC_SUCCESS(rc)) {
645         int len  = bufs.ib[0].cbBuffer + bufs.ib[1].cbBuffer + bufs.ib[2].cbBuffer;
646         char *p = (char *) moz_xmalloc(len);
647 
648         if (!p)
649             return NS_ERROR_OUT_OF_MEMORY;
650 
651         *outToken = (void *) p;
652         *outTokenLen = len;
653 
654         memcpy(p, bufs.ib[0].pvBuffer, bufs.ib[0].cbBuffer);
655         p += bufs.ib[0].cbBuffer;
656 
657         memcpy(p,bufs.ib[1].pvBuffer, bufs.ib[1].cbBuffer);
658         p += bufs.ib[1].cbBuffer;
659 
660         memcpy(p,bufs.ib[2].pvBuffer, bufs.ib[2].cbBuffer);
661 
662         return NS_OK;
663     }
664 
665     return NS_ERROR_FAILURE;
666 }
667