1 /* vim:set ts=4 sw=4 sts=4 et cindent: */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is the SSPI NegotiateAuth Module
17 * The Initial Developer of the Original Code is IBM Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 2004
19 * the Initial Developer. All Rights Reserved.
22 * Darin Fisher <darin@meer.net>
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
39 // Negotiate Authentication Support Module
41 // Described by IETF Internet draft: draft-brezak-kerberos-http-00.txt
42 // (formerly draft-brezak-spnego-http-04.txt)
44 // Also described here:
45 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/http-sso-1.asp
48 #include "nsAuthSSPI.h"
49 #include "nsIServiceManager.h"
50 #include "nsIDNSService.h"
51 #include "nsIDNSRecord.h"
55 #define SEC_SUCCESS(Status) ((Status) >= 0)
57 #ifndef KERB_WRAP_NO_ENCRYPT
58 #define KERB_WRAP_NO_ENCRYPT 0x80000001
61 #ifndef SECBUFFER_PADDING
62 #define SECBUFFER_PADDING 9
65 #ifndef SECBUFFER_STREAM
66 #define SECBUFFER_STREAM 10
69 //-----------------------------------------------------------------------------
71 static const char *const pTypeName
[] = {
78 #define CASE_(_x) case _x: return # _x;
79 static const char *MapErrorCode(int rc
)
83 CASE_(SEC_I_CONTINUE_NEEDED
)
84 CASE_(SEC_I_COMPLETE_NEEDED
)
85 CASE_(SEC_I_COMPLETE_AND_CONTINUE
)
86 CASE_(SEC_E_INCOMPLETE_MESSAGE
)
87 CASE_(SEC_I_INCOMPLETE_CREDENTIALS
)
88 CASE_(SEC_E_INVALID_HANDLE
)
89 CASE_(SEC_E_TARGET_UNKNOWN
)
90 CASE_(SEC_E_LOGON_DENIED
)
91 CASE_(SEC_E_INTERNAL_ERROR
)
92 CASE_(SEC_E_NO_CREDENTIALS
)
93 CASE_(SEC_E_NO_AUTHENTICATING_AUTHORITY
)
94 CASE_(SEC_E_INSUFFICIENT_MEMORY
)
95 CASE_(SEC_E_INVALID_TOKEN
)
100 #define MapErrorCode(_rc) ""
103 //-----------------------------------------------------------------------------
105 static HINSTANCE sspi_lib
;
106 static PSecurityFunctionTable sspi
;
111 PSecurityFunctionTable (*initFun
)(void);
113 LOG((" InitSSPI\n"));
115 sspi_lib
= LoadLibrary("secur32.dll");
117 sspi_lib
= LoadLibrary("security.dll");
119 LOG(("SSPI library not found"));
120 return NS_ERROR_UNEXPECTED
;
124 initFun
= (PSecurityFunctionTable (*)(void))
125 GetProcAddress(sspi_lib
, "InitSecurityInterfaceA");
127 LOG(("InitSecurityInterfaceA not found"));
128 return NS_ERROR_UNEXPECTED
;
133 LOG(("InitSecurityInterfaceA failed"));
134 return NS_ERROR_UNEXPECTED
;
140 //-----------------------------------------------------------------------------
143 MakeSN(const char *principal
, nsCString
&result
)
147 nsCAutoString
buf(principal
);
149 // The service name looks like "protocol@hostname", we need to map
150 // this to a value that SSPI expects. To be consistent with IE, we
151 // need to map '@' to '/' and canonicalize the hostname.
152 PRInt32 index
= buf
.FindChar('@');
153 if (index
== kNotFound
)
154 return NS_ERROR_UNEXPECTED
;
156 nsCOMPtr
<nsIDNSService
> dns
= do_GetService(NS_DNSSERVICE_CONTRACTID
, &rv
);
160 // This could be expensive if our DNS cache cannot satisfy the request.
161 // However, we should have at least hit the OS resolver once prior to
162 // reaching this code, so provided the OS resolver has this information
163 // cached, we should not have to worry about blocking on this function call
164 // for very long. NOTE: because we ask for the canonical hostname, we
165 // might end up requiring extra network activity in cases where the OS
166 // resolver might not have enough information to satisfy the request from
167 // its cache. This is not an issue in versions of Windows up to WinXP.
168 nsCOMPtr
<nsIDNSRecord
> record
;
169 rv
= dns
->Resolve(Substring(buf
, index
+ 1),
170 nsIDNSService::RESOLVE_CANONICAL_NAME
,
171 getter_AddRefs(record
));
176 rv
= record
->GetCanonicalName(cname
);
177 if (NS_SUCCEEDED(rv
)) {
178 result
= StringHead(buf
, index
) + NS_LITERAL_CSTRING("/") + cname
;
179 LOG(("Using SPN of [%s]\n", result
.get()));
184 //-----------------------------------------------------------------------------
186 nsAuthSSPI::nsAuthSSPI(pType package
)
187 : mServiceFlags(REQ_DEFAULT
)
191 memset(&mCred
, 0, sizeof(mCred
));
192 memset(&mCtxt
, 0, sizeof(mCtxt
));
195 nsAuthSSPI::~nsAuthSSPI()
199 if (mCred
.dwLower
|| mCred
.dwUpper
) {
201 (sspi
->FreeCredentialsHandle
)(&mCred
);
203 (sspi
->FreeCredentialHandle
)(&mCred
);
205 memset(&mCred
, 0, sizeof(mCred
));
212 if (mCtxt
.dwLower
|| mCtxt
.dwUpper
) {
213 (sspi
->DeleteSecurityContext
)(&mCtxt
);
214 memset(&mCtxt
, 0, sizeof(mCtxt
));
218 NS_IMPL_ISUPPORTS1(nsAuthSSPI
, nsIAuthModule
)
221 nsAuthSSPI::Init(const char *serviceName
,
222 PRUint32 serviceFlags
,
223 const PRUnichar
*domain
,
224 const PRUnichar
*username
,
225 const PRUnichar
*password
)
227 LOG((" nsAuthSSPI::Init\n"));
229 // we don't expect to be passed any user credentials
230 NS_ASSERTION(!domain
&& !username
&& !password
, "unexpected credentials");
232 // if we're configured for SPNEGO (Negotiate) or Kerberos, then it's critical
233 // that the caller supply a service name to be used.
234 if (mPackage
!= PACKAGE_TYPE_NTLM
)
235 NS_ENSURE_TRUE(serviceName
&& *serviceName
, NS_ERROR_INVALID_ARG
);
239 // XXX lazy initialization like this assumes that we are single threaded
248 package
= (SEC_CHAR
*) pTypeName
[(int)mPackage
];
250 if (mPackage
!= PACKAGE_TYPE_NTLM
)
252 rv
= MakeSN(serviceName
, mServiceName
);
255 mServiceFlags
= serviceFlags
;
261 rc
= (sspi
->QuerySecurityPackageInfo
)(package
, &pinfo
);
262 if (rc
!= SEC_E_OK
) {
263 LOG(("%s package not found\n", package
));
264 return NS_ERROR_UNEXPECTED
;
266 mMaxTokenLen
= pinfo
->cbMaxToken
;
267 (sspi
->FreeContextBuffer
)(pinfo
);
271 rc
= (sspi
->AcquireCredentialsHandle
)(NULL
,
273 SECPKG_CRED_OUTBOUND
,
281 return NS_ERROR_UNEXPECTED
;
287 nsAuthSSPI::GetNextToken(const void *inToken
,
290 PRUint32
*outTokenLen
)
295 DWORD ctxAttr
, ctxReq
= 0;
297 SecBufferDesc ibd
, obd
;
300 LOG(("entering nsAuthSSPI::GetNextToken()\n"));
302 if (mServiceFlags
& REQ_DELEGATE
)
303 ctxReq
|= ISC_REQ_DELEGATE
;
304 if (mServiceFlags
& REQ_MUTUAL_AUTH
)
305 ctxReq
|= ISC_REQ_MUTUAL_AUTH
;
308 ib
.BufferType
= SECBUFFER_TOKEN
;
309 ib
.cbBuffer
= inTokenLen
;
310 ib
.pvBuffer
= (void *) inToken
;
311 ibd
.ulVersion
= SECBUFFER_VERSION
;
317 // If there is no input token, then we are starting a new
318 // authentication sequence. If we have already initialized our
319 // security context, then we're in trouble because it means that the
320 // first sequence failed. We need to bail or else we might end up in
322 if (mCtxt
.dwLower
|| mCtxt
.dwUpper
) {
323 LOG(("Cannot restart authentication sequence!"));
324 return NS_ERROR_UNEXPECTED
;
330 obd
.ulVersion
= SECBUFFER_VERSION
;
333 ob
.BufferType
= SECBUFFER_TOKEN
;
334 ob
.cbBuffer
= mMaxTokenLen
;
335 ob
.pvBuffer
= nsMemory::Alloc(ob
.cbBuffer
);
337 return NS_ERROR_OUT_OF_MEMORY
;
338 memset(ob
.pvBuffer
, 0, ob
.cbBuffer
);
342 if (mPackage
== PACKAGE_TYPE_NTLM
)
345 sn
= (SEC_CHAR
*) mServiceName
.get();
347 rc
= (sspi
->InitializeSecurityContext
)(&mCred
,
352 SECURITY_NATIVE_DREP
,
353 inToken
? &ibd
: NULL
,
359 if (rc
== SEC_I_CONTINUE_NEEDED
|| rc
== SEC_E_OK
) {
361 nsMemory::Free(ob
.pvBuffer
);
364 *outToken
= ob
.pvBuffer
;
365 *outTokenLen
= ob
.cbBuffer
;
368 return NS_SUCCESS_AUTH_FINISHED
;
373 LOG(("InitializeSecurityContext failed [rc=%d:%s]\n", rc
, MapErrorCode(rc
)));
375 nsMemory::Free(ob
.pvBuffer
);
376 return NS_ERROR_FAILURE
;
380 nsAuthSSPI::Unwrap(const void *inToken
,
383 PRUint32
*outTokenLen
)
391 ibd
.ulVersion
= SECBUFFER_VERSION
;
394 ib
[0].BufferType
= SECBUFFER_STREAM
;
395 ib
[0].cbBuffer
= inTokenLen
;
396 ib
[0].pvBuffer
= nsMemory::Alloc(ib
[0].cbBuffer
);
398 return NS_ERROR_OUT_OF_MEMORY
;
400 memcpy(ib
[0].pvBuffer
, inToken
, inTokenLen
);
403 ib
[1].BufferType
= SECBUFFER_DATA
;
405 ib
[1].pvBuffer
= NULL
;
407 rc
= (sspi
->DecryptMessage
)(
410 0, // no sequence numbers
414 if (SEC_SUCCESS(rc
)) {
415 *outToken
= ib
[1].pvBuffer
;
416 *outTokenLen
= ib
[1].cbBuffer
;
419 nsMemory::Free(ib
[1].pvBuffer
);
421 nsMemory::Free(ib
[0].pvBuffer
);
423 if (!SEC_SUCCESS(rc
))
424 return NS_ERROR_FAILURE
;
429 // utility class used to free memory on exit
436 secBuffers() { memset(&ib
, 0, sizeof(ib
)); }
441 nsMemory::Free(ib
[0].pvBuffer
);
444 nsMemory::Free(ib
[1].pvBuffer
);
447 nsMemory::Free(ib
[2].pvBuffer
);
452 nsAuthSSPI::Wrap(const void *inToken
,
456 PRUint32
*outTokenLen
)
462 SecPkgContext_Sizes sizes
;
464 rc
= (sspi
->QueryContextAttributes
)(
469 if (!SEC_SUCCESS(rc
))
470 return NS_ERROR_FAILURE
;
473 ibd
.pBuffers
= bufs
.ib
;
474 ibd
.ulVersion
= SECBUFFER_VERSION
;
477 bufs
.ib
[0].cbBuffer
= sizes
.cbSecurityTrailer
;
478 bufs
.ib
[0].BufferType
= SECBUFFER_TOKEN
;
479 bufs
.ib
[0].pvBuffer
= nsMemory::Alloc(sizes
.cbSecurityTrailer
);
481 if (!bufs
.ib
[0].pvBuffer
)
482 return NS_ERROR_OUT_OF_MEMORY
;
485 bufs
.ib
[1].BufferType
= SECBUFFER_DATA
;
486 bufs
.ib
[1].pvBuffer
= nsMemory::Alloc(inTokenLen
);
487 bufs
.ib
[1].cbBuffer
= inTokenLen
;
489 if (!bufs
.ib
[1].pvBuffer
)
490 return NS_ERROR_OUT_OF_MEMORY
;
492 memcpy(bufs
.ib
[1].pvBuffer
, inToken
, inTokenLen
);
495 bufs
.ib
[2].BufferType
= SECBUFFER_PADDING
;
496 bufs
.ib
[2].cbBuffer
= sizes
.cbBlockSize
;
497 bufs
.ib
[2].pvBuffer
= nsMemory::Alloc(bufs
.ib
[2].cbBuffer
);
499 if (!bufs
.ib
[2].pvBuffer
)
500 return NS_ERROR_OUT_OF_MEMORY
;
502 rc
= (sspi
->EncryptMessage
)(&mCtxt
,
503 confidential
? 0 : KERB_WRAP_NO_ENCRYPT
,
506 if (SEC_SUCCESS(rc
)) {
507 int len
= bufs
.ib
[0].cbBuffer
+ bufs
.ib
[1].cbBuffer
+ bufs
.ib
[2].cbBuffer
;
508 char *p
= (char *) nsMemory::Alloc(len
);
511 return NS_ERROR_OUT_OF_MEMORY
;
513 *outToken
= (void *) p
;
516 memcpy(p
, bufs
.ib
[0].pvBuffer
, bufs
.ib
[0].cbBuffer
);
517 p
+= bufs
.ib
[0].cbBuffer
;
519 memcpy(p
,bufs
.ib
[1].pvBuffer
, bufs
.ib
[1].cbBuffer
);
520 p
+= bufs
.ib
[1].cbBuffer
;
522 memcpy(p
,bufs
.ib
[2].pvBuffer
, bufs
.ib
[2].cbBuffer
);
527 return NS_ERROR_FAILURE
;