/* * * sst: Simple Ssl Tunneling. * * - yet another generic SSL client/server/forwarder. * * - can be used as a drop-in program for encoding * or decoding networked I/O. * * - can be used as an SSL preprocessor/decoder for inetd servers. * * NOTE: * netcat (nc) is needed to handle networking I/O * when operating as a standalone client, as a * standalone server, or as an inetd forwarder. * *** * * Written by P Kern . * * Copyright (c) 2000 University of Toronto. All rights reserved. * Anyone may use or copy this software, except that this copyright * notice remain intact, and that credit is given where it is due. * The University of Toronto and the author make no warranty and * accept no liability for this software. * *** * * Partly inspired by: * * stunnel Universal SSL tunnel * Copyright (c) 1998-2000 Michal Trojnara * All Rights Reserved * *** * * SSL code usage based on openssl/demos/ssl/{cli,serv}.cpp: * * from cli.cpp: * /* cli.cpp - Minimal ssleay client for Unix * 30.9.1996, Sampo Kellomaki ** * /* mangled to work with SSLeay-0.9.0b and OpenSSL 0.9.2b * Simplified to be even more minimal * 12/98 - 4/99 Wade Scholine ** * * from serv.cpp: * /* serv.cpp - Minimal ssleay server for Unix * 30.9.1996, Sampo Kellomaki ** * /* mangled to work with SSLeay-0.9.0b and OpenSSL 0.9.2b * Simplified to be even more minimal * 12/98 - 4/99 Wade Scholine ** * *** */ /***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** * example usages: ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** * example 1. * * sst -c * ------ * +-----------+ * unencrypted stream | | SSL encrypted stream * <======================>+fd0 fd1+<======================> * | | * +-----------+ * * - relay between unencrypted data on stdin * and SSL encrypted data on stdout. * - expects the remote side to have certificates in order. * - NOTE: not intended for interactive use since shells tend to * create stdin and stdout as unidirectional file-descriptors. * in this mode, sst expects to be able to both read and write * on either descriptor. * ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** * example 2. * * sst -s * ------ * +-----------+ * SSL encrypted stream | | decrypted stream * <======================>+fd0 fd1+<======================> * | | * +-----------+ * * - relay between SSL encrypted data on stdin * and decrypted raw data on stdout * - try to have the certificates in order. * - NOTE: not intended for interactive use since shells tend to * create stdin and stdout as unidirectional file-descriptors. * in this mode, sst expects to be able to both read and write * on either descriptor. * ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** * example 3. * * sst -c -p host:NNN * ------------------ * +-----------+ * unencrypted stream | | decrypted stream * >>-------------------->>+fd0 fd1+>>--------------------->> * | | * | | (parent process) * +-----+-----+ * ^ * | SSL encrypted stream * | * v * +-----+-----+ * (child process) | fd0/fd1 | * | | network data stream * | +<======================> * "nc host NNN" | | * +-----------+ * * - same as example 1 but use netcat to establish the * network connection to host hhhh at port NNN on the remote side. * - suitable for interactive use/testing. * - sample usages: * % sst -c -p mbox.ca:993 # interact with a remote IMAP/SSL server * % sst -c -p 995 # interact with the local POP/SSL server * ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** * example 4. * * sst -s -p NNN * ------------- * +-----------+ * unencrypted stream | | decrypted stream * >>-------------------->>+fd0 fd1+>>--------------------->> * | | * (parent process) | | * +-----+-----+ * ^ * | SSL encrypted stream * | * v * +-----+-----+ * | fd0/fd1 | (child process) * network data stream | | * <======================>+ | * | | "nc -l -p NNN" * +-----------+ * * - same as example 2 but use netcat to listen * for network connections on port NNN. * - suitable for interactive use/testing. * ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** * example 5. * * sst -i -p hhhh:NNN * ------------------ * +-----------+ * SSL encrypted stream | | * <======================>+fd0 | * [inherited from inetd] | | (parent process) * | fd1 | * +-----+-----+ * ^ * | decrypted stream * v * +-----+-----+ * (child process) | fd0/fd1 | * | | network data stream * | +<======================> * "nc hhhh NNN" | | * +-----------+ * * - inetd mode ("-i") implies server mode ("-s") as with example 2. * - forwards (punts) the decrypted stream to another host (hhhh) and * port (NNN) by using netcat to establish the network connection. * * - sample inetd.conf entries: * * # handle SSL encryption for our local imap server. * simap stream tcp nowait root /local/bin/sst sst -i -p 143 * * or * * # handle SSL encryption for the imap server on mboxhost. * simap stream tcp nowait root /local/bin/sst sst -i -p mboxhost:143 * ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** * example 6. * * sst -i -- * ----------------------- * +-----------+ * SSL encrypted stream | | * <======================>+fd0 | * [inherited from inetd] | | (parent process) * | fd1 | * +-----+-----+ * ^ * | decrypted stream * v * +-----+-----+ * (child process) | fd0/fd1 | * | | * "" | | * +-----------+ * - as with example 5 but instead of running netcat, just execute an * arbitrary command to use/process the data on the decrypted stream. * * - a sample inetd.conf entry: * * # encrypted quotes - make fortunes available via SSL. * qotd stream tcp nowait root /local/bin/sst sst -i -- /usr/games/fortune -l * ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** */ #ifndef lint static char rcsid[] = "$Header: /c/src/local.bin/sst/RCS/sst.c,v 1.23 2015/05/06 13:24:00 pkern Exp $"; #endif #include #include #include #include #include #include #include /* for MAXPATHLEN */ #include #include #include #include #include #include #if defined(sun) || defined(__FreeBSD__) #include #endif #include #ifndef AF_LOCAL #define AF_LOCAL AF_UNIX #endif /* #include /* SSLeay stuff */ #include #include #include #include #include #define Perror(s) \ if (logging) syslog(LOG_ERR, "%s: %m", (s)); else perror((s)); #define CHK_NULL(x) if ((x)==NULL) quit (1) #define CHK_ERR(err,s) if ((err)==-1) { Perror(s); quit(2); } #define CHK_SSL(err) if ((err)==-1) { show_SSL_errors(); quit(3); } int server = 0; int debug = 0; int verbose = 0; int logging = 0; int timeout = 0; int inetd = 0; int eofclnt = 0; int nossl = 0; int require_certs = 0; int verify_cert = 0; int self_signed_ok = 1; char *prog = "sst"; char *host = NULL; char *port = NULL; char *method = NULL; char certfbuf[MAXPATHLEN], ssldbuf[MAXPATHLEN]; char *certf = NULL, *pkeyf = NULL, *ssld = NULL; #ifndef CONFDIR #define CONFDIR "/local/lib/openssl" #endif #ifndef CERTF #define CERTF "certs/sst.pem" #endif #ifndef LOG_SSL #define LOG_SSL LOG_USER #endif #ifndef NETCAT #define NETCAT "/local/bin/nc" #endif #ifdef USE_EGD /* * USE_EGD - added for openssl-0.9.6a. * according to OpenSSL's FAQs, the need for this extra USE_EGD code * will be made obsolete because the RAND_* routines in openssl-0.9.7 * will automatically search for EGD sockets at pre-defined locations. */ #ifndef EGD_SOCK #define EGD_SOCK "/tmp/.egd-pool" #endif char *egds = EGD_SOCK; #endif /* USE_EGD */ FILE *tty = NULL; pid_t pid = 0; /* * ERR_log_errors(): * adapted from ERR_print_errors_fp() * as found in OpenSSL's ... * crypto/err/err_prn.c * Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. */ void ERR_log_errors() { unsigned long l; char buf[200]; const char *file,*data; int line,flags; unsigned long es; es=CRYPTO_thread_id(); while ((l=ERR_get_error_line_data(&file,&line,&data,&flags)) != 0) { syslog(LOG_ERR,"%lu:%s:%s:%d:%s\n", es,ERR_error_string(l,buf),file,line, (flags&ERR_TXT_STRING)?data:""); } } void show_SSL_errors() { if (logging) ERR_log_errors(); else ERR_print_errors_fp(stderr); } #define SHOW_x(L,F,x) do { \ if (logging) syslog((L), "%s", (x)); \ else fprintf((F), "%d: %s\n", getpid(), (x)); } while(0) #define SHOW_x1(L,F,M,x) do { \ if (logging) syslog((L), (M), (x)); \ else { fprintf((F), "%d: ", getpid()); \ fprintf((F), (M), (x)); fputs("\n", (F)); } } while(0) #define SHOW_x2(L,F,M,x1,x2) do { \ if (logging) syslog((L), (M), (x1), (x2)); \ else { fprintf((F), "%d: ", getpid()); \ fprintf((F), (M), (x1), (x2)); fputs("\n", (F)); } } while(0) #define SHOW_err(a) SHOW_x(LOG_ERR,stderr,a) #define SHOW_err1(f,a) SHOW_x1(LOG_ERR,stderr,f,a) #define SHOW_err2(f,a1,a2) SHOW_x2(LOG_ERR,stderr,f,a1,a2) #define SHOW_info(a) SHOW_x(LOG_DEBUG,tty,a) #define SHOW_info1(f,a) SHOW_x1(LOG_DEBUG,tty,f,a) #define SHOW_info2(f,a1,a2) SHOW_x2(LOG_DEBUG,tty,f,a1,a2) char *usageopts[] = { "", " options:", " --------", " -c = client mode.", " -s = server mode.", " -e = shutdown on client's EOF.", " -l = redirect all messages to syslog(3).", " -i = inetd mode (implies both '-s' and '-l').", " -n = raw mode. don't use SSL to process the I/O.", " -v = be chatty about what's happening.", " -r = server mode: require a peer certificate.", " client mode: provide a certificate.", " -d = enable debugging messages (this implies '-v').", " repeated use of '-d' enables more debugging output.", " -p [host:]port = connect to (or listen to) portnum.", " {client,inetd - assumes 'localhost' if no 'host:' part given}", " {server - 'host:' part is ignored if it's present }", " -t timeout = set maximum idle time (in seconds).", " -C cert-file = use instead of the default certificate.", " -K pkey-file = use instead of the default private key file.", " -D ssl-conf = use as the path to default cert/keys.", " -M method = use a specific SSL method (ssl2, ssl3 or tls1).", #ifdef USE_EGD " -E skt-path = use instead of the default EGD socket.", #endif " -V number = verify peer certificate to a 'depth' of .", " -S = [server mode] strict checking. no self-signed certificates.", "", " auxiliary command:", " ------------------", " the command to be executed instead of the default netcat commands.", "", NULL }; usage() { char **uop = usageopts; if (logging) { syslog(LOG_ERR, "usage: %s [ '--' ]", prog); while (*uop != NULL) syslog(LOG_ERR, "%s", *uop++); } else { fprintf(stderr, "usage: %s [ '--' ]\n", prog); while (*uop != NULL) fprintf(stderr, "%s\n", *uop++); } } /* reaper -- zombie prevention */ void reaper() { int w; pid_t p; while ((p = wait3(&w, WNOHANG, 0)) > 0) { if (debug) SHOW_info1("reaped %d", p); if (p == pid) pid = 0; continue; } } /* quit -- clean up and exit */ static void quit(n) int n; { if (debug) SHOW_info1("quit %d", n); if (pid > 0) { if (debug) SHOW_info1("kill %d", pid); (void) kill (pid, SIGKILL); } exit(n); } /* * relay data. * * - decrypt data recvd on the SSL descriptor (sd) and * write the resulting output to the wd descriptor. * * - read data recvd on the rd descriptor, and send it out, via * SSL_write, to be encrypted and written to the sd descriptor. * * - EOF on the SSL stream means that the SSL connection has shutdown. * * - EOF on rd when in server mode means the actual server has finished. */ relay(ssl, sd, rd, wd) SSL *ssl; int sd, rd, wd; { fd_set fds; char *p, buf[4096]; int err, r, tblw; struct timeval tv, *tp = NULL; off_t nsr, nsw, nlr, nlw; int n, nw, rf = 1, sf = 1; nsr = nsw = nlr = nlw = (off_t)0; if (timeout > 0) { tp = &tv; tv.tv_sec = timeout; tv.tv_usec = 0; } while (sf || rf) { FD_ZERO(&fds); if (sf) FD_SET(sd, &fds); if (rf) FD_SET(rd, &fds); tblw = 1 + (( sd > rd ) ? sd : rd ); #if defined(__FreeBSD__) && __FreeBSD__ >= 9 r = select(FD_SETSIZE, &fds, NULL, NULL, tp); #else /* */ r = select(tblw, &fds, NULL, NULL, tp); #endif /* */ if (debug > 3) SHOW_info1("select = %d", r); if (r < 0) { if (errno == EINTR) { /* Interrupted system call */ if (debug) Perror("select"); } else { Perror("select"); quit(10); } } else if (r == 0) { if (verbose) SHOW_info("timed out."); goto done; } else for ( ; r > 0; r--) { if (FD_ISSET(sd, &fds)) { if (debug > 3) SHOW_info1("select: sd: %d", r); if (ssl != NULL) { err = SSL_read(ssl, buf, sizeof(buf) - 1); CHK_SSL(err); } else { err = read(sd, buf, sizeof(buf) - 1); if (err < 0) { Perror("read(sd)"); quit(41); } } buf[err] = '\0'; if (err == 0) { sf = 0; if (debug) SHOW_info("EOF(sd)"); goto done; } nsr += err; for (p = buf, nw = err; nw > 0; nw -= n, p += n) { n = write(wd, p, nw); if (n < 0) { Perror("local write"); quit(12); } } nlw += err; FD_CLR(sd, &fds); continue; } if (FD_ISSET(rd, &fds)) { if (debug > 3) SHOW_info1("select: rd: %d", r); n = read(rd, buf, sizeof(buf)-1); if (n < 0) { Perror("local read"); quit(13); } if (n == 0) { rf = 0; if (debug) SHOW_info("EOF(rd)"); if (!server && eofclnt) { if (ssl != NULL) { err = SSL_shutdown(ssl); if (verbose) SHOW_info1("shutdown ssl client (%d)", err); } } else if (server && sf) { if (ssl != NULL) { err = SSL_shutdown(ssl); if (verbose) SHOW_info1("shutdown ssl server (%d)", err); } if (err) { sf = 0; if (verbose) SHOW_info("close relay"); } } } else { nlr += n; if (ssl != NULL) { err = SSL_write(ssl, buf, n); CHK_SSL(err); } else { err = write(sd, buf, n); if (err < 0) { Perror("write(sd)"); quit(42); } } nsw += err; } FD_CLR(rd, &fds); continue; } } } done: if (sf && ssl != NULL) { err = SSL_shutdown(ssl); if (verbose) SHOW_info1("shutdown ssl (%d)", err); } if (verbose) { if (sizeof(off_t) > 4) { if (ssl != NULL) { SHOW_info1("bytes from ssl: %qd", nsr); SHOW_info1("bytes to ssl: %qd", nsw); } else { SHOW_info1("bytes from remote: %qd", nsr); SHOW_info1("bytes to remote: %qd", nsw); } SHOW_info1("bytes from local: %qd", nlr); SHOW_info1("bytes to local: %qd", nlw); } else { if (ssl != NULL) { SHOW_info1("bytes from ssl: %ld", nsr); SHOW_info1("bytes to ssl: %ld", nsw); } else { SHOW_info1("bytes from remote: %ld", nsr); SHOW_info1("bytes to remote: %ld", nsw); } SHOW_info1("bytes from local: %ld", nlr); SHOW_info1("bytes to local: %ld", nlw); } } } /* * see SSL_CTX_set_verify(3). */ static int my_vrfy(pv_ok, ctx) int pv_ok; X509_STORE_CTX *ctx; { char *subj = "''", *bp; SSL *ssl; X509 *err_cert; int err, depth; err_cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx); /* * Retrieve the pointer to the SSL of the connection currently treated * and the application specific data stored into the SSL object. */ ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); bp = X509_NAME_oneline(X509_get_subject_name(err_cert), 0, 0); if (bp) { subj = strdup(bp); CRYPTO_free(bp); } /* * Catch a too long certificate chain. The depth limit set using * SSL_CTX_set_verify_depth() is by purpose set to "limit+1" so * that whenever the "depth>verify_depth" condition is met, we * have violated the limit and want to log this error condition. * We must do it here, because the CHAIN_TOO_LONG error would not * be found explicitly; only errors introduced by cutting off the * additional certificates would be logged. */ if (depth > verify_cert) { pv_ok = 0; err = X509_V_ERR_CERT_CHAIN_TOO_LONG; X509_STORE_CTX_set_error(ctx, err); } if (!pv_ok) { if (debug > 1) { SHOW_err1("verify: cert error #%d", err); SHOW_err1("verify: cert error: %s", X509_verify_cert_error_string(err)); SHOW_err1("verify: cert depth: %d", depth); SHOW_err1("verify: cert subject: %s", subj); } } else if (debug > 1) { SHOW_info1("verify: cert depth: %d", depth); SHOW_info1("verify: cert subject: %s", subj); } if (subj) free(subj); if (pv_ok) return pv_ok; /* * At this point, err contains the last verification error. * We can use it for something special. */ switch (err) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: bp = X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), 0, 0); if (bp == NULL) SHOW_err("verify: cert: no issuer."); else { if (debug > 1) SHOW_info1("verify: cert issuer: %s", bp); CRYPTO_free(bp); } break; case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: pv_ok = self_signed_ok; break; } return pv_ok; } peer_cert_prep(ctx) SSL_CTX *ctx; { if (verify_cert <= 0 && !require_certs) return; SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, my_vrfy); /* * Let the verify_callback catch the verify_depth error * so that we get an appropriate error in the logfile. */ SSL_CTX_set_verify_depth(ctx, verify_cert + 1); } /* * Get peer's certificate * (note: beware of dynamic allocation) */ peer_cert_chk(ctx, ssl) SSL_CTX *ctx; SSL *ssl; { X509 *peer_cert; if (verbose) SHOW_info1("cipher: [%s]", SSL_get_cipher (ssl)); peer_cert = SSL_get_peer_certificate (ssl); if (peer_cert == NULL) { if (require_certs) { SHOW_err("no peer certificate."); quit(51); } if (verbose) SHOW_info("no peer certificate."); return; } if (verbose) { char *bp; bp = X509_NAME_oneline (X509_get_subject_name (peer_cert), 0, 0); if (bp == NULL) SHOW_err("peer cert: no subject."); else { SHOW_info1("peer cert subject: %s", bp); CRYPTO_free(bp); } bp = X509_NAME_oneline (X509_get_issuer_name (peer_cert), 0, 0); if (bp == NULL) SHOW_err("peer cert: no issuer."); else { SHOW_info1("peer cert issuer: %s", bp); CRYPTO_free(bp); } } if (verify_cert > 0) { int r = SSL_get_verify_result(ssl); switch (r) { case X509_V_OK: if (verbose) SHOW_info("peer cert is OK."); break; case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: if (verbose) SHOW_info("peer cert is self-signed."); break; default: SHOW_err1("peer cert error #%d", r); SHOW_err1("peer cert error: %s", X509_verify_cert_error_string(r)); } } /* deallocate the certificate. */ X509_free (peer_cert); } cert_prep(ctx) SSL_CTX *ctx; { if (certf == NULL) { SHOW_err("need a cert file."); quit(60); } if (pkeyf == NULL) { SHOW_err("need a pkey file."); quit(61); } if (SSL_CTX_use_certificate_file(ctx, certf, SSL_FILETYPE_PEM) <= 0) { show_SSL_errors(); quit(62); } if (SSL_CTX_use_PrivateKey_file(ctx, pkeyf, SSL_FILETYPE_PEM) <= 0) { show_SSL_errors(); quit(63); } if (!SSL_CTX_check_private_key(ctx)) { SHOW_err("private key does not match the certificate public key"); quit(64); } } srvr_prep(ctx, ssl, sd) SSL_CTX **ctx; SSL **ssl; int sd; { int err; SSL_METHOD *meth; X509 *client_cert; /* * SSL preliminaries: * keep the certificate and key with the context. */ SSL_load_error_strings(); SSLeay_add_ssl_algorithms(); if (method == NULL) meth = SSLv23_server_method(); else if (strcmp(method, "ssl2") == 0) meth = SSLv2_server_method(); else if (strcmp(method, "ssl3") == 0) meth = SSLv3_server_method(); else if (strcmp(method, "tls1") == 0) meth = TLSv1_server_method(); else meth = SSLv23_server_method(); *ctx = SSL_CTX_new (meth); if (!*ctx) { show_SSL_errors(); quit(2); } cert_prep(*ctx); #ifdef USE_EGD if (RAND_egd(egds) == -1) { SHOW_err1("failed to contact EGD via %s", egds); quit(23); } #endif peer_cert_prep(*ctx); *ssl = SSL_new (*ctx); CHK_NULL(ssl); SSL_set_fd (*ssl, sd); err = SSL_accept (*ssl); CHK_SSL(err); peer_cert_chk(*ctx, *ssl); } clnt_prep(ctx, ssl, sd) SSL_CTX **ctx; SSL **ssl; int sd; { int err; SSL_METHOD *meth; X509 *server_cert; /* * SSL preliminaries: * keep the certificate and key with the context. */ SSL_load_error_strings(); SSLeay_add_ssl_algorithms(); if (method == NULL) meth = SSLv23_client_method(); else if (strcmp(method, "ssl2") == 0) meth = SSLv2_client_method(); else if (strcmp(method, "ssl3") == 0) meth = SSLv3_client_method(); else if (strcmp(method, "tls1") == 0) meth = TLSv1_client_method(); else meth = SSLv23_client_method(); *ctx = SSL_CTX_new (meth); if (!*ctx) { show_SSL_errors(); quit(2); } if (require_certs) cert_prep(*ctx); #ifdef USE_EGD if (RAND_egd(egds) == -1) { SHOW_err1("failed to contact EGD via %s", egds); quit(33); } #endif *ssl = SSL_new (*ctx); CHK_NULL(*ssl); SSL_set_fd (*ssl, sd); err = SSL_connect (*ssl); CHK_SSL(err); require_certs = 1; /* XXX */ peer_cert_chk(*ctx, *ssl); } main(ac, av) int ac; char *av[]; { SSL *ssl; SSL_CTX *ctx; int sd = -1, rd, wd; extern char *optarg; extern int optind; int ch, errflg = 0; rd = fileno(stdin); wd = fileno(stdout); prog = av[0]; while ((ch = getopt(ac, av, #ifdef USE_EGD "cdeilnqsvrSp:t:C:K:D:M:E:V:" #else "cdeilnqsvrSp:t:C:K:D:M:V:" #endif )) != -1) { switch(ch) { case 'c': server = 0; break; case 's': server = 1; break; case 'd': debug++; break; case 'e': eofclnt = 1; break; case 'i': inetd = 1, logging = 1, server = 1 ; break; case 'n': nossl = 1; break; case 'p': port = optarg; break; case 'q': verbose = 0; break; case 'v': verbose = 1; break; case 'l': logging = 1; break; case 't': timeout = atoi(optarg); break; case 'K': pkeyf = optarg; break; case 'C': certf = optarg; break; case 'D': ssld = optarg; break; case 'M': method = optarg; break; #ifdef USE_EGD case 'E': egds = optarg; break; #endif case 'r': require_certs = 1; break; case 'V': verify_cert = atoi(optarg); break; case 'S': self_signed_ok = 0; break; default: errflg = 1; break; } } if (logging) openlog(prog, LOG_PID, LOG_SSL); if (errflg) { usage: usage(); quit(1); } if (ssld == NULL) { strcpy(ssldbuf, CONFDIR); ssld = ssldbuf; } if (certf == NULL) { strcpy(certfbuf, ssld); strcat(certfbuf, "/"); strcat(certfbuf, CERTF); certf = certfbuf; } if (pkeyf == NULL) pkeyf = certf; if (debug) verbose = debug; if (tty == NULL) tty = stderr; if (port != NULL) { char *cp = (char *)index(port, ':'); if (cp == NULL) host = "127.0.0.1"; else { host = port; *cp++ = '\0'; port = cp; } } if (verbose) { SHOW_info1("base: %s", ssld); SHOW_info1("cert: %s", certf); SHOW_info1("pkey: %s", pkeyf); #ifdef USE_EGD SHOW_info1("EGD sock: %s", egds); #endif if (host != NULL) SHOW_info1("punt host: %s", host); if (port != NULL) SHOW_info1("punt port: %s", port); if (timeout > 0) SHOW_info1("timeout: %d", timeout); } /* * if an additional command was specified or if a host/port was * given (which means that netcat will be used to handle the raw * network I/O), then create the socket/filedescriptors which * will be used to communicate with the child process and then * fork+exec that child process. */ if (optind < ac || port != NULL) { int sv[2]; if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, sv) < 0) { Perror("socketpair()"); quit(1); } signal(SIGCHLD, reaper); /* zombie prevention */ pid = fork(); if (pid < 0) { Perror("fork()"); quit(1); } if (pid == 0) { /* child */ if (dup2(sv[1], 0) < 0) Perror("dup2(0)"); if (dup2(sv[1], 1) < 0) Perror("dup2(1)"); for (sd = getdtablesize(); sd > 2; sd--) (void) close(sd); if (verbose && logging) openlog(prog, LOG_PID, LOG_SSL); if (optind < ac) { av += optind; if (verbose) { SHOW_info1("exec '%s ...'", av[0]); if (logging) closelog(); } execvp(av[0], av); } else if (port && server && !inetd) { if (verbose) { SHOW_info1("exec 'nc -l -p %s'", port); if (logging) closelog(); } execl(NETCAT, "nc", "-l", "-p", port, NULL); } else if (port) { if (verbose) { SHOW_info2("exec 'nc %s %s'", host, port); if (logging) closelog(); } execl(NETCAT, "nc", host, port, NULL); } if (logging) openlog(prog, LOG_PID, LOG_SSL); Perror("execl/execvp"); _exit(1); } /* parent */ if (debug) SHOW_info1("launched %d", pid); if (inetd) { if (debug > 2) { SHOW_info1("rd %d", rd); SHOW_info1("wd %d", wd); SHOW_info1("sd %d", sd); SHOW_info1("sv[0] %d", sv[0]); SHOW_info1("sv[1] %d", sv[1]); } sd = rd; rd = wd = sv[0]; } else sd = sv[0]; close(sv[1]); } if (nossl) { ssl = NULL; ctx = NULL; } if (server) { if (sd < 0) { /* see example 2. */ sd = fileno(stdin); rd = wd; } if (!nossl) srvr_prep(&ctx, &ssl, sd); } else { if (sd < 0) { /* see example 1. */ sd = fileno(stdout); wd = rd; } if (!nossl) clnt_prep(&ctx, &ssl, sd); } if (debug > 2) { SHOW_info1("rd: %d", rd); SHOW_info1("wd: %d", wd); SHOW_info1("sd: %d", sd); } relay(ssl, sd, rd, wd); close (sd); if (ssl != NULL) SSL_free (ssl); if (ctx != NULL) SSL_CTX_free (ctx); if (pid > 0) (void) kill (pid, SIGKILL); quit(0); }