ActiveUp.Pop3Client Authenticate results to -ERR Protocol error. 14 - c#

I'm using MailSystem.NET library for sendting and recieving e-mails. Everything works fine except Pop3Client authentication using SSL when there is a backslash in the username.
Let's say that I have the following code:
var client = new Pop3Client();
var username = #"xxx\admin";
var password = #"passW0rd";
var host = #"abc.def.gh";
var port = 995;
var result = client.ConnectSsl(host, port, true);
Console.WriteLine(result);
result = client.Authenticate(username, password, SaslMechanism.Login);
Console.WriteLine(result);
And the output is:
+OK The Microsoft Exchange POP3 service is ready.
Command "auth login" failed : -ERR Protocol error. 14
So, what the heck? When I try to connect and authenticate e.g. to a google with username such a johnnyboy#gmail.com, it works. But if there is a backslash in it and I go against MS Exchange, it doesn't work.
The credentials are ok, I doublechecked them using PegasusMail.
Can someone explain what could be wrong?

Ok, answer is simple.
Since 2003, Exchange does not support obsolete SASL mechanism AUTH LOGIN. There must be used at least AUTH PLAIN. But to do it, the whole authentication must be reworked.
After AUTH PLAIN there should be username and password in one command with \000 char as a leading and as a separator. So, the resulting command should be base64 encoded string like:
\000username\000password
see Connecting to POP/SMTP Server via Telnet
So, what I did was simple. I extended Pop3Client class and created a new method Authenticate(string username, string password) without SaslMechanism.
public class Pop3ClientExt : Pop3Client
{
public string Authenticate(string username, string password)
{
var nullchar = '\u0000';
var auth = nullchar + username + nullchar + password;
this.Command("auth plain");
string response = this.Command(System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("iso-8859-1").GetBytes(auth)));
return response;
}
}
And now, in case of Microsoft Exchange server, I'll call this Authenticate method instead of the old one.

Related

Sending mail with EWS and OAuth 403 error

A while ago I wrote in C# a simple utility method that was called by an application to send emails automatically.
The application used to authenticate in EWS with Basic authentication (username + password) and everything worked fine.
Starting from September 2022 Microsoft started disabling this now deprecated authentication method, so I decided to update this utility method with an OAuth system autentication.
The method is the following:
public static void SendMail(string to, string cc, string bcc, string replyTo, string from, string subject, bool isHtmlFormat, string body, string[] attachments)
{
var cca = ConfidentialClientApplicationBuilder.Create("my-app-id")//app id
.WithClientSecret("my-client-secret") //Client secret
.WithTenantId("my-tenant-id") //Id tenant
.Build();
var authResult = cca.AcquireTokenForClient(new string[] { "https://outlook.office365.com/.default" }).ExecuteAsync().Result;
string[] recipients = to.Replace(" ", "").Split(';');
string[] repliesTo = string.IsNullOrWhiteSpace(replyTo) ? Array.Empty<string>() : replyTo.Replace(" ", "").Split(';');
string[] ccs = string.IsNullOrWhiteSpace(cc) ? Array.Empty<string>() : cc.Replace(" ", "").Split(';');
string[] bccs = string.IsNullOrWhiteSpace(bcc) ? Array.Empty<string>() : bcc.Replace(" ", "").Split(';');
ExchangeService service = new ExchangeService
{
Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx"),
Credentials = new OAuthCredentials(authResult.AccessToken),
ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, from)
};
service.HttpHeaders.Add("X-AnchorMailbox", from); //Include x-anchormailbox header
EmailMessage emailMessage = new EmailMessage(service)
{
From = new EmailAddress(from),
Subject = subject,
Body = new MessageBody(isHtmlFormat ? BodyType.HTML : BodyType.Text, body)
};
emailMessage.ToRecipients.AddRange(recipients);
emailMessage.ReplyTo.AddRange(repliesTo);
emailMessage.CcRecipients.AddRange(ccs);
emailMessage.BccRecipients.AddRange(bccs);
foreach (string attachment in attachments ?? Array.Empty<string>())
{
emailMessage.Attachments.AddFileAttachment(attachment);
}
emailMessage.Send();
}
The function is called in a very straightforward way:
MailHelper.SendMail("MyEmail#myCompany.com", null, null, null, "NoReply#myCompany.com", "Test subject", false, "This is a test body", null);
The problem is that as soon as the Method emailMessage.Send(); is called, a Microsoft.Exchange.WebServices.Data.ServiceRequestException reporting a 403 forbidden error is thrown.
I already registered the app in the Azure Active Directory interface, set a secret and set the following permissions:
The accounts in my tenant are set to allow Exchange services:
I already double checked IDs and account names to be sure it was not a trivial mistake, but I'm not an expert when it comes to EWS, so I'm surely missing something, unfortunately I don't know where.
Thanks id advance.
The error 403 Forbidden usually occurs if you don't have required permissions or missed granting admin consent to the added API permissions.
Please note that, the code you mentioned is using "Client credentials flow" that works with only Application permissions but you added all Delegated permissions.
In that case, you will get 403 Forbidden error even you granted consent to Delegated permissions.
I tried to reproduce the same in my environment via Postman and got the below results:
I created one Azure AD application and added API permissions same as you like below:
Now, I generated an access token using "Client credentials flow" via Postman like below:
POST https://login.microsoftonline.com/tenantID/oauth2/v2.0/token
client_id:appID
grant_type:client_credentials
client_secret:secret
scope:https://graph.microsoft.com/.default
Response:
When I used the above token to send mail with below query, I got 403 Forbidden error like this:
POST https://graph.microsoft.com/v1.0/users/userID/sendmail
Response:
To resolve the error, you need to add Application permissions and grant admin consent to them like below:
Now I generated the access token again and used it in running below query to send sample mail:
In your case, add Application permissions by granting admin consent to them and run the code again.

