repo: tlswrap action: commit revision: path_from: revision_from: f5f8ee4c04fbcfef45a8a9e0bb87035d32064101: path_to: revision_to:
commit f5f8ee4c04fbcfef45a8a9e0bb87035d32064101 Author: epochDate: Wed Dec 20 15:07:03 2023 +0000 tlswrap got pid grouping so it can kill off all children. also made a version that does not actually do any TLS to do /just/ the pid grouping and killing. diff --git a/Makefile b/Makefile
--- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ PREFIX:=/usr/local .PHONY: install all -all: tlswrap-ja3 tlswrap +all: tlswrap-ja3 tlswrap nontlswrap tlswrap: CFLAGS=-pedantic -Wall tlswrap: LDLIBS=-lssl -lcrypto @@ -12,6 +12,10 @@ tlswrap-ja3: CFLAGS=-DJA3 -pedantic -Wall tlswrap-ja3: LDLIBS=-lssl -lcrypto tlswrap-ja3: tlswrap-ja3.c +nontlswrap: CFLAGS=-pedantic -Wall +nontlswrap: nontlswrap.c + install: all install -Dt $(PREFIX)/bin tlswrap install -Dt $(PREFIX)/bin tlswrap-ja3 + install -Dt $(PREFIX)/bin nontlswrap diff --git a/nontlswrap.c b/nontlswrap.c new file mode 100644 index 0000000000000000000000000000000000000000..d9454045ec2e4ce940b8981f43b3577182fdfae9 --- /dev/null +++ b/nontlswrap.c @@ -0,0 +1,209 @@ +#include// snprintf +#include // setenv +#include +#include +#include +#include + +#include +#include // waitpid +#include +#include + +#include + +int main(int argc,char *argv[]) { + setpgid(0,0); + setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + syslog(LOG_DAEMON|LOG_DEBUG,"started"); + struct sockaddr_in6 sa6; + char ra[NI_MAXHOST],rp[NI_MAXSERV]; + char sa[NI_MAXHOST],sp[NI_MAXSERV]; + char ru[6+3+NI_MAXHOST+NI_MAXSERV+1];//6 for "tcp://", 3 just in case [ and ] are needed and the :, +1 for null + char su[6+3+NI_MAXHOST+NI_MAXSERV+1]; + unsigned int sl=sizeof(sa6); + + argv++;//skip argv[0] + argc--; + + if(argc == 0 + || !strcmp(argv[0],"--help") + || !strcmp(argv[0],"-h")) { + fprintf(argc?stdout:stderr,"usage: nontlswrap [--help|-h] [ ] [ ] [...]\n"); + return 1; + } + if(argc == 0) { + fprintf(stderr,"missing argument. need the absolute path of an executable and arguments to execv into at the end.\n"); + return 1; + } + + if(getsockname(0,(struct sockaddr *)&sa6,&sl) != -1) { + if(getnameinfo((struct sockaddr *)&sa6,sl,sa,sizeof(sa),sp,sizeof(sp),NI_NUMERICHOST|NI_NUMERICSERV) == 0) { + setenv("SERVER_ADDR",sa,1); + setenv("SERVER_PORT",sp,1); + if(sa6.sin6_family == AF_INET6) { + snprintf(su,sizeof(su)-1,"tcp://[%s]:%s",sa,sp); + } else { + snprintf(su,sizeof(su)-1,"tcp://%s:%s",sa,sp); + } + setenv("SERVER_URL",su,1); + } + } + if(getpeername(0,(struct sockaddr *)&sa6,&sl) != -1) { + if(getnameinfo((struct sockaddr *)&sa6,sl,ra,sizeof(ra),rp,sizeof(rp),NI_NUMERICHOST|NI_NUMERICSERV) == 0) { + setenv("REMOTE_ADDR",ra,1); + setenv("REMOTE_PORT",rp,1); + if(sa6.sin6_family == AF_INET6) { + snprintf(ru,sizeof(ru)-1,"tcp://[%s]:%s",ra,rp); + } else { + snprintf(ru,sizeof(ru)-1,"tcp://%s:%s",ra,rp); + } + setenv("REMOTE_URL",ru,1); + } + } + int x; + + int a[2]; //a is subprocess's stdin, so need to read decrypted data from stdin and write to a[1] + int b[2]; //b is subprocees's stdout, so need to read it, and give it to SSL to encrypt and push out. + int c[2]; //c is subprocess's stderr, so need to read it, and write lines to syslog. + pipe(a); + pipe(b); + pipe(c); + + syslog(LOG_DAEMON|LOG_DEBUG,"accepted a connection!"); + char buffer[65535];//fuck it. let's make it big. + + int child=fork(); + if(child == 0) { + x=dup(0); + dup2(a[0],0); + dup2(b[1],1); + dup2(c[1],2); + close(a[0]); + close(b[1]); + close(a[1]); + close(b[0]); + close(c[0]); + close(c[1]); + dup2(x,3);//we're passing this to the child ONLY so it can do getpeername and stuff. this can probably be closed. + execv(argv[0],argv); + printf("[!!!] server-side tlswrap configuration error.\n"); + printf("[!!!] failed to execute subprocess.\n"); + for(;*argv;argv++) { + printf("[!!!] argv: %s\n",*argv); + } + return 0; + } + if(child == -1) { + syslog(LOG_DAEMON|LOG_WARNING,"failed to fork"); + return 1; + } + //fprintf(stderr,"made it here\n"); + syslog(LOG_DAEMON|LOG_ERR,"our pid: %d child pid: %d",getpid(),child); + int j; + int r1; + int r2; + int r3; + int fdmax=0; + fd_set master; + fd_set readfs; + fd_set errorfs; + FD_ZERO(&master); + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + FD_SET(0,&master);//SSL is ready to be read from + FD_SET(b[0],&master);//subprocess's stdout is ready to be read from + FD_SET(c[0],&master);//subprocess's stderr + fdmax=b[0]>c[0]?b[0]:c[0]; + struct timeval orig_timeout; + struct timeval timeout; + orig_timeout.tv_sec=0; + orig_timeout.tv_usec=10000;// 1/100th of a second. (10ms) sound good? + close(a[0]); + close(b[1]); + close(c[1]); + //unsigned int error_code; + //unsigned int error_code_size = sizeof(error_code); + //syslog(LOG_DAEMON|LOG_DEBUG,"entering select loop"); + //fprintf(stderr,"made it here\n"); + for(;FD_ISSET(b[0],&master) || FD_ISSET(c[0],&master);) { //a select() brick that reads from ssl and writes to subprocess and reads from subprocess and writes to ssl + readfs=master; + errorfs=master; + timeout=orig_timeout; + if((j=select(fdmax+1,&readfs,0,&errorfs,&timeout)) == -1 ) { + //syslog(LOG_DAEMON|LOG_ERR,"giving up. error'd in select"); + break; + } + + if(recv(0,NULL,1, MSG_PEEK | MSG_DONTWAIT) == 0) { //make sure the TLS is still connected. :D + syslog(LOG_DAEMON|LOG_ERR,"non-TLS connection seems to have dropped unexpectedly.\n"); + break; + } + + if(FD_ISSET(0,&errorfs)) syslog(LOG_DAEMON|LOG_ERR,"select: stdin error"); + if(FD_ISSET(b[0],&errorfs)) syslog(LOG_DAEMON|LOG_ERR,"select: b[0] error"); + if(FD_ISSET(c[0],&errorfs)) syslog(LOG_DAEMON|LOG_ERR,"select: c[0] error"); + if(FD_ISSET(0,&readfs)) { + if((r1=read(0,buffer,sizeof(buffer))) <= 0) { + syslog(LOG_DAEMON|LOG_DEBUG,"EOF or error on stdin %d msg: %s",r1,strerror(errno)); + FD_CLR(0,&master); + close(a[1]); + } else { + syslog(LOG_DAEMON|LOG_DEBUG,"SSL read? %d msg: %s",r1,strerror(errno)); + syslog(LOG_DAEMON|LOG_DEBUG,"read %d bytes from ssl!",r1); + if((r3=write(a[1],buffer,r1) < 0)) { + syslog(LOG_DAEMON|LOG_ERR,"a write failed. -_- %d",r3); + } + } + } + if(FD_ISSET(b[0],&readfs)) { + if((r2=read(b[0],buffer,sizeof(buffer))) <= 0) { + syslog(LOG_DAEMON|LOG_DEBUG,"subprocess stdout done."); + FD_CLR(b[0],&master); + close(b[0]); + } else { + syslog(LOG_DAEMON|LOG_DEBUG,"read %d bytes from subprocess!",r2); + if((r3=write(1,buffer,r2)) <= 0) { + syslog(LOG_DAEMON|LOG_ERR,"write had an error: %d %s",r3,strerror(errno)); + } + } + } + if(FD_ISSET(c[0],&readfs)) { + if((r2=read(c[0],buffer,sizeof(buffer)-1)) <= 0) { + syslog(LOG_DAEMON|LOG_DEBUG,"subprocess stderr done."); + FD_CLR(c[0],&master); + } else { + //write(2,buffer,r2); + buffer[r2]=0;//gotta null this off sice we're passing to something that expects a string. + //fprintf(stderr,"%s",buffer); + syslog(LOG_DAEMON|LOG_WARNING,"%s -> %s stderr of [%s] : %s",ru,su,argv[0],buffer); + } + } + } + + int status; + if(killpg(getpid(),SIGCONT)) { //if the process hasn't exited already, let's tell them they've been hungup on. + syslog(LOG_DAEMON|LOG_WARNING,"killpg(%d,SIGCONT): %s",getpid(),strerror(errno)); + } + sleep(5); + if(waitpid(child,&status,WNOHANG) == child) { + syslog(LOG_DAEMON|LOG_ERR,"child process exited after a SIGCONT and a 5s wait. gonna send a HUP to everything else anyway."); + } + if(killpg(getpid(),SIGHUP)) { + syslog(LOG_DAEMON|LOG_ERR,"killpg: %s",strerror(errno)); + } + sleep(5); + if(killpg(getpid(),SIGTERM)) { + syslog(LOG_DAEMON|LOG_ERR,"killpg: %s",strerror(errno)); + } + sleep(5); + if(killpg(getpid(),SIGKILL)) { + syslog(LOG_DAEMON|LOG_ERR,"killpg: %s",strerror(errno)); + } + syslog(LOG_DAEMON|LOG_ERR,"sent signals to all processes in our process group. they should be DEAD"); + return 0; +} + + diff --git a/tlswrap.c b/tlswrap.c
--- a/tlswrap.c
+++ b/tlswrap.c
@@ -398,6 +398,7 @@ 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);
@@ -539,9 +540,9 @@ int main(int argc,char *argv[]) {
}
//now, let's try harder on these error messages.
err_err = ERR_get_error(); //???
- if(errno == 0) {//good nuff?y
- return 1;
- }
+ //if(errno == 0) {//good nuff?y
+ // return 1;
+ //}
syslog(LOG_DAEMON|LOG_ERR,"%s -> %s SSL_accept() failed. %d / %d / %d / %s / %s / %d / %s",
ru,
@@ -686,22 +687,27 @@ int main(int argc,char *argv[]) {
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.
+ 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_DEBUG,"child process exited as it should :)");
- return 0;
+ 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_WARNING,"waitpid: %s",strerror(errno));
- if(kill(child,SIGTERM)) { //if the process hasn't exited already, let's try to kill it.
- syslog(LOG_DAEMON|LOG_WARNING,"kill: %s",strerror(errno));
+ if(killpg(getpid(),SIGHUP)) {
+ syslog(LOG_DAEMON|LOG_ERR,"%s: killpg: %s",url,strerror(errno));
}
sleep(5);
- if(waitpid(child,&status,WNOHANG) == child) {
- syslog(LOG_DAEMON|LOG_WARNING,"child process exited after a SIGTERM and a 5s wait.");
- return 0;
+ if(killpg(getpid(),SIGTERM)) {
+ syslog(LOG_DAEMON|LOG_ERR,"%s: killpg: %s",url,strerror(errno));
+ }
+ sleep(5);
+ if(killpg(getpid(),SIGKILL)) {
+ syslog(LOG_DAEMON|LOG_ERR,"%s: killpg: %s",url,strerror(errno));
}
- syslog(LOG_DAEMON|LOG_CRIT,"waitpid: %s",strerror(errno));
+ syslog(LOG_DAEMON|LOG_ERR,"%s: sent signals to all processes in our process group. they should be DEAD",url);
return 0;
}
-
-
-----END OF PAGE-----