Pine+OpenSSL HOWTO

Paul Heinlein
First published on April 19, 2002
Last updated on November 6, 2006

Note: Feel free to head right to the summary recipe at the end of this article and skip the whole explanation, called “long winded” by at least one reader. If you’re looking for general SSL information, you may find my OpenSSL Command-Line HOWTO somewhat helpful.

Introduction

I think pine was given the ability to work with SSL-encryption when 4.20 was released—but don’t quote me on that. In any event, the feature has been around for a while, and I’ve used it pretty consistently to avoid sending passwords and such in clear text over the Internet.

Getting started

The basic setup is pretty simple. In your .pinerc file, you specify that you want to use SSL to communicate with your mail server. The ssl option can be used anywhere you’d ordinarily point to a remote server:

# SMTP over SSL on smtps port, 465/tcp
smtp-server=smtp.yourcompany.com/ssl

# IMAP over SSL on imaps port, 993/tcp
inbox-path={mail.yourcompany.com/ssl}inbox
incoming-folders="home-in" {your.isp.com/ssl}inbox
folder-collections=Work {mail.yourcompany.com/ssl}mail/[],
    Home {your.isp.com/ssl}mail/[]

In place of the ssl option, you can use tls if your remote SMTP or IMAP server is TLS-enabled. Actually, as Mark Crispin has noted, Pine will use TLS by default if the remote server advertises that capability. Using the tls option “causes an ‘Unable to negotiate TLS with this server’ error if the server does not advertise TLS. If you know that the server advertises TLS, this will protect you from a ‘man in the middle’ attack, since an attacker would either not offer TLS or would offer TLS with an invalid certificate for your server.”

# SMTP using TLS on smtp port, 25/tcp
smtp-server=smtp.yourcompany.com/tls

# SMTP using TLS on submission port, 587/tcp
smtp-server=smtp.yourcompany.com:587/tls

# IMAP using TLS on imap port, 143/tcp
inbox-path={mail.yourcompany.com/tls}inbox

Finally, it’s worth noting that you can specify a username per connnection, just in case it’s different on the remote host than it is locally.

# SMTP using TLS on smtp port, 25/tcp
smtp-server=smtp.yourcompany.com/user=workacct/tls

# IMAP over SSL on imaps port, 993/tcp
inbox-path={mail.yourcompany.com/user=workacct/ssl}inbox
incoming-folders="home-in" {your.isp.com/user=homeacct/ssl}inbox
folder-collections=Work {mail.yourcompany.com/user=workacct/ssl}mail/[],
    Home {your.isp.com/user=homeacct/ssl}mail/[]

Validation problems

There was only one problem with this scheme: it didn’t work.

Oh, it probably worked for Mark Crispin and the other Pine gurus up at the University of Washington, but I would always get a failure message along the lines of

unable to get local issuer certificate: …

or

self signed certificate: …

which would soon fade to

Can’t establish SSL session with…

and finally,

No folder opened

So pine would just sit there, with me unable to access my mail over an SSL link. It didn’t matter whether or not the server certificate was a legitimate one signed by Verisign or a home-brewed one cobbled together by amateurs like me. No dice, either way.

A dirty hack

Well, the UW folks didn’t leave me completely high and dry. They introduced a little hack to deal with rogue certificates. Using the novalidate-cert option, you could get around the validation problem:

inbox-path={mail.yourcompany.com/ssl/novalidate-cert}inbox

incoming-folders="home-in" {your.isp.com/ssl/novalidate-cert}inbox

And so forth.

It was a nice hack. At least I got encryption and didn’t have to worry about rogue sniffers catching wind of a plain-text password.

Still, it was a hack, because I couldn’t validate the server on the other end of my connection. One of the great features of SSL is that it provides authentication in addition to encryption. The remote server presents a signed certificate, and I’m supposed to be able to follow the signing chain back to an authority I trust. At that point, I can be reasonably certain that my connection hasn’t been hijacked by a DNS spoof or a man-in-the-middle attack.

