Showing posts with label SYS. Show all posts
Showing posts with label SYS. Show all posts

Saturday, April 16, 2016

Debian: disabling SSLv3 in courier server

Debian wheezy (currently oldstable) ships courier-0.68 which was probably released on 2012.
“Probably”, because, head over to the Courier website and try to find the NEWS/ChangeLog.

The Problem

Anyway. Courier-0.68 has built-in openssl support on Debian and it initializes a SSL context the following way:

        ctx=SSL_CTX_new(protocol && strcmp(protocol, "SSL3") == 0
                        ? SSLv3_method():
                        protocol && strcmp(protocol, "SSL23") == 0
                        ? SSLv23_method():
                        TLSv1_method());

        // ...
        SSL_CTX_set_options(ctx, SSL_OP_ALL);

        if (!ssl_cipher_list)
                ssl_cipher_list="SSLv3:TLSv1:HIGH:!LOW:!MEDIUM:!EXP:!NULL:!aNULL@STRENGTH";

        SSL_CTX_set_cipher_list(ctx, ssl_cipher_list);
 
Some clarifications:
  • SSLv3_method would only allow SSLv3
  • TLSv1_method would only allow TLSv1.0
  • SSLv23_method is “the general-purpose version-flexible SSL/TLS method”

So, if we do not want to limit ourselves to TLSv1.0, i.e. allow TLSv1.0, TLSv1.1 and TLSv1.2, we have to limit our protocol version support through other means.

Openssl-1.0.1 (remember, we’re on Debian wheezy) does neither come with TLS_method() the generic TLS-only method, nor does it come with SSL_CTX_set_min_proto_version and we cannot disable SSLv3 with the cipher list if we also want to allow TLSv1.0.

Solution 1: Upgrading to Jessie

What if we upgrade to Debian jessie (current stable)? Jessie ships courier-0.73, so let’s see how SSL context intitialization looks there:

        options=SSL_OP_ALL;

        method=((!protocol || !*protocol)
                ? NULL:
                strcmp(protocol, "SSL3") == 0
                        ? SSLv3_method():
                strcmp(protocol, "SSL23") == 0
                        ? SSLv23_method():
                strcmp(protocol, "TLSv1") == 0
                ? TLSv1_method():
#ifdef HAVE_TLSV1_1_METHOD
                strcmp(protocol, "TLSv1.1") == 0
                ? TLSv1_1_method():
#endif
#ifdef HAVE_TLSV1_2_METHOD
                strcmp(protocol, "TLSv1.2") == 0
                ? TLSv1_2_method():
#endif
                NULL);

        if (!method)
        {
                method=SSLv23_method();
                options|=SSL_OP_NO_SSLv2;
        }

        ctx=SSL_CTX_new(method);

        // ...
        SSL_CTX_set_options(ctx, options);

        if (!ssl_cipher_list)
                ssl_cipher_list="SSLv3:TLSv1:HIGH:!LOW:!MEDIUM:!EXP:!NULL:!aNULL@STRENGTH";

        SSL_CTX_set_cipher_list(ctx, ssl_cipher_list);
 
Jessie also comes with openssl-1.0.1, so the situation would not improve for our undertaking by upgrading to jessie.

Solution 2: Augmenting SSL_CTX_new

What I came up with, is augmenting SSL_CTX_new and setting the desired SSL_OP_NO_SSLv3 on each newly created SSL context.

#include <stdio.h>
#include <dlfcn.h>
#include <openssl/ssl.h>

static void *lib;
static void *new_sym;
static void *opt_sym;

static void dl() {
        char *error;

        if (!lib) {
                lib = dlopen("libssl.so", RTLD_LAZY|RTLD_LOCAL);
                if (!lib) {
                        fprintf(stderr, "dlopen: %s\n", dlerror());
                        exit(1);
                }   
                dlerror();
        }   

        if (!new_sym) {
                *(void **) &new_sym = dlsym(lib, "SSL_CTX_new");
                if ((error = dlerror())) {
                        fprintf(stderr, "dlsym: %s\n", error);
                        dlclose(lib);
                        exit(1);
                }   
        }   

        if (!opt_sym) {
                *(void **) &opt_sym = dlsym(lib, "SSL_CTX_ctrl");
                if ((error = dlerror())) {
                        fprintf(stderr, "dlsym: %s\n", error);
                        dlclose(lib);
                        exit(1);
                }   
        }   
}

