/// <summary> /// Queries a string-valued context attribute by the named attribute. /// </summary> /// <param name="attrib">The string-valued attribute to query.</param> /// <returns></returns> private string QueryContextString(CContextQueryAttrib attrib) { CSecPkgContext_String stringAttrib; CSecurityStatus status = CSecurityStatus.InternalError; string result = null; bool gotRef = false; if (attrib != CContextQueryAttrib.Names && attrib != CContextQueryAttrib.Authority) { throw new InvalidOperationException("QueryContextString can only be used to query context Name and Authority attributes"); } stringAttrib = new CSecPkgContext_String(); RuntimeHelpers.PrepareConstrainedRegions(); try { this.ContextHandle.DangerousAddRef(ref gotRef); } catch (Exception) { if (gotRef) { this.ContextHandle.DangerousRelease(); gotRef = false; } throw; } finally { if (gotRef) { status = CContextNativeMethods.QueryContextAttributes_String( ref this.ContextHandle.rawHandle, attrib, ref stringAttrib ); this.ContextHandle.DangerousRelease(); if (status == CSecurityStatus.OK) { result = Marshal.PtrToStringUni(stringAttrib.StringResult); CContextNativeMethods.FreeContextBuffer(stringAttrib.StringResult); } } } if (status == CSecurityStatus.Unsupported) { return(null); } else if (status != CSecurityStatus.OK) { throw new CSSPIException("Failed to query the context's associated user name", status); } return(result); }
/// <summary> /// Returns a list of all known security package providers and their properties. /// </summary> /// <returns></returns> public static CSecPkgInfo[] EnumeratePackages() { CSecurityStatus status = CSecurityStatus.InternalError; CSecPkgInfo[] packages = null; IntPtr pkgArrayPtr; IntPtr pkgPtr; int numPackages = 0; int pkgSize = Marshal.SizeOf(typeof(CSecPkgInfo)); pkgArrayPtr = new IntPtr(); RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { status = CNativeMethods.EnumerateSecurityPackages(ref numPackages, ref pkgArrayPtr); if (pkgArrayPtr != IntPtr.Zero) { try { if (status == CSecurityStatus.OK) { // Bwooop Bwooop Alocation Alert // 1) We allocate the array // 2) We allocate the individual elements in the array (they're class objects). // 3) We allocate the strings in the individual elements in the array when we // call Marshal.PtrToStructure() packages = new CSecPkgInfo[numPackages]; for (int i = 0; i < numPackages; i++) { packages[i] = new CSecPkgInfo(); } for (int i = 0; i < numPackages; i++) { pkgPtr = IntPtr.Add(pkgArrayPtr, i * pkgSize); Marshal.PtrToStructure(pkgPtr, packages[i]); } } } finally { CNativeMethods.FreeContextBuffer(pkgArrayPtr); } } } if (status != CSecurityStatus.OK) { throw new CSSPIException("Failed to enumerate security package providers", status); } return(packages); }
/// <summary> /// Signs the message using the context's session key. /// </summary> /// <remarks> /// The structure of the returned buffer is as follows: /// - 4 bytes, unsigned big-endian integer indicating the length of the plaintext message /// - 2 bytes, unsigned big-endian integer indicating the length of the signture /// - The plaintext message /// - The message's signature. /// </remarks> /// <param name="message"></param> /// <returns></returns> public byte[] MakeSignature(byte[] message) { CSecurityStatus status = CSecurityStatus.InternalError; CSecPkgContext_Sizes sizes; CSecureBuffer dataBuffer; CSecureBuffer signatureBuffer; CSecureBufferAdapter adapter; CheckLifecycle(); sizes = QueryBufferSizes(); dataBuffer = new CSecureBuffer(new byte[message.Length], CBufferType.Data); signatureBuffer = new CSecureBuffer(new byte[sizes.MaxSignature], CBufferType.Token); Array.Copy(message, dataBuffer.Buffer, message.Length); using (adapter = new CSecureBufferAdapter(new[] { dataBuffer, signatureBuffer })) { status = CContextNativeMethods.SafeMakeSignature( this.ContextHandle, 0, adapter, 0 ); } if (status != CSecurityStatus.OK) { throw new CSSPIException("Failed to create message signature.", status); } byte[] outMessage; int position = 0; // Enough room for // - original message length (4 bytes) // - signature length (2 bytes) // - original message // - signature outMessage = new byte[4 + 2 + dataBuffer.Length + signatureBuffer.Length]; CByteWriter.WriteInt32_BE(dataBuffer.Length, outMessage, position); position += 4; CByteWriter.WriteInt16_BE((Int16)signatureBuffer.Length, outMessage, position); position += 2; Array.Copy(dataBuffer.Buffer, 0, outMessage, position, dataBuffer.Length); position += dataBuffer.Length; Array.Copy(signatureBuffer.Buffer, 0, outMessage, position, signatureBuffer.Length); position += signatureBuffer.Length; return(outMessage); }
protected override bool ReleaseHandle() { CSecurityStatus status = CContextNativeMethods.DeleteSecurityContext( ref base.rawHandle ); base.ReleaseHandle(); return(status == CSecurityStatus.OK); }
protected override bool ReleaseHandle() { CSecurityStatus status = CCredentialNativeMethods.FreeCredentialsHandle( ref base.rawHandle ); base.ReleaseHandle(); return(status == CSecurityStatus.OK); }
/// <summary> /// Queries the security package's expections regarding message/token/signature/padding buffer sizes. /// </summary> /// <returns></returns> private CSecPkgContext_Sizes QueryBufferSizes() { CSecPkgContext_Sizes sizes = new CSecPkgContext_Sizes(); CSecurityStatus status = CSecurityStatus.InternalError; bool gotRef = false; RuntimeHelpers.PrepareConstrainedRegions(); try { this.ContextHandle.DangerousAddRef(ref gotRef); } catch (Exception) { if (gotRef) { this.ContextHandle.DangerousRelease(); gotRef = false; } throw; } finally { if (gotRef) { status = CContextNativeMethods.QueryContextAttributes_Sizes( ref this.ContextHandle.rawHandle, CContextQueryAttrib.Sizes, ref sizes ); this.ContextHandle.DangerousRelease(); } } if (status != CSecurityStatus.OK) { throw new CSSPIException("Failed to query context buffer size attributes", status); } return(sizes); }
/// <summary> /// Returns the properties of the named package. /// </summary> /// <param name="packageName">The name of the package.</param> /// <returns></returns> public static CSecPkgInfo GetPackageCapabilities(string packageName) { CSecPkgInfo info; CSecurityStatus status = CSecurityStatus.InternalError; IntPtr rawInfoPtr; rawInfoPtr = new IntPtr(); info = new CSecPkgInfo(); RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { status = CNativeMethods.QuerySecurityPackageInfo(packageName, ref rawInfoPtr); if (rawInfoPtr != IntPtr.Zero) { try { if (status == CSecurityStatus.OK) { // This performs allocations as it makes room for the strings contained in the SecPkgInfo class. Marshal.PtrToStructure(rawInfoPtr, info); } } finally { CNativeMethods.FreeContextBuffer(rawInfoPtr); } } } if (status != CSecurityStatus.OK) { throw new CSSPIException("Failed to query security package provider details", status); } return(info); }
/// <summary> /// Safely invokes the native VerifySignature function, making sure that handle ref counting is /// performed in a proper CER. /// </summary> /// <param name="handle"></param> /// <param name="qualityOfProtection"></param> /// <param name="adapter"></param> /// <param name="sequenceNumber"></param> /// <returns></returns> internal static CSecurityStatus SafeVerifySignature( CSafeContextHandle handle, int qualityOfProtection, CSecureBufferAdapter adapter, int sequenceNumber) { bool gotRef = false; CSecurityStatus status = CSecurityStatus.InternalError; RuntimeHelpers.PrepareConstrainedRegions(); try { handle.DangerousAddRef(ref gotRef); } catch (Exception) { if (gotRef) { handle.DangerousRelease(); gotRef = false; } throw; } finally { if (gotRef) { status = CContextNativeMethods.VerifySignature( ref handle.rawHandle, adapter.Handle, sequenceNumber, qualityOfProtection ); handle.DangerousRelease(); } } return(status); }
private void Init(CCredentialUse use) { string packageName; CTimeStamp rawExpiry = new CTimeStamp(); CSecurityStatus status = CSecurityStatus.InternalError; // -- Package -- // Copy off for the call, since this.SecurityPackage is a property. packageName = this.SecurityPackage; this.Handle = new CSafeCredentialHandle(); // The finally clause is the actual constrained region. The VM pre-allocates any stack space, // performs any allocations it needs to prepare methods for execution, and postpones any // instances of the 'uncatchable' exceptions (ThreadAbort, StackOverflow, OutOfMemory). RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { status = CCredentialNativeMethods.AcquireCredentialsHandle( null, packageName, use, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, ref this.Handle.rawHandle, ref rawExpiry ); } if (status != CSecurityStatus.OK) { throw new CSSPIException("Failed to call AcquireCredentialHandle", status); } this.Expiry = rawExpiry.ToDateTime(); }
/// <summary> /// Verifies the signature of a signed message /// </summary> /// <remarks> /// The expected structure of the signed message buffer is as follows: /// - 4 bytes, unsigned integer in big endian format indicating the length of the plaintext message /// - 2 bytes, unsigned integer in big endian format indicating the length of the signture /// - The plaintext message /// - The message's signature. /// </remarks> /// <param name="signedMessage">The packed signed message.</param> /// <param name="origMessage">The extracted original message.</param> /// <returns>True if the message has a valid signature, false otherwise.</returns> public bool VerifySignature(byte[] signedMessage, out byte[] origMessage) { CSecurityStatus status = CSecurityStatus.InternalError; CSecPkgContext_Sizes sizes; CSecureBuffer dataBuffer; CSecureBuffer signatureBuffer; CSecureBufferAdapter adapter; CheckLifecycle(); sizes = QueryBufferSizes(); if (signedMessage.Length < 2 + 4 + sizes.MaxSignature) { throw new ArgumentException("Input message is too small to possibly fit a valid message"); } int position = 0; int messageLen; int sigLen; messageLen = CByteWriter.ReadInt32_BE(signedMessage, 0); position += 4; sigLen = CByteWriter.ReadInt16_BE(signedMessage, position); position += 2; if (messageLen + sigLen + 2 + 4 > signedMessage.Length) { throw new ArgumentException("The buffer contains invalid data - the embedded length data does not add up."); } dataBuffer = new CSecureBuffer(new byte[messageLen], CBufferType.Data); Array.Copy(signedMessage, position, dataBuffer.Buffer, 0, messageLen); position += messageLen; signatureBuffer = new CSecureBuffer(new byte[sigLen], CBufferType.Token); Array.Copy(signedMessage, position, signatureBuffer.Buffer, 0, sigLen); position += sigLen; using (adapter = new CSecureBufferAdapter(new[] { dataBuffer, signatureBuffer })) { status = CContextNativeMethods.SafeVerifySignature( this.ContextHandle, 0, adapter, 0 ); } if (status == CSecurityStatus.OK) { origMessage = dataBuffer.Buffer; return(true); } else if (status == CSecurityStatus.MessageAltered || status == CSecurityStatus.OutOfSequence) { origMessage = null; return(false); } else { throw new CSSPIException("Failed to determine the veracity of a signed message.", status); } }
/// <summary> /// Encrypts the byte array using the context's session key. /// </summary> /// <remarks> /// The structure of the returned data is as follows: /// - 2 bytes, an unsigned big-endian integer indicating the length of the trailer buffer size /// - 4 bytes, an unsigned big-endian integer indicating the length of the message buffer size. /// - 2 bytes, an unsigned big-endian integer indicating the length of the encryption padding buffer size. /// - The trailer buffer /// - The message buffer /// - The padding buffer. /// </remarks> /// <param name="input">The raw message to encrypt.</param> /// <returns>The packed and encrypted message.</returns> public byte[] Encrypt(byte[] input) { // The message is encrypted in place in the buffer we provide to Win32 EncryptMessage CSecPkgContext_Sizes sizes; CSecureBuffer trailerBuffer; CSecureBuffer dataBuffer; CSecureBuffer paddingBuffer; CSecureBufferAdapter adapter; CSecurityStatus status = CSecurityStatus.InvalidHandle; byte[] result; CheckLifecycle(); sizes = QueryBufferSizes(); trailerBuffer = new CSecureBuffer(new byte[sizes.SecurityTrailer], CBufferType.Token); dataBuffer = new CSecureBuffer(new byte[input.Length], CBufferType.Data); paddingBuffer = new CSecureBuffer(new byte[sizes.BlockSize], CBufferType.Padding); Array.Copy(input, dataBuffer.Buffer, input.Length); using (adapter = new CSecureBufferAdapter(new[] { trailerBuffer, dataBuffer, paddingBuffer })) { status = CContextNativeMethods.SafeEncryptMessage( this.ContextHandle, 0, adapter, 0 ); } if (status != CSecurityStatus.OK) { throw new CSSPIException("Failed to encrypt message", status); } int position = 0; // Enough room to fit: // -- 2 bytes for the trailer buffer size // -- 4 bytes for the message size // -- 2 bytes for the padding size. // -- The encrypted message result = new byte[2 + 4 + 2 + trailerBuffer.Length + dataBuffer.Length + paddingBuffer.Length]; CByteWriter.WriteInt16_BE((short)trailerBuffer.Length, result, position); position += 2; CByteWriter.WriteInt32_BE(dataBuffer.Length, result, position); position += 4; CByteWriter.WriteInt16_BE((short)paddingBuffer.Length, result, position); position += 2; Array.Copy(trailerBuffer.Buffer, 0, result, position, trailerBuffer.Length); position += trailerBuffer.Length; Array.Copy(dataBuffer.Buffer, 0, result, position, dataBuffer.Length); position += dataBuffer.Length; Array.Copy(paddingBuffer.Buffer, 0, result, position, paddingBuffer.Length); position += paddingBuffer.Length; return(result); }
/// <summary> /// Initializes a new instance of the SSPIException class from serialization data. /// </summary> /// <param name="info"></param> /// <param name="context"></param> protected CSSPIException(SerializationInfo info, StreamingContext context) : base(info, context) { this.message = info.GetString("messsage"); this.errorCode = (CSecurityStatus)info.GetUInt32("errorCode"); }
/// <summary> /// Initializes a new instance of the SSPIException class with the given message and status. /// </summary> /// <param name="message">A message explaining what part of the system failed.</param> /// <param name="errorCode">The error code observed during the failure.</param> public CSSPIException(string message, CSecurityStatus errorCode) { this.message = message; this.errorCode = errorCode; }
/// <summary> /// Returns whether or not the status represents an error. /// </summary> /// <param name="status"></param> /// <returns>True if the status represents an error condition.</returns> public static bool IsError(this CSecurityStatus status) { return((uint)status > 0x80000000u); }
/// <summary> /// Changes the current thread's security context to impersonate the user of the client. /// </summary> /// <remarks> /// Requires that the security package provided with the server's credentials, as well as the /// client's credentials, support impersonation. /// /// Currently, only one thread may initiate impersonation per security context. Impersonation may /// follow threads created by the initial impersonation thread, however. /// </remarks> /// <returns>A handle to capture the lifetime of the impersonation. Dispose the handle to revert /// impersonation. If the handle is leaked, the impersonation will automatically revert at a /// non-deterministic time when the handle is finalized by the Garbage Collector.</returns> public CImpersonationHandle ImpersonateClient() { CImpersonationHandle handle; CSecurityStatus status = CSecurityStatus.InternalError; bool gotRef = false; if (this.Disposed) { throw new ObjectDisposedException("ServerContext"); } else if (this.Initialized == false) { throw new InvalidOperationException( "The server context has not been completely initialized." ); } else if (impersonating) { throw new InvalidOperationException("Cannot impersonate again while already impersonating."); } else if (this.SupportsImpersonate == false) { throw new InvalidOperationException( "The ServerContext is using a security package that does not support impersonation." ); } handle = new CImpersonationHandle(this); RuntimeHelpers.PrepareConstrainedRegions(); try { this.ContextHandle.DangerousAddRef(ref gotRef); } catch (Exception) { if (gotRef) { this.ContextHandle.DangerousRelease(); gotRef = false; } throw; } finally { if (gotRef) { status = CContextNativeMethods.ImpersonateSecurityContext( ref this.ContextHandle.rawHandle ); this.ContextHandle.DangerousRelease(); this.impersonating = true; } } if (status == CSecurityStatus.NoImpersonation) { throw new CSSPIException("Impersonation could not be performed.", status); } else if (status == CSecurityStatus.Unsupported) { throw new CSSPIException("Impersonation is not supported by the security context's Security Support Provider.", status); } else if (status != CSecurityStatus.OK) { throw new CSSPIException("Failed to impersonate the client", status); } return(handle); }