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