repo: tlswrap action: commit revision: path_from: revision_from: cc004d0bef4111be96f779f8ee768fee57dd887d: path_to: revision_to:
commit cc004d0bef4111be96f779f8ee768fee57dd887d Author: epochDate: Mon Apr 8 08:46:42 2024 +0000 added the ability to pick per-servername certs based on SNI. diff --git a/tlswrap.c b/tlswrap.c
--- a/tlswrap.c +++ b/tlswrap.c @@ -2,6 +2,7 @@ #include#include #include +#include #include #include // waitpid @@ -147,10 +148,55 @@ int client_cert(const SSL *ssl) { return 1; } +#include + +// returns 1 on success. +int cert_cb(SSL *ssl, void *arg) { + servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if(!servername) { + return 1;// no servername, let's just use whatever we had already. + } + if(chdir("/etc/tlswrap/") != 0) { + return 1;// if no extra certs are configured, no worries. + } + + // I think I am going to open the dir for reading, then compare the servername to all of the dirs in there using fnmatch. + // configuration can just be done with a subdir named *.stuff for wildcard domains. + syslog(LOG_DAEMON|LOG_ERR,"servername: %s",servername); + if(strstr(servername,"..")) { // is there any good reason for this? + syslog(LOG_DAEMON|LOG_ERR,"someone tried to directory traversal the servername"); + return 0;//might as well make it error here. + } + DIR *dir=opendir("."); + struct dirent *dent; + while(dent=readdir(dir)) { + if(!strcmp(dent->d_name,".") || !strcmp(dent->d_name,"..")) continue; + syslog(LOG_DAEMON|LOG_ERR,"testing domain %s against %s",dent->d_name,servername); + if(!fnmatch(dent->d_name,servername,FNM_NOESCAPE)) { + syslog(LOG_DAEMON|LOG_ERR,"FOUND THE MATCHING DOMAIN"); + if(chdir(dent->d_name) == 0) { + break; // we found a configured domain. + } else { + syslog(LOG_DAEMON|LOG_ERR,"failed to chdir to %s",servername); + } + } + } + closedir(dir); + if(SSL_use_certificate_chain_file(ssl, "cert") <= 0) { + syslog(LOG_DAEMON|LOG_ERR,"failed to load servername cert %s %s",getcwd(0,200),strerror(errno)); + return 1;// even if we can't find the cert, no worries. we got sane fallback. + } + if(SSL_use_PrivateKey_file(ssl, "key", SSL_FILETYPE_PEM) <= 0) { + syslog(LOG_DAEMON|LOG_ERR,"failed to load servername key %s %s",getcwd(0,200),strerror(errno)); + return 0;// if we loaded the cert but can't find the key. worries. key and cert gotta match. + } + return 1; +} + //what is ad and arg? int sni_cb(SSL *ssl, int *ad, void *arg) { if(!ssl) return SSL_TLSEXT_ERR_NOACK; - //SSL_CTX *ctx=SSL_get_SSL_CTX(ssl); +// SSL_CTX *ctx=SSL_get_SSL_CTX(ssl); servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); if(!servername || servername[0] == '\0') { syslog(LOG_DAEMON|LOG_DEBUG,"no SNI"); @@ -162,23 +208,6 @@ int sni_cb(SSL *ssl, int *ad, void *arg) { } syslog(LOG_DAEMON|LOG_DEBUG,"SNI: %s",servername); return SSL_TLSEXT_ERR_OK; - //TODO: figure out a good way to do certs based on vhost here. - if(chdir("/etc/ssl/certs/") != 0) { - return SSL_TLSEXT_ERR_OK;//skipping per-vhost certs and keys - } - //if(SSL_CTX_use_certificate_chain_file(ctx, servername) <= 0) { - // syslog(LOG_DAEMON|LOG_ERR,"failed to load servername cert"); - // return SSL_TLSEXT_ERR_NOACK; - // } - if(chdir("/etc/ssl/keys/") != 0) { - return SSL_TLSEXT_ERR_OK;//not sure if returning here will break stuff or not - } - //if(SSL_CTX_use_PrivateKey_file(ctx, servername, SSL_FILETYPE_PEM) <= 0) { - // syslog(LOG_DAEMON|LOG_ERR,"failed to load servername key"); - // return SSL_TLSEXT_ERR_NOACK; - // } - //probably attempt to open certs named after the vhosts in a config dir. - return SSL_TLSEXT_ERR_OK; } int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { @@ -398,7 +427,6 @@ int tlswrap_SSL_early_cb_fn(SSL *s, int *al, void *arg) { #endif int main(int argc,char *argv[]) { - setpgid(0,0); setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); @@ -511,6 +539,7 @@ int main(int argc,char *argv[]) { } SSL_CTX_set_tlsext_servername_callback(ctx,sni_cb); + SSL_CTX_set_cert_cb(ctx,cert_cb,NULL); ssl = SSL_new(ctx); #ifdef JA3 @@ -580,6 +609,7 @@ int main(int argc,char *argv[]) { int child=fork(); if(child == 0) { + setpgid(0,0); x=dup(0); dup2(a[0],0); dup2(b[1],1); @@ -684,28 +714,32 @@ int main(int argc,char *argv[]) { } } } +#ifdef NOSHUTDOWN + exit(0); +#endif SSL_shutdown(ssl); SSL_free(ssl); EVP_cleanup(); int status; char *url=getenv("REMOTE_URL"); - if(killpg(getpid(),SIGCONT)) { //if the process hasn't exited already, let's tell them they've been hungup on. + if(kill(-child,SIGCONT)) { //if the process hasn't exited already, let's tell them they've been hungup on. syslog(LOG_DAEMON|LOG_WARNING,"%s: killpg(%d,SIGCONT): %s",url,getpid(),strerror(errno)); } sleep(5); if(waitpid(child,&status,WNOHANG) == child) { - syslog(LOG_DAEMON|LOG_ERR,"%s: child process exited after a SIGCONT and a 5s wait. gonna send a HUP to everything else anyway.",url); + syslog(LOG_DAEMON|LOG_DEBUG,"%s: child process exited after a SIGCONT and a 5s wait. gonna send a HUP to everything else anyway.",url); + // this is fine. no need to ERR it. } - if(killpg(getpid(),SIGHUP)) { + if(kill(-child,SIGHUP)) { syslog(LOG_DAEMON|LOG_ERR,"%s: killpg: %s",url,strerror(errno)); } sleep(5); - if(killpg(getpid(),SIGTERM)) { + if(kill(-child,SIGTERM)) { syslog(LOG_DAEMON|LOG_ERR,"%s: killpg: %s",url,strerror(errno)); } sleep(5); - if(killpg(getpid(),SIGKILL)) { + if(kill(-child,SIGKILL)) { syslog(LOG_DAEMON|LOG_ERR,"%s: killpg: %s",url,strerror(errno)); } syslog(LOG_DAEMON|LOG_ERR,"%s: sent signals to all processes in our process group. they should be DEAD",url);
-----END OF PAGE-----