So I lived in encryption-without-authentication land for a while, but the lack of authentication was always a sore point.

The breaking point

That soreness finally got to me when I had to add a fourth server to my .pinerc—and a fourth set of novalidate-cert options. Ugh.

I plunged into the murky waters of SSL server certificates, CA’s, and signing chains. Take a deep breath now, while you’ve got the chance. It’s a long dive from here…

Finding the files

The first step to discovering how to validate certs was ridding my .pinerc of all the novalidate-cert stuff. This was made a bit easier with the release of pine 4.41. In pine 4.33 and earlier releases, if pine couldn’t validate a remote server certificate, you were stuck. In pine 4.41, you were told why pine was having problems and given the option to proceed anyway. Neat.

A brief aside

The OpenSSL suite comes with one main binary file: openssl. It’s the main command-line entry into all the features of the OpenSSL libraries. On my Solaris box, it’s /usr/local/ssl/bin/openssl, on Red Hat, /usr/bin/openssl. I’ll just refer to it as openssl and assume you can find it in your $PATH.

When you compile the OpenSSL suite, you can pass an --openssldir option to the Configure script. It’ll default to the --prefix or, failing that, to /usr/local/ssl if you don’t specify one. On my Solaris box at home, this was /usr/local/ssl. On Red Hat boxes, it’s /usr/share.

Within that directory, there’s an ssl/ directory (e.g., /usr/local/ssl/ssl, /usr/share/ssl, etc.). That’s where the certificate action takes place. From here on out, I’ll refer to that directory as $SSLDIR; you can find it on your system by querying the openssl binary:

openssl version -d

Tracing the calls

I had set up my test .pinerc to poll three IMAP servers. One had a server certificate I knew to be valid and signed by Verisign. The other two had self-signed, home-brewed certs; one of them was on my local network (so I had easy access to it), the other was on a remote server.

So I fired up my novalidate-cert-less configuration, and it complained like I expected. I quit pine and launched it in a system-call-trap wrapper. On Solaris boxes, you use truss to do this. On Linux systems, it’s strace. In either case, you’re best off using the -o option to send the output to a file.

I ran truss -o /tmp/PINEDEBUG pine to capture the system calls. Then I grep-ed the PINEDEBUG file for ‘cert,’ figuring that was as good a place as any to start. I found that pine was looking for a few files in the OpenSSL directory:

$ grep cert /tmp/PINEDEBUG
open("/usr/local/ssl/ssl/cert.pem", O_RDONLY) Err#2 ENOENT
stat("/usr/local/ssl/ssl/certs/ac2316fe.0", 0xFFBEC378) Err#2 ENOENT
open("/usr/local/ssl/ssl/cert.pem", O_RDONLY) Err#2 ENOENT
stat("/usr/local/ssl/ssl/certs/f73e89fd.0", 0xFFBEBE58) Err#2 ENOENT
open("/usr/local/ssl/ssl/cert.pem", O_RDONLY) Err#2 ENOENT
stat("/usr/local/ssl/ssl/certs/13550b38.0", 0xFFBEBE58) Err#2 ENOENT

For each server that I tried to access, pine would first look for $SSLDIR/cert.pem and then it would look for $SSLDIR/certs/hhhhhhhh.n, where “hhhhhhhh” is an eight-character hex string and “n” is a single digit. At first, the “n” was always zero (0). In my case, the actual files for which pine was looking were ac2316fe.0, f73e89fd.0, and 13550b38.0.

Concerning cert.pem, I knew that a .pem file typically contained an ASCII representation of an SSL key or certificate, so I was pretty sure that I had found at least one of the files pine assumed would contain the information necessary for validating the remote certificates.

Concerning hhhhhhhh.n, I figured it must be something similar since it resided in the certs/ directory of $SSLDIR. (I love filenames that are at least semi-self-documenting!)

So the pattern seemed to be 1) look up cert.pem and 2) look up the hhhhhhhh.n file appropriate to that server.

Decoding the h’s

Trying to make sense of the hhhhhhhh string, my first thought was that it was a hex representation of the remote server’s IP address. Nope.