How to create CallCredentials from SslCredentials and token string

I am porting a gRPC client from python to c#. Both the python client and the c# client are using the gRPC Framework from grpc.io.
The python client uses the following code to open a secure, non-authenticated channel, which it then uses to procure a token string, which it then uses to create call credentials with the grpc.composite_channel_credentials() function:
channel = grpc.secure_channel(url_server_address, ssl_creds)
stub = gateway.GatewayStub(channel)
# Acquire access token via password authentication
pw_cmd = gateway.PasswordAuthenticateCmd(account_name=url.username, password=url.password)
auth_rsp = stub.PasswordAuthenticate(pw_cmd)
# Open a secure, authenticated channel
auth_creds = grpc.access_token_call_credentials(auth_rsp.access_token)
composite_creds = grpc.composite_channel_credentials(ssl_creds, auth_creds)
channel = grpc.secure_channel(url_server_address, composite_creds)
stub = gateway.GatewayStub(channel)
In c#, I have been able to compile the protocol buffer definitions, and connect with the generated client to successfully acquire the access token:
SslCredentials secureChannel = new SslCredentials(File.ReadAllText(SSLCertificatePath));
Channel channel = new Channel(ServerURL, PortNum, secureChannel);
var client = new GrpcClient(new Grpc.Gateway.GatewayClient(channel));
var response = client.client.PasswordAuthenticate(new PasswordAuthenticateCmd() { AccountName = UserName, Password = UserPassword });
Console.WriteLine(response.AccessToken);
From here, however, I can't find the c# analog to the grpc.composite_channel_credentials() function to take the SslCredentials and the access token string to create combined credentials.
None of the examples here https://grpc.io/docs/guides/auth.html here use a token string, and I haven't been able to find any other examples out there.
What you're looking for is:
https://github.com/grpc/grpc/blob/c5311260fd923079637f5d43bd410ba6de740443/src/csharp/Grpc.Core/CallCredentials.cs#L49 and https://github.com/grpc/grpc/blob/c5311260fd923079637f5d43bd410ba6de740443/src/csharp/Grpc.Core/ChannelCredentials.cs#L67.
Feel free to also look at:
https://github.com/grpc/grpc/blob/c5311260fd923079637f5d43bd410ba6de740443/src/csharp/Grpc.Auth/GoogleAuthInterceptors.cs#L58
I solved my problem using CallCredentials.FromInterceptor().
The grpc.access_token_call_credentials() python call adds an authorization entry to the metadata, and sets its value to "Bearer " + AccessToken, so I just had to do the same:
SslCredentials secureCredentials = new SslCredentials(File.ReadAllText(SSLCertificatePath));
Channel secureChannel = new Channel(ServerURL, PortNum, secureCredentials);
var client = new GrpcClient(new Grpc.Gateway.GatewayClient(secureChannel));
var response = client.client.PasswordAuthenticate(new PasswordAuthenticateCmd() { AccountName = UserName, Password = UserPassword });
var accessTokenCredentials = CallCredentials.FromInterceptor(new AsyncAuthInterceptor((context, metadata) =>
{
metadata.Add("authorization", "Bearer " + passwordResponse.AccessToken);
return TaskUtils.CompletedTask;
}));
var authenticatedCredentials = ChannelCredentials.Create(secureCredentials, accessTokenCredentials);
Channel authenticatedChannel = new Channel(hostURL, hostPort, authenticatedCredentials);
As Jan pointed out in his answer, there is a function in the Grpc.Auth namespace that does the same thing as the function that I wrote: https://github.com/grpc/grpc/blob/c5311260fd923079637f5d43bd410ba6de740443/src/csharp/Grpc.Auth/GoogleAuthInterceptors.cs#L58

LDAP C# error when trying to connect

