Unit SChannel.Utils
Description
Helper functions for easy implementation of TLS communication by means of Windows SChannel. The functions are transport-agnostic so they could be applied to any socket implementation or even other transport.
Inspired by TLS-Sample
Uses JEDI API units
(c) Fr0sT-Brutal
License MIT
Overview
Classes, Interfaces, Objects and Records
Functions and Procedures
Types
Constants
Variables
Description
Functions and Procedures
procedure LoadSecurityLibrary; |
Mainly for internal use
Exceptions raised
- ESSPIError
- on error
|
procedure CreateCredentials(const User: string; out hCreds: CredHandle; var SchannelCred: SCHANNEL_CRED); |
Mainly for internal use.
Parameters
- SchannelCred
- - [?IN/OUT] If
SchannelCred.dwVersion = SCHANNEL_CRED_VERSION , the parameter is considered "IN/OUT" and won't be modified before AcquireCredentialsHandle call. Otherwise the parameter is considered "OUT" and is init-ed with default values. Thus user can pass desired values to AcquireCredentialsHandle function.
Exceptions raised
- ESSPIError
- on error
|
procedure VerifyServerCertificate(pServerCert: PCCERT_CONTEXT; const szServerName: string; dwCertFlags: DWORD); |
Validate certificate. Mainly for internal use. Gets called by CheckServerCert
Parameters
- pServerCert
- - pointer to cert context
- szServerName
- - host name of the server to check. Could be empty but appropriate flags must be set as well, otherwise the function will raise error
- dwCertFlags
- - value of
SSL_EXTRA_CERT_CHAIN_POLICY_PARA.fdwChecks field: flags defining errors to ignore. 0 to ignore nothing
Exceptions raised
- ESSPIError
- on error
|
function GetCertContext(const hContext: CtxtHandle): PCCERT_CONTEXT; |
Retrieve server certificate. Mainly for internal use. Gets called by CheckServerCert . Returned result must be freed via CertFreeCertificateContext .
Parameters
- hContext
- - current session context
Returns
server certificate data Exceptions raised
- ESSPIError
- on error
|
procedure Init; |
Load global stuff. Must be called before any other function called. Could be called multiple times without checks. Thread-unsafe! Uses global variables
Exceptions raised
- ESSPIError
- on error
|
procedure Fin; |
Dispose and nullify global stuff. Could be called multiple times without checks. Thread-unsafe! Uses global variables
|
procedure CreateSessionCreds(var SessionCreds: TSessionCreds); |
Init session creds, return data record to be used in calling other functions. Could be called multiple times (nothing will be done on already init-ed record)
Parameters
- SessionCreds
- - [IN, OUT] record that receives values. On first call must be zeroed. Alternatively, user could fill
SessionCreds.SchannelCred with desired values to tune channel properties.
Exceptions raised
- ESSPIError
- on error
|
procedure FreeSessionCreds(var SessionCreds: TSessionCreds); |
Finalize session creds
|
procedure FinSession(var SessionData: TSessionData); |
Finalize session, free credentials
|
function DoClientHandshake(var SessionData: TSessionData; var HandShakeData: THandShakeData; DebugLogFn: TDebugFn = nil): SECURITY_STATUS; |
Function to prepare all necessary handshake data. No transport level actions.
Function to prepare all necessary handshake data. No transport level actions. Function actions and returning data depending on input stage:
Parameters
- SessionData
- - [IN/OUT] record with session data
- HandShakeData
- - [IN/OUT] record with handshake data
- DebugLogFn
- - logging callback, could be
Nil
Exceptions raised
- ESSPIError
- on error
- ESSPIError
- on error
|
procedure GetShutdownData(const SessionData: TSessionData; const hContext: CtxtHandle; out OutBuffer: SecBuffer); |
Generate data to send to a server on connection shutdown
Exceptions raised
- ESSPIError
- on error
|
function GetCurrentCert(const hContext: CtxtHandle): TBytes; |
Retrieve server certificate linked to current context. More suitable for userland application than GetCertContext , without any SChannel-specific stuff.
Parameters
- hContext
- - current session context
Returns
server certificate data Exceptions raised
- ESSPIError
- on error
|
function CheckServerCert(const hContext: CtxtHandle; const ServerName: string; const TrustedCerts: TTrustedCerts = nil; CertCheckIgnoreFlags: TCertCheckIgnoreFlags = []): TCertCheckResult; overload; |
Check server certificate with a certificate that contains a common name that is not valid will be ignored. (SECURITY_FLAG_IGNORE_CERT_CN_INVALID flag will be set calling CertVerifyCertificateChainPolicy )
Parameters
- hContext
- - current session context
- ServerName
- - host name of the server to check. If empty, errors associated
- TrustedCerts
- - list of trusted certs. If cert is in this list, it won't be checked by system
- CertCheckIgnoreFlags
- - set of cert aspects to ignore when checking.
Returns
Result of cert check Exceptions raised
- ESSPIError
- on error
|
function CheckServerCert(const hContext: CtxtHandle; const SessionData: TSessionData): TCertCheckResult; overload; |
Check server certificate - variant using SessionData only
Returns
Result of cert check Exceptions raised
- ESSPIError
- on error
|
procedure DeleteContext(var hContext: CtxtHandle); |
Dispose and nullify security context
|
procedure InitBuffers(const hContext: CtxtHandle; out pbIoBuffer: TBytes; out Sizes: SecPkgContext_StreamSizes); |
Receive size values for current session and init buffer length to contain full message including header and trailer
Exceptions raised
- ESSPIError
- on error
|
procedure EncryptData(const hContext: CtxtHandle; const Sizes: SecPkgContext_StreamSizes; pbMessage: PByte; cbMessage: DWORD; pbIoBuffer: PByte; pbIoBufferLength: DWORD; out cbWritten: DWORD); |
Encrypt data (prepare for sending to server).
Parameters
- hContext
- - current session context
- Sizes
- - current session sizes
- pbMessage
- - input data to encrypt
- cbMessage
- - length of input data
- pbIoBuffer
- - buffer to receive encrypted data
- pbIoBufferLength
- - size of buffer
- cbWritten
- - [OUT] size of encrypted data written to buffer
Exceptions raised
- ESSPIError
- on error
|
function DecryptData(const hContext: CtxtHandle; const Sizes: SecPkgContext_StreamSizes; pbIoBuffer: PByte; var cbEncData: DWORD; pbDecData: PByte; cbDecDataLength: DWORD; out cbWritten: DWORD): SECURITY_STATUS; |
Decrypt data received from server. If input data is not processed completely, unprocessed chunk is copied to beginning of buffer. Thus subsequent call to Recv could just receive to @Buffer[DataLength]
Parameters
- hContext
- - current session context
- Sizes
- - current session sizes
- pbIoBuffer
- - input encrypted data to decrypt
- cbEncData
- - [IN/OUT] length of encrypted data in buffer. After function call it is set to amount of unprocessed data that is placed from the beginning of the buffer
- pbDecData
- - buffer to receive decrypted data
- cbDecDataLength
- - size of buffer
- cbWritten
- - [OUT] size of decrypted data written to buffer
Returns
SEC_I_CONTEXT_EXPIRED - server signaled end of session
SEC_E_OK - message processed fully
SEC_E_INCOMPLETE_MESSAGE - need more data
SEC_I_RENEGOTIATE - server wants to perform another handshake sequence
Exceptions raised
- ESSPIError
- on error or if Result is not one of above mentioned values
|
function SecIsNullHandle(const x: SecHandle): Boolean; |
Check if handle x is null (has both fields equal to zero)
|
function SecStatusErrStr(scRet: SECURITY_STATUS): string; |
Returns string representaion of given security status (locale message + constant name + numeric value)
|
function WinVerifyTrustErrorStr(Status: DWORD): string; |
Returns string representaion of given verify trust error (locale message + constant name + numeric value)
|
function IsWinHandshakeBug(scRet: SECURITY_STATUS): Boolean; |
Check if status is likely a Windows TLS v1.2 handshake bug (SEC_E_BUFFER_TOO_SMALL or SEC_E_MESSAGE_ALTERED status is returned by InitializeSecurityContext on handshake). This function only checks if parameter is one of these two values.
|
function GetSessionCredsPtr(const SessionData: TSessionData): PSessionCreds; |
Return effective pointer to session credentials - either personal or shared
|
Types
THandShakeStage = (...); |
Detect replayed messages that have been encoded by using the EncryptMessage or MakeSignature functions Encrypt messages by using the EncryptMessage function When errors occur, the remote party will be notified The security package allocates output buffers for you. When you have finished using the output buffers, free them by calling the FreeContextBuffer function Stage of handshake
Values
-
hssNotStarted: Initial stage
-
hssSendCliHello: Sending client hello
-
hssReadSrvHello: Reading server hello - general
-
hssReadSrvHelloNoRead: Reading server hello - repeat call without reading
-
hssReadSrvHelloContNeed: Reading server hello - in process, send token
-
hssReadSrvHelloOK: Reading server hello - success, send token
-
hssDone: Final stage
|
TChannelState = (...); |
State of secure channel
Values
-
chsNotStarted: Initial stage
-
chsHandshake: Handshaking with server
-
chsEstablished: Channel established successfully
-
chsShutdown: Sending shutdown signal and closing connection
|
TSessionFlag = (...); |
Session options
Values
-
sfNoServerVerify: If set, SChannel won't automatically verify server certificate by setting
ISC_REQ_MANUAL_CRED_VALIDATION flag in InitializeSecurityContextW call. User has to call manual verification via CheckServerCert after handshake is established (this allows connecting to an IP, domains with custom certs, expired certs, valid certs if system CRL is expired etc).
|
TDebugFn = procedure (const Msg: string) of object; |
Logging method that is used by functions to report non-critical errors and handshake details.
|
TCertCheckIgnoreFlag = (...); |
Flags to ignore some cert aspects when checking manually via CheckServerCert . Mirror of SECURITY_FLAG_IGNORE_* constants
Values
-
ignRevokation: don't check cert revokation (useful if CRL is unavailable)
-
ignUnknownCA: don't check CA
-
ignWrongUsage
-
ignCertCNInvalid: don't check cert name (useful when connecting by IP)
-
ignCertDateInvalid: don't check expiration date
|
TCertCheckResult = (...); |
Result of CheckServerCert function
Values
-
ccrValid: Cert is valid
-
ccrTrusted: Cert was in trusted list, no check was performed
-
ccrValidWithFlags: Cert is valid with some ignore flags
|
Constants
LogPrefix = '[SChannel]: '; |
Just a suggested prefix for log output
|
IO_BUFFER_SIZE = $10000; |
Size of handshake buffer
|
USED_PROTOCOLS: DWORD = SP_PROT_TLS1_1 or SP_PROT_TLS1_2; |
Set of used protocols. Note: TLS 1.0 is not used by default, add SP_PROT_TLS1_0 if needed
|
USED_ALGS: ALG_ID = 0; |
Detect messages received out of sequence
|
SSPI_FLAGS =
ISC_REQ_SEQUENCE_DETECT or
ISC_REQ_REPLAY_DETECT or
ISC_REQ_CONFIDENTIALITY or
ISC_RET_EXTENDED_ERROR or
ISC_REQ_ALLOCATE_MEMORY or
ISC_REQ_STREAM; |
Support a stream-oriented connection
|
S_Msg_Received = 'Received %d bytes of encrypted data / %d bytes of payload'; |
Messages that could be written to log by various implementations. None of these are used in this unit
|
S_Msg_SessionClosed = 'Server closed the session [SEC_I_CONTEXT_EXPIRED]'; |
|
S_Msg_Renegotiate = 'Server requested renegotiate'; |
|
S_Msg_Sending = 'Sending %d bytes of payload / %d bytes encrypted'; |
|
S_Msg_StartingTLS = 'Starting TLS handshake'; |
|
S_Msg_HShStageW1Success = 'Handshake @W1 - %d bytes sent'; |
|
S_Msg_HShStageW1Incomplete = 'Handshake @W1 - ! incomplete data sent'; |
|
S_Msg_HShStageW1Fail = 'Handshake @W1 - ! error sending data ([#%d] %s)'; |
|
S_Msg_HShStageRFail = 'Handshake @R - ! no data received or error receiving ([#%d] %s)'; |
|
S_Msg_HShStageRSuccess = 'Handshake @R - %d bytes received'; |
|
S_Msg_HandshakeBug = 'Handshake bug: "%s", retrying'; |
|
S_Msg_HShStageW2Success = 'Handshake @W2 - %d bytes sent'; |
|
S_Msg_HShStageW2Incomplete = 'Handshake @W2 - ! incomplete data sent'; |
|
S_Msg_HShStageW2Fail = 'Handshake @W2 - ! error sending data ([#%d] %s)'; |
|
S_Msg_HShExtraData = 'Handshake: got "%d" bytes of extra data'; |
|
S_Msg_Established = 'Handshake established'; |
|
S_Msg_SrvCredsAuth = 'Server credentials authenticated'; |
|
S_Msg_CredsInited = 'Credentials initialized'; |
|
S_Msg_AddrIsIP = 'Address "%s" is IP, verification by name is disabled'; |
|
S_Msg_ShuttingDownTLS = 'Shutting down TLS'; |
|
S_Msg_SendingShutdown = 'Sending shutdown notify - %d bytes of data'; |
|
S_Err_ListeningNotSupported = 'Listening is not supported with SChannel yet'; |
|
S_Msg_CertIsTrusted = 'Certificate is in trusted list'; |
|
S_Msg_CertIsValidWithFlags = 'Certificate is valid using some ignore flags'; |
|
Variables
hMYCertStore: HCERTSTORE = nil; |
|
g_pSSPI: PSecurityFunctionTable; |
|
Generated by PasDoc 0.16.0.