Difficulty passing a secure string to a system.data.odbc.odbcconnection object - powershell

Im trying to pass a secure string to an odbc connection object to save me from passing a plain password over a network but cant quite figure it out. See the current iteration of my script below.
$encryptedPassword = Get-Content "<encrypted password.txt>"
$runPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR((ConvertTo-SecureString $encryptedPassword)))
$Db2Connection = new-object system.data.odbc.odbcconnection
$Db2Connection.connectionstring = "DSN=<dsn>;Userid=<user>;Password=$runPassword"
$Db2Connection.open()
$SqlQuery = #"
SELECT *
FROM <database>.<table>
"#
$SqlCmd = New-Object system.Data.Odbc.OdbcCommand($SqlQuery,$Db2Connection)
$DataAdapter = New-Object system.Data.Odbc.OdbcDataAdapter($SqlCmd)
$DataTable = New-Object system.Data.datatable
$RowCount = $DataAdapter.fill($DataTable)
$DataAdapter = new-object System.Data.odbc.odbcDataAdapter($SqlCmd)
$DataSet = new-object System.Data.DataSet
$DataAdapter.Fill($DataSet)
$result = $DataSet.Tables[0].Rows[4]
$result
As you can see, while i have encryption at rest the credentials will be handled in clear text using the following.
[Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR((ConvertTo-SecureString $encryptedPassword)))
Any thoughts or recommendations would be appreciated

As you are using an IBM-supplied Db2-driver, it is the Db2-driver itself that handles encrypting the password before transmission.
The Db2-driver does this encrypting transparently to the application, and the application gives the plain-text password to the Db2-driver which encrypts is before transmission. The Db2-server platform (Z/OS, i-series, Linux/Unix/Windows) determines the configuration details for encrypted passwords.
So the application cannot supply an already-encrpyted password to Db2-driver.
Such transparent password-encryption requires correct configuration of the Db2-server authentication setup, and a suitable (meaning: recent and patch-maintained) Db2-driver at the workstations. Db2-clients also support data-encryption in transit in addition to password encryption, but that is a different matter.
As part of the connection attempt, the Db2-driver will obtain from the Db2-server the names of available encryption algorithms the Db2-server can support, and the Db2-client chooses (by default) the most secure algorithm that it can perform that is also available on the Db2-server. You can influence this choice by further configuration as documented in the Db2-Knowledge-Centre.
You can also write your own security-related plugins, if you have special requirements.

Related

Pipelining between two SEPARATE Powershell processes

Let's say I have two PowerShell programs running: Producer.ps1 and Consumer.ps1.
Is there any way to establish a client-server relationship between the two .ps1 files I have?
Specifically, Producer.ps1 outputs a PSObject containing login info. Is there any way I can establish a listener and named pipe between the two objects to pass this PSObject from Producer.ps1 directly into Consumer.ps1?
(Note: the two files cannot be combined, because they each need to run as different Windows users. I know one possible solution for communicating is to write the PSObject to a text/xml file, then have the client read and erase the file, but I'd rather not do this since it exposes the credentials. I'm open to whatever suggestions you have)
I found this link that describes how to do what you're requesting:
https://gbegerow.wordpress.com/2012/04/09/interprocess-communication-in-powershell/
I tested it and I was able to pass data between two separate Powershell sessions.
Server Side:
$pipe=new-object System.IO.Pipes.NamedPipeServerStream("\\.\pipe\Wulf");
'Created server side of "\\.\pipe\Wulf"'
$pipe.WaitForConnection();
$sr = new-object System.IO.StreamReader($pipe);
while (($cmd= $sr.ReadLine()) -ne 'exit')
{
$cmd
};
$sr.Dispose();
$pipe.Dispose();
Client Side:
$pipe = new-object System.IO.Pipes.NamedPipeClientStream("\\.\pipe\Wulf");
$pipe.Connect();
$sw = new-object System.IO.StreamWriter($pipe);
$sw.WriteLine("Go");
$sw.WriteLine("start abc 123");
$sw.WriteLine('exit');
$sw.Dispose();
$pipe.Dispose();

How do you set a message Reply-To address using EWS Managed API?

How do you set the reply-to header when sending a message using Exchange Web Services Managed API in Powershell v3?
I have a Microsoft.Exchange.WebServices.Data.EmailMessage object and can set the from address, add attachments, and send mail successfully.
I was able to add an x-header using:
$xheader = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::InternetHeaders,"x-my-header",[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)
and adding it to $pspropset but if I use reply-to as the value the header is not inserted.
Using valuable and hard to find information posted by Glen Scales in this thread I believe two extended properties, PidTagReplyRecipientEntries and PidTagReplyRecipientNames need to be set on the EmailMessage object.
I am able to set both extended properties without errors but this does not result in a Reply-To header in the message.
Relevant code below:
function SendResponse($orgMsg, $bodyTxt){
$message = [Microsoft.Exchange.WebServices.Data.EmailMessage]::Bind($service, $($orgMsg.Id), $psPropset)
$reply = $message.CreateReply($true)
$reply.BodyPrefix = $bodyTxt
$replyMsg = $reply.Save($drftFolderid.Id)
$replyMsg.From = "my_desired_from#example.com"
$replyMsg.SetExtendedProperty($PidTagReplyRecipientEntries, $byteVal)
$replyMsg.SetExtendedProperty($PidTagReplyRecipientNames, "my_desired_replyto#example.com")
$replyMsg.Update([Microsoft.Exchange.WebServices.Data.ConflictResolutionMode]::AlwaysOverwrite)
$replyMsg.SendAndSaveCopy($sentFolderid.Id)
}
function convert-fromhex {
process
{
$_ -replace '^0x', '' -split "(?<=\G\w{2})(?=\w{2})" | %{ [Convert]::ToByte( $_, 16 ) }
}
}
# below is hex of string "my_desired_replyto#example.com"
[Byte[]]$byteVal = "6d795f646573697265645f7265706c79746f406578616d706c652e636f6d" | convert-fromhex
$PidTagReplyRecipientEntries = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x004F,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary)
$PidTagReplyRecipientNames = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x0050,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)
$psPropset = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
$psPropset.Add($PidTagReplyRecipientEntries)
$psPropset.Add($PidTagReplyRecipientNames)
Does anyone know how this might be accomplished?
No clue why you were downvoted, but there does appear to be an EmailMessage.ReplyTo property in the Microsoft.Exchange.WebServices.Data.EmailMessage class. I can't tell if it's read-only, however. Looks like it might be.
As far as I know you can't. Replyto is read-only property. I've been trying to use 'ImpersonatedUserId' but it seems a little clunky (read I can't getting it working). However I did find that if you have impersonation permissions then you can set Fromproperty and it will send. I understand this might not be what you're looking for but it will get the email to come from the right place.
Microsoft.Exchange.WebServices.Data.EmailMessage.ReplyTo is a read-only EmailAddressCollection deriving from IEnumerable. You don't instantiate it. It's auto instantiated when you create your EmailMessage, and all you need to do is to add your reply-to email address(es) to it (sorry not PowerShell but C# code):
message.ReplyTo.Add(new EmailAddress("someReply-ToEmailAddress#email.com");
message.ReplyTo.Add(new EmailAddress("anotherReply-ToEmailAddress#email.com");

How to do LDAP query using Powershell and PKI

I'm new to Powershell, and I'm trying to do a secure LDAP query using PKI authentication. I'm getting stuck on how to set the certificate and key. Based on Googling/research, I have some of the basics, e.g.:
$connection = new-object System.DirectoryServices.Protocols.LDAPConnection('$domainName:$portNum')
[string[] $get] = "$attribute1", "$attribute2", "attribute3"
$request = new-object System.DirectoryServices.Protocol.SearchRequest("$targetOu", "$filter", "subtree", $get)
$response = new-object $connection.SendRequest($request)
Like I said, I'm getting stuck on how to set/send the certificate and key. I thought I could do $connection.ClientCertificates = $path, but that property is read-only. I also thought I had to do something with $System.Net.NetworkCredential, but I'm not sure if the cert and key actually correspond to username and password. I referred to a Perl script that did an LDAP query and used PKI, and you could do:
clientcert => '/path/to/cert.pem'
clientkey => '/path/to/key.pem'
What's the equivalent for Powershell? Do I have to do something with System.Security.Cryptography.X509Certificates.X509Certificate?
Any help would be appreciated!
$connection.ClientCertificates.Add($cert)
the $cert must be X509Certificate class
and get certificates from store using
$allPersonalCerts = #( Get-ChildItem -Path 'Cert:\CurrentUser\my' )
It returns array of X509Certificate objects (or X509Certificate2 which is child class for X509Certificate )
NB: When doing PowerShell programming, you can always search for help by googling C# or VB.net solutions. This is .Net and examples on .net-oriented languages just differ on syntax

New LDAP User with Powershell

I have the challenge to create new LDAP Users with a Powershell Script.
I have googled a lot but I found no good results...
This is my Code to get Users from the LDAP...
$authenticationType = [System.DirectoryServices.AuthenticationTypes]::ServerBind
$objSearcherRoot = New-Object System.DirectoryServices.DirectoryEntry("LDAP://***.local.com:777/ou=user,o=company", "uid=testuser,ou=system,o=company", "password" , $authenticationType)
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SizeLimit= 0
$objSearcher.PageSize = 1000
$objSearcher.SearchRoot = $objSearcherRoot
$objSearcher.Filter = "cn=$cn"
$result = $objSearcher.FindAll()
My Problem is, I don't know how to insert a new LDAP User (not Active Directory)
It would be really nice if someone could help me... ;)
Thanks
Yes, it's possible, I've done it. You need to bind to the LDAP server using a System.DirectoryServices.Protocols.LdapConnection object (let's say $c) and then create a System.DirectoryServices.Protocols.AddRequest object and populate its attributes (I'm only showing a couple in this example):
[System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.Protocols")
[System.Reflection.Assembly]::LoadWithPartialName("System.Net")
$c = New-Object -TypeName System.DirectoryServices.Protocols.LdapConnection -ArgumentList "***.local.com:777,uid=testuser,ou=system,o=company", "password" , $authenticationType"
$c.Bind()
$r = New-Object -TypeName System.DirectoryServices.Protocols.AddRequest
$r.DistinguishedName = "uid= xxxx, ou=user, o=company"
$r.Attributes.Add((New-Object -TypeName System.DirectoryServices.Protocols.DirectoryAttribute -ArgumentList "objectclass",#("top","organizationalPerson","person","inetorgperson","inetuser","mailrecipient","pwmuser","posixAccount"))) | Out-Null
$r.Attributes.Add((New-Object -TypeName System.DirectoryServices.Protocols.DirectoryAttribute -ArgumentList "cn",($FirstName+" "+$LastName))) | Out-Null
Then just send the request:
$c.SendRequest($r)
LDAP does not support "inserts", but supports "adds". The LDAP client must create an entry and transmit that entry to the directory server using the ADD request. The server returns an ADD result to the LDAP client which contains information about the success or failure of the ADD request. So, check the documentation for information on transmitting an ADD request to the directory server and interpreting the subsequent response.
The LDAP client must have permission to ADD an entry (a user in this case). This involves using the BIND request to change the authorization state of the connection to one which permits adding an entry at the designated place in the directory information tree.
Perhaps this link will help.
You say "create new LDAP Users" but you could create AD users and then they would be available Using LDAP.
I used a script from Microsoft to do something similar.
If you look through the code, you should be able to see how they did it. We used their code with a few tweaks to do what we needed.
-jim

Powershell: Setting PsCredentials to $Null for username and $Null for password

How can I create a $Null username and $Null password PScredentials object?
According to this article, the null PSCredential causes Powershell to use Windows Authentication, which seems a much easier way to run scripts in a domain setting. Unfortunatelly I cant seem to figure out where/how he's setting it to Null:
http://blogs.msdn.com/b/dvespa/archive/2010/02/22/how-to-use-windows-authentication-with-the-pscredential-class.aspx
Other resources:
This answer specified $Null for password, but wont allow $Null username.
Create PSCredential without a password
Thank you.
Why do you need a $null PSCredential object ?
According to the documentation
-Credential <PSCredential>
Specifies a user account that has permission to perform this action. The default is the current user.
It means tha if you just don't use this parameter you will use Windows Authentication.
Edited :
So in Powershell if you want a null credential you just have to specify it :
test :
Get-WmiObject Win32_Process -Credential
Get-WmiObject Win32_Process -Credential $null
and
Get-WmiObject Win32_Process -Credential (Get-Credential)
The constant [PSCredential]::Empty (aka [System.Management.Automation.PSCredential]::Empty) gives you a valid object of type PSCredential but with both username and password set to null.
However, that is not the current user's credentials; rather it means "no credentials". There may be logic in the function you're calling for this to be a moot point (i.e. where the function's the logic says to use the current security context when $credentials -eq [PSCredential]::Empty), but in some contexts this same value may be used for other purposes (e.g. to say you want to use anonymous authentication).
You cannot get the current user's credentials without prompting for them or explicitly assigning them in some other way; otherwise this would present a security risk
#create a credential object for demo purposes.
#Imagine that instead of this line we were saying `$credential = Get-CurrentUserCredential`
#(you have to imagine this, since no such function exists).
$credential = [PSCredential]::new('myUsername', ('superSecretPassword' | ConvertTo-SecureString -AsPlainText -Force))
#we can now see this user's password;
#someone malicious could have this script run under the current user's
#profile & have this information reported back to them.
$credential.GetNetworkCredential().Password
If you want to run something as the current user, you already are (i.e. hence them being the current user); so you don't need to get their credentials.
That said, there are cases where it would be nice to have their credentials; e.g. if accessing a resource which doesn't use the current user context / needs explicit credentials. In such cases you have to either prompt for credentials (Get-Credential $env:username), or read them from some resource (see https://stackoverflow.com/a/6240319/361842).
There's a really thorough explanation of credentials here: http://duffney.io/AddCredentialsToPowerShellFunctions; definitely worth a read.