diff --git a/lib/core/logging/Icinga_EventLog_Enums.psm1 b/lib/core/logging/Icinga_EventLog_Enums.psm1 index ccb10e0..eea1d15 100644 --- a/lib/core/logging/Icinga_EventLog_Enums.psm1 +++ b/lib/core/logging/Icinga_EventLog_Enums.psm1 @@ -25,6 +25,36 @@ 'Details' = 'A client connection was terminated by the Framework because no secure SSL handshake could be established. This issue in general is followed by EventId 1500.'; 'EventId' = 1501; }; + 1550 = @{ + 'EntryType' = 'Error'; + 'Message' = 'Unsupported web authentication used'; + 'Details' = 'A web client tried to authenticate with an unsupported authorization method.'; + 'EventId' = 1550; + }; + 1551 = @{ + 'EntryType' = 'Warning'; + 'Message' = 'Invalid authentication credentials provided'; + 'Details' = 'A web request for a client was rejected because of invalid formated base64 encoded credentials.'; + 'EventId' = 1551; + }; + 1552 = @{ + 'EntryType' = 'Error'; + 'Message' = 'Failed to parse use credentials from base64 encoding'; + 'Details' = 'Provided user credentials encoded as base64 could not be converted to domain, user and password objects.'; + 'EventId' = 1552; + }; + 1560 = @{ + 'EntryType' = 'Error'; + 'Message' = 'Failed to test user login as no Principal Context could be established'; + 'Details' = 'A web client trying to authenticate failed as no Principal Context for the provided domain could be established.'; + 'EventId' = 1560; + }; + 1561 = @{ + 'EntryType' = 'Error'; + 'Message' = 'Failed to authenticate user with given credentials'; + 'Details' = 'A web client trying to authenticate failed as the provided user credentials could not be verified.'; + 'EventId' = 1561; + }; } }; diff --git a/lib/web/Convert-Base64ToCredentials.psm1 b/lib/web/Convert-Base64ToCredentials.psm1 new file mode 100644 index 0000000..c6bb82c --- /dev/null +++ b/lib/web/Convert-Base64ToCredentials.psm1 @@ -0,0 +1,102 @@ +<# +.SYNOPSIS + Converts a provided Authorization header to credentials +.DESCRIPTION + Converts a provided Authorization header to credentials +.FUNCTIONALITY + Converts a provided Authorization header to credentials +.EXAMPLE + PS>Convert-Base64ToCredentials -AuthString "Basic =Bwebb474567b56756b..."; +.PARAMETER AuthString + The value of the Authorization header of the web request +.INPUTS + System.String +.OUTPUTS + System.Hashtable +.LINK + https://github.com/Icinga/icinga-powershell-framework +#> + +function Convert-Base64ToCredentials() +{ + param ( + [String]$AuthString + ); + + [hashtable]$Credentials = @{}; + + $AuthArray = $AuthString.Split(' '); + + switch ($AuthArray[0]) { + 'Basic' { + $AuthString = $AuthArray[1]; + }; + default { + Write-IcingaEventMessage -EventId 1550 -Namespace 'Framework' -Objects $AuthArray[0]; + return @{}; + } + } + + # Convert the Base64 Secure String back to a normal string + [string]$AuthString = [System.Text.Encoding]::UTF8.GetString( + [System.Convert]::FromBase64String( + $AuthString + ) + ); + + # If no ':' is within the string, the credential data is not properly formated + if ($AuthString.Contains(':') -eq $FALSE) { + Write-IcingaEventMessage -EventId 1551 -Namespace 'Framework'; + $AuthString = $null; + return $Credentials; + } + + try { + # Build our User Data and Password from the string + [string]$UserData = $AuthString.Substring( + 0, + $AuthString.IndexOf(':') + ); + $Credentials.Add( + 'password', + (ConvertTo-IcingaSecureString ` + $AuthString.Substring( + $AuthString.IndexOf(':') + 1, + $AuthString.Length - $UserData.Length - 1 + ) + ) + ); + + $AuthString = $null; + + # Extract a possible domain + if ($UserData.Contains('\')) { + # Split the auth string on the '\' + [array]$AuthData = $UserData.Split('\'); + # First value of the array is the Domain, second is the Username + $Credentials.Add('domain', $AuthData[0]); + $Credentials.Add( + 'user', + (ConvertTo-IcingaSecureString ` + $AuthData[1] + ) + ); + $AuthData = $null; + } else { + $Credentials.Add('domain', $null); + $Credentials.Add( + 'user', + (ConvertTo-IcingaSecureString ` + $UserData + ) + ); + } + + $UserData = $null; + } catch { + Write-IcingaEventMessage -EventId 1552 -Namespace 'Framework' -Objects $_.Exception; + return @{}; + } + + return $Credentials; +} diff --git a/lib/web/Send-IcingaWebAuthMessage.psm1 b/lib/web/Send-IcingaWebAuthMessage.psm1 new file mode 100644 index 0000000..79bc162 --- /dev/null +++ b/lib/web/Send-IcingaWebAuthMessage.psm1 @@ -0,0 +1,32 @@ +<# +.SYNOPSIS + Sends a basic auth request back to the client +.DESCRIPTION + Sends a basic auth request back to the client +.FUNCTIONALITY + Sends a basic auth request back to the client +.EXAMPLE + PS>Send-IcingaWebAuthMessage -Connection $Connection; +.PARAMETER Connection + The connection data of the Framework containing the client and stream object +.INPUTS + System.Hashtable +.OUTPUTS + Null +.LINK + https://github.com/Icinga/icinga-powershell-framework +#> + +function Send-IcingaWebAuthMessage() +{ + param ( + [Hashtable]$Connection = @{} + ); + + Send-IcingaTCPClientMessage -Message ( + New-IcingaTCPClientRESTMessage ` + -HTTPResponse ($IcingaHTTPEnums.HTTPResponseType.Unauthorized) ` + -ContentBody 'Please provide your credentials for login.' ` + -BasicAuth + ) -Stream $Connection.Stream; +} diff --git a/lib/web/Test-IcingaRESTCredentials.psm1 b/lib/web/Test-IcingaRESTCredentials.psm1 new file mode 100644 index 0000000..26a5e77 --- /dev/null +++ b/lib/web/Test-IcingaRESTCredentials.psm1 @@ -0,0 +1,76 @@ +<# +.SYNOPSIS + Tests provided credentials against either the local machine or a domain controller +.DESCRIPTION + Tests provided credentials against either the local machine or a domain controller +.FUNCTIONALITY + Tests provided credentials against either the local machine or a domain controller +.EXAMPLE + PS>Test-IcingaRESTCredentials $UserName $SecureUser -Password $SecurePassword; +.EXAMPLE + PS>Test-IcingaRESTCredentials $UserName $SecureUser -Password $SecurePassword -Domain 'Example'; +.PARAMETER UserName + The username to use for login as SecureString +.PARAMETER Password + The password to use for login as SecureString +.PARAMETER Domain + The domain to use for login as string +.INPUTS + System.SecureString +.OUTPUTS + System.Boolean +.LINK + https://github.com/Icinga/icinga-powershell-framework +#> + +function Test-IcingaRESTCredentials() +{ + param ( + [SecureString]$UserName, + [SecureString]$Password, + [String]$Domain + ); + + Add-Type -AssemblyName System.DirectoryServices.AccountManagement; + + # Base handling: We try to authenticate against a local user on the machine + [string]$AuthMethod = [System.DirectoryServices.AccountManagement.ContextType]::Machine; + [string]$AuthDomain = $env:COMPUTERNAME; + + # If we specify a domain, we should authenticate against our Domain + if ([string]::IsNullOrEmpty($Domain) -eq $FALSE) { + $AuthMethod = [System.DirectoryServices.AccountManagement.ContextType]::Domain; + $AuthDomain = $Domain; + } + + try { + # Create an Account Management object based on the above determined settings + $AccountService = New-Object System.DirectoryServices.AccountManagement.PrincipalContext( + $AuthMethod, + $AuthDomain + ); + } catch { + # Regardless of the error, print the message and return false to prevent further execution + Write-IcingaEventMessage -EventId 1560 -Namespace 'Framework' -Objects $_.Exception; + return $FALSE; + } + + # In case we couldn't setup the Account Service, always return false + if ($null -eq $AccountService) { + return $FALSE; + } + + try { + # Try to authenticate and either return true or false as integer + [bool]$AuthResult = [int]($AccountService.ValidateCredentials( + (ConvertFrom-IcingaSecureString $UserName), + (ConvertFrom-IcingaSecureString $Password) + )); + + return $AuthResult; + } catch { + Write-IcingaEventMessage -EventId 1561 -Namespace 'Framework' -Objects $_.Exception; + } + + return $FALSE; +}