Connecting to Microsoft Exchange IMAP4 service using perl Mail::IMAPClient - perl

I am trying to use Mail::IMAPClient to connect to a MS Exchange mailbox.
Read messages in the INBOX and depending on the contents formats and sends email to other mailboxes.
This snippet program is a test to only connect using IMAP and then disconnect. The snippet fails to connect, but I can connect using openssl.
!/usr/bin/perl
use strict;
use warnings;
use Mail::IMAPClient;
use lib "/usr/local/share/perl5";
my $IMAPserver = "imapx.servers.net";
my $IMAPuser = "emailUser";
my $IMAPpass = "resUliame";
my $IMAPport = 993;
print "Start\n";
# Attach a client using an SSL socket.
my $client = Mail::IMAPClient->new(
Server => $IMAPserver,
Port => $IMAPport,
User => $IMAPuser,
Password => $IMAPpass,
Ssl => 1,
DEBUG => 1
)
or die "new(): $#";
$client->State(Mail::IMAPClient::Connected);
# Close IMAP connections
$client->logout();
print "Done\n";
This is the output I get...
Start
Started at Mon Sep 26 10:28:16 2016
Using Mail::IMAPClient version 3.38 on perl 5.010001
Connecting with IO::Socket::SSL PeerAddr imapx.servers.net PeerPort 993 Proto tcp Timeout 600 Debug 1
ERROR: Unable to connect to imapx.servers.net: at /usr/local/share/perl5/Mail/IMAPClient.pm line 370.
Mail::IMAPClient::connect(Mail::IMAPClient=HASH(0x2064b98)) called at /usr/local/share/perl5/Mail/IMAPClient.pm line 313
Mail::IMAPClient::new("Mail::IMAPClient", "Server", "imapx.servers.net", "Port", 993, "User", "emailUser", "Password", "resUliame", ...) called at ./testEmail.pl line 17
new(): Unable to connect to imapx.servers.net: at ./testEmail.pl line 17
I can connect with openssl so I know the server, port, user & passwd is correct.
# openssl s_client -connect imapx.servers.net:993 -crlf
CONNECTED(00000003)
* OK The Microsoft Exchange IMAP4 service is ready.
* LOGIN emailUser resUliame
* OK LOGIN completed.
* SELECT Inbox
* 28027 EXISTS
* 1 RECENT
* FLAGS (\Seen \Answered \Flagged \Deleted \Draft $MDNSent)
* OK [PERMANENTFLAGS (\Seen \Answered \Flagged \Deleted \Draft $MDNSent)] Permanent flags
* OK [UNSEEN 27544] Is the first unseen message
* OK [UIDVALIDITY 64268] UIDVALIDITY value
* OK [UIDNEXT 46567390] The next unique identifier value
* OK [READ-WRITE] SELECT completed.

Related

Perl LWP: why does IO::Socket::SSL use TLS 1.0, while Net::SSL uses TLS 1.2?

