Serving multiple domains in one box with SNI - c

I'm using OpenSSL 0.9.8q in FreeBSD-8.2. I have 3 virtual hosts on my system and want to implement SNI to serve for all 3 of them in one server.
I have 3 separate certificates one for each, and in my ssl-server code I have to somehow find out what is the domain-name of client's request, and use the appropriate certificate file based on that. For this I wrote a function named get_ssl_servername_cb and passed it as callback function to SSL_CTX_set_tlsext_servername_callback. This way, in callback function I can get the the domain-name of the client's request.
But my problem is, this callback function is being executed after execution of SSL_accept function, but I have to choose and use the appropriate certificate before using SSL_new command, which is way before execution of SSL_accept.
So my question is, how can I use SSL_CTX_set_tlsext_servername_callback function for SNI?

but my problem is, this callback function is being executed after execution of "SSL_accept" function, but I have to choose and use the appropriate certificate before using "SSL_new" command, which is way before execution of SSL_accept.
When you start your server, you provide a default SSL_CTX. This is used for non-SNI clients, like SSLv3 clients and TLS clients that don't utilize SNI (like Windows XP). This is needed because the callback is not invoked in this situation.
Here are some examples to tickle the behavior using OpenSSL's s_client. To simulate a non-SNI client so that your get_ssl_servername_cb is not called, issue:
openssl s_client -connect localhost:8443 -ssl3 # SNI added at TLSv1
openssl s_client -connect localhost:8443 -tls1 # Windows XP client
To simulate a SNI client so that your get_ssl_servername_cb is called, issue:
openssl s_client -connect localhost:8443 -tls1 -servername localhost
You can also avoid the certificate verification errors by adding -CAfile. This is from one of my test scripts (for testing DSS/DSA certificates on localhost):
printf "GET / HTTP/1.1\r\n\r\n" | /usr/local/ssl/bin/openssl s_client \
-connect localhost:8443 -tls1 -servername localhost \
-CAfile pki/signing-dss-cert.pem
so my question is, how can I use "SSL_CTX_set_tlsext_servername_callback" function for SNI?
See the OpenSSL source code at <openssl dir>/apps/s_server.c; or see How to implement Server Name Indication(SNI) on OpenSSL in C or C++?.
In your get_ssl_servername_cb (set with SSL_CTX_set_tlsext_servername_callback), you examine the server name. One of two situations occur: you already have a SSL_CTX for the server's name, or you need to create a SSL_CTX for server's name.
Once you fetch the SSL_CTX from cache or create a new SSL_CTX, you then use SSL_set_SSL_CTX to swap in the context. There's an example of swapping in the new context in the OpenSSL source files. See the code for s_server.c (in <openssl dir>/apps/s_server.c). Follow the trail of ctx2,
Here's what it looks like in one of my projects. IsDomainInDefaultCert determines if the requested server name is provided by the default server certificate. If not, GetServerContext fetches the needed SSL_CTX. GetServerContext pulls the needed certificate out of an app-level cache; or creates it and puts it in the app-level cache (GetServerContext also asserts one reference count on the SSL_CTX so the OpenSSL library does not delete it from under the app).
static int ServerNameCallback(SSL *ssl, int *ad, void *arg)
{
UNUSED(ad);
UNUSED(arg);
ASSERT(ssl);
if (ssl == NULL)
return SSL_TLSEXT_ERR_NOACK;
const char* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
ASSERT(servername && servername[0]);
if (!servername || servername[0] == '\0')
return SSL_TLSEXT_ERR_NOACK;
/* Does the default cert already handle this domain? */
if (IsDomainInDefCert(servername))
return SSL_TLSEXT_ERR_OK;
/* Need a new certificate for this domain */
SSL_CTX* ctx = GetServerContext(servername);
ASSERT(ctx != NULL);
if (ctx == NULL)
return SSL_TLSEXT_ERR_NOACK;
/* Useless return value */
SSL_CTX* v = SSL_set_SSL_CTX(ssl, ctx);
ASSERT(v == ctx);
if (v != ctx)
return SSL_TLSEXT_ERR_NOACK;
return SSL_TLSEXT_ERR_OK;
}
In the code above, ad and arg are unused parameters. I don't know what ad does because I don't use it. arg can be used to pass in a context to the callback. I don't use arg either, but s_server.c uses it to print some debug information (the arg is a pointer to a BIOs tied to stderr (and a few others), IIRC).
For completeness, SSL_CTX are reference counted and they can be re-used. A newly created SSL_CTX has a count of 1, which is delegated to the OpenSSL internal caching mechanism. When you hand the SSL_CTX to a SSL object, the count increments to 2. When the SSL object calls SSL_CTX_free on the SSL_CTX, the function will decrement the reference count. If the context is expired and the reference count is 1, then the OpenSSL library will delete it from its internal cache.