SSL_CTX *SSL_CTX_new(const SSL_METHOD *m) 
{
        SSL_CTX *ctx;

        dl();

        ctx = ((SSL_CTX *(*)(const SSL_METHOD*))new_sym)(m);

        if (ctx) {
                ((long (*)(SSL_CTX *, int, long, void*))opt_sym)(ctx, SSL_CTRL_OPTIONS, SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3, NULL);
        }   
        return ctx;
}
 
This is the source of a tiny shared library pre-defining our SSL_CTX_new; to build like e.g.
gcc -ldl -fPIC -shared -o preload.so preload.c

It does the following:
  • augments SSL_CTX_new with our own version, i.e. whenever courier calls SSL_CTX_new our own version gets called
  • when it’s called the first time, it
    • dlopen’s libssl
    • fetches the addresses of the original SSL_CTX_new and SSL_CTX_ctrl (which is the actual function SSL_CTX_set_options calls)
  • calls the original SSL_CTX_new to actually create the SSL context
  • calls SSL_CTX_ctrl on the new context with the options we want to set (SSL_OP_NO_SSLv3)
  • returns the context to the caller

Usage

Courier config files are basically shell scripts which set a environment variables, so we’ll enable it as follows:

cd /etc/courier
cat >>esmtpd >>esmtpd-msa >>esmtpd-ssl \
    >>pop3d >>pop3d-ssl \
    >>imapd >>imapd-ssl \
    >>courierd <<EOF
LD_PRELOAD=/path/to/preload.so
EOF
 
Then restart each courier service.

Verifying

Last, we have to verify that our solution actually works:
openssl s_client -CApath /etc/ssl/certs/ -starttls imap -connect localhost:143 -crlf -quiet -ssl3 <<<LOGOUT
139690858608296:error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure:s3_pkt.c:1261:SSL alert number 40
139690858608296:error:1409E0E5:SSL routines:SSL3_WRITE_BYTES:ssl handshake failure:s3_pkt.c:599:
A bunch of errors. “Good”! :)
Let’s see if we can still connect with TLSv1+:
openssl s_client -CApath /etc/ssl/certs/ -starttls imap -connect localhost:143 -crlf -quiet -tls1 <<<LOGOUT
depth=2 ...
verify return:1
depth=1 ...
verify return:1
depth=0 ...
verify return:1
. OK CAPABILITY completed
* BYE Courier-IMAP server shutting down
LOGOUT OK LOGOUT completed
Awesome. Mission accomplished.

Addendum

Here’s my cipher list for the interested:
openssl ciphers -v 'HIGH+aRSA:+kEDH:+kRSA:+SHA:+3DES:!kSRP' \
  | awk '{ printf "%-28s %-8s %-8s %-18s %-16s\n",$1,$2,$3,$5,$6 }'