When I run the following code:
use strict;
use warnings;
use IO::Socket::SSL;
use LWP::UserAgent;
my $ua = LWP::UserAgent->new(ssl_opts => {
verify_hostname => 0,
});
my $res = $ua->get('https://internal.foo.bar.baz:20002');
print $res->as_string;
I get an internal error from LWP:
500 Can't connect to internal.foo.bar.baz:20002 (Bad file descriptor)
Content-Type: text/plain
Client-Date: Fri, 29 Jun 2018 21:23:13 GMT
Client-Warning: Internal response
Can't connect to internal.foo.bar.baz:20002 (Bad file descriptor)
Bad file descriptor at D:/strawberry/perl/site/lib/LWP/Protocol/http.pm line 50.
Network traffic shows that it's a "client hello" followed by an immediate reset from the server, and that the protocol is TLSv1. The server only allows TLS 1.2 connections, so that makes sense.
When I change my code to specify that the client should only use TLS 1.2, I get the same response.
my $ua = LWP::UserAgent->new(ssl_opts => {
verify_hostname => 0,
SSL_version => 'TLSv1_2',
});
And, in fact, the network traffic looks identical:
When I explicitly use Net::SSL instead of IO::Socket::SSL:
use strict;
use warnings;
use Net::SSL;
use LWP::UserAgent;
my $ua = LWP::UserAgent->new(ssl_opts => {
verify_hostname => 0,
});
my $res = $ua->get('https://internal.foo.bar.baz:20002');
print $res->as_string;
It works:
HTTP/1.1 401 Unauthorized
Date: Fri, 29 Jun 2018 21:33:35 GMT
Server: Kestrel
Client-Date: Fri, 29 Jun 2018 21:33:37 GMT
Client-Peer: ***.**.**.209:20002
Client-Response-Num: 1
Client-SSL-Cert-Issuer: *******************************************************
Client-SSL-Cert-Subject: **************************************************************************
Client-SSL-Cipher: ECDHE-RSA-AES256-SHA384
Client-SSL-Socket-Class: Net::SSL
Client-SSL-Warning: Peer certificate not verified
Client-Transfer-Encoding: chunked
Client-Warning: Missing Authenticate header
Strict-Transport-Security: max-age=2592000
X-Powered-By: ASP.NET
And the protocol is correctly set to TLSv1.2:
Oddly enough, analyze-ssl.pl negotiates TLS 1.2 with IO::Socket::SSL:
-- internal.foo.bar.baz port 20002
! using certificate verification (default) -> SSL connect attempt failed error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed
* maximum SSL version : TLSv1_2 (SSLv23)
* supported SSL versions with handshake used and preferred cipher(s):
* handshake protocols ciphers
* SSLv23 TLSv1_2 ECDHE-RSA-AES256-SHA384
* TLSv1_2 TLSv1_2 ECDHE-RSA-AES256-SHA384
* TLSv1_1 FAILED: SSL connect attempt failed
* TLSv1 FAILED: SSL connect attempt failed
* cipher order by : unknown
* SNI supported : certificate verify fails without SNI
* certificate verified : FAIL: SSL connect attempt failed error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed
* chain on ***.**.**.209
* [0/0] bits=2048, ocsp_uri=, *******************************************************************************************************
* [1/1] bits=2048, ocsp_uri=, *******************************************************
* [2/2] bits=2048, ocsp_uri=, ****************************************************************
What can I do to prevent IO::Socket::SSL from attempting a TLS 1.0 connection from LWP?
Perl 5.26.2 MSWin32-x64-multi-thread (Strawberry)
OpenSSL 1.1.0h 27 Mar 2018
LWP 6.34
Net::HTTPS 6.18
IO::Socket::SSL 2.056
Net::SSL 2.86
Output from Steffen Ullrich's script:
openssl version compiled=0x1010008f linked=0x1010008f -- OpenSSL 1.1.0h 27 Mar 2018
IO::Socket::SSL version=2.056
LWP::UserAgent version=6.34
LWP::Protocol::https version=6.07
Net::HTTPS version=6.18
Since analyze-ssl.pl worked and my test script didn't when pointed at the same server, I started comparing them to find out what the differences were. One of the major differences is that analyze-ssl.pl attempts a connection with SSL_cipher_list => '', and it turns out that this was, in fact, the issue.
Changing my LWP::UserAgent instantiation solved the problem:
my $ua = LWP::UserAgent->new(ssl_opts => {
verify_hostname => 0,
SSL_cipher_list => '',
});
It should not behave the way you describe and in fact I cannot reproduce your problem with a freshly installed latest Strawberry Perl on Win10, i.e. using the same Perl version you use. Your first code was taken unchanged apart from the destination and using an openssl s_server -www... as target. It perfectly connects with TLS 1.2 and a packet capture clearly shows TLS 1.2 too.
My guess is that something is messed up with your setup: maybe some older Perl installation still on the system interfering or something similar. This messed up setup is probably specific to how you run your script since running analyze.pl shows no such problems. Therefore I recommend to actually check inside your script what gets actually used, i.e.
use strict;
use warnings;
use IO::Socket::SSL;
use LWP::UserAgent;
use LWP::Protocol::https;
printf("openssl version compiled=0x%0x linked=0x%0x -- %s\n",
Net::SSLeay::OPENSSL_VERSION_NUMBER(),
Net::SSLeay::SSLeay(),
Net::SSLeay::SSLeay_version(0));
printf("IO::Socket::SSL version=%s\n",$IO::Socket::SSL::VERSION);
printf("LWP::UserAgent version=%s\n",$LWP::UserAgent::VERSION);
printf("LWP::Protocol::https version=%s\n",$LWP::Protocol::https::VERSION);
printf("Net::HTTPS version=%s\n",$Net::HTTPS::VERSION);
my $ua = LWP::UserAgent->new(ssl_opts => {
verify_hostname => 0,
});
my $res = $ua->get('https://internal.foo.bar.baz:20002');
print $res->as_string;
This gives for me on the fresh setup slightly different versions of LWP (6.33 vs. 6.34) and Net::HTTPS (6.17 vs. 6.18) but the rest fits with your version. But the important part is probably the version of OpenSSL actually loaded by the code. My guess is that in your specific script setup it uses not the OpenSSL 1.1.0 you expect but some old OpenSSL 1.0.0 or older which have no support for TLS 1.2.

Not able to configure Bugzilla for sending emails from intranet SMTP server

I am setting up a Bugzilla server. I am getting following error when I submit email delivery method description on Bugzilla page during configuration.
The new value for smtpserver is invalid: Cannot connect to 10.71.2.16.
Email delivery method description
mail_delivery_method: SMTP
mailfrom: bugzilla#mydomain.com
use_mailer_queue: off
smtpserver: 10.71.2.16
(I have also tried putting domain name, all ports such as 25, 465, 587)
smtp_username: bugzilla#mydomain.com
smtp_password: <password for user bugzilla#mydomain.com>
smtp_ssl: I have tried On and Off both but no sucsess
smtp_debug: On (but not sure where it generates logs.
Checked /var/log/apache2/. But nothing over there.)
My environment
Ubuntu: 16.04 Server (Internal Server IP: 10.71.6.45)
Bugzilla: 5.0.3 (Internal Server IP: 10.71.6.45)
SMTP: Zimbra Release 8.0.6_GA_5922.RHEL6_64 RHEL6_64 FOSS edition.
(Internal Server IP: 10.71.2.16)
This Zimbra is hosted on a remote server (10.71.2.16) in Intranet.
It is sending and receiving emails successfully from long time without any issues.
/var/log/zimbra.log on (server 10.71.2.16 where SMTP is hosted)
Dec 1 00:25:22 email postfix/smtps/smtpd[18064]: connect from unknown[10.71.6.45]
Dec 1 00:25:22 email postfix/smtps/smtpd[18064]: SSL_accept error from unknown[10.71.6.45]: lost connection
Dec 1 00:25:22 email postfix/smtps/smtpd[18064]: lost connection after CONNECT from unknown[10.71.6.45]
Dec 1 00:25:22 email postfix/smtps/smtpd[18064]: disconnect from unknown[10.71.6.45]
/var/log/maillog (on server 10.71.2.16 where SMTP is hosted)
Dec 1 00:24:36 email postfix/smtps/smtpd[18064]: connect from unknown[10.71.6.45]
Dec 1 00:24:36 email postfix/smtps/smtpd[18064]: SSL_accept error from unknown[10.71.6.45]: lost connection
Dec 1 00:24:36 email postfix/smtps/smtpd[18064]: lost connection after CONNECT from unknown[10.71.6.45]
Dec 1 00:24:36 email postfix/smtps/smtpd[18064]: disconnect from unknown[10.71.6.45]
However when I am sending email from command prompt with below command emails are getting delivered
echo "This is Email Body. This email is sent from Ubuntu Server 10.71.6.45 using SMTP server 10.71.2.16" | mailx -v -r "bugzilla#mydomain.com" -s "Bugzilla test mail - This is the subject" -S smtp="10.71.2.16" -S smtp-use-starttls -S smtp-auth=login -S smtp-auth-user="bugzilla#mydomain.com" -S smtp-auth-password="bugzilla#mydomain.com-emailid-password" -S ssl-verify=ignore test#mydomain.com
/var/www/html/bugzilla/Bugzilla/Mailer.pm snippet
if ($method eq "SMTP") {
my ($host, $port) = split(/:/, Bugzilla->params->{'smtpserver'}, 2);
$transport = Bugzilla->request_cache->{smtp} //=
Email::Sender::Transport::SMTP::Persistent->new({
host => $host,
defined($port) ? (port => $port) : (),
sasl_username => Bugzilla->params->{'smtp_username'},
sasl_password => Bugzilla->params->{'smtp_password'},
helo => $hostname,
ssl => Bugzilla->params->{'smtp_ssl'},
debug => Bugzilla->params->{'smtp_debug'} });
}
I have tried all possible combinations. But it always give following error in Bugzilla
The new value for smtpserver is invalid: Cannot connect to 10.71.2.16.
What is that which I am doing wrong or am I missing?

Using Perl to send emails from remote "Mail-in-aBox" server

On DigitalOcean I've got a vps running MIAB ("Mail-in-aBox") at box.emailserver.com and a web app server on another vps at appserver.com.
I want to send emails from the web app using a Perl script.
I'm using the MIAB DNS server for both domain names. With the MIAB server I can send and receive email using my Mac Mini "Mail" app so that's working great.
I've edited a perl script I found on CPAN that attempts to authenticate a user and send a simple email from appserver.com but it is not passing the auth stage. I get this error:
Error sending email: failed AUTH: Command unknown: 'AUTH'
Anyone have any ideas on what I'm doing wrong?
Here's the script on the web app server, the mail.log from the MIAB, and what the script outputs when run:
#!/usr/bin/perl
use Email::Sender::Simple qw(sendmail);
use Email::Sender::Transport::SMTPS;
use Try::Tiny;
my $transport = Email::Sender::Transport::SMTPS->new(
host => 'box.emailserver.com',
ssl => 'starttls',
sasl_username => 'hostmaster#emailserver.com',
sasl_password => 'xxxx',
debug => 1, # or 1
);
use Email::Simple::Creator; # or other Email::
my $message = Email::Simple->create(
header => [
From => 'hostmaster#emailserver.com',
To => 'bill#my.com',
Subject => 'appserver.com perl test',
],
body => 'This is the test.',
);
try {
sendmail($message, { transport => $transport });
} catch {
die "Error sending email: $_";
};
MIAB mail.log:
Apr 16 13:17:10 box postfix/submission/smtpd[31729]: connect from appserver.com[45.55.229.99]
Apr 16 13:17:10 box postfix/submission/smtpd[31729]: lost connection after EHLO from appserver.com[45.55.229.99]
Apr 16 13:17:10 box postfix/submission/smtpd[31729]: disconnect from appserver.com[45.55.229.99]
appserver.com perl script output
root#appserver:/usr/lib/cgi-bin# perl test6.pl
Net::SMTPS>>> Net::SMTPS(0.06)
Net::SMTPS>>> IO::Socket::IP(0.37)
Net::SMTPS>>> IO::Socket(1.38)
Net::SMTPS>>> IO::Handle(1.35)
Net::SMTPS>>> Exporter(5.72)
Net::SMTPS>>> Net::SMTP(3.10)
Net::SMTPS>>> Net::Cmd(3.10)
Net::SMTPS=GLOB(0x33b7958)<<< 220 box.emailserver.com ESMTP Hi, I'm a Mail-in-a-Box (Ubuntu/Postfix; see https://mailinabox.email/)
Net::SMTPS=GLOB(0x33b7958)>>> EHLO localhost.localdomain
Net::SMTPS=GLOB(0x33b7958)<<< 250-box.emailserver.com
Net::SMTPS=GLOB(0x33b7958)<<< 250-PIPELINING
Net::SMTPS=GLOB(0x33b7958)<<< 250-SIZE 134217728
Net::SMTPS=GLOB(0x33b7958)<<< 250-VRFY
Net::SMTPS=GLOB(0x33b7958)<<< 250-ETRN
Net::SMTPS=GLOB(0x33b7958)<<< 250-STARTTLS
Net::SMTPS=GLOB(0x33b7958)<<< 250-ENHANCEDSTATUSCODES
Net::SMTPS=GLOB(0x33b7958)<<< 250-8BITMIME
Net::SMTPS=GLOB(0x33b7958)<<< 250 DSN
Error sending email: failed AUTH: Command unknown: 'AUTH'
Trace begun at /usr/local/share/perl/5.22.1/Email/Sender/Transport/SMTPS.pm line 98
Email::Sender::Transport::SMTPS::_throw('Email::Sender::Transport::SMTPS=HASH(0x24d8b80)', 'failed AUTH', 'Net::SMTPS=GLOB(0x33b7958)') called at /usr/local/share/perl/5.22.1/Email/Sender/Transport/SMTPS.pm line 71
Email::Sender::Transport::SMTPS::smtpclient('Email::Sender::Transport::SMTPS=HASH(0x24d8b80)') called at /usr/local/share/perl/5.22.1/Email/Sender/Transport/SMTPS.pm line 107
Email::Sender::Transport::SMTPS::send_email('Email::Sender::Transport::SMTPS=HASH(0x24d8b80)', 'Email::Abstract=ARRAY(0x3023ae0)', 'HASH(0x3023ca8)') called at /usr/local/share/perl/5.22.1/Email/Sender/Role/CommonSending.pm line 45
Email::Sender::Role::CommonSending::try {...} at /usr/share/perl5/Try/Tiny.pm line 92
eval {...} at /usr/share/perl5/Try/Tiny.pm line 83
Try::Tiny::try('CODE(0x307b370)', 'Try::Tiny::Catch=REF(0x3023d38)') called at /usr/local/share/perl/5.22.1/Email/Sender/Role/CommonSending.pm line 58
Email::Sender::Role::CommonSending::send('Email::Sender::Transport::SMTPS=HASH(0x24d8b80)', 'Email::Abstract=ARRAY(0x3023ae0)', 'HASH(0x3069a30)') called at /usr/local/share/perl/5.22.1/Email/Sender/Simple.pm line 119
Email::Sender::Simple::send_email('Email::Sender::Simple', 'Email::Abstract=ARRAY(0x3023ae0)', 'HASH(0x3023cf0)') called at /usr/local/share/perl/5.22.1/Email/Sender/Role/CommonSending.pm line 45
Email::Sender::Role::CommonSending::try {...} at /usr/share/perl5/Try/Tiny.pm line 92
eval {...} at /usr/share/perl5/Try/Tiny.pm line 83
Try::Tiny::try('CODE(0x3023b58)', 'Try::Tiny::Catch=REF(0x3005228)') called at /usr/local/share/perl/5.22.1/Email/Sender/Role/CommonSending.pm line 58
Email::Sender::Role::CommonSending::send('Email::Sender::Simple', 'Email::Simple=HASH(0x3004f88)', 'HASH(0x3004ee0)') called at /usr/share/perl5/Sub/Exporter/Util.pm line 18
Sub::Exporter::Util::ANON('Email::Simple=HASH(0x3004f88)', 'HASH(0x3004ee0)') called at test6.pl line 26
main::try {...} at /usr/share/perl5/Try/Tiny.pm line 92
eval {...} at /usr/share/perl5/Try/Tiny.pm line 83
Try::Tiny::try('CODE(0x2fd50b0)', 'Try::Tiny::Catch=REF(0x2f96d08)') called at test6.pl line 29
Net::SMTPS=GLOB(0x33b7958)<<< 220 box.emailserver.com ESMTP Hi, I'm a Mail-in-a-Box (Ubuntu/Postfix; see https://mailinabox.email/)
Net::SMTPS=GLOB(0x33b7958)>>> EHLO localhost.localdomain
Net::SMTPS=GLOB(0x33b7958)<<< 250-box.emailserver.com
Net::SMTPS=GLOB(0x33b7958)<<< 250-PIPELINING
Net::SMTPS=GLOB(0x33b7958)<<< 250-SIZE 134217728
Net::SMTPS=GLOB(0x33b7958)<<< 250-VRFY
Net::SMTPS=GLOB(0x33b7958)<<< 250-ETRN
Net::SMTPS=GLOB(0x33b7958)<<< 250-STARTTLS
Net::SMTPS=GLOB(0x33b7958)<<< 250-ENHANCEDSTATUSCODES
Net::SMTPS=GLOB(0x33b7958)<<< 250-8BITMIME
Net::SMTPS=GLOB(0x33b7958)<<< 250 DSN
Based on this output from the EHLO command the server does not support any kind of authentication. If it would there would be a line like 250-AUTH LOGIN PLAIN ..., i.e. a line which declares support for the AUTH command and shows which authentication methods are supported. But since there is no such line the server does not support authentication.
If you want to use authentication you need to fix this at the server setup. While I can find no information about it in the setup guid of MIAB it uses Postfix underneath which support authentication and there are various tutorials online which describe the process. But, any questions for this server setup are probably more on-topic at serverfault.com.
None of the instructions here or elsewhere worked for me, so I crafted my own solution using Net::SMTP. I have Mail-In-A-Box working successfully on Namecheap with full STARTTLS support using a Perl script. The e-mails pass Gmail's spam folder, passing all their authenticity tests. Once you have your mailinabox server fully configured, this script can be used to send e-mails via SMTP.
#!/usr/bin/perl
use warnings;
use strict;
use Net::SMTP;
use Authen::SASL;
my $host = 'box.yoursite.com'; #server1.yoursite.com if on namecheap
my $port = 587;
my $username = 'you#yoursite.com';
my $password = 'yourpassword';
my $sendto = 'your#friend.com';
my $smtp = Net::SMTP->new(
$host,
Port => $port,
SSL => 0, #No SSL on initial handshake.
Timeout => 10,
Debug => 1
);
#Postfix has no AUTH option until STARTTLS.
$smtp->starttls();
$smtp->auth($username, $password);
$smtp->mail($username);
if ($smtp->to($sendto)) {
$smtp->data();
$smtp->datasend("From: $username\n");
$smtp->datasend("To: $sendto\n");
$smtp->datasend("Subject: Test message\n");
$smtp->datasend("\n");
$smtp->datasend("The test works!\n");
$smtp->dataend();
} else {
print "Error: ", $smtp->message();
}
$smtp->quit;
A few notes:
The first EHLO handshake needs to be performed without SSL (SSL=>0), and once initiated, a STARTTLS connection can then be established. Hence, this initial EHLO handshake fails to return a 250-AUTH response from the mailinabox server, but upgrading the SMTP connection constructor to STARTTLS ($smtp->starttls()) succeeds in returning 250-AUTH PLAIN LOGIN, at which point, you can proceed to authenticate with the SMTP server in the normal way.
Make sure both the Net::SMTP and Authen::SASL cpan modules are both installed/upgraded.
I added the server IP address to mynetworks as described by Bill Stephenson, but did not find it to be necessary.

Answer Browser Request with IO::Socket::SSL

Question formulated in the end, first detailed description of the problem and what I have tested already
I'm writing some code to showcase some basic principles to others. The code will never be productive and simplification is intended.
My goal (appart from others) is to write a simple application which uses a Web-Certificat to encrypt its network traffic.
The starting point is an application which does not encrypt:
#!/usr/bin/env perl
use strict;
use warnings;
use IO::Socket::INET;
# auto-flush on socket
$| = 1;
# creating a listening socket
my $socket = new IO::Socket::INET (
LocalAddr => '0.0.0.0', # local server address
LocalPort => '7777', # local server port
Listen => 5, # queue size for connections
Proto => 'tcp', # protocol used
);
die "cannot create socket $!\n" unless $socket;
print "server waiting for client connection on port 7777\n";
while(1)
{
# waiting for a new client connection
my $client_socket = $socket->accept() or die "socket accept failed $!";
# get information about a newly connected client
my $client_address = $client_socket->peerhost();
my $client_port = $client_socket->peerport();
print "connection from $client_address:$client_port\n";
# read up to 1024 characters from the connected client
my $client_data = '';
sysread( $client_socket, $client_data, 1024);
print "received data: $client_data\n";
# write response data to the connected client
print $client_socket "Hey $client_data!";
# notify client that response has been sent
shutdown($client_socket, 1);
}
END {
$socket->close();
}
This Server-Application can be called with this Client:
#!/usr/bin/env perl
use IO::Socket::INET;
# auto-flush on socket
$| = 1;
# create a connecting socket
my $socket = new IO::Socket::INET (
PeerHost => '127.0.0.1',
PeerPort => '7777',
Proto => 'tcp',
);
die "cannot connect to the server $!\n" unless $socket;
print "connected to the server\n";
# data to send to a server
my $req = $ARGV[0] . '';
print $socket $req;
# notify server that request has been sent
shutdown($socket, 1);
# receive a response of up to 1024 characters from server
my $response = '';
sysread( $socket, $response,1024);
print "received response: $response\n";
$socket->close();
An interaction of Client and Server could look like this:
inet$ perl server.pl
server waiting for client connection on port 7777
connection from 127.0.0.1:40028
received data: Herbert
inet$ perl client.pl "Herbert"
connected to the server
received response: Hey Herbert!
The cool thing is: The server can also be called from a browser:
So the first conclusion: The code works and is good to showcase basic functionality of a simple Client-Server interaction.
Now the Programs should use SSL to communicate
The Server and Client are written so, that SSL-Ability can be achieved by just adding a few lines in the code:
$ diff inet/server.pl ssl/server.pl
7c7
< use IO::Socket::INET;
---
> use IO::Socket::SSL 'inet4';
13c13
< my $socket = new IO::Socket::INET (
---
> my $socket = IO::Socket::SSL->new (
17a18,19
> SSL_cert_file => 'cert.pem', # SSL certificate
> SSL_key_file => 'key.pem', # SSL certificate key
$ diff inet/client.pl ssl/client.pl
5c5
< use IO::Socket::INET;
---
> use IO::Socket::SSL 'inet4';
11c11
< my $socket = new IO::Socket::INET (
---
> my $socket = new IO::Socket::SSL (
14a15
> SSL_ca_file => 'cert.pem',
So the new SSL enabled code is:
#!/usr/bin/env perl
use strict;
use warnings;
use IO::Socket::SSL 'inet4';
# auto-flush on socket
$| = 1;
# creating a listening socket
my $socket = IO::Socket::SSL->new (
LocalAddr => '0.0.0.0', # local server address
LocalPort => '7777', # local server port
Listen => 5, # queue size for connections
Proto => 'tcp', # protocol used
SSL_cert_file => 'cert.pem', # SSL certificate
SSL_key_file => 'key.pem', # SSL certificate key
);
die "cannot create socket $!\n" unless $socket;
print "server waiting for client connection on port 7777\n";
while(1)
{
# waiting for a new client connection
my $client_socket = $socket->accept() or die "socket accept failed $!";
# get information about a newly connected client
my $client_address = $client_socket->peerhost();
my $client_port = $client_socket->peerport();
print "connection from $client_address:$client_port\n";
# read up to 1024 characters from the connected client
my $client_data = '';
sysread( $client_socket, $client_data, 1024);
print "received data: $client_data\n";
# write response data to the connected client
print $client_socket "Hey $client_data!";
# notify client that response has been sent
shutdown($client_socket, 1);
}
END {
$socket->close();
}
and
#!/usr/bin/env perl
use IO::Socket::SSL 'inet4';
# auto-flush on socket
$| = 1;
# create a connecting socket
my $socket = new IO::Socket::SSL (
PeerHost => '127.0.0.1',
PeerPort => '7777',
Proto => 'tcp',
SSL_ca_file => 'cert.pem',
);
die "cannot connect to the server $!\n" unless $socket;
print "connected to the server\n";
# data to send to a server
my $req = $ARGV[0] . '';
print $socket $req;
# notify server that request has been sent
shutdown($socket, 1);
# receive a response of up to 1024 characters from server
my $response = '';
sysread( $socket, $response,1024);
print "received response: $response\n";
$socket->close();
To run the code, a certificate has to be created first:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365
Now Server and Client can be started and interact nicely:
ssl$ perl server.pl
Enter PEM pass phrase:
server waiting for client connection on port 7777
connection from 127.0.0.1:40041
received data: Sabine
ssl$ perl client.pl "Sabine"
connected to the server
received response: Hey Sabine!
But what does not work is a connection from a browser like Firefox or Chrome, even though I converted the certificate using:
openssl pkcs12 -export -in cert.pem -inkey key.pem -out webcert.p12
I also imported the newly created certificate into the browser over its import menu.
The connection just gets refused. The browser can't connect and the server fails at $socket->accept() without any helpful message.
UPDATE: There is a message in the exported variable $SSL_ERROR:
SSL accept attempt failed error:1407609C:SSL routines:SSL23_GET_CLIENT_HELLO:http request
I did some testing with the tool analyze ssl
p5-ssl-tools-master$ perl analyze-ssl.pl --show-chain --all-ciphers -v3 127.0.0.1:7777
+ checking host=127.0.0.1(127.0.0.1) port=7777
* version SSLv23 no verification, ciphers= -> TLSv1_2,ECDHE-RSA-AES128-GCM-SHA256
* version SSLv23 no verification, ciphers=HIGH:ALL -> TLSv1_2,ECDHE-RSA-AES128-GCM-SHA256
* version TLSv1_2 no verification, ciphers= -> TLSv1_2,ECDHE-RSA-AES128-GCM-SHA256
* version TLSv1_2 no verification, ciphers=HIGH:ALL -> TLSv1_2,ECDHE-RSA-AES128-GCM-SHA256
* version TLSv1_1 no verification, ciphers= -> TLSv1_1,ECDHE-RSA-AES256-SHA
* version TLSv1_1 no verification, ciphers=HIGH:ALL -> TLSv1_1,ECDHE-RSA-AES256-SHA
* version TLSv1 no verification, ciphers= -> TLSv1,ECDHE-RSA-AES256-SHA
* version TLSv1 no verification, ciphers=HIGH:ALL -> TLSv1,ECDHE-RSA-AES256-SHA
* version SSLv3, no verification, ciphers= -> FAIL! SSL connect attempt failed because of handshake problems error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure
+ 127.0.0.1 failed permanently 'tcp connect: Verbindungsaufbau abgelehnt', no more IP to try
tcp connect: Verbindungsaufbau abgelehnt
The server seems to accept certain requests and fails at the end, maybe in the same way as with the browser?
ssl$ perl server.pl
Enter PEM pass phrase:
server waiting for client connection on port 7777
connection from 127.0.0.1:40042
received data:
connection from 127.0.0.1:40043
received data:
connection from 127.0.0.1:40044
received data:
connection from 127.0.0.1:40045
received data:
connection from 127.0.0.1:40046
received data:
connection from 127.0.0.1:40047
received data:
connection from 127.0.0.1:40048
received data:
connection from 127.0.0.1:40049
received data:
socket accept failed at server.pl line 27.
My Question
Why is the request with the browser not working?
The code should support a browser request in general since it works without SSL.
It seems to be a browser specific SSL setting since the SSL-Perl-Client has no problem.
Or is the import of the certificate into the browser not working as intended?
Can anyone give me a hint or solution in this matter?
UPDATE: When I look at the error message "SSL23" in $SSL_ERROR and the error message in the client test "SSL3" there seems to be a compatibility problem with SSL versions? Do I need to specify explicitly which version I want? (On the other hand the Client-Test for "SSL23" seems to run through successfully...)
Thanks a lot.
UPDATE: Output with $IO::Socket::SSL::DEBUG = 3;
ssl$ perl server.pl
Enter PEM pass phrase:
DEBUG: .../IO/Socket/SSL.pm:2554: new ctx 42708208
server waiting for client connection on port 7777
DEBUG: .../IO/Socket/SSL.pm:799: no socket yet
DEBUG: .../IO/Socket/SSL.pm:801: accept created normal socket IO::Socket::SSL=GLOB(0x28ac158)
DEBUG: .../IO/Socket/SSL.pm:829: starting sslifying
DEBUG: .../IO/Socket/SSL.pm:873: Net::SSLeay::accept -> -1
DEBUG: .../IO/Socket/SSL.pm:1779: SSL accept attempt failed
DEBUG: .../IO/Socket/SSL.pm:1784: SSL accept attempt failed error:1407609C:SSL routines:SSL23_GET_CLIENT_HELLO:http request
socket accept failed: SSL accept attempt failed error:1407609C:SSL routines:SSL23_GET_CLIENT_HELLO:http request at server.pl line 28.
DEBUG: .../IO/Socket/SSL.pm:2587: free ctx 42708208 open=42708208
DEBUG: .../IO/Socket/SSL.pm:2599: OK free ctx 42708208
... SSL accept attempt failed error:1407609C:SSL routines:SSL23_GET_CLIENT_HELLO:http request
My guess is that you still use a http:// URL to access the server even though you would need to use a https:// URL. This means that the browser sends a HTTP request to the server instead of first doing the TLS handshake and only sending the HTTP request after the successful handshake.
Apart from that you count on the browser understanding the old and long deprecated HTTP 0.9 protocol which consists of only the response body without any HTTP header. Not all browser understand this.

Perl IMAP connection to Exchange using Mail::IMAPClient?

I keep getting a NO LOGIN failed message when trying to connect to the Exchange server using Mail::IMAPClient. From what I've read everything should work. I've gotten it to connect to Gmail but trying to connect to MS Exchange is more difficult it seems like. What could be causing it to fail on authenticating?
use strict;
use warnings;
use Authen::NTLM;
use Mail::IMAPClient;
## Option variables
my $debug
my $authmech = "NTLM";
my $username = "useraccount";
my $password = "set by prompt;
## Settings for connecting to IMAP server
my $imap = Mail::IMAPClient->new(
Server => 'mail.server.domain',
User => $username,
Password => $password,
Port => 993,
Ssl => 1,
Authmechanism => $authmech,
Debug => 1
) or die "Cannot connect through IMAPClient: $#\n";
The out put from running the script.
~]./status_page_msg.pl -d
Logging in as : user_account
Started at Sat Nov 12 19:20:11 2011
Using Mail::IMAPClient version 3.29 on perl 5.008008
Connecting via IO::Socket::SSL to mail.server.domain:993 Timeout 600
Connected to mail.server.domain
Read: * OK The Microsoft Exchange IMAP4 service is ready - 'serverName'
Sending: 1 AUTHENTICATE NTLM
Sent 21 bytes
Read: +
Sending: TlRMTVNTUAABAAAAB6IAAAoACgAgAAAAAAAAAAoAAABlYW0tc3RhdHVz
Sent 58 bytes
Read: 1 NO AUTHENTICATE failed.
ERROR: 1 NO AUTHENTICATE failed. at /usr/lib/perl5/site_perl/5.8.8/Mail/IMAPClient.pm line 3047
Mail::IMAPClient::authenticate('Mail::IMAPClient=HASH(0x1ac95440)', 'NTLM', 'undef') called at /usr/lib/perl5/site_perl/5.8.8/Mail/IMAPClient.pm line 443
Mail::IMAPClient::login('Mail::IMAPClient=HASH(0x1ac95440)') called at /usr/lib/perl5/site_perl/5.8.8/Mail/IMAPClient.pm line 395
Mail::IMAPClient::Socket('Mail::IMAPClient=HASH(0x1ac95440)', 'IO::Socket::SSL=GLOB(0x1b43e110)') called at /usr/lib/perl5/site_perl/5.8.8/Mail/IMAPClient.pm line 351
Mail::IMAPClient::connect('Mail::IMAPClient=HASH(0x1ac95440)') called at /usr/lib/perl5/site_perl/5.8.8/Mail/IMAPClient.pm line 307
Mail::IMAPClient::new('Mail::IMAPClient', 'Server', 'mail.server.domain', 'User', 'user_account', 'Password', 'Correct', 'Port', 993, ...) called at ./status_page_msg.pl line 63
Cannot connect through IMAPClient: 1 NO AUTHENTICATE failed.
You should have said that it used to work, but stopped working when the server was upgraded to Exchange 2010.
A quick Google for "Exchange 2010 NTLM IMAP" turned up Discontinued Features from Exchange 2007 to Exchange 2010:
NTLM isn't supported for POP3 or IMAP4 client connectivity in Exchange 2010 RTM. Connections from POP3 or IMAP4 client programs using NTLM will fail. If you are running Exchange 2010 RTM, the recommended POP3 and IMAP4 setting alternatives to NTLM are:
Kerberos (GSSAPI)
Plain Text Authentication with SSL
If you are using Exchange 2010 RTM, to use NTLM, you must retain an Exchange 2003 or Exchange 2007 server in your Exchange 2010 organization.
Support for NTLM authentication for POP3 and IMAP4 connectivity has been brought back in Exchange 2010 SP1.
Since you're using SSL, you should be able to switch to plain text authentication (just delete Authmechanism). Or, get your sysadmins to install SP1.