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