I keep getting the following error when trying to connect to an LDAP server. The user name or password is incorrect.
It occurs on the .FindOne()
If I use AuthenticationTypes.Encryption i get an error: The server is not operational.
I've also tried to prepend the username with ownme\username
I'm extremely newbish with AD so the issue might be so simple.
Domain = domain;
_entry = new DirectoryEntry("LDAP://DC1/DC=ownme,DC=local", username, password, AuthenticationTypes.ServerBind);
_directorySearcher = new DirectorySearcher(_entry, "(objectClass=*)", new string[] {"namingContexts"}, SearchScope.Subtree);
var namingContext = _directorySearcher.FindOne();
The problem was credentials. You need to specify the domain prefix in the username or look at one of the comments in my question.
I had var username = "domain\username";
I should have written var username = #"domain\username";

Is this a good security/validation method to use in WCF services?

I am creating a host/client style that uses WCF and its wsHttpBinding to communicate clients to a server running the host and I wanted to provide some sort of security or validation so I have this and I was wondering how good or secure it is.
Every method in the service library has a USERNAME and PASSWORD variable which have to both be populated with a value that gets hashed by the client using the SHA512 hash algorithm. So the username, password and any other parameters for the method are sent along to the server which will check the hashed username and password against a database of hashed usernames and passwords to see if a match is found. If it does match then it returns the data that the client requested but if it does not match then it returns an error or message and doesn't send back the data. A code snipped of a method with this 'security' is below:
// The USERNAME and PASSWORD parameter values have been hashed by the client before
[OperationContract]
string SayHello(string USERNAME, string PASSWORD, string name)
//-------------------------------------------------------------------
public string SayHello(string USERNAME, string PASSWORD, string name)
{
if (USERNAME == "username" & PASSWORD == "password")
{ return string.Format("Hello, {0}!", name); }
else { return "Invalid credentials, method aborted"; }
}
Is this a good security method for validating 'calls'? It is more on the side of checking that they have an account as opposed to being secure but I think it may be quite secure. What do you think, how secure or good is it but more importantly, how could it be broken by hackers or other methods?
This article should help you: How to: Authenticate with a User Name and Password.
WSHttpBinding userNameBinding = new WSHttpBinding();
userNameBinding.Security.Mode = SecurityMode.Message;
userNameBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
svcHost.AddServiceEndpoint(typeof(IService1), userNameBinding, "");
...
string username;
string password;
// Instantiate the proxy
Service1Client proxy = new Service1Client();
// Prompt the user for username & password
GetPassword(out username, out password);
// Set the user’s credentials on the proxy
proxy.ClientCredentials.UserName.UserName = username;
proxy.ClientCredentials.UserName.Password = password;
// Treat the test certificate as trusted
proxy.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode =
System.ServiceModel.Security.X509CertificateValidationMode.PeerOrChainTrust;
// Call the service operation using the proxy

Azure ACS Credential Confusion

I downloaded the source for this project http://code.msdn.microsoft.com/windowsazure/MVC4-Web-API-With-SWT-232d69da#content because I am trying to understand ACS authentication and how to apply it in my MVC Web API.
The code has this:
// USE CONFIGURATION FILE, WEB.CONFIG, TO MANAGE THIS DATA
static string serviceNamespace = "<YOUR SERVICE NAMESPACE>";
static string acsHostUrl = "accesscontrol.windows.net";
static string realm = "<REALM>";
static string uid = "USERNAME";
static string pwd = "PASSWORD";
static string serviceUrl = "http://localhost:51388/api";
static string serviceAction = #"/values";
What USERNAME and PASSWORD is it requesting that I use? Does it want me to create a "Service Identity" and use the "password" option?
You need to read the associated article found at: http://blogs.msdn.com/b/alikl/archive/2011/06/05/how-to-request-swt-token-from-acs-and-how-to-validate-it-at-the-rest-wcf-service-hosted-in-windows-azure.aspx follow the steps to Configure ACS to Issue a SWT Token. The information you enter when completing the section "To configure a service identity for the REST web service" is what goes here.
If you are using a Symmetric key for your password then you need the client to request a token from ACS in a different way than the example. The following code is an example of what that request looks like and was taken from http://msdn.microsoft.com/en-us/library/hh674475.aspx. See the section "SWT token requests".
WebClient client = new WebClient();
client.BaseAddress = string.Format("https://mysnservice.accesscontrol.windows.net");
NameValueCollection values = new NameValueCollection();
// add the wrap_scope
values.Add("wrap_scope", "http://mysnservice.com/services");
// add the format
values.Add("wrap_assertion_format", "SWT");
// add the SWT
values.Add("wrap_assertion", "Issuer=mysncustomer1&HMACSHA256=b%2f%2bJFwbngGdufECFjQb8qhb9YH0e32Cf9ABMDZFiPPA%3d");
// WebClient takes care of the remaining URL Encoding
byte[] responseBytes = client.UploadValues("WRAPv0.9", "POST", values);
// the raw response from ACS
string response = Encoding.UTF8.GetString(responseBytes);

Categories