Which results in the following ciphers:
ECDHE-RSA-AES256-GCM-SHA384  TLSv1.2  Kx=ECDH  Enc=AESGCM(256)    Mac=AEAD
ECDHE-RSA-AES256-SHA384      TLSv1.2  Kx=ECDH  Enc=AES(256)       Mac=SHA384
ECDHE-RSA-AES128-GCM-SHA256  TLSv1.2  Kx=ECDH  Enc=AESGCM(128)    Mac=AEAD
ECDHE-RSA-AES128-SHA256      TLSv1.2  Kx=ECDH  Enc=AES(128)       Mac=SHA256
DHE-RSA-AES256-GCM-SHA384    TLSv1.2  Kx=DH    Enc=AESGCM(256)    Mac=AEAD
DHE-RSA-AES256-SHA256        TLSv1.2  Kx=DH    Enc=AES(256)       Mac=SHA256
DHE-RSA-AES128-GCM-SHA256    TLSv1.2  Kx=DH    Enc=AESGCM(128)    Mac=AEAD
DHE-RSA-AES128-SHA256        TLSv1.2  Kx=DH    Enc=AES(128)       Mac=SHA256
AES256-GCM-SHA384            TLSv1.2  Kx=RSA   Enc=AESGCM(256)    Mac=AEAD
AES256-SHA256                TLSv1.2  Kx=RSA   Enc=AES(256)       Mac=SHA256
AES128-GCM-SHA256            TLSv1.2  Kx=RSA   Enc=AESGCM(128)    Mac=AEAD
AES128-SHA256                TLSv1.2  Kx=RSA   Enc=AES(128)       Mac=SHA256
ECDHE-RSA-AES256-SHA         SSLv3    Kx=ECDH  Enc=AES(256)       Mac=SHA1
ECDHE-RSA-AES128-SHA         SSLv3    Kx=ECDH  Enc=AES(128)       Mac=SHA1
DHE-RSA-AES256-SHA           SSLv3    Kx=DH    Enc=AES(256)       Mac=SHA1
DHE-RSA-CAMELLIA256-SHA      SSLv3    Kx=DH    Enc=Camellia(256)  Mac=SHA1
DHE-RSA-AES128-SHA           SSLv3    Kx=DH    Enc=AES(128)       Mac=SHA1
DHE-RSA-CAMELLIA128-SHA      SSLv3    Kx=DH    Enc=Camellia(128)  Mac=SHA1
AES256-SHA                   SSLv3    Kx=RSA   Enc=AES(256)       Mac=SHA1
CAMELLIA256-SHA              SSLv3    Kx=RSA   Enc=Camellia(256)  Mac=SHA1
AES128-SHA                   SSLv3    Kx=RSA   Enc=AES(128)       Mac=SHA1
CAMELLIA128-SHA              SSLv3    Kx=RSA   Enc=Camellia(128)  Mac=SHA1
ECDHE-RSA-DES-CBC3-SHA       SSLv3    Kx=ECDH  Enc=3DES(168)      Mac=SHA1
EDH-RSA-DES-CBC3-SHA         SSLv3    Kx=DH    Enc=3DES(168)      Mac=SHA1
DES-CBC3-SHA                 SSLv3    Kx=RSA   Enc=3DES(168)      Mac=SHA1
 
Note, that courier actually does not support ECDH key exchange, but I didn’t exclude it for the sake of simplicity for using the same cipher list for every server (e.g. web etc.)

Wednesday, August 19, 2015

Blank DELL on DisplayPort

I've been struggling since ever with my DELL U3014 on the DisplayPort saying there's no signal after a power cycle of the monitor.

I just found a solution to bring the monitor back to life without rebooting the box; just ssh into and issue:

$ DISPLAY=:0 xset dpms force off
$ DISPLAY=:0 xset dpms force on

I'm running Gnome3 on Arch Linux, so YMMV.

Monday, March 14, 2011

courier imap, authdaemond and vpopmails vchkpw

A few years ago, it was possible to have courier-imap update the open-smtp relay file with it's authvchkpw module. This feature and thus imap-before-smtp disappeared with the introduction of courier-authdaemond because the vchkpw code of authdaemond does not have a chance to see the TCPREMOTEIP environment variable.


I more or less lived with that, until a friend of mine got a new iphone... well yeah, one of those i-geeks, and that gadget apparently only supports imap, no pop.


To re-enable imap-before-smtp I wrote a little setuid wrapper, calling vpopmails open_smtp_relay(). This wrapper has to replace the imapd command in your courier-imap startup script and will exec imapd, after opening the smtp relay, wile imap-login is still in place to authenticate via authdaemond's vchkpw module.



#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <vpopmail_config.h>
#include <vauth.h>
#include <vpopmail.h>

#ifndef IMAPD
# define IMAPD "/usr/bin/imapd"
#endif

extern char **environ;

int main(int argc, const char *argv[]) {
if (argc != 2) {
printf("1 NO argc != 2n");
return -1;
}

open_smtp_relay();
/* no need to chown vpopmail.vchkpw the open-smtp file
* because vchkpw used by qmail-pop3d is setuid root */

sleep(1);

if (setgid(VPOPMAILGID) || setuid(VPOPMAILUID)) {
printf("1 NO setuid/gid, getuid=%dn", getuid());
return -2;
}

execl(IMAPD, IMAPD, argv[1], NULL);

printf("1 NO exec %s failedn", IMAPD);
return -3;
}

So while this worked to my satisfaction, I noticed that the tcp.smtp.cdb not always contained the IPs of the open-smtp file:



$ for ip in $(sed -re 's/:.*//' < open-smtp); do
cdbget $ip < tcp.smtp.cdb > /dev/null
|| echo "$ip is missing";
done

I also found out that, while the vpopmail FAQ is claiming that clearopensmtp is requesting locks on the open-smtp(.lock) file, the source does not read like it would.
I opened a bug report for that issue, but as you might have already guessed, I'm running all of this on an archaic debian box, where no upgrades are planned. So I came up with another home-brewed solution:



#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <vpopmail_config.h>
#include <vauth.h>
#include <vpopmail.h>
#include <fcntl.h>

extern int get_write_lock(FILE*);
extern int lock_reg(int,int,int,off_t,int,off_t);
#define unlock(F) lock_reg(fileno(F), F_SETLK, F_UNLCK, 0, SEEK_SET, 0)

int main(int argc, const char *argv[]) {
FILE *fs_temp, *fs_smtp, *fs_lock = fopen(OPEN_SMTP_LOK_FILE, "w+");
time_t clear = RELAY_CLEAR_MINUTES * 60, now = time(NULL);
int cc = 0, rc = 0;

if (!fs_lock) {
return -1;
}
if (get_write_lock(fs_lock)) {
unlock(fs_lock);
fclose(fs_lock);
return -2;
}

if (!(fs_temp = fopen(OPEN_SMTP_TMP_FILE ".clear", "w"))) {
rc = -3;
} else if (!(fs_smtp = fopen(OPEN_SMTP_CUR_FILE, "r+"))) {
fclose(fs_temp);
rc = -4;
} else {
while (!rc && !feof(fs_smtp)) {
unsigned stime;
char sdata[256];

switch (fscanf(fs_smtp, "%255st%un", sdata, &stime)) {
case 2:
if ((clear + stime) >= now) {
fprintf(fs_temp, "%st%un", sdata, stime);
} else {
++cc;
}
break;

default:
rc = -5;
case EOF:
break;
}
}
fclose(fs_smtp);
fclose(fs_temp);
}

if (!rc) {
if (cc) {
if (rename(OPEN_SMTP_TMP_FILE ".clear", OPEN_SMTP_CUR_FILE)) {
rc = -6;
} else if(update_rules()) {
rc = -7;
}
} else {
unlink(OPEN_SMTP_TMP_FILE ".clear");
}
}

unlock(fs_lock);
fclose(fs_lock);
return rc;
}

It's not the prettiest piece of code, but it helps.
Well, maybe someone else running last-millenium software finds this useful

Tuesday, November 18, 2008

Vpopmail and people reaching their maildir quota

This tiny program lists all users of a domain with a maildir quota usage above 90%.



#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <pwd.h>
#include <errno.h>

#include <vpopmail.h>
#include <vauth.h>

int main(int argc, char **argv) {
struct vqpasswd *user;
char dir[1024];
uid_t uid;
gid_t gid;
int offset_counter = -1, sort = 1, usage;

if (argc != 2) {
fprintf(stderr, "Usage: %s <domain>n", argv[0]);
return EXIT_FAILURE;
}
if (vget_assign(argv[1], NULL, 0, &uid, &gid) == NULL) {
fprintf(stderr, "domain '%s' does not existn", argv[1]);
return EXIT_FAILURE;
}
if (setgid(gid) || setuid(uid)) {
fprintf(stderr, "could not setuid/setgid to %d:%dn", uid, gid);
return EXIT_FAILURE;
}

while (NULL != (user = vauth_getall(argv[1], !++offset_counter, sort))) {
if (strcmp(user->pw_shell, "NOQUOTA")) {
snprintf(dir, sizeof(dir), "%s/Maildir", user->pw_dir);
usage = vmaildir_readquota(dir,
format_maildirquota(user->pw_shell));

if (usage >= 90) {
printf("%s %s %dn", user->pw_name, user->pw_shell, usage);
}
}
}

return EXIT_SUCCESS;
}

Qmail + SpamAssassin + selectivity

Here's how my qmail-queue script looks like to selectively check messages with SpamAssassin for non-relay clients only:



#!/bin/sh
(if [ -n "${RELAYCLIENT+1}" -o `id -u` != 64011 ];
then cat -;
else /usr/bin/spamc -x -U /var/run/spamd/spamd.sock;
fi) | /usr/sbin/qmail-queue.orig

For the records.