So I launched the openssl binary without any arguments, which puts you into an OpenSSL subshell. From there, I tried some other hypotheses:

  • that it was all or part of a digest of the remote server’s hostname; I tried all the digests available via the openssl binary: md2, md4, md5, mdc2, rmd160, sha, and sha1,

  • that it was all or part of a digest of the remote server’s certificate (remember, I had one of them locally on my home network); I tried the same digests I had tried on the hostname,

  • that there might be some switch using the rsa or dsa options that would produce an appropriate hash.

Arrgh. No luck.

Finally, I started to poke around the x509 subcommand within the OpenSSL shell. It was there I discovered the hash mechanism. Here’s the command-line way to get the hhhhhhhh value from a server cert that’s stored as /tmp/server.pem:

$ openssl x509 -in /tmp/server.pem -hash -noout
ac2316fe

Aha! My grep of the PINEDEBUG file had told me that pine was looking for $SSLDIR/certs/ac2316fe.0. That seemed to explain the h’s.

As for the “n” portion of hhhhhhhh.n, I figured that to be an iterator since it’s possible for multiple server certificates to generate identical hash values.

Success

Anyway, I copied that certificate to the machine on which I was running pine and installed it as $SSLDIR/certs/ac2316fe.0.

I fired up pine. Woohoo! I was given the login dialog right away, with not a single warning about the trustworthiness of the remote certificate.

It’s worth reiterating at this point that the local certificate on which I performed my openssl hashing experiments is self-signed.

Retrieving remote certificates

Now that I knew how to name and where to put certificates I could trust, the next task was learning how to retrieve the certificates of remote mail servers.

I figured the remote server had to present its certificate at some point in the authentication process, so there had to be a way to retrieve it that didn’t involve sending an e-mail to the remote sysadmin asking him or her to mail it to me.

Back to openssl.

At this point, I ought to say that my current method for retrieving remote certs lacks elegance. It works, but that’s about all you can say for it.

The openssl binary allows you to imitate an SSL client using its s_client subcommand. Here’s part of the output generated when I initiated a SSL client session with a RSA Security’s secure web server:

$ openssl s_client -connect www.rsasecurity.com:443
CONNECTED(00000003)
depth=0 /C=US/ST=Massachusetts/L=Bedford/O=RSA Security Inc./OU=Information Services/CN=www.rsasecurity.com
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 /C=US/ST=Massachusetts/L=Bedford/O=RSA Security Inc./OU=Information Services/CN=www.rsasecurity.com
verify error:num=27:certificate not trusted
verify return:1
depth=0 /C=US/ST=Massachusetts/L=Bedford/O=RSA Security Inc./OU=Information Services/CN=www.rsasecurity.com
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
 0 s:/C=US/ST=Massachusetts/L=Bedford/O=RSA Security Inc./OU=Information Services/CN=www.rsasecurity.com
   i:/C=ZA/ST=Western Cape/L=Cape Town/O=Thawte Consulting cc/OU=Certification Services Division/CN=Thawte Server CA/Email=server-certs@thawte.com
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIC8zCCAlygAwIBAgIDCGWaMA0GCSqGSIb3DQEBBAUAMIHEMQswCQYDVQQGEwJa
QTEVMBMGA1UECBMMV2VzdGVybiBDYXBlMRIwEAYDVQQHEwlDYXBlIFRvd24xHTAb
BgNVBAoTFFRoYXd0ZSBDb25zdWx0aW5nIGNjMSgwJgYDVQQLEx9DZXJ0aWZpY2F0
aW9uIFNlcnZpY2VzIERpdmlzaW9uMRkwFwYDVQQDExBUaGF3dGUgU2VydmVyIENB
MSYwJAYJKoZIhvcNAQkBFhdzZXJ2ZXItY2VydHNAdGhhd3RlLmNvbTAeFw0wMTEw
MDUyMTE4NDlaFw0wMjEwMDUyMTE4NDlaMIGQMQswCQYDVQQGEwJVUzEWMBQGA1UE
CBMNTWFzc2FjaHVzZXR0czEQMA4GA1UEBxMHQmVkZm9yZDEaMBgGA1UEChMRUlNB
IFNlY3VyaXR5IEluYy4xHTAbBgNVBAsTFEluZm9ybWF0aW9uIFNlcnZpY2VzMRww
GgYDVQQDExN3d3cucnNhc2VjdXJpdHkuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GN
ADCBiQKBgQCyHCUiYjfMkYbL8N/3rjsUdbRt1u1Q8RcMvTs9HeyLr94br2R649mT
r85i9qkVQOyFQUn2cWBa0xdhO0GBFX5b3LflaxUvsDe8vgpXMJ7WFdx9NbXny9gy
Wvyqr0QdaNUqyGxjvm7YPz7q3nJvkUKCHA51PcYR1DDEtiuzTOSirwIDAQABoyUw
IzATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEB
BAUAA4GBAHsKnZODBYsbRuY8d4P15AChmwcvHI4FuJBvSFMkVW+zFuA94SRbJsqC
9sy9sIV9//s0/ovwzVp1xkTgfmKE/KgIoaz2SM/qQtlM4EoE8k7fjnCL0kVYjS/R
+1la+VANo/4sBfvd3GMJ83aDWP8i1luw1qATkA2/10UAwpbgFR5l
-----END CERTIFICATE-----
subject=/C=US/ST=Massachusetts/L=Bedford/O=RSA Security Inc./OU=Information Services/CN=www.rsasecurity.com
issuer=/C=ZA/ST=Western Cape/L=Cape Town/O=Thawte Consulting cc/OU=Certification Services Division/CN=Thawte Server CA/Email=server-certs@thawte.com

And there it was, in the midst of all the ASCII flotsam in my xterm’s history buffer, a .pem representation of the server certificate! (You might find it easier to use a cleaner scripted version of this procedure.)

I knew that the certs in which I was most interested were for IMAP over SSL, described in /etc/services as “imaps” and/or “s-imap” and assigned port 993.

So I used openssl s_client to retrieve the certificate, which I copied into a file I called mail.work.com.pem. Using openssl x509 -hash, I figured out the hhhhhhhh hash value (13550b38). Finally, I renamed and moved mail.work.com.pem to $SSLDIR/certs/13550b38.0.

Breaking the chain

Back to pine to test it out.

I wasn’t so lucky this time. The cert from mail.work.com wasn’t self-signed; rather, it was signed by VeriSign. You can divine this by running an openssl s_client session and peeking at the output.

By way of example, here’s part of what you’d see if you initiated an SSL session to the https port (443) on Red Hat’s web server:

$ openssl s_client -connect www.redhat.com:443
CONNECTED(00000003)
depth=1 /C=US/O=RSA Data Security, Inc./OU=Secure Server Certification Authority
verify error:num=19:self signed certificate in certificate chain
verify return:0
---
Certificate chain
 0 s:/C=US/ST=North Carolina/L=Durham/O=Red Hat, Inc./OU=Web Operations/CN=www.redhat.com
   i:/C=US/O=RSA Data Security, Inc./OU=Secure Server Certification Authority
 1 s:/C=US/O=RSA Data Security, Inc./OU=Secure Server Certification Authority
   i:/C=US/O=RSA Data Security, Inc./OU=Secure Server Certification Authority
---

What all this means is that Red Hat has had its server certificate issued and signed by RSA Data Security, Inc. RSA, on the other hand, has signed its own certificate—so that’s where the buck stops.

The obscure part was getting a copy of the original RSA self-signed certificate, along with the Certificate Authority (CA) certs from companies like VeriSign, Thawte, American Express, et al.

I poked around the web sites of these fine companies, but nowhere do they tell you how to obtain copies of their CA certificates.

