This is a small program that basically tries to connect to an https server and establish a connection. The sole aim is to determine if the server supports the cipher that has been passed on the command line. This may not sound useful, but it allowed me to diagnose a problem with a mod_ssl enabled apache server today. A script to use it is attached at the bottom.
/* ssl_cipher.c
*
* Test an ssl connection using the named cipher
*
* cc -Wall -Wshadow -g -O0 -o ssl_cipher ssl_cipher.c -lssl -lcrypto
*
* Written by david reid, 4th November 2005
*/
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <getopt.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
static int port=443;
static int errExit(const char *string)
{
fprintf(stderr,"%s\n",string);
exit(0);
}
int tcpConnect(char *host, int _port)
{
struct sockaddr sa;
struct addrinfo hints, *res0;
int sock;
memset(&sa, 0, sizeof(sa));
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_INET;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(host, NULL, &hints, &res0) == 0) {
memcpy(&sa, res0->ai_addr, res0->ai_addrlen);
freeaddrinfo(res0);
} else {
printf("unable to resolve hostname %s\n", host);
errExit("Resolve failed");
}
((struct sockaddr_in *)&sa)->sin_port = htons(_port);
if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
errExit("Couldn't create socket");
if(connect(sock, &sa, sizeof(sa)) < 0)
errExit("Couldn't connect socket");
return sock;
}
int main(int argc, char **argv)
{
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;
int sock = -1, rv;
char *host = NULL, *ciphers = NULL;
extern char *optarg;
int c;
OpenSSL_add_all_algorithms();
OpenSSL_add_all_digests();
SSL_load_error_strings();
while((c = getopt(argc, argv, "h:p:c:")) != -1){
switch(c){
case 'h':
if(!(host = strdup(optarg)))
errExit("Out of memory");
break;
case 'p':
if(!(port = atoi(optarg)))
errExit("Bogus port specified");
break;
case 'c':
if(!(ciphers = strdup(optarg)))
errExit("Out of memory");
break;
}
}
if (!host)
errExit("A host name must be supplied, '-h '");
if (!ciphers)
errExit("You MUST supply the cipher to use '-c '");
/* Connect the TCP socket*/
sock = tcpConnect(host, port);
/* Build our SSL context*/
ctx = SSL_CTX_new(SSLv23_client_method());
/* Connect the SSL socket */
if (!(ssl = SSL_new(ctx)))
errExit("Unable to get an SSL object");
/* Set our cipher list */
if (! SSL_set_cipher_list(ssl, ciphers))
errExit("Unable to set the cipher requested");
if (! SSL_set_fd(ssl, sock))
errExit("Unable to set the socket into the SSL context!");
rv = SSL_connect(ssl);
SSL_shutdown(ssl);
close(sock);
return (rv == 1 ? 0 : 1);
}
This is the script that I used to call the above program and so determine the actual list of ciphers that could be used.
#!/usr/bin/env perl use strict; my %works; die "Usage: $0\n" if $#ARGV == -1; my @list = split(/:|\n/, `openssl ciphers`); for my $i (0 .. $#list) { `./ssl_cipher -h $ARGV[0] -c $list[$i]`; if ($?) { print "Cipher $list[$i] didn't work...\n"; } else { $works{$list[$i]}++; } } print "Working ciphers:\n"; foreach my $c (sort(keys(%works))) { print "\t$c\n"; }



