/*++ Copyright (c) 1993 Microsoft Corporation Module Name: stub.c Abstract: NT LM Security Support Provider client stubs. Author: Cliff Van Dyke (CliffV) 29-Jun-1993 Environment: User Mode Revision History: --*/ #include // Include files common to DLL side of NtLmSsp #include // LPC data and routines #include // PSEC_WINNT_AUTH_IDENTITY #include // wcstombs PSecurityFunctionTableA SspInitSecurityInterfaceA( VOID ) /*++ Routine Description: RPC calls this function to get the addresses of all the other functions that it might call (ANSI version). Arguments: None. Return Value: A pointer to our static SecurityFunctionTable. The caller need not deallocate this table. --*/ { return &SspDllSecurityFunctionTableA; } PSecurityFunctionTableW SspInitSecurityInterfaceW( VOID ) /*++ Routine Description: RPC calls this function to get the addresses of all the other functions that it might call (UNICODE version). Arguments: None. Return Value: A pointer to our static SecurityFunctionTable. The caller need not deallocate this table. --*/ { return &SspDllSecurityFunctionTableW; } SECURITY_STATUS SspQuerySecurityPackageInfoA( IN CHAR * PackageName, OUT PSecPkgInfoA *PackageInfo ) /*++ Routine Description: This API is intended to provide basic information about Security Packages themselves. This information will include the bounds on sizes of authentication information, credentials and contexts. Arguments: PackageName - Name of the package being queried. PackageInfo - Returns a pointer to an allocated block describing the security package. The allocated block must be freed using FreeContextBuffer. Return Value: STATUS_SUCCESS -- Call completed successfully SEC_E_PACKAGE_UNKNOWN -- Package being queried is not this package SEC_E_INSUFFICIENT_MEMORY -- Not enough memory --*/ { CHAR *Where; // // Ensure the correct package name was passed in. // if ( _stricmp( PackageName, NTLMSP_NAME_A ) != 0 ) { return SEC_E_PACKAGE_UNKNOWN; } // // Allocate a buffer for the PackageInfo // *PackageInfo = LocalAlloc( 0, sizeof(SecPkgInfoW) + sizeof(NTLMSP_NAME_A) + sizeof(NTLMSP_COMMENT_A) ); if ( *PackageInfo == NULL ) { return SEC_E_INSUFFICIENT_MEMORY; } // // Fill in the information. // (*PackageInfo)->fCapabilities = NTLMSP_CAPABILITIES; // // Disable encryption if it is not permitted // if (!IsEncryptionPermitted()) { (*PackageInfo)->fCapabilities &= ~SECPKG_FLAG_PRIVACY; } (*PackageInfo)->wVersion = NTLMSP_VERSION; (*PackageInfo)->wRPCID = NTLMSP_RPCID; (*PackageInfo)->cbMaxToken = NTLMSP_MAX_TOKEN_SIZE; Where = (CHAR *)((*PackageInfo)+1); (*PackageInfo)->Name = Where; strcpy( Where, NTLMSP_NAME_A); Where += strlen(Where) + 1; (*PackageInfo)->Comment = Where; strcpy( Where, NTLMSP_COMMENT_A); Where += strlen(Where) + 1; return STATUS_SUCCESS; } SECURITY_STATUS SspQuerySecurityPackageInfoW( IN WCHAR * PackageName, OUT PSecPkgInfoW *PackageInfo ) /*++ Routine Description: This API is intended to provide basic information about Security Packages themselves. This information will include the bounds on sizes of authentication information, credentials and contexts. Arguments: PackageName - Name of the package being queried. PackageInfo - Returns a pointer to an allocated block describing the security package. The allocated block must be freed using FreeContextBuffer. Return Value: STATUS_SUCCESS -- Call completed successfully SEC_E_PACKAGE_UNKNOWN -- Package being queried is not this package SEC_E_INSUFFICIENT_MEMORY -- Not enough memory --*/ { WCHAR *Where; // // Ensure the correct package name was passed in. // if ( _wcsicmp( PackageName, NTLMSP_NAME ) != 0 ) { return SEC_E_PACKAGE_UNKNOWN; } // // Allocate a buffer for the PackageInfo // *PackageInfo = LocalAlloc( 0, sizeof(SecPkgInfoW) + sizeof(NTLMSP_NAME) + sizeof(NTLMSP_COMMENT) ); if ( *PackageInfo == NULL ) { return SEC_E_INSUFFICIENT_MEMORY; } // // Fill in the information. // (*PackageInfo)->fCapabilities = NTLMSP_CAPABILITIES; // // Disable encryption if it is not permitted // if (!IsEncryptionPermitted()) { (*PackageInfo)->fCapabilities &= ~SECPKG_FLAG_PRIVACY; } (*PackageInfo)->wVersion = NTLMSP_VERSION; (*PackageInfo)->wRPCID = NTLMSP_RPCID; (*PackageInfo)->cbMaxToken = NTLMSP_MAX_TOKEN_SIZE; Where = (WCHAR *)((*PackageInfo)+1); (*PackageInfo)->Name = Where; wcscpy( Where, NTLMSP_NAME); Where += wcslen(Where) + 1; (*PackageInfo)->Comment = Where; wcscpy( Where, NTLMSP_COMMENT); Where += wcslen(Where) + 1; return STATUS_SUCCESS; } SECURITY_STATUS SspEnumerateSecurityPackagesA( OUT PULONG PackageCount, OUT PSecPkgInfoA *PackageInfo ) /*++ Routine Description: This API returns a list of Security Packages available to client (i.e. those that are either loaded or can be loaded on demand). The caller must free the returned buffer with FreeContextBuffer. This API returns a list of all the security packages available to a service. The names returned can then be used to acquire credential handles, as well as determine which package in the system best satisfies the requirements of the caller. It is assumed that all available packages can be included in the single call. This is really a dummy API that just returns information about this security package. It is provided to ensure this security package has the same interface as the multiplexer DLL does. Arguments: PackageCount - Returns the number of packages supported. PackageInfo - Returns an allocate array of structures describing the security packages. The array must be freed using FreeContextBuffer. Return Value: STATUS_SUCCESS -- Call completed successfully SEC_E_PACKAGE_UNKNOWN -- Package being queried is not this package SEC_E_INSUFFICIENT_MEMORY -- Not enough memory --*/ { SECURITY_STATUS SecStatus; // // Get the information for this package. // SecStatus = SspQuerySecurityPackageInfoA( NTLMSP_NAME_A, PackageInfo ); if ( !NT_SUCCESS(SecStatus) ) { return SecStatus; } *PackageCount = 1; return STATUS_SUCCESS; } SECURITY_STATUS SspEnumerateSecurityPackagesW( OUT PULONG PackageCount, OUT PSecPkgInfoW *PackageInfo ) /*++ Routine Description: This API returns a list of Security Packages available to client (i.e. those that are either loaded or can be loaded on demand). The caller must free the returned buffer with FreeContextBuffer. This API returns a list of all the security packages available to a service. The names returned can then be used to acquire credential handles, as well as determine which package in the system best satisfies the requirements of the caller. It is assumed that all available packages can be included in the single call. This is really a dummy API that just returns information about this security package. It is provided to ensure this security package has the same interface as the multiplexer DLL does. Arguments: PackageCount - Returns the number of packages supported. PackageInfo - Returns an allocate array of structures describing the security packages. The array must be freed using FreeContextBuffer. Return Value: STATUS_SUCCESS -- Call completed successfully SEC_E_PACKAGE_UNKNOWN -- Package being queried is not this package SEC_E_INSUFFICIENT_MEMORY -- Not enough memory --*/ { SECURITY_STATUS SecStatus; // // Get the information for this package. // SecStatus = SspQuerySecurityPackageInfoW( NTLMSP_NAME, PackageInfo ); if ( !NT_SUCCESS(SecStatus) ) { return SecStatus; } *PackageCount = 1; return STATUS_SUCCESS; } SECURITY_STATUS SspCallService( IN HANDLE LpcHandle, IN ULONG ApiNumber, IN OUT PSSP_API_MESSAGE Message, IN CSHORT MessageSize ) /*++ Routine Description: Calls the NTLMSSP service with the specified message and returns the response. Arguments: LpcHandle - LPC handle to the NTLMSSP service ApiNumber - API number of the API being called Message - Message to pass to the service. MessageSize - Size (in bytes) of the variable length portion of the message Return Value: STATUS_SUCCESS -- Call completed successfully SEC_E_NO_SPM -- Security Support Provider is not running SEC_E_INSUFFICIENT_MEMORY -- Not enough memory --*/ { SECURITY_STATUS SecStatus; NTSTATUS Status; // // Fill in the common fields of the LPC message. // Message->ApiNumber = ApiNumber; Message->PortMessage.u1.s1.DataLength = MessageSize + sizeof(SSP_API_NUMBER) + sizeof(SECURITY_STATUS); Message->PortMessage.u1.s1.TotalLength = Message->PortMessage.u1.s1.DataLength + sizeof(PORT_MESSAGE); Message->PortMessage.u2.ZeroInit = 0; // // Pass the message to the service and wait for a response. // Status = NtRequestWaitReplyPort( LpcHandle, &Message->PortMessage, &Message->PortMessage ); // // If the NtLmSsp service has gone away, // check to see if it has come back. // if ( Status == STATUS_PORT_DISCONNECTED ) { BOOLEAN CallLsaDirectly; LpcHandle = SspDllGetLpcHandle( TRUE, &CallLsaDirectly ); if ( LpcHandle == NULL ) { SecStatus = SEC_E_NO_SPM; return SecStatus; } Status = NtRequestWaitReplyPort( LpcHandle, &Message->PortMessage, &Message->PortMessage ); } if ( !NT_SUCCESS(Status) ) { SecStatus = SspNtStatusToSecStatus( Status, SEC_E_NO_SPM ); } else { SecStatus = Message->ReturnedStatus; } return SecStatus; } SECURITY_STATUS SspUnicodeStringFromOemString( IN LPSTR Oem, OPTIONAL IN ULONG OemLength, OUT LPWSTR * Unicode, OUT PULONG UnicodeSize ) /*++ Routine Description: Converts an ascii null terminated string into the equivalent unicode string. Arguments: Oem - String to convert, may be NULL OemLength - length, in characters, of string to convert Unicode - Gets new string UnicodeSize - gets size of new string in bytes Return Value: SEC_E_INSUFFICIENT_MEMORY - out of memory --*/ { OEM_STRING OemString; UNICODE_STRING UnicodeString; NTSTATUS Status; if ( Oem != NULL ) { RtlInitString( &OemString, Oem ); } else { *Unicode = NULL; *UnicodeSize = 0; return(SEC_E_OK); } if (strlen(Oem) != OemLength) { return(SEC_E_INVALID_TOKEN); } Status = RtlOemStringToUnicodeString( &UnicodeString, &OemString, TRUE // allocate the string for me. ); if (!NT_SUCCESS(Status)) { return(SEC_E_INSUFFICIENT_MEMORY); } *Unicode = UnicodeString.Buffer; *UnicodeSize = UnicodeString.Length + sizeof(WCHAR); return(SEC_E_OK); } SECURITY_STATUS SspAcquireCredentialsHandleW( IN WCHAR * PrincipalName, IN WCHAR * PackageName, IN ULONG CredentialUseFlags, IN PLUID LogonId, IN PVOID AuthData, IN SEC_GET_KEY_FN GetKeyFunction, IN PVOID GetKeyArgument, OUT PCredHandle CredentialHandle, OUT PTimeStamp Lifetime ) /*++ Routine Description: This API allows applications to acquire a handle to pre-existing credentials associated with the user on whose behalf the call is made i.e. under the identity this application is running. These pre-existing credentials have been established through a system logon not described here. Note that this is different from "login to the network" and does not imply gathering of credentials. This API returns a handle to the credentials of a principal (user, client) as used by a specific security package. This handle can then be used in subsequent calls to the Context APIs. This API will not let a process obtain a handle to credentials that are not related to the process; i.e. we won't allow a process to grab the credentials of another user logged into the same machine. There is no way for us to determine if a process is a trojan horse or not, if it is executed by the user. Arguments: PrincipalName - Name of the principal for whose credentials the handle will reference. Note, if the process requesting the handle does not have access to the credentials, an error will be returned. A null string indicates that the process wants a handle to the credentials of the user under whose security it is executing. PackageName - Name of the package with which these credentials will be used. CredentialUseFlags - Flags indicating the way with which these credentials will be used. #define CRED_INBOUND 0x00000001 #define CRED_OUTBOUND 0x00000002 #define CRED_BOTH 0x00000003 The credentials created with CRED_INBOUND option can only be used for (validating incoming calls and can not be used for making accesses. LogonId - Pointer to NT style Logon Id which is a LUID. (Provided for file system ; processes such as network redirectors.) AuthData - If not NULL, specifies the credentials that override the default values. CredentialHandle - Returned credential handle. Lifetime - Time that these credentials expire. The value returned in this field depends on the security package. Return Value: STATUS_SUCCESS -- Call completed successfully SEC_E_NO_SPM -- Security Support Provider is not running SEC_E_PACKAGE_UNKNOWN -- Package being queried is not this package SEC_E_PRINCIPAL_UNKNOWN -- No such principal SEC_E_NOT_OWNER -- caller does not own the specified credentials SEC_E_INSUFFICIENT_MEMORY -- Not enough memory --*/ { SECURITY_STATUS SecStatus; HANDLE LpcHandle = NULL; LPWSTR DomainName = NULL; ULONG DomainNameSize = 0; LPWSTR UserName = NULL; ULONG UserNameSize = 0; LPWSTR Password = NULL; ULONG PasswordSize = 0; BOOLEAN CallLsaDirectly; BOOLEAN DoUnicode = TRUE; // // Get an LPC Handle to the NtLmSsp service. // LpcHandle = SspDllGetLpcHandle( FALSE, &CallLsaDirectly ); if ( LpcHandle == NULL ) { SecStatus = SEC_E_NO_SPM; goto Cleanup; } // // Validate the arguments // if ( _wcsicmp( PackageName, NTLMSP_NAME ) != 0 ) { SecStatus = SEC_E_PACKAGE_UNKNOWN; goto Cleanup; } if ( (CredentialUseFlags & SECPKG_CRED_OUTBOUND) && ARGUMENT_PRESENT(PrincipalName) && *PrincipalName != L'\0' ) { SecStatus = SEC_E_UNKNOWN_CREDENTIALS; goto Cleanup; } if ( ARGUMENT_PRESENT(LogonId) ) { SecStatus = SEC_E_UNSUPPORTED_FUNCTION; goto Cleanup; } if ( ARGUMENT_PRESENT(GetKeyFunction) ) { SecStatus = SEC_E_UNSUPPORTED_FUNCTION; goto Cleanup; } if ( ARGUMENT_PRESENT(GetKeyArgument) ) { SecStatus = SEC_E_UNSUPPORTED_FUNCTION; goto Cleanup; } // // Break AuthData into it's components. // if ( AuthData != NULL ) { PSEC_WINNT_AUTH_IDENTITY AuthIdentity; AuthIdentity = (PSEC_WINNT_AUTH_IDENTITY) AuthData; if (AuthIdentity->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) { if ( AuthIdentity->User != NULL ) { UserName = AuthIdentity->User; if ( AuthIdentity->UserLength != wcslen(AuthIdentity->User)) { SecStatus = SEC_E_INVALID_TOKEN; goto Cleanup; } UserNameSize = (AuthIdentity->UserLength + 1) * sizeof(WCHAR); } if ( AuthIdentity->Domain != NULL ) { DomainName = AuthIdentity->Domain; if ( AuthIdentity->DomainLength != wcslen(AuthIdentity->Domain)) { SecStatus = SEC_E_INVALID_TOKEN; goto Cleanup; } DomainNameSize = (AuthIdentity->DomainLength + 1) * sizeof(WCHAR); } if ( AuthIdentity->Password != NULL ) { Password = AuthIdentity->Password; if ( AuthIdentity->PasswordLength != wcslen(AuthIdentity->Password)) { SecStatus = SEC_E_INVALID_TOKEN; goto Cleanup; } PasswordSize = (AuthIdentity->PasswordLength + 1) * sizeof(WCHAR); } } else { // // OEM // if ((AuthIdentity->Flags & SEC_WINNT_AUTH_IDENTITY_ANSI) == 0) { SecStatus = SEC_E_INVALID_TOKEN; goto Cleanup; } DoUnicode = FALSE; SecStatus = SspUnicodeStringFromOemString( (LPSTR) AuthIdentity->User, AuthIdentity->UserLength, &UserName, &UserNameSize ); if (!NT_SUCCESS(SecStatus)) { goto Cleanup; } SecStatus = SspUnicodeStringFromOemString( (LPSTR) AuthIdentity->Domain, AuthIdentity->DomainLength, &DomainName, &DomainNameSize ); if (!NT_SUCCESS(SecStatus)) { goto Cleanup; } SecStatus = SspUnicodeStringFromOemString( (LPSTR) AuthIdentity->Password, AuthIdentity->PasswordLength, &Password, &PasswordSize ); if (!NT_SUCCESS(SecStatus)) { goto Cleanup; } } } // // If we can call the LSA directly, // skip the NtLmSsp service. // if ( CallLsaDirectly ) { LUID LogonId; HANDLE ClientTokenHandle = NULL; SecStatus = SspGetLogonId( &LogonId, &ClientTokenHandle ); if (NT_SUCCESS(SecStatus)) { SecStatus = SsprAcquireCredentialHandle( NULL, // No client connection &ClientTokenHandle, &LogonId, CredentialUseFlags, CredentialHandle, Lifetime, DomainName, DomainNameSize, UserName, UserNameSize, Password, PasswordSize ); if ( SecStatus == SEC_I_CALL_NTLMSSP_SERVICE ) { CallLsaDirectly = FALSE; } if (ClientTokenHandle != NULL) { (VOID) NtClose(ClientTokenHandle); } } else { goto Cleanup; } } // // Handle LPCing to the NtLmSsp service. // if ( !CallLsaDirectly ) { SSP_API_MESSAGE Message; PSSP_ACQUIRE_CREDENTIAL_HANDLE_ARGS Args; // // Copy the caller's arguments to the LPC message. // Args = &Message.Arguments.AcquireCredentialHandleArgs; Args->CredentialUseFlags = CredentialUseFlags; Args->DomainName = DomainName; Args->DomainNameSize = DomainNameSize; Args->UserName = UserName; Args->UserNameSize = UserNameSize; Args->Password = Password; Args->PasswordSize = PasswordSize; // // Pass the message to the service and wait for a response. // SecStatus = SspCallService( LpcHandle, SspLpcAcquireCredentialHandle, &Message, sizeof(*Args) ); if ( !NT_SUCCESS(SecStatus) ) { goto Cleanup; } // // Copy the return values to the caller. // *CredentialHandle = Args->CredentialHandle; *Lifetime = Args->Lifetime; } Cleanup: if (!DoUnicode) { UNICODE_STRING TempString; TempString.Buffer = UserName; RtlFreeUnicodeString(&TempString); TempString.Buffer = DomainName; RtlFreeUnicodeString(&TempString); TempString.Buffer = Password; RtlFreeUnicodeString(&TempString); } return SecStatus; } SECURITY_STATUS SspAcquireCredentialsHandleA( IN CHAR * PrincipalName, IN CHAR * PackageName, IN ULONG CredentialUseFlags, IN PLUID LogonId, IN PVOID AuthData, IN SEC_GET_KEY_FN GetKeyFunction, IN PVOID GetKeyArgument, OUT PCredHandle CredentialHandle, OUT PTimeStamp Lifetime ) /*++ Routine Description: Ansi thunk to AcquireCredentialsHandleU --*/ { LPWSTR PackageNameW; ULONG PackageNameLength = 0; LPWSTR PrincipalNameW; ULONG PrincipalNameLength = 0; SECURITY_STATUS SecStatus; if (PackageName != NULL) { PackageNameLength = strlen(PackageName); } SecStatus = SspUnicodeStringFromOemString( PackageName, PackageNameLength, &PackageNameW, &PackageNameLength ); if (!NT_SUCCESS(SecStatus)) { return(SecStatus); } if (PrincipalName != NULL) { PrincipalNameLength = strlen(PrincipalName); } SecStatus = SspUnicodeStringFromOemString( PrincipalName, PrincipalNameLength, &PrincipalNameW, &PrincipalNameLength ); if (!NT_SUCCESS(SecStatus)) { goto Cleanup; } if (NT_SUCCESS(SecStatus)) { SecStatus = SspAcquireCredentialsHandleW( PrincipalNameW, PackageNameW, CredentialUseFlags, LogonId, AuthData, GetKeyFunction, GetKeyArgument, CredentialHandle, Lifetime ); } Cleanup: LocalFree(PackageNameW); if (PrincipalNameW != NULL) { LocalFree(PrincipalNameW); } return(SecStatus); } SECURITY_STATUS SspFreeCredentialsHandle( IN PCredHandle CredentialHandle ) /*++ Routine Description: This API is used to notify the security system that the credentials are no longer needed and allows the application to free the handle acquired in the call described above. When all references to this credential set has been removed then the credentials may themselves be removed. Arguments: CredentialHandle - Credential Handle obtained through AcquireCredentialHandle. Return Value: STATUS_SUCCESS -- Call completed successfully SEC_E_NO_SPM -- Security Support Provider is not running SEC_E_INVALID_HANDLE -- Credential Handle is invalid --*/ { SECURITY_STATUS SecStatus; HANDLE LpcHandle = NULL; BOOLEAN CallLsaDirectly; // // Get an LPC Handle to the NtLmSsp service. // LpcHandle = SspDllGetLpcHandle( FALSE, &CallLsaDirectly ); if ( LpcHandle == NULL ) { SecStatus = SEC_E_NO_SPM; goto Cleanup; } // // If we can call the LSA directly, // skip the NtLmSsp service. // if ( CallLsaDirectly ) { SecStatus = SsprFreeCredentialHandle( NULL, // No client connection CredentialHandle ); if ( SecStatus == SEC_I_CALL_NTLMSSP_SERVICE ) { CallLsaDirectly = FALSE; } } // // Handle LPCing to the NtLmSsp service. // if ( !CallLsaDirectly ) { SSP_API_MESSAGE Message; PSSP_FREE_CREDENTIAL_HANDLE_ARGS Args; // // Copy the caller's arguments to the LPC message. // Args = &Message.Arguments.FreeCredentialHandleArgs; Args->CredentialHandle = *CredentialHandle; // // Pass the message to the service and wait for a response. // SecStatus = SspCallService( LpcHandle, SspLpcFreeCredentialHandle, &Message, sizeof(*Args) ); if ( !NT_SUCCESS(SecStatus) ) { goto Cleanup; } } Cleanup: return SecStatus; } BOOLEAN SspGetTokenBuffer( IN PSecBufferDesc TokenDescriptor OPTIONAL, OUT PVOID * TokenBuffer, OUT PULONG * TokenSize, IN BOOLEAN ReadonlyOK ) /*++ Routine Description: This routine parses a Token Descriptor and pulls out the useful information. Arguments: TokenDescriptor - Descriptor of the buffer containing (or to contain) the token. If not specified, TokenBuffer and TokenSize will be returned as NULL. TokenBuffer - Returns a pointer to the buffer for the token. TokenSize - Returns a pointer to the location of the size of the buffer. ReadonlyOK - TRUE if the token buffer may be readonly. Return Value: TRUE - If token buffer was properly found. --*/ { ULONG i; // // If there is no TokenDescriptor passed in, // just pass out NULL to our caller. // if ( !ARGUMENT_PRESENT( TokenDescriptor) ) { *TokenBuffer = NULL; *TokenSize = NULL; return TRUE; } // // Check the version of the descriptor. // if ( TokenDescriptor->ulVersion != SECBUFFER_VERSION ) { return FALSE; } // // Loop through each described buffer. // for ( i=0; icBuffers ; i++ ) { PSecBuffer Buffer = &TokenDescriptor->pBuffers[i]; if ( (Buffer->BufferType & (~SECBUFFER_READONLY)) == SECBUFFER_TOKEN ) { // // If the buffer is readonly and readonly isn't OK, // reject the buffer. // if ( !ReadonlyOK && (Buffer->BufferType & SECBUFFER_READONLY) ) { return FALSE; } // // Return the requested information // *TokenBuffer = Buffer->pvBuffer; *TokenSize = &Buffer->cbBuffer; return TRUE; } } // // If we didn't have a buffeer, fine. // *TokenBuffer = NULL; *TokenSize = NULL; return TRUE; } SECURITY_STATUS SspInitializeSecurityContextW( IN PCredHandle CredentialHandle, IN PCtxtHandle OldContextHandle, IN WCHAR * TargetName, IN ULONG ContextReqFlags, IN ULONG Reserved1, IN ULONG TargetDataRep, IN PSecBufferDesc InputToken, IN ULONG Reserved2, OUT PCtxtHandle NewContextHandle, OUT PSecBufferDesc OutputToken, OUT PULONG ContextAttributes, OUT PTimeStamp ExpirationTime ) /*++ Routine Description: This routine initiates the outbound security context from a credential handle. This results in the establishment of a security context between the application and a remote peer. The routine returns a token which must be passed to the remote peer which in turn submits it to the local security implementation via the AcceptSecurityContext() call. The token generated should be considered opaque by all callers. This function is used by a client to initialize an outbound context. For a two leg security package, the calling sequence is as follows: The client calls the function with OldContextHandle set to NULL and InputToken set either to NULL or to a pointer to a security package specific data structure. The package returns a context handle in NewContextHandle and a token in OutputToken. The handle can then be used for message APIs if desired. The OutputToken returned here is sent across to target server which calls AcceptSecuirtyContext() with this token as an input argument and may receive a token which is returned to the initiator so it can call InitializeSecurityContext() again. For a three leg (mutual authentication) security package, the calling sequence is as follows: The client calls the function as above, but the package will return SEC_I_CALLBACK_NEEDED. The client then sends the output token to the server and waits for the server's reply. Upon receipt of the server's response, the client calls this function again, with OldContextHandle set to the handle that was returned from the first call. The token received from the server is supplied in the InputToken parameter. If the server has successfully responded, then the package will respond with success, or it will invalidate the context. Initialization of security context may require more than one call to this function depending upon the underlying authentication mechanism as well as the "choices" indicated via ContextReqFlags. The ContextReqFlags and ContextAttributes are bit masks representing various context level functions viz. delegation, mutual authentication, confidentiality, replay detection and sequence detection. When ISC_REQ_PROMPT_FOR_CREDS flag is set the security package always prompts the user for credentials, irrespective of whether credentials are present or not. If user indicated that the supplied credentials be used then they will be stashed (overwriting existing ones if any) for future use. The security packages will always prompt for credentials if none existed, this optimizes for the most common case before a credentials database is built. But the security packages can be configured to not do that. Security packages will ensure that they only prompt to the interactive user, for other logon sessions, this flag is ignored. When ISC_REQ_USE_SUPPLIED_CREDS flag is set the security package always uses the credentials supplied in the InitializeSecurityContext() call via InputToken parameter. If the package does not have any credentials available it will prompt for them and record it as indicated above. It is an error to set both these flags simultaneously. If the ISC_REQ_ALLOCATE_MEMORY was specified then the caller must free the memory pointed to by OutputToken by calling FreeContextBuffer(). For example, the InputToken may be the challenge from a LAN Manager or NT file server. In this case, the OutputToken would be the NTLM encrypted response to the challenge. The caller of this API can then take the appropriate response (case-sensitive v. case-insensitive) and return it to the server for an authenticated connection. Arguments: CredentialHandle - Handle to the credentials to be used to create the context. OldContextHandle - Handle to the partially formed context, if this is a second call (see above) or NULL if this is the first call. TargetName - String indicating the target of the context. The name will be security package specific. For example it will be a fully qualified Cairo name for Kerberos package and can be UNC name or domain name for the NTLM package. ContextReqFlags - Requirements of the context, package specific. #define ISC_REQ_DELEGATE 0x00000001 #define ISC_REQ_MUTUAL_AUTH 0x00000002 #define ISC_REQ_REPLAY_DETECT 0x00000004 #define ISC_REQ_SEQUENCE_DETECT 0x00000008 #define ISC_REQ_CONFIDENTIALITY 0x00000010 #define ISC_REQ_USE_SESSION_KEY 0x00000020 #define ISC_REQ_PROMT_FOR__CREDS 0x00000040 #define ISC_REQ_USE_SUPPLIED_CREDS 0x00000080 #define ISC_REQ_ALLOCATE_MEMORY 0x00000100 #define ISC_REQ_USE_DCE_STYLE 0x00000200 Reserved1 - Reserved value, MBZ. TargetDataRep - Long indicating the data representation (byte ordering, etc) on the target. The constant SECURITY_NATIVE_DREP may be supplied by the transport indicating that the native format is in use. InputToken - Pointer to the input token. In the first call this token can either be NULL or may contain security package specific information. Reserved2 - Reserved value, MBZ. NewContextHandle - New context handle. If this is a second call, this can be the same as OldContextHandle. OutputToken - Buffer to receive the output token. ContextAttributes -Attributes of the context established. #define ISC_RET_DELEGATE 0x00000001 #define ISC_RET_MUTUAL_AUTH 0x00000002 #define ISC_RET_REPLAY_DETECT 0x00000004 #define ISC_RET_SEQUENCE_DETECT 0x00000008 #define ISC_REP_CONFIDENTIALITY 0x00000010 #define ISC_REP_USE_SESSION_KEY 0x00000020 #define ISC_REP_USED_COLLECTED_CREDS 0x00000040 #define ISC_REP_USED_SUPPLIED_CREDS 0x00000080 #define ISC_REP_ALLOCATED_MEMORY 0x00000100 #define ISC_REP_USED_DCE_STYLE 0x00000200 ExpirationTime - Expiration time of the context. Return Value: STATUS_SUCCESS - Message handled SEC_I_CALLBACK_NEEDED -- Caller should call again later SEC_E_NO_SPM -- Security Support Provider is not running SEC_E_INVALID_TOKEN -- Token improperly formatted SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid SEC_E_BUFFER_TOO_SMALL -- Buffer for output token isn't big enough SEC_E_NO_CREDENTIALS -- There are no credentials for this client SEC_E_INSUFFICIENT_MEMORY -- Not enough memory --*/ { SECURITY_STATUS SecStatus; HANDLE LpcHandle = NULL; BOOLEAN CallLsaDirectly; BOOLEAN FirstCallLsaDirectly = FALSE; PVOID InputTokenBuffer; PULONG InputTokenSize; ULONG LocalInputTokenSize; PVOID OutputTokenBuffer; PULONG OutputTokenSize; ULONG LocalOutputTokenSize; LPWSTR DomainName = NULL; ULONG DomainNameSize = 0; LPWSTR UserName = NULL; ULONG UserNameSize = 0; LPWSTR Password = NULL; ULONG PasswordSize = 0; CtxtHandle TempContextHandle; CtxtHandle OriginalContextHandle; ULONG NegotiateFlags; UCHAR SessionKey[MSV1_0_USER_SESSION_KEY_LENGTH]; WCHAR ContextNames[UNLEN+DNLEN+2]; LUID LogonId = {0,0}; HANDLE ClientTokenHandle = NULL; ContextNames[0] = L'\0'; // // Get an LPC Handle to the NtLmSsp service. // LpcHandle = SspDllGetLpcHandle( FALSE, &CallLsaDirectly ); if ( LpcHandle == NULL ) { SecStatus = SEC_E_NO_SPM; goto Cleanup; } // // Check argument validity // if ( Reserved1 != 0 || Reserved2 != 0 ) { SecStatus = STATUS_INVALID_PARAMETER; goto Cleanup; } #ifdef notdef // ? RPC passes 0x10 or 0 here depending on attitude if ( TargetDataRep != SECURITY_NATIVE_DREP ) { SecStatus = STATUS_INVALID_PARAMETER; goto Cleanup; } #else // notdef UNREFERENCED_PARAMETER( TargetDataRep ); #endif // notdef if ( !SspGetTokenBuffer( InputToken, &InputTokenBuffer, &InputTokenSize, TRUE ) ) { SecStatus = SEC_E_INVALID_TOKEN; goto Cleanup; } if ( InputTokenSize == NULL ) { InputTokenSize = &LocalInputTokenSize; LocalInputTokenSize = 0; } if ( !SspGetTokenBuffer( OutputToken, &OutputTokenBuffer, &OutputTokenSize, FALSE ) ) { SecStatus = SEC_E_INVALID_TOKEN; goto Cleanup; } if (OutputTokenSize == NULL) { OutputTokenSize = &LocalOutputTokenSize; LocalOutputTokenSize = 0; } // // Save the old context handle, in case someone changes it // if ( !ARGUMENT_PRESENT( OldContextHandle ) ) { TempContextHandle.dwUpper = 0; TempContextHandle.dwLower = 0; } else { TempContextHandle = *OldContextHandle; } OriginalContextHandle = TempContextHandle; // // If we can call the LSA directly, // skip the NtLmSsp service. // if ( CallLsaDirectly ) { // // If no previous context was passed in this is the first call. // if ( !ARGUMENT_PRESENT( OldContextHandle ) ) { if ( !ARGUMENT_PRESENT( CredentialHandle ) ) { SecStatus = SEC_E_INVALID_HANDLE; } SecStatus = SsprHandleFirstCall( NULL, // No client connection CredentialHandle, NewContextHandle, ContextReqFlags, *InputTokenSize, InputTokenBuffer, OutputTokenSize, OutputTokenBuffer, ContextAttributes, ExpirationTime, SessionKey, &NegotiateFlags ); TempContextHandle = *NewContextHandle; // // If context was passed in, continue where we left off. // } else { *NewContextHandle = *OldContextHandle; SecStatus = SsprHandleChallengeMessage( NULL, // No client connection CredentialHandle, &TempContextHandle, NULL, // No client token NULL, // No logon ID ContextReqFlags, NULL, // no domain name 0, NULL, // no user name 0, NULL, // no password 0, *InputTokenSize, InputTokenBuffer, OutputTokenSize, OutputTokenBuffer, ContextAttributes, ExpirationTime, SessionKey, &NegotiateFlags, ContextNames ); FirstCallLsaDirectly = TRUE; } if ( SecStatus == SEC_I_CALL_NTLMSSP_SERVICE ) { CallLsaDirectly = FALSE; // // Get data needed for the new context from the old one // if ( ARGUMENT_PRESENT(OldContextHandle) ) { SecStatus = SsprContextGetCredentials( &OriginalContextHandle, &DomainName, &DomainNameSize, &UserName, &UserNameSize, &Password, &PasswordSize, &ClientTokenHandle, &LogonId ); if (!NT_SUCCESS(SecStatus)) { goto Cleanup; } } } } // // Handle LPCing to the NtLmSsp service. // if ( !CallLsaDirectly ) { SSP_API_MESSAGE Message; PSSP_INITIALIZE_SECURITY_CONTEXT_ARGS Args; // // Copy the caller's arguments to the LPC message. // Args = &Message.Arguments.InitializeSecurityContextArgs; if ( !ARGUMENT_PRESENT( CredentialHandle ) ) { Args->CredentialHandle.dwUpper = 0; Args->CredentialHandle.dwLower = 0; } else { Args->CredentialHandle = *CredentialHandle; } Args->ContextHandle = TempContextHandle; Args->ContextReqFlags = ContextReqFlags; Args->InputTokenSize = *InputTokenSize; Args->InputToken = InputTokenBuffer; Args->OutputTokenSize = *OutputTokenSize; Args->OutputToken = OutputTokenBuffer; Args->DomainName = DomainName; Args->DomainNameSize = DomainNameSize; Args->UserName = UserName; Args->UserNameSize = UserNameSize; Args->Password = Password; Args->PasswordSize = PasswordSize; Args->ContextNames = ContextNames; Args->LogonId = LogonId; Args->ClientTokenHandle = ClientTokenHandle; // // Pass the message to the service and wait for a response. // SecStatus = SspCallService( LpcHandle, SspLpcInitializeSecurityContext, &Message, sizeof(*Args) ); // // This has to be returned on both success and failure // *ContextAttributes = Args->ContextAttributes; if ( !NT_SUCCESS(SecStatus) ) { goto Cleanup; } // // Copy the return values to the caller. // TempContextHandle = Args->ContextHandle; *NewContextHandle = Args->ContextHandle; *OutputTokenSize = Args->OutputTokenSize; *ExpirationTime = Args->ExpirationTime; NegotiateFlags = Args->NegotiateFlags; RtlCopyMemory(SessionKey,Args->SessionKey,MSV1_0_USER_SESSION_KEY_LENGTH); if (ARGUMENT_PRESENT(OldContextHandle) && FirstCallLsaDirectly) { // // Update the context in the dll with the context in // the service, and reset the new handle to be what it // is supposed to be // SecStatus = SsprContextUpdateContext( &OriginalContextHandle, &Args->ContextHandle ); *NewContextHandle = OriginalContextHandle; } } // // If the original handle is zero, set it to be the TempContextHandle. // This is for the datagram case, where we map the context after the // first call to initialize. // if ((OriginalContextHandle.dwLower == 0) && (OriginalContextHandle.dwUpper == 0)) { OriginalContextHandle = TempContextHandle; } // // Only map the context if this is the real authentication, not a re-auth // or if this was datagram. // if (((SecStatus == SEC_I_CONTINUE_NEEDED) && ((*ContextAttributes & ISC_RET_DATAGRAM) != 0)) || ((SecStatus == SEC_E_OK) && ((*ContextAttributes & (SSP_RET_REAUTHENTICATION | ISC_RET_DATAGRAM)) == 0))) { SECURITY_STATUS TempStatus; TempStatus = SspMapContext( &OriginalContextHandle, SessionKey, NegotiateFlags, NULL, // no token handle for clients ContextNames, NULL // no password expiry for clients ); if (!NT_SUCCESS(TempStatus)) { SecStatus = TempStatus; } } // // Make sure this bit isn't sent to the caller // *ContextAttributes &= ~SSP_RET_REAUTHENTICATION; Cleanup: if (DomainName != NULL) { LocalFree(DomainName); } if (UserName != NULL) { LocalFree(UserName); } if (Password != NULL) { LocalFree(Password); } return SecStatus; // Ignore TargetName. By convention, it is being passed as \\server\ipc$. // This implementation makes no use of that information. Perhaps, I'll // display it if I prompt for credentials. // UNREFERENCED_PARAMETER( TargetName ); } SECURITY_STATUS SspInitializeSecurityContextA( IN PCredHandle CredentialHandle, IN PCtxtHandle OldContextHandle, IN CHAR * TargetName, IN ULONG ContextReqFlags, IN ULONG Reserved1, IN ULONG TargetDataRep, IN PSecBufferDesc InputToken, IN ULONG Reserved2, OUT PCtxtHandle NewContextHandle, OUT PSecBufferDesc OutputToken, OUT PULONG ContextAttributes, OUT PTimeStamp ExpirationTime ) /*++ Routine Description: Ansi thunk to InitializeSecurityContextU --*/ { LPWSTR TargetNameW; ULONG TargetNameLength = 0; SECURITY_STATUS SecStatus; if (TargetName != NULL) { TargetNameLength = strlen(TargetName); } SecStatus = SspUnicodeStringFromOemString( TargetName, TargetNameLength, &TargetNameW, &TargetNameLength ); if (NT_SUCCESS(SecStatus)) { SecStatus = SspInitializeSecurityContextW( CredentialHandle, OldContextHandle, TargetNameW, ContextReqFlags, Reserved1, TargetDataRep, InputToken, Reserved2, NewContextHandle, OutputToken, ContextAttributes, ExpirationTime ); LocalFree(TargetNameW); } return(SecStatus); } SECURITY_STATUS SspAcceptSecurityContext( IN PCredHandle CredentialHandle, IN PCtxtHandle OldContextHandle, IN PSecBufferDesc InputToken, IN ULONG ContextReqFlags, IN ULONG TargetDataRep, OUT PCtxtHandle NewContextHandle, IN PSecBufferDesc OutputToken, OUT PULONG ContextAttributes, OUT PTimeStamp ExpirationTime ) /*++ Routine Description: Allows a remotely initiated security context between the application and a remote peer to be established. To complete the establishment of context one or more reply tokens may be required from remote peer. This function is the server counterpart to the InitializeSecurityContext API. The ContextAttributes is a bit mask representing various context level functions viz. delegation, mutual authentication, confidentiality, replay detection and sequence detection. This API is used by the server side. When a request comes in, the server uses the ContextReqFlags parameter to specify what it requires of the session. In this fashion, a server can specify that clients must be capable of using a confidential or integrity checked session, and fail clients that can't meet that demand. Alternatively, a server can require nothing, and whatever the client can provide or requires is returned in the pfContextAttributes parameter. For a package that supports 3 leg mutual authentication, the calling sequence would be: Client provides a token, server calls Accept the first time, generating a reply token. The client uses this in a second call to InitializeSecurityContext, and generates a final token. This token is then used in the final call to Accept to complete the session. Another example would be the LAN Manager/NT authentication style. The client connects to negotiate a protocol. The server calls Accept to set up a context and generate a challenge to the client. The client calls InitializeSecurityContext and creates a response. The server then calls Accept the final time to allow the package to verify the response is appropriate for the challenge. Arguments: CredentialHandle - Handle to the credentials to be used to create the context. OldContextHandle - Handle to the partially formed context, if this is a second call (see above) or NULL if this is the first call. InputToken - Pointer to the input token. In the first call this token can either be NULL or may contain security package specific information. ContextReqFlags - Requirements of the context, package specific. #define ASC_REQ_DELEGATE 0x00000001 #define ASC_REQ_MUTUAL_AUTH 0x00000002 #define ASC_REQ_REPLAY_DETECT 0x00000004 #define ASC_REQ_SEQUENCE_DETECT 0x00000008 #define ASC_REQ_CONFIDENTIALITY 0x00000010 #define ASC_REQ_USE_SESSION_KEY 0x00000020 #define ASC_REQ_ALLOCATE_MEMORY 0x00000100 #define ASC_REQ_USE_DCE_STYLE 0x00000200 TargetDataRep - Long indicating the data representation (byte ordering, etc) on the target. The constant SECURITY_NATIVE_DREP may be supplied by the transport indicating that the native format is in use. NewContextHandle - New context handle. If this is a second call, this can be the same as OldContextHandle. OutputToken - Buffer to receive the output token. ContextAttributes -Attributes of the context established. #define ASC_RET_DELEGATE 0x00000001 #define ASC_RET_MUTUAL_AUTH 0x00000002 #define ASC_RET_REPLAY_DETECT 0x00000004 #define ASC_RET_SEQUENCE_DETECT 0x00000008 #define ASC_RET_CONFIDENTIALITY 0x00000010 #define ASC_RET_USE_SESSION_KEY 0x00000020 #define ASC_RET_ALLOCATED_BUFFERS 0x00000100 #define ASC_RET_USED_DCE_STYLE 0x00000200 ExpirationTime - Expiration time of the context. Return Value: STATUS_SUCCESS - Message handled SEC_I_CALLBACK_NEEDED -- Caller should call again later SEC_E_NO_SPM -- Security Support Provider is not running SEC_E_INVALID_TOKEN -- Token improperly formatted SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid SEC_E_BUFFER_TOO_SMALL -- Buffer for output token isn't big enough SEC_E_LOGON_DENIED -- User is no allowed to logon to this server SEC_E_INSUFFICIENT_MEMORY -- Not enough memory --*/ { SECURITY_STATUS SecStatus; HANDLE LpcHandle = NULL; BOOLEAN CallLsaDirectly; PVOID InputTokenBuffer; PULONG InputTokenSize; ULONG LocalInputTokenSize; PVOID OutputTokenBuffer; PULONG OutputTokenSize; ULONG LocalOutputTokenSize; ULONG NegotiateFlags; UCHAR SessionKey[MSV1_0_USER_SESSION_KEY_LENGTH]; HANDLE TokenHandle = NULL; NTSTATUS SubStatus = STATUS_SUCCESS; WCHAR ContextNames[UNLEN+DNLEN+2]; TimeStamp PasswordExpiry; ContextNames[0] = L'\0'; // // Get an LPC Handle to the NtLmSsp service. // LpcHandle = SspDllGetLpcHandle( FALSE, &CallLsaDirectly ); if ( LpcHandle == NULL ) { SecStatus = SEC_E_NO_SPM; goto Cleanup; } // // Validate the arguments // #ifdef notdef // ? RPC passes 0x10 here if ( TargetDataRep != SECURITY_NATIVE_DREP ) { SecStatus = STATUS_INVALID_PARAMETER; goto Cleanup; } #else // notdef UNREFERENCED_PARAMETER( TargetDataRep ); #endif // notdef if ( !SspGetTokenBuffer( InputToken, &InputTokenBuffer, &InputTokenSize, TRUE ) ) { SecStatus = SEC_E_INVALID_TOKEN; goto Cleanup; } if ( InputTokenSize == 0 ) { InputTokenSize = &LocalInputTokenSize; LocalInputTokenSize = 0; } if ( !SspGetTokenBuffer( OutputToken, &OutputTokenBuffer, &OutputTokenSize, FALSE ) ) { SecStatus = SEC_E_INVALID_TOKEN; goto Cleanup; } if ( OutputTokenSize == 0 ) { OutputTokenSize = &LocalOutputTokenSize; LocalOutputTokenSize = 0; } // // If we can call the LSA directly, // skip the NtLmSsp service. // if ( CallLsaDirectly ) { // // If no previous context was passed in this is the first call. // if ( !ARGUMENT_PRESENT( OldContextHandle ) ) { if ( !ARGUMENT_PRESENT( CredentialHandle ) ) { SecStatus = SEC_E_INVALID_HANDLE; } SecStatus = SsprHandleNegotiateMessage( NULL, // No client connection CredentialHandle, NewContextHandle, ContextReqFlags, *InputTokenSize, InputTokenBuffer, OutputTokenSize, OutputTokenBuffer, ContextAttributes, ExpirationTime ); // // If context was passed in, continue where we left off. // } else { *NewContextHandle = *OldContextHandle; SecStatus = SsprHandleAuthenticateMessage( NULL, // No client connection CredentialHandle, NewContextHandle, ContextReqFlags, *InputTokenSize, InputTokenBuffer, OutputTokenSize, OutputTokenBuffer, ContextAttributes, ExpirationTime, SessionKey, &NegotiateFlags, &TokenHandle, &SubStatus, ContextNames, &PasswordExpiry ); } if ( SecStatus == SEC_I_CALL_NTLMSSP_SERVICE ) { CallLsaDirectly = FALSE; } } // // Handle LPCing to the NtLmSsp service. // if ( !CallLsaDirectly ) { SSP_API_MESSAGE Message; PSSP_ACCEPT_SECURITY_CONTEXT_ARGS Args; // // Copy the caller's arguments to the LPC message. // Args = &Message.Arguments.AcceptSecurityContextArgs; if ( !ARGUMENT_PRESENT( CredentialHandle ) ) { Args->CredentialHandle.dwUpper = 0; Args->CredentialHandle.dwLower = 0; } else { Args->CredentialHandle = *CredentialHandle; } if ( !ARGUMENT_PRESENT( OldContextHandle ) ) { Args->ContextHandle.dwUpper = 0; Args->ContextHandle.dwLower = 0; } else { Args->ContextHandle = *OldContextHandle; } Args->ContextReqFlags = ContextReqFlags; Args->InputTokenSize = *InputTokenSize; Args->InputToken = InputTokenBuffer; Args->OutputTokenSize = *OutputTokenSize; Args->OutputToken = OutputTokenBuffer; Args->TokenHandle = NULL; Args->SubStatus = STATUS_SUCCESS; Args->ContextNames = ContextNames; // // Pass the message to the service and wait for a response. // SecStatus = SspCallService( LpcHandle, SspLpcAcceptSecurityContext, &Message, sizeof(*Args) ); // // This has to be copied on both success and failure // *ContextAttributes = Args->ContextAttributes; SubStatus = Args->SubStatus; if ( !NT_SUCCESS(SecStatus) ) { goto Cleanup; } // // Copy the return values to the caller. // *NewContextHandle = Args->ContextHandle; *OutputTokenSize = Args->OutputTokenSize; *ExpirationTime = Args->ExpirationTime; NegotiateFlags = Args->NegotiateFlags; TokenHandle = Args->TokenHandle; RtlCopyMemory(SessionKey,Args->SessionKey, MSV1_0_USER_SESSION_KEY_LENGTH); PasswordExpiry = Args->PasswordExpiry; } if ((SecStatus == SEC_E_OK) && !(*ContextAttributes & SSP_RET_REAUTHENTICATION)) { SecStatus = SspMapContext( NewContextHandle, SessionKey, NegotiateFlags, TokenHandle, ContextNames, &PasswordExpiry ); if (NT_SUCCESS(TokenHandle)) { TokenHandle = NULL; } } else { // // Make sure this bit isn't sent to the caller // *ContextAttributes &= ~SSP_RET_REAUTHENTICATION; } Cleanup: if (TokenHandle != NULL) { NtClose(TokenHandle); } SetLastError(RtlNtStatusToDosError(SubStatus)); return SecStatus; } SECURITY_STATUS SspImpersonateSecurityContext ( PCtxtHandle ContextHandle ) /*++ Routine Description: This API is allows service providers to impersonate the caller. This API allows the application server to act as the client and thus all necessary access controls are enforced. The server must have obtained a valid context handle by submitting to security system the incoming security token from the client via AcceptSecurityContext() API. The server winds up with a context handle if the inbound context was validated successfully. The API creates an impersonation token and allows the thread or process to run with the impersonation context. The application server must call RevertSecurityContext() when it is done or wants to restore its own security context. Arguments: ContextHandle - Handle to the context to impersonate. This handle must have been obtained in the AcceptSecurityContext() call. Return Value: STATUS_SUCCESS - Call completed successfully SEC_E_NO_SPM -- Security Support Provider is not running SEC_E_INVALID_HANDLE -- Context Handle is invalid --*/ { return(SsprImpersonateSecurityContext( ContextHandle )); } SECURITY_STATUS SspRevertSecurityContext ( PCtxtHandle ContextHandle ) /*++ Routine Description: This API is called by the service provider or an application server when it wishes to stop impersonating the caller. The server must have used this handle in the ImpersonateSecurityContext() API. Arguments: ContextHandle - Handle to the context to query. This handle must have been obtained in the AcceptSecurityContext() call and used in the ImpersonateSecurityContext() call. Return Value: STATUS_SUCCESS - Call completed successfully SEC_E_NO_SPM -- Security Support Provider is not running SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid SEC_E_UNSUPPORTED_FUNCTION -- Function code is not supported --*/ { return( SsprRevertSecurityContext( ContextHandle )); } SECURITY_STATUS SspQueryContextAttributesW( IN PCtxtHandle ContextHandle, IN ULONG Attribute, OUT PVOID Buffer ) /*++ Routine Description: This API allows a customer of the security services to determine certain attributes of the context. These are: sizes, names, and lifespan. Arguments: ContextHandle - Handle to the context to query. Attribute - Attribute to query. #define SECPKG_ATTR_SIZES 0 #define SECPKG_ATTR_NAMES 1 #define SECPKG_ATTR_LIFESPAN 2 Buffer - Buffer to copy the data into. The buffer must be large enough to fit the queried attribute. Return Value: STATUS_SUCCESS - Call completed successfully SEC_E_NO_SPM -- Security Support Provider is not running SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid SEC_E_UNSUPPORTED_FUNCTION -- Function code is not supported --*/ { SECURITY_STATUS SecStatus; HANDLE LpcHandle = NULL; BOOLEAN CallLsaDirectly; CtxtHandle TempContextHandle = *ContextHandle; PSecPkgContext_NamesW Names = NULL; PSecPkgContext_DceInfo DceInfo = NULL; // // Get an LPC Handle to the NtLmSsp service. // LpcHandle = SspDllGetLpcHandle( FALSE, &CallLsaDirectly ); if ( LpcHandle == NULL ) { SecStatus = SEC_E_NO_SPM; goto Cleanup; } // // If caller wants names, we have to allocate the buffer now since the // service can't allocate memory with LocalAlloc. // if (Attribute == SECPKG_ATTR_NAMES) { Names = (PSecPkgContext_NamesW) Buffer; Names->sUserName = (PWCHAR) LocalAlloc(0, (UNLEN+1) * sizeof(WCHAR)); if (Names->sUserName == NULL) { SecStatus = SEC_E_INSUFFICIENT_MEMORY; goto Cleanup; } } // // If caller wants dce info, we have to allocate the buffer now since the // service can't allocate memory with LocalAlloc. // if (Attribute == SECPKG_ATTR_DCE_INFO) { DceInfo = (PSecPkgContext_DceInfo) Buffer; DceInfo->pPac = LocalAlloc(0, (UNLEN+1) * sizeof(WCHAR)); if (DceInfo->pPac == NULL) { SecStatus = SEC_E_INSUFFICIENT_MEMORY; goto Cleanup; } } // // First try the local contexts // SecStatus = SspLocalQueryContextAttributes( ContextHandle, Attribute, Buffer ); if (SecStatus != SEC_E_INVALID_HANDLE) { // // If the context did not exist try the main list ofcontexts. This // handles querying attributes before a context is finalized. // goto Cleanup; } // // If we can call the LSA directly, // skip the NtLmSsp service. // if ( CallLsaDirectly ) { SecStatus = SsprQueryContextAttributes( NULL, // No client connection &TempContextHandle, Attribute, Buffer ); if ( SecStatus == SEC_I_CALL_NTLMSSP_SERVICE ) { CallLsaDirectly = FALSE; } } // // Handle LPCing to the NtLmSsp service. // if ( !CallLsaDirectly ) { SSP_API_MESSAGE Message; PSSP_QUERY_CONTEXT_ATTRIBUTES_ARGS Args; // // Copy the caller's arguments to the LPC message. // Args = &Message.Arguments.QueryContextAttributesArgs; Args->ContextHandle = TempContextHandle; Args->Attribute = Attribute; Args->Buffer = Buffer; // // Pass the message to the service and wait for a response. // SecStatus = SspCallService( LpcHandle, SspLpcQueryContextAttributes, &Message, sizeof(*Args) ); if ( !NT_SUCCESS(SecStatus) ) { goto Cleanup; } // // Copy the return values to the caller. // /* None */ } Cleanup: if (!NT_SUCCESS(SecStatus)) { if ((Attribute == SECPKG_ATTR_NAMES) && (Names != NULL) && (Names->sUserName != NULL)) { LocalFree(Names->sUserName); } else if ((Attribute == SECPKG_ATTR_DCE_INFO) && (DceInfo != NULL) && (DceInfo->pPac != NULL)) { LocalFree(DceInfo->pPac); } } return SecStatus; } SECURITY_STATUS SspQueryContextAttributesA( IN PCtxtHandle ContextHandle, IN ULONG Attribute, OUT PVOID Buffer ) /*++ RoutineDescription: Ansi thunk to QueryContextAttributesU. --*/ { SECURITY_STATUS SecStatus; SecStatus = SspQueryContextAttributesW( ContextHandle, Attribute, Buffer ); // // If that succeeded and the attribute was NAMES, convert from unicode // to ansi // if (NT_SUCCESS(SecStatus) && (Attribute == SECPKG_ATTR_NAMES)) { CHAR Name[UNLEN]; PSecPkgContext_NamesW ContextNames = (PSecPkgContext_NamesW) Buffer; // // Convert into the buffer and then copy over the unicode name // wcstombs(Name, ContextNames->sUserName, UNLEN); strcpy((CHAR *) ContextNames->sUserName, Name); } return(SecStatus); } SECURITY_STATUS SspDeleteSecurityContext ( PCtxtHandle ContextHandle ) /*++ Routine Description: Deletes the local data structures associated with the specified security context. This API terminates a context on the local machine. Arguments: ContextHandle - Handle to the context to delete Return Value: STATUS_SUCCESS - Call completed successfully SEC_E_NO_SPM -- Security Support Provider is not running SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid --*/ { SECURITY_STATUS SecStatus; HANDLE LpcHandle = NULL; BOOLEAN CallLsaDirectly; CtxtHandle TempContextHandle = *ContextHandle; // // Get an LPC Handle to the NtLmSsp service. // LpcHandle = SspDllGetLpcHandle( FALSE, &CallLsaDirectly ); if ( LpcHandle == NULL ) { SecStatus = SEC_E_NO_SPM; goto Cleanup; } // // Delete any local context // SspHandleLocalDelete(ContextHandle); // // If we can call the LSA directly, // skip the NtLmSsp service. // if ( CallLsaDirectly ) { SecStatus = SsprDeleteSecurityContext( NULL, // No client connection &TempContextHandle ); if ( SecStatus == SEC_I_CALL_NTLMSSP_SERVICE ) { CallLsaDirectly = FALSE; } } // // Handle LPCing to the NtLmSsp service. // if ( !CallLsaDirectly ) { SSP_API_MESSAGE Message; PSSP_DELETE_SECURITY_CONTEXT_ARGS Args; // // Copy the caller's arguments to the LPC message. // Args = &Message.Arguments.DeleteSecurityContextArgs; Args->ContextHandle = TempContextHandle; // // Pass the message to the service and wait for a response. // SecStatus = SspCallService( LpcHandle, SspLpcDeleteSecurityContext, &Message, sizeof(*Args) ); if ( !NT_SUCCESS(SecStatus) ) { goto Cleanup; } } Cleanup: return SecStatus; } SECURITY_STATUS NtLmSspControl( IN ULONG FunctionCode, IN ULONG Data ) /*++ Routine Description: This API allows the NtLmSsp service to be controlled in a test environment. Arguments: FunctionCode - Specifies what function is to be performed. Data - Specifies data specific to the function Return Value: STATUS_SUCCESS - Call completed successfully SEC_E_NO_SPM -- Security Support Provider is not running SEC_E_UNSUPPORTED_FUNCTION -- Function code is not supported --*/ { #if DBG SECURITY_STATUS SecStatus; HANDLE LpcHandle = NULL; BOOLEAN CallLsaDirectly; SSP_API_MESSAGE Message; PSSP_NTLMSSP_CONTROL_ARGS Args; // // Get an LPC Handle to the NtLmSsp service. // LpcHandle = SspDllGetLpcHandle( FALSE, &CallLsaDirectly ); if ( LpcHandle == NULL ) { SecStatus = SEC_E_NO_SPM; goto Cleanup; } // // Copy the caller's arguments to the LPC message. // Args = &Message.Arguments.NtLmSspControlArgs; Args->FunctionCode = FunctionCode; Args->Data = Data; // // Pass the message to the service and wait for a response. // SecStatus = SspCallService( LpcHandle, SspLpcNtLmSspControl, &Message, sizeof(*Args) ); if ( !NT_SUCCESS(SecStatus) ) { goto Cleanup; } // // Copy the return values to the caller. // /* NONE */ Cleanup: return SecStatus; #else // DBG return SEC_E_UNSUPPORTED_FUNCTION; UNREFERENCED_PARAMETER( Data ); UNREFERENCED_PARAMETER( FunctionCode ); #endif // DBG } SECURITY_STATUS SEC_ENTRY SspFreeContextBuffer ( void __SEC_FAR * ContextBuffer ) /*++ Routine Description: This API is provided to allow callers of security API such as InitializeSecurityContext() for free the memory buffer allocated for returning the outbound context token. Arguments: ContextBuffer - Address of the buffer to be freed. Return Value: STATUS_SUCCESS - Call completed successfully --*/ { // // The only allocated buffer that NtLmSsp currently returns to the caller // is from EnumeratePackages. It uses LocalAlloc to allocate memory. If // we ever need memory to be allocated by the service, we have to rethink // how this routine distinguishes between to two types of allocated memory. // (VOID) LocalFree( ContextBuffer ); return STATUS_SUCCESS; } // // The functions below are merely stub functions to ensure our function table // doesn't have any NULL values. They simply return SEC_E_UNSUPPORTED_FUNCTION // so no additional documentation is required. // SECURITY_STATUS SspApplyControlToken ( PCtxtHandle ContextHandle, PSecBufferDesc Input ) { #if DBG SspPrint(( SSP_API, "ApplyContextToken Called\n" )); #endif // DBG return SEC_E_UNSUPPORTED_FUNCTION; UNREFERENCED_PARAMETER( ContextHandle ); UNREFERENCED_PARAMETER( Input ); } SECURITY_STATUS SspMakeSignature ( PCtxtHandle ContextHandle, unsigned long QualityOfProtection, PSecBufferDesc Message, unsigned long SequenceNumber ) { return(SspHandleSignMessage(ContextHandle, QualityOfProtection, Message, SequenceNumber)); } SECURITY_STATUS SspVerifySignature ( PCtxtHandle ContextHandle, PSecBufferDesc Message, unsigned long SequenceNumber, unsigned long * QualityOfProtection ) { return (SspHandleVerifyMessage(ContextHandle, Message, SequenceNumber, QualityOfProtection)); } SECURITY_STATUS SspSealMessage ( PCtxtHandle ContextHandle, unsigned long QualityOfProtection, PSecBufferDesc Message, unsigned long SequenceNumber ) { return(SspHandleSealMessage(ContextHandle, QualityOfProtection, Message, SequenceNumber)); } SECURITY_STATUS SspUnsealMessage ( PCtxtHandle ContextHandle, PSecBufferDesc Message, unsigned long SequenceNumber, unsigned long * QualityOfProtection ) { return (SspHandleUnsealMessage(ContextHandle, Message, SequenceNumber, QualityOfProtection)); } SECURITY_STATUS SEC_ENTRY SspCompleteAuthToken ( PCtxtHandle ContextHandle, PSecBufferDesc BufferDescriptor ) { #if DBG SspPrint(( SSP_API, "CompleteAuthToken Called\n" )); #endif // DBG return SEC_E_UNSUPPORTED_FUNCTION; UNREFERENCED_PARAMETER( ContextHandle ); UNREFERENCED_PARAMETER( BufferDescriptor ); }