Related

C: client program reports SSL Certificate verify failed error with localCA

I am writing a simple TLS client/server program to securely communicate over the network. Initially I am building and running both the client and server on the same machine running RHEL 8.2.
First, I am using custom self signned ssl certificate and key for my programs. I have placed the rootCA.crt (my custom CA certificate in /root/CA/rootCA.crt). Also copied the rootCA.pem to /etc/pki/ca-trust/source/anchors/ and executed update-ca-trust enable then update-ca-trust extract to install the certificate to the system. (Not sure if I need to reboot the system for it to take effect.)
Initially, the client and server were able to communicate usint TLS untill I added the certificate validation part of the code on the client side.
Certificate Verification snippet:
ctx = SSL_CTX_new(method); /* Create new context */
if ( ctx == NULL )
{
ERR_print_errors_fp(stderr);
abort();
}
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
SSL_CTX_set_verify_depth(ctx, 4);
const long flags = SSL_OP_NO_SSLv2 |
SSL_OP_NO_SSLv3 |
SSL_OP_NO_TLSv1 |
SSL_OP_NO_TLSv1_1 |
SSL_OP_NO_COMPRESSION;
SSL_CTX_set_options(ctx, flags);
if(SSL_CTX_load_verify_locations(ctx, NULL,
"/root/CA/") == 0){
ERR_print_errors_fp(stderr);
abort();
}
ssl = SSL_new(ctx); /* create new SSL connection state */
SSL_set_fd(ssl, server); /* attach the socket descriptor */
if ( SSL_connect(ssl) == FAIL ) /* perform the connection */
ERR_print_errors_fp(stderr);
else
{
sprintf(acClientRequest, "%s", cpRequestMessage); /* construct reply */
printf("\n\nConnected with %s encryption\n", SSL_get_ciphe
}
when I run the server and client programs I see the following error messafe =>
Onclient:
140736372886336:error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed:ssl/statem/statem_clnt.c:1915:
On Server:
140736022137664:error:14094418:SSL routines:ssl3_read_bytes:tlsv1 alert unknown ca:ssl/record/rec_layer_s3.c:1543:SSL alert number 48
Not sure, what is going wrong with the certificate validation process. Can anyone suggest me how to fix this error?
Just copying your rootCA.crt file into /root/CA/ is not enough.
SSL_CTX_load_verify_locations explicitly states that the files in this directory have to use a specific format:
If CApath is not NULL, it points to a directory containing CA
certificates in PEM format. The files each contain one CA certificate.
The files are looked up by the CA subject name hash value, which must
hence be available. If more than one CA certificate with the same name
hash value exist, the extension must be different (e.g. 9d66eef0.0,
9d66eef0.1 etc). The search is performed in the ordering of the
extension number, regardless of other properties of the certificates.
Use the c_rehash utility to create the necessary links.
Therefore make sure your rootCa.crt is in PEM format. And then generate the required hash and rename it accordingly. The following command generates the hashvalue. Rename the file to <hashvalue>.0.
openssl x509 -inform PEM -subject_hash_old -in rootCa.crt | head -1
If your code then still does not work I would first test if your code works at all. For doing so change the server URL to a real server that sues an HTTPS certificate that is already trusted on your system.

librpm - How to verify the signature key of an installed package

I would like to programmatically check if a RPM package is (1) signed (has a signature) and (2) the key used to sign is trusted.
[root]$ rpm -qi setup
Name : setup
Signature : RSA/SHA1, Wed 02 Oct 2013 05:15:22 AM MDT, Key ID 0946
[root]$ rpm -qi testing
Name : testing
Signature : (none)
I'm browsing the librpm API but I don't see any public methods allowing signature verification on already installed packages.
# This requires a file descriptor
rpmcli.h:rpmVerifySignatures
# This also requires a file descriptor
rpmlib.h:rpmReadPackageFile
Digging further I see:
# This uses a callback `qva_showPackage` which gives (QVA_t, rpmts, Header)
rpmcli.h:rpmcliVerify
But I cannot seem to get RPM tags (RPMTAG_SHA1HEADER) from the Header passed in by the callback. If I could get these tags then it would make sense to call into rpmpgp.h:pgpVerifySig to verify the signature.
Edit:
I see the bulk of the signature verification work is done in a static method rpmchecksig.c:rpmpkgVerifySigs which is only available through rpmcli.h:rpmVerifySignatures. But this method requires a file descriptor. Is there a way to get a FD from an already installed package to be able to use this method?
RPM will verify header-only signatures when retrieving from an rpmdb if enabled through various mode-specific %_vsflags* settings. See /usr/lib/rpm/macros for values.
You will see the verification if you do, say, "rpm -Vvv bash". You can also enable the header-only signature verification on --query (or other) rpm modes by changing specific macros.
There is a means (but not a specific call) to retrieve the header plaintext, the header-only signature, and the pubkey if you wish to verify external to rpm.

How to remove the authorization set by TCP_MD5SIG in setsockopt - C, Linux?

I have used TCP_MD5SIG to create password/key for the connection using the API - setsockopt() in C, Linux. This works fine for me. However, when I use it to remove the password/disable authorization with the same API except that the
struct_tcp_md5sig.tcm_Key = 0;
struc tcp_md5sig.tcm_keylen = 0;
I see that when I invoke the same API -
rc = setsockopt(sock_fd, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof(md5sig));
I see that the rc is -1. The strerror says that "no such file or directory".
I am confused on what I should do to disable the authorization. The same API works, when I pass the password. On the other hand, the same API doesn't work when I want to disable the authorization. I have enabled this protocol in the kernel. So, there is no issue with enabling of this feature in kernel.
AFAICT, You need to ensure that tcp_md5sig.tcpm_addr is the same as when you registered to have it removed.

Using libwebsockets + ssl in asterisk getting error creating ssl context 140A90A1:lib(20):func(169):reason(161)

We are using libwebsockets 1.3 in our ssl enabled web socket client program written in c, we are compiling on Centos 6.5 with openssl 1.0.1 installed, making a .so library which is later used in asterisk. The compilation goes fine but I'm getting this runtime error:
problem creating ssl context 336236705: error:140A90A1:lib(20):func(169):reason(161)
Going through libwebsockets code I spotted the part that is generating the error message (lib/ssl.c line 90):
/* basic openssl init */
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
openssl_websocket_private_data_index =
SSL_get_ex_new_index(0, "libwebsockets", NULL, NULL, NULL);
/*
* Firefox insists on SSLv23 not SSLv3
* Konq disables SSLv2 by default now, SSLv23 works
*/
method = (SSL_METHOD *)SSLv23_server_method();
if (!method) {
error = ERR_get_error();
lwsl_err("problem creating ssl method %lu: %s\n",
error, ERR_error_string(error,
(char *)context->service_buffer));
return 1;
}
context->ssl_ctx = SSL_CTX_new(method); /* create context */
if (!context->ssl_ctx) {
error = ERR_get_error();
lwsl_err("problem creating ssl context %lu: %s\n",
error, ERR_error_string(error,
(char *)context->service_buffer));
return 1;
}
Which according to examples I've seen on the web looks absolutely fine, I've been scratching my head, searching and trying everything for the past couple of days including reinstalling different versions of openssl, changing the code above, replacing SSLv23_server_method with other methods, etc... but can't get it to work, does anybody know where the problem might be?
Additional informaiton:
Using ERR_print_errors_fp() I get:
3077879544:error:140A90A1:lib(20):func(169):reason(161):ssl_lib.c:1802:
part of our code that calls libwebsocket_create_context looks like this:
int opts = 0;
const char *interface = NULL;
int listen_port;
memset(&wsInfo, 0, sizeof wsInfo);
listen_port = CONTEXT_PORT_NO_LISTEN;
wsInfo.port = listen_port;
wsInfo.iface = interface;
wsInfo.protocols = protocols;
wsInfo.extensions = libwebsocket_get_internal_extensions();
wsInfo.gid = -1;
wsInfo.uid = -1;
wsInfo.options = opts;
wsContext = libwebsocket_create_context(&wsInfo);
The program is compiled into an .so library and the library is used in our modified version of asterisk (which itself uses openssl as far as I know).
problem creating ssl context 336236705: error:140A90A1:lib(20):func(169):reason(161)
This may have helped:
$ openssl errstr 0x140A90A1
error:140A90A1:SSL routines:SSL_CTX_new:library has no ciphers
"library has no ciphers" is a sure sign the library was not initialized. See OpenSSL's wiki page on intializing the library at Library Initialization.
Since Asterisk is doing really clever things, you should check what else its doing. In particular, you should ensure its not using weak/wounded/broken protocols and cipher suites. An example of how to improve a security posture can be found at SSL/TLS Client. The sample ensure TLS 1.0 and above, and uses "strong" cipher suites.
I got this error too by using a library that used the boost asio.
The lib was compiled against openssl-1.0, while my binary was compiled against openssl-1.1.
Switching my binary to also use openssl-1.0 solved the issue for me.
The problem is asterisk overrides all openssl initialization functions including SSL_library_init() and OpenSSL_add_all_algorithms() in main\libasteriskssl.c and replaces them with dummy functions that do nothing, instead it defines an ast_ssl_init() which does all the initializations and is called once in main() in main/asterisk.c, my code happened to be before that call.
Too long for a comment, but:
First things first, let's eliminate your code. In the libwebsockets distribution, in test/test-server.c there is a test server that works with SSL. Does that work? If so, I'm guessing it's something you are doing in your code (in which case we are going to need some of your code). If not, I'm guessing it's your distribution.
Next, let's make that error message a bit more informative. Can you introduce ERR_print_errors_fp() to print SSL errors to stderr or similar, and tell us what it says?

Peer to Peer linux authentication in C

I have two embedded systems running Angstrom Linux that are connected via a Ethernet cross-over cable. I'm developing a C program that will allow the two systems to communicate with each other.
When the two computers talk to each other they first need to verify the identity of the other and encrypt the connection. I'm trying to use openssl to accomplish the authentication and encryption but I'm not totally sure what to do.
All the peer to peer questions are related to other languages or aren't related to openssl.
I’ve been trying to modify the code from An Introduction to OpenSSL Programming http://www.linuxjournal.com/article/4822 to get my embedded systems working, but haven’t been successful. The in the SSL_CTX *initialize_ctx which is in common.c and also load_dh_params(ctx,file) in server.c seem to be the problem areas. Here is my code for common.c with some of my modifications.
SSL_CTX *initialize_ctx(keyfile,password)
char *keyfile;
char *password;
{
SSL_METHOD *meth;
SSL_CTX *ctx;
char buffer[200];
if (!bio_err)
{
/* Global system initialization*/
SSL_library_init();
SSL_load_error_strings();
/* An error write context */
bio_err=BIO_new_fp(stderr,BIO_NOCLOSE);
}
debuglocation(__LINE__,__FILE__);
/* Set up a SIGPIPE handler */
signal(SIGPIPE,sigpipe_handle);
/* Create our context*/
meth=SSLv23_method();
ctx=SSL_CTX_new(meth);
debuglocation(__LINE__,__FILE__);
/* Load our keys and certificates*/
// if (!(SSL_CTX_use_certificate_chain_file(ctx,keyfile)))
// berr_exit("Can't read certificate file");
debuglocation(__LINE__,__FILE__);
pass=password;
/* TODO need to put a password on the key*/
//SSL_CTX_set_default_passwd_cb(ctx,password_cb);
//if (!(SSL_CTX_use_PrivateKey_file(ctx,keyfile,SSL_FILETYPE_PEM)))
//http://www.openssl.org/docs/ssl/SSL_CTX_use_certificate.html#NOTES
if(!(SSL_CTX_use_RSAPrivateKey_file(ctx,"private.pem", SSL_FILETYPE_PEM)))
berr_exit("Can't read priveate rsa");
debuglocation(__LINE__,__FILE__);
//berr_exit("Can't read key file");
// /* Load the CAs we trust*/
// if (!(SSL_CTX_load_verify_locations(ctx,
// CA_LIST,0)))
// berr_exit("Can't read CA list");
#if (OPENSSL_VERSION_NUMBER < 0x00905100L)
SSL_CTX_set_verify_depth(ctx,1);
#endif
return ctx;
}
And here is the server.c
void load_dh_params(ctx,file)
SSL_CTX *ctx;
char *file;
{
DH *ret=0;
BIO *bio;
//http://www.openssl.org/docs/crypto/BIO_s_file.html
// opens a file just like fopen with the second parameter as the type of open. Here it is read 'r'.
if ((bio=BIO_new_file(file,"r")) == NULL)
berr_exit("Couldn't open DH file");
//http://www.openssl.org/docs/crypto/pem.html
ret=PEM_read_bio_DHparams(bio,NULL,NULL,
NULL);
BIO_free(bio);
if(SSL_CTX_set_tmp_dh(ctx,ret)<0)
berr_exit("Couldn't set DH parameters");
}
My debuglocation function looks like this.
int debuglocation(int line, char * file)
{
static char c = 'A';
printf("Made it to line %d in %s call it %c\n",line,file, c);
c++;
return 0;
}
So when I run all that I get from the server.
2535:error:1408A0C1:SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher:s3_srvr.c:1075:
And this from the client.
SSL connect error
2616:error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure:s23_clnt.c:596:
Also I’m not sure what ssl commands to use to make the needed certificates.
It seemed like RSA would work well if both the embedded devices had one public and one priviate key, so I tried following http://www.devco.net/archives/2006/02/13/public_-_private_key_encryption_using_openssl.php
and made a script to make them for me.
openssl genrsa -out private.pem 1024
openssl rsa -in private.pem -out public.pem -outform PEM -pubout
Thanks in advance for the help. If you need more information please let me know. I think that answers to this question could be really helpful to anyone developing in C for an embedded system who needs some authentication.
Anthony
As the user that runs the comm process, do ssh_keygen.
Append the public part of the output, id_rsa.pub, to the ~/.ssh/authorized_keys on the other machine. Now you can run remote programs using ssh without logging in.
Edit. I suggested the above because of the hassle of working with certs. You need to have a trust store, correct directory permissions, etc. I think the first thing you're missing is loading the data on trusted certificates. See the link on how to do that. It's easier to check the authorization using the command line tools in openssl then to debug your program and get the SSL set up at the same time.
I ended up using ssh rather than trying to use openssl. It did make life much simpler. Maybe when I have more time I will figure it out the other way.

Resources