I'm running scripts under domain accounts which are sending various mails using the Send-MailMessage cmdlet with various sender addresses.
Unfortunately Send-MailMessage always authenticates with that account to the mail relay (an exchange server) but then gets told it isn't allowed to send mail:
Send-MailMessage : Mailbox unavailable. The server response was: 5.7.1 Client does not have permissions to send as this sender
I know this could be enabled on the exchange side, but it's really dumb because anonymously sending mail internally with any sender is allowed on these relays.
I also know it's easy to build my own Sendmail function and I do have one at my disposal, but is it possible to just disable authentication for Send-MailMessage? I haven't found any switch to do just that.
I don't have much experience with the cmdlet but I too don't see any switch to send as anonymous. If the credentials passed by default are of the currently logged on user, have tried you passing an empty credential object?
Send-MailMessage -Credential ([System.Management.Automation.PSCredential]::Empty)
Let me know if it works, I am interested n the answer also. :)
It doesn't seem to like an empty object:Send-Mailmessage -Credential ([System.Management.Automation.PSCredential]::Empty) -To $EmailTo -From $EmailFrom -Subject $EmailSubject -SmtpServer $SMTPSRV -Body "test"Send-MailMessage : Object reference not set to an instance of an object.At line:1 char:17+ Send-Mailmessage <<<< -Credential ([System.Management.Automation.PSCredential]::Empty) -To $EmailTo -From $EmailFrom -Subject $EmailSubject -SmtpServer $SMTPSRV -Body "test" + CategoryInfo : NotSpecified: (:) [Send-MailMessage], NullReferenceException + FullyQualifiedErrorId : System.NullReferenceException,Microsoft.PowerShell.Commands.SendMailMessage
Just tested it without specifying the -Credential parameter against some online SMTP server and it works flawlessly. Are you sure we are solving the correct issue? Can you obtain any logs from the SMTP server that say the access was denied because the user does not have permissions to send the mail?
Maybe there is something I don't see. Can't access the logs of the test SMTP server to see the authentication unfortunately.
I saw numerous topics on the internet that mention you need to have anonymous enabled for the send-mailmessage and some that say it is sent using the credentials of the current user - not sure which one is true at the moment.
How does the script you said works do it?
It's probably because the online SMTP servers you tested don't advertise NTLM authentication after the EHLO greeting, unlike our internal one.
I don't have access to the exchange server directly but here is a transcript of the SMTP conversation as done by Send-Mailmessage, displaying the behavior pretty clearly:
220 mailserver.somedomain Microsoft ESMTP MAIL Service ready at Tue, 18 Dec 2012 16:42:20 +0100EHLO clientname250-mailserver.somedomain Hello [22.214.171.124]250-SIZE 15728640250-PIPELINING250-DSN250-ENHANCEDSTATUSCODES250-STARTTLS250-AUTH NTLM LOGIN250-8BITMIME250-BINARYMIME250 CHUNKINGAUTH ntlm [...]334 [...]235 2.7.0 Authentication successfulMAIL FROM:<[...]>250 2.1.0 Sender OKRCPT TO:<[...]>250 2.1.5 Recipient OKDATA354 Start mail input; end with <CRLF>.<CRLF>MIME-Version: 1.0From: [...]To: [...]Date: 18 Dec 2012 16:42:21 +0100Subject: =?us-ascii?Q?Subject?=Content-Type: text/plain; charset=us-asciiContent-Transfer-Encoding: quoted-printabletest.550 5.7.1 Client does not have permissions to send as this sender
For comparison here is the conversation with an SMTP Server not offering NTLM authentication, as you can see it doesn't even attempt to authenticate:
220 ESMTP ServerEHLO clientname250-somenonexchangeserver.domain250-PIPELINING250-SIZE 20971520250-VRFY250-ETRN250-STARTTLS250-ENHANCEDSTATUSCODES250-8BITMIME250 DSNMAIL FROM:<[...]>250 2.1.0 OkRCPT TO:<[...]>
Unfortunately I don't see any way to get around it using the cmdlet. :/ Not saying it is not possible. :)
Ok, thank you anyways.
I know this is a very old post, but I think I found a couple of ways around this short coming. For the longest time I didn't realize the cmdlet doesn't send anonymously. The old PSCX Send-SMPTMail command had an anonymous switch but I hadn't used it much, favoring my own function from the PoSh 1.0 days. The main issue for me was testing SMTP submit capability using Send-MailMessage wasn't an accurate indicator as to whether my ReceiveConnectors were configured correctly. At any rate here's what I came up with.The key is you have to specify a credential object and it cannot be empty. There are a couple of ways I can think to resolve this:
1) Encode the credentials on the system where the script will be run:
This is widely used to access alternate credentials, of course in this case you wouldn't be using Anonymous, instead you would use some other account that has permisison to send, domain admin or whatnot. Follow this procedure:
Read-host -Prompt "enter password" -AsSecureString | ConvertFrom-SecureString | Out-File C:\AdminPassword.txt
$pass = Get-Content C:\AdminPassword.txt | ConvertTo-SecureString $creds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList <UserName>, $pass
Don't forget to fill in your own information for the above commands. Also make
sure you encode the file on the system and logged in under the account that will
run the ultimate script.
2) You can create an account with very limited access and permission
your ReceiveConnectors to allow it to send. You can combine this
approach with option 1 so you can use those credentials in scripts
without the same security risks.
3) If you really want to send using the Anonymous account you'll run into a problem. I appreciate the suggestion to use an empty credential object, but the cmdlet isn't expecting that. Using Anonymous requires a credential object an Anonymous user but with an "empty" password. You cannot pass a null or empty string as the password argument in the New-Object command I previously showed. In fact that argument expects a SecureString object, what's worse is you cannot pass an empty or null string to ConverTo-SecureString so you can't prep your argument beforehand. The question becomes, how can you get an empty string into a SecureString Object? Well Read-Host -AsSecureString can do it, if you just hit enter at the prompt. But that's interactive and won't serve us if we want this script to run by itself . Moreover, I assume if we attempted to take the output of Read-Host and put it in a file as we did in option 1 above we would again have issues with ConvertToSecure-String not liking the empty string. The solution to this dilemma is kind of crafty:
$pass = ConvertTo-SecureString "whatever" -asplaintext -force $creds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "NT AUTHORITY\ANONYMOUS LOGON", $pass
Hard to believe but this actually works. The obvious benefit of this approach is you don't need to store or create anything, and you can use Anonymous instead of a real account with potentially greater and/or more dangerous access. Even though options 1 & 2 are pretty secure many administrators or even security officers may have a problem with stored credentials. There's certainly potential for abuse, but option number 3 seems to work completely with no exposure other than what's been explicitly granted Anonymous.
Very nice post, good to know. :)
nashiooka said:The question becomes, how can you get an empty string into a SecureString Object?
Using the default type constructor of System.Security.SecureString.
$s = New-Object System.Security.SecureString
$creds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "NT AUTHORITY\ANONYMOUS LOGON", $S
How do I know? I created a $credential object using the get-credential cmdlet, did not provide any password and it did not fail. So empty passwords must be possible. Then I just checked what type the password property of my credential object holds ( the credential object is referenced by $creds):
and looked up what constructors the type has.
Turns out creating empty SecureString object is way easier then a populated one (if not using the ConvertTo-SecureString cmdlet).
You're right your way of creating the empty secure string is easier, than what I was doing. Also I can see that being more portable meaning you can use it for other things besides anonymous. I didn't think to use New-Object to get it. You'd be interested to know that my journey to all this info was similar to yours I too created the creds with Get-Credential and no password etc...
I love this amendment, I'm definitely going to use it.
Thanks for the info. :)
Now that this post is even older I just wanted to let you know this was super helpful.