I ended finding a nice bundle of them on a Red Hat 7.2 system in a file named /usr/share/ssl/certs/ca-bundle.crt (another self-documenting filename!) that’s included in the openssl RPM. It’s a plain text file, and the Red Hat package maintainer says that it was lifted from the Apache mod_ssl source tree; the mod_ssl maintainer in turn says that he lifted it from a Netscape Communicator certificate database.

More success

I noticed that Red Hat had a symlink /usr/share/ssl/cert.pem that pointed to ca-bundle.crt. Hmm. I copied the file to my Solaris test box and made a similar symlink.

That did the trick. pine opened cert.pem and found within it the VeriSign CA cert it needed to validate the VeriSign-signed cert offered up by mail.work.com.

All was now well in pine land. It authenticates remote servers now in addition to encrypting the traffic.

Certificates in PC-Pine

Adding certificates for use with PC-Pine is in many ways much easier than it is on Unix hosts.

Assuming you’ve retrieved the certificate in question (a task I’ve never done on a Windows host, so I’m a bit out of my element on that point), you can make it available to pine.exe via the Internet Options applet in the Control Panel. The directions below apply to Windows 2000 and may need to be modified for other versions of Windows.

  1. Launch the Control Panel and double-click the Internet Options icon.

    Alternatively, you can choose Internet Options from the Tools menu in Microsoft Internet Explorer.

  2. Select the Content tab and press the Certificates button.

  3. Press the Import button to launch the Certificate Import Wizard. Press the Next button.

    1. Specify the file to import and press the Next button.

    2. Choose the “Automatically select the certificate store” option and press the Next button.

    3. Press Finish.

pine should now be able to validate the remote certificate.

Only root need apply

On my Unix systems, I had no trouble storing the certificates I wanted to trust because I (as root) had write access to $SSLDIR.

Many pine users, however, don’t have that luxury. Sure, they could build an entire OpenSSL/pine infrastructure in their home directories, but that’s a hassle (and may push them over their disk-space quotas). The only recourse they have is to ask the local sysadmin to install the certs—and who knows whether the admin has either the time or inclination to do so.

It’d be really nice if the pine developers would allow a user to specify in his/her .pinerc one or more directories that contain trusted certificates. Then, at least, normal users would be able to authenticate remote servers with little or no hand-holding.

Summary recipe

Here’s the whole solution in five easy-to-follow steps:

  1. Make sure your $HOME/.pinerc is set up to handle SSL or TLS sessions.

  2. Find out where in the local filesystem pine and the OpenSSL libraries expect to find certificates.

  3. Use openssl s_client -connect to retrieve the remote certificate.

  4. Use openssl x509 -hash to generate the filename or symlink for the remote certificate. Place the renamed file or symlink in your local certificates directory.

  5. Make sure you’ve got certificates of the major trusted certificate authorities in your OpenSSL directory.

A side lesson in fetchmail

I have yet another mail account, but this one gets very little traffic. I thought that rather than adding the server to my ever-growing .pinerc file, I’d just use fetchmail to download mail from that account to my home machine.

The server in question supports IMAP over SSL, so I wondered: Could I take advantage of SSL with fetchmail?

Well, of course. Eric Raymond thinks of everything. :-)

Actually, fetchmail is very complete in this regard. Unlike pine, fetchmail lets specify a path to the CA and server certificates you trust. That’s a great boon for users who don’t have administrative rights on their machines.

In my case, however, I decided that I needed to do nothing more than check the fingerprint of the cert offered by the remote host. So I retrieved the remote cert using the openssl s_client method mentioned above. Then I obtained its fingerprint:

$ openssl x509 -in server.pem -noout -fingerprint
MD5 Fingerprint=00:9F:8A:E8:A4:9A:9F:E0:56:35:DD:87:27:9E:90:37

The resulting entry in my .fetchmailrc uses the sslfingerprint option with the value returned from the openssl fingerprint operation:

poll yet.another.mailhost.com proto pop3
    user "remote-me" with password "wackypasswd" is "local-me" here,
    ssl,
    sslfingerprint "00:9F:8A:E8:A4:9A:9F:E0:56:35:DD:87:27:9E:90:37"

Voila!

Howto