/// <summary> /// Create a new instance of the interop and allow this instance to behave as SYSTEM. /// Note that this call requires the TrustedComputingBase privilege to execute. /// </summary> /// <param name="name">The optional logical name of the process as understood by LSA. Otherwise uses the default "KerberosNet".</param> /// <param name="package">The name of the LSA authentication package that will be interacted with.</param> /// <returns>Returns an instance of the <see cref="LsaInterop"/> class.</returns> public static LsaInterop RegisterLogonProcess(string name = null, string package = KerberosPackageName) { string processNameStr; if (string.IsNullOrWhiteSpace(name)) { processNameStr = ProcessName; } else { processNameStr = name; } if (string.IsNullOrWhiteSpace(package)) { package = KerberosPackageName; } var processName = new LSA_STRING { Buffer = processNameStr, Length = (ushort)processNameStr.Length, MaximumLength = (ushort)processNameStr.Length }; var result = LsaRegisterLogonProcess(ref processName, out LsaSafeHandle lsaHandle, out ulong securityMode); LsaThrowIfError(result); return(new LsaInterop(lsaHandle, package)); }
public LsaStringWrapper(string value) { _string = new LSA_STRING(); _string.Length = (ushort)value.Length; _string.MaximumLength = (ushort)value.Length; _string.Buffer = Marshal.StringToHGlobalAnsi(value); }
/* * Windows creates a new ticket cache for primary NT tokens. This allows callers to create a dedicated cache for whatever they're doing * that way the cache operations like purge or import don't polute the current users cache. * * To make this work we need to create a new NT token, which is only done during logon. We don't actually want Windows to validate the credentials * so we tell it to treat the logon as `NewCredentials` which means Windows will just use those credentials as SSO credentials only. * * From there a new cache is created and any operations against the "current cache" such as SSPI ISC calls will hit this new cache. * We then let callers import tickets into that cache using the krb-cred structure. * * When done the call to dispose will * 1. Revert the impersonation context * 2. Close the NT token handle * 3. Close the Lsa Handle * * This destroys the cache and closes the logon session. * * For any operation that require native allocation and PtrToStructure copies we try and use the CryptoPool mechanism, which checks out a shared * pool of memory to create a working for the current operation. On dispose it zeros the memory and returns it to the pool. */ private LsaInterop(LsaSafeHandle lsaHandle, string packageName = KerberosPackageName) { this.lsaHandle = lsaHandle; var kerberosPackageName = new LSA_STRING { Buffer = packageName, Length = (ushort)packageName.Length, MaximumLength = (ushort)packageName.Length }; var result = LsaLookupAuthenticationPackage(this.lsaHandle, ref kerberosPackageName, out this.selectedAuthPackage); LsaThrowIfError(result); var negotiatePackageName = new LSA_STRING { Buffer = NegotiatePackageName, Length = (ushort)NegotiatePackageName.Length, MaximumLength = (ushort)NegotiatePackageName.Length }; result = LsaLookupAuthenticationPackage(this.lsaHandle, ref negotiatePackageName, out this.negotiateAuthPackage); LsaThrowIfError(result); }
public void LsaLookupAuthenticationPackage_Invalid_Handle() { var lsaString = new LSA_STRING("Negotiate"); var lsaStatus = LsaLookupAuthenticationPackage(IntPtr.Zero, ref lsaString, out var authenticationPackage); var winErrorCode = LsaNtStatusToWinError(lsaStatus); var win32Exception = new Win32Exception((int)winErrorCode); Assert.AreEqual(win32Exception.Message, "An unexpected network error occurred."); }
public static void CreateNewSession() { var kli = new KERB_INTERACTIVE_LOGON { MessageType = KERB_LOGON_SUBMIT_TYPE.KerbInteractiveLogon, UserName = "", Password = "" }; IntPtr pluid; IntPtr lsaHan; uint authPackID; IntPtr kerbLogInfo; var logonProc = new LSA_STRING { Buffer = Marshal.StringToHGlobalAuto("InstaLogon"), Length = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("InstaLogon")), MaximumLength = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("InstaLogon")) }; var originName = new LSA_STRING { Buffer = Marshal.StringToHGlobalAuto("InstaLogon"), Length = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("InstaLogon")), MaximumLength = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("InstaLogon")) }; var authPackage = new LSA_STRING { Buffer = Marshal.StringToHGlobalAuto("MICROSOFT_KERBEROS_NAME_A"), Length = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("MICROSOFT_KERBEROS_NAME_A")), MaximumLength = (ushort)Marshal.SizeOf(Marshal.StringToHGlobalAuto("MICROSOFT_KERBEROS_NAME_A")) }; var hLogonProc = Marshal.AllocHGlobal(Marshal.SizeOf(logonProc)); Marshal.StructureToPtr(logonProc, hLogonProc, false); ADVAPI32.AllocateLocallyUniqueId(out pluid); LsaConnectUntrusted(out lsaHan); //SECUR32.LsaRegisterLogonProcess(hLogonProc, out lsaHan, out secMode); LsaLookupAuthenticationPackage(lsaHan, ref authPackage, out authPackID); kerbLogInfo = Marshal.AllocHGlobal(Marshal.SizeOf(kli)); Marshal.StructureToPtr(kli, kerbLogInfo, false); var ts = new TOKEN_SOURCE("Insta"); IntPtr profBuf; uint profBufLen; long logonID; IntPtr logonToken; QUOTA_LIMITS quotas; WinStatusCodes subStatus; LsaLogonUser(lsaHan, ref originName, SecurityLogonType.Interactive, authPackID, kerbLogInfo, (uint)Marshal.SizeOf(kerbLogInfo), IntPtr.Zero, ref ts, out profBuf, out profBufLen, out logonID, out logonToken, out quotas, out subStatus); }
public void LsaLookupAuthenticationPackage_MSV1_0_Success() { var connectStatus = LsaConnectUntrusted(out var lsaHande); var lsaString = new LSA_STRING("MICROSOFT_AUTHENTICATION_PACKAGE_V1_0"); var lsaStatus = LsaLookupAuthenticationPackage(lsaHande, ref lsaString, out var authenticationPackage); var winErrorCode = LsaNtStatusToWinError(lsaStatus); var win32Exception = new Win32Exception((int)winErrorCode); Assert.AreEqual(win32Exception.Message, "The operation completed successfully."); }
public void LsaLookupAuthenticationPackage_Negotiate_Success() { var connectStatus = LsaConnectUntrusted(out var lsaHande); var lsaString = new LSA_STRING("Negotiate"); var lsaStatus = LsaLookupAuthenticationPackage(lsaHande, ref lsaString, out var authenticationPackage); var winErrorCode = LsaNtStatusToWinError(lsaStatus); var win32Exception = new Win32Exception((int)winErrorCode); Assert.AreEqual(win32Exception.Message, "The operation completed successfully."); }
public void LsaLookupAuthenticationPackage_Packages_Unknown() { var connectStatus = LsaConnectUntrusted(out var lsaHande); var lsaString = new LSA_STRING("unknown"); var lsaStatus = LsaLookupAuthenticationPackage(lsaHande, ref lsaString, out var authenticationPackage); var winErrorCode = LsaNtStatusToWinError(lsaStatus); var win32Exception = new Win32Exception((int)winErrorCode); Assert.AreEqual(win32Exception.Message, "A specified authentication package is unknown."); }
public IntPtr MarshalManagedToNative(object ManagedObj) { var s = ManagedObj as string; if (s == null) { return(IntPtr.Zero); } var str = new LSA_STRING(s); return(str.StructureToPtr(Marshal.AllocCoTaskMem, out int _)); }
internal static extern int LsaLogonUser( [In] SafeLsaHandle LsaHandle, [In] ref LSA_STRING OriginName, [In] SECURITY_LOGON_TYPE LogonType, [In] int AuthenticationPackage, [In] IntPtr AuthenticationInformation, [In] int AuthenticationInformationLength, [In] IntPtr LocalGroups, [In] ref TOKEN_SOURCE SourceContext, [Out] out SafeLsaReturnBufferHandle ProfileBuffer, [Out] out int ProfileBufferLength, [Out] out LUID LogonId, [Out] out SafeAccessTokenHandle Token, [Out] out QUOTA_LIMITS Quotas, [Out] out int SubStatus );
public static extern WinStatusCodes LsaLogonUser( [In] IntPtr LsaHandle, [In] ref LSA_STRING OriginName, [In] SecurityLogonType LogonType, [In] UInt32 AuthenticationPackage, [In] IntPtr AuthenticationInformation, [In] UInt32 AuthenticationInformationLength, [In] /*PTOKEN_GROUPS*/ IntPtr LocalGroups, [In] ref TOKEN_SOURCE SourceContext, [Out] /*PVOID*/ out IntPtr ProfileBuffer, [Out] out UInt32 ProfileBufferLength, [Out] out Int64 LogonId, [Out] out IntPtr Token, [Out] out QUOTA_LIMITS Quotas, [Out] out WinStatusCodes SubStatus );
public static extern /*WinStatusCodes */ uint LsaLogonUser( [In] IntPtr LsaHandle, [In] ref LSA_STRING OriginName, [In] SECURITY_LOGON_TYPE LogonType, [In] UInt32 AuthenticationPackage, [In] IntPtr AuthenticationInformation, [In] UInt32 AuthenticationInformationLength, [In] PTOKEN_GROUPS LocalGroups, [In] ref TOKEN_SOURCE SourceContext, [Out] /*PVOID*/ out IntPtr ProfileBuffer, [Out] out UInt32 ProfileBufferLength, [Out] out Int64 LogonId, [Out] out IntPtr Token, [Out] out QUOTA_LIMITS Quotas, [Out] out /*WinStatusCodes */ uint SubStatus );
public static extern int LsaLogonUser( LsaSafeHandle LsaHandle, ref LSA_STRING OriginName, SECURITY_LOGON_TYPE LogonType, int AuthenticationPackage, void *AuthenticationInformation, int AuthenticationInformationLength, IntPtr LocalGroups, ref TOKEN_SOURCE SourceContext, out LsaBufferSafeHandle ProfileBuffer, ref int ProfileBufferLength, out LUID LogonId, out LsaTokenSafeHandle Token, out IntPtr Quotas, out int SubStatus );
public void LsaLogonUser_Success() { var connectStatus = LsaConnectUntrusted(out var lsaHande); var lsaString = new LSA_STRING("Kerberos"); var lsaStatus = LsaLookupAuthenticationPackage(lsaHande, ref lsaString, out var authenticationPackage); AllocateLocallyUniqueId(out var srcLuid); var tokenSource = new TOKEN_SOURCE { SourceName = "foobar12".ToCharArray(), SourceIdentifier = srcLuid }; var lsaOriginName = new LSA_STRING("Kerberos"); var kerb = new KERB_INTERACTIVE_LOGON() { MessageType = KERB_LOGON_SUBMIT_TYPE.KerbInteractiveLogon, LogonDomainName = new UNICODE_STRING("eu"), UserName = new UNICODE_STRING("martijn"), Password = new UNICODE_STRING("Unisys!1") }; IntPtr info = (IntPtr)1024;// Marshal.SizeOf(kerb); Marshal.StructureToPtr(kerb, info, false); PTOKEN_GROUPS groups = new PTOKEN_GROUPS() { GroupCount = 0 }; IntPtr profileBuffer = IntPtr.Zero; UInt32 profileBufferLength = 0; Int64 logonId; IntPtr token = IntPtr.Zero; UInt32 subStatus; QUOTA_LIMITS quotas; var logon = LsaLogonUser(lsaHande, ref lsaOriginName, SECURITY_LOGON_TYPE.Interactive, authenticationPackage, info, 1024, groups, ref tokenSource, out profileBuffer, out profileBufferLength, out logonId, out token, out quotas, out subStatus); }
/// <summary> /// Create a "NewCredentials" logon session for the current LSA Handle. This does not authenticate the user /// and only uses the credentials provided for outbound calls similar to the /netonly flag for runas.exe. /// /// Note: this will call <see cref="ImpersonateLoggedOnUser(LsaTokenSafeHandle)" /> and set the current /// thread's primary token to the generated NT Token. /// </summary> /// <param name="username">The username to be used. Note leaving this null will use the default value "user". /// Passing an empty string will cause LSA to treat this as an anonymous user.</param> /// <param name="password">The password to be used by LSA for any future outbound ticket requests not already cached.</param> /// <param name="realm">The default realm to be used by LSA for the any outbound ticket requests not already cached.</param> public unsafe void LogonUser(string username = null, string password = null, string realm = null) { if (username == null) { username = DefaultUserName; } if (password == null) { password = string.Empty; } if (realm == null) { realm = string.Empty; } var originName = new LSA_STRING { Buffer = ProcessName, Length = (ushort)(ProcessName.Length * 2), MaximumLength = (ushort)(ProcessName.Length * 2) }; var bufferSize = Marshal.SizeOf(typeof(KERB_INTERACTIVE_LOGON)) + (realm.Length * 2) + (username.Length * 2) + (password.Length * 2); if (this.impersonationContext != null) { this.impersonationContext.Dispose(); this.impersonationContext = null; } LsaBufferSafeHandle profileBuffer = null; WithFixedBuffer(bufferSize, (p, _) => { try { var pLogon = (KERB_INTERACTIVE_LOGON *)p; pLogon->MessageType = KERB_LOGON_SUBMIT_TYPE.KerbInteractiveLogon; int offset = Marshal.SizeOf(typeof(KERB_INTERACTIVE_LOGON)); SetString(realm, pLogon, ref pLogon->LogonDomainName, ref offset); SetString(username, pLogon, ref pLogon->UserName, ref offset); SetString(password, pLogon, ref pLogon->Password, ref offset); var tokenSource = new TOKEN_SOURCE() { SourceName = Encoding.UTF8.GetBytes("kerb.net") }; int profileLength = 0; int result = LsaLogonUser( this.lsaHandle, ref originName, SECURITY_LOGON_TYPE.NewCredentials, this.negotiateAuthPackage, pLogon, bufferSize, IntPtr.Zero, ref tokenSource, out profileBuffer, ref profileLength, out this.luid, out this.impersonationContext, out IntPtr pQuotas, out int subStatus ); LsaThrowIfError(result); }
public static extern int LsaLookupAuthenticationPackage( LsaSafeHandle LsaHandle, ref LSA_STRING PackageName, out int AuthenticationPackage );
public static extern int LsaRegisterLogonProcess( ref LSA_STRING LogonProcessName, out LsaSafeHandle LsaHandle, out ulong SecurityMode );
public static extern uint LsaLookupAuthenticationPackage([In] IntPtr lsaHandle, [In] ref LSA_STRING packageName, [Out] out UInt32 authenticationPackage);
public LsaStringWrapper(string value) { _string = new LSA_STRING(); _string.Length = (ushort) value.Length; _string.MaximumLength = (ushort) value.Length; _string.Buffer = Marshal.StringToHGlobalAnsi(value); }
internal static extern int LsaLookupAuthenticationPackage(SafeLsaHandle LsaHandle, [In] ref LSA_STRING PackageName, out int AuthenticationPackage);
public static extern WinStatusCodes LsaLookupAuthenticationPackage([In] IntPtr LsaHandle, [In] ref LSA_STRING PackageName, [Out] out UInt32 AuthenticationPackage);
/// <summary> /// Create a "NewCredentials" logon session for the current LSA Handle. This does not authenticate the user /// and only uses the credentials provided for outbound calls similar to the /netonly flag for runas.exe. /// /// Note: this will call <see cref="ImpersonateLoggedOnUser(LsaTokenSafeHandle)" /> and set the current /// thread's primary token to the generated NT Token. /// </summary> /// <param name="username">The username to be used. Note leaving this null will use the default value "user". /// Passing an empty string will cause LSA to treat this as an anonymous user.</param> /// <param name="password">The password to be used by LSA for any future outbound ticket requests not already cached.</param> /// <param name="realm">The default realm to be used by LSA for the any outbound ticket requests not already cached.</param> public unsafe void LogonUser(string username = null, string password = null, string realm = null) { if (username == null) { username = DefaultUserName; } if (password == null) { password = string.Empty; } if (realm == null) { realm = string.Empty; } var originName = new LSA_STRING { Buffer = ProcessName, Length = (ushort)(ProcessName.Length * 2), MaximumLength = (ushort)(ProcessName.Length * 2) }; var bufferSize = Marshal.SizeOf(typeof(KERB_INTERACTIVE_LOGON)) + (realm.Length * 2) + (username.Length * 2) + (password.Length * 2); if (this.impersonationContext != null) { this.impersonationContext.Dispose(); this.impersonationContext = null; } LsaBufferSafeHandle profileBuffer = null; using (var pool = CryptoPool.Rent <byte>(bufferSize)) { var buffer = pool.Memory.Slice(0, bufferSize); try { fixed(byte *pBuffer = &MemoryMarshal.GetReference(buffer.Span)) { KERB_INTERACTIVE_LOGON *pLogon = (KERB_INTERACTIVE_LOGON *)pBuffer; pLogon->MessageType = KERB_LOGON_SUBMIT_TYPE.KerbInteractiveLogon; int offset = Marshal.SizeOf(typeof(KERB_INTERACTIVE_LOGON)); SetString(realm, (IntPtr)pLogon, ref pLogon->LogonDomainName, ref offset); SetString(username, (IntPtr)pLogon, ref pLogon->UserName, ref offset); SetString(password, (IntPtr)pLogon, ref pLogon->Password, ref offset); var tokenSource = new TOKEN_SOURCE() { SourceName = Encoding.UTF8.GetBytes("kerb.net") }; int profileLength = 0; int result = LsaLogonUser( this.lsaHandle, ref originName, SECURITY_LOGON_TYPE.NewCredentials, this.negotiateAuthPackage, pLogon, bufferSize, IntPtr.Zero, ref tokenSource, out profileBuffer, ref profileLength, out this.luid, out this.impersonationContext, out IntPtr pQuotas, out int subStatus ); LsaThrowIfError(result); } } finally { profileBuffer?.Dispose(); } } // this call to impersonate will set the current thread token to be the token out of LsaLogonUser // do we need to do anything special if this gets used within an async context? this.impersonationContext.Impersonate(); }