/** @file sslclient.c @author web, fefe, feh @brief IPv6 enabled sslclient */ #include #include #include #include #include #include "ucspissl.h" #include "sig.h" #include "exit.h" #include "getoptb.h" #include "uint_t.h" #include "fmt.h" #include "scan.h" #include "str.h" #include "ip.h" #include "socket_if.h" #include "fd.h" #include "stralloc.h" #include "buffer.h" #include "getln.h" #include "logmsg.h" #include "pathexec.h" #include "timeoutconn.h" #include "remoteinfo.h" #include "dnsresolv.h" #include "byte.h" #include "ndelay.h" #include "wait.h" #include "auto_cafile.h" #include "auto_cadir.h" #include "auto_ciphers.h" #define WHO "sslclient" void nomem(void) { logmsg(WHO,111,FATAL,"out of memory"); } void env(const char *s,const char *t) { if (!pathexec_env(s,t)) nomem(); } void usage(void) { logmsg(WHO,100,USAGE,"sslclient \ [ -463hHrRdDiqQveEsSnNxX ] \ [ -i localip ] \ [ -p localport ] \ [ -T timeoutconn ] \ [ -l localname ] \ [ -t timeoutinfo ] \ [ -I interface ] \ [ -a cafile ] \ [ -A cadir ] \ [ -c certfile ] \ [ -z ciphers ] \ [ -k keyfile ] \ [ -V verifydepth ] \ [ -w progtimeout ] \ host port program"); } int verbosity = 1; int flagdelay = 0; int flagremoteinfo = 0; int flagremotehost = 1; int flag3 = 0; int flagsslenv = 0; int flagtcpenv = 0; int flagsni = 0; unsigned long itimeout = 26; unsigned long ctimeout[2] = { 2, 58 }; unsigned int progtimeout = 3600; uint32 netif = 0; const char *loopback = "127.0.0.1"; char iplocal[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }; uint16 portlocal = 0; const char *forcelocal = 0; char ipremote[16]; uint16 portremote; const char *hostname; int flagname = 1; int flagservercert = 1; static stralloc addresses; static stralloc certname; static stralloc moreaddresses; static stralloc tmp; static stralloc fqdn; static char strnum[FMT_ULONG]; static char ipstr[IP6_FMT]; char seed[128]; char bspace[16]; buffer b; SSL_CTX *ctx; const char *certfile = 0; const char *keyfile = 0; const char *cafile = auto_cafile; const char *cadir = auto_cadir; const char *ciphers = auto_ciphers; stralloc password = {0}; int match = 0; int verifydepth = 1; int pi[2]; int po[2]; int pt[2]; void read_passwd() { if (!password.len) { buffer_init(&b,buffer_unixread,3,bspace,sizeof(bspace)); if (getln(&b,&password,&match,'\0') == -1) logmsg(WHO,111,ERROR,"unable to read password"); close(3); if (match) --password.len; } } int passwd_cb(char *buf,int size,int rwflag,void *userdata) { if (size < password.len) logmsg(WHO,111,ERROR,"password too long"); byte_copy(buf,password.len,password.s); return password.len; } int main(int argc,char * const *argv) { unsigned long u; int opt; const char *x; int j; int s; int r; int cloop; SSL *ssl; int wstat; int ipflag = 0; dns_random_init(seed); close(6); close(7); sig_ignore(sig_pipe); while ((opt = getopt(argc,argv,"dDvqQhHrRimM:p:t:T:l:a:A:c:z:k:V:346eEsSnN0xXw:")) != opteof) switch(opt) { case '4': ipflag = 1; break; case '6': ipflag = 0; break; case 'd': flagdelay = 1; break; case 'D': flagdelay = 0; break; case 'm': flagsni = 1; break; case 'M': flagsni = 0; break; case 'v': verbosity = 2; break; case 'q': verbosity = 0; break; case 'Q': verbosity = 1; break; case 'l': forcelocal = optarg; break; case 'H': flagremotehost = 0; break; case 'h': flagremotehost = 1; break; case 'R': flagremoteinfo = 0; break; case 'r': flagremoteinfo = 1; break; case 't': scan_ulong(optarg,&itimeout); break; case 'T': j = scan_ulong(optarg,&ctimeout[0]); if (optarg[j] == '+') ++j; scan_ulong(optarg + j,&ctimeout[1]); break; case 'w': scan_uint(optarg,&progtimeout); break; case 'i': if (!ip6_scan(optarg,iplocal)) usage(); break; case 'I': netif = socket_getifidx(optarg); break; case 'p': scan_ulong(optarg,&u); portlocal = u; break; case 'a': cafile = optarg; break; case 'A': cadir = optarg; break; case 'c': certfile = optarg; break; case 'z': ciphers = optarg; break; case 'k': keyfile = optarg; break; case 'V': scan_ulong(optarg,&u); verifydepth = u; break; case '3': flag3 = 1; break; case 'S': flagsslenv = 0; break; case 's': flagsslenv = 1; break; case 'E': flagtcpenv = 0; break; case 'e': flagtcpenv = 1; break; case 'N': flagname = 0; break; case 'n': flagname = 1; break; case 'x': flagservercert = 1; break; case 'X': flagservercert = 0; break; default: usage(); } argv += optind; if (!verbosity) buffer_2->fd = -1; hostname = *argv; if (!hostname || str_equal((char *)hostname,"")) usage(); if (str_equal((char *)hostname,"0")) hostname = loopback; x = *++argv; if (!x) usage(); if (!x[scan_ulong(x,&u)]) portremote = u; else { struct servent *se; se = getservbyname(x,"tcp"); if (!se) logmsg(WHO,111,FATAL,B("unable to figure out port number for ",x)); uint16_unpack_big((char*)&se->s_port,&portremote); } if (flag3) read_passwd(); if (cafile && str_equal(cafile,"")) cafile = 0; if (cadir && str_equal(cadir,"")) cadir= 0; if (ciphers && str_equal(ciphers,"")) ciphers= 0; if (certfile && str_equal(certfile,"")) certfile = 0; if (keyfile && str_equal(keyfile,"")) keyfile = 0; if (!*++argv) usage(); if (!stralloc_copys(&tmp,hostname)) nomem(); dns_ip_qualify(&addresses,&fqdn,&tmp); if (addresses.len < 16) logmsg(WHO,111,ERROR,B("No IP address for: ",hostname)); if (addresses.len == 16) { ctimeout[0] += ctimeout[1]; ctimeout[1] = 0; } for (cloop = 0; cloop < 2; ++cloop) { if (!stralloc_copys(&moreaddresses,"")) nomem(); for (j = 0; j + 16 <= addresses.len; j += 16) { if (ipflag == 1 || ip6_isv4mapped(addresses.s + j)) { s = socket_tcp4(); if (s == -1) logmsg(WHO,111,FATAL,"unable to create socket"); r = socket_bind4(s,iplocal,portlocal); } else { s = socket_tcp6(); if (s == -1) logmsg(WHO,111,FATAL,"unable to create socket"); r = socket_bind6(s,iplocal,portlocal,netif); } if (r == -1) { strnum[fmt_ulong(strnum,portlocal)] = 0; if (ip6_isv4mapped(addresses.s + j)) ipstr[ip4_fmt(ipstr,addresses.s + j + 12)] = 0; else ipstr[ip6_fmt(ipstr,addresses.s + j)] = 0; logmsg(WHO,111,FATAL,B("unable to bind to: ",ipstr," port: ",strnum)); } if (timeoutconn(s,addresses.s + j,portremote,ctimeout[cloop],netif) == 0) goto CONNECTED; close(s); if (!cloop && ctimeout[1] && (errno == ETIMEDOUT)) { if (!stralloc_catb(&moreaddresses,addresses.s + j,16)) nomem(); } else { strnum[fmt_ulong(strnum,portremote)] = 0; if (ip6_isv4mapped(addresses.s + j)) ipstr[ip4_fmt(ipstr,addresses.s + j + 12)] = 0; else ipstr[ip6_fmt(ipstr,addresses.s + j)] = 0; } } if (!stralloc_copy(&addresses,&moreaddresses)) nomem(); } logmsg(WHO,110,DROP,B("unable to connect to: ",ipstr," port: ",strnum)); _exit(111); CONNECTED: /* Local */ if (socket_local(s,iplocal,&portlocal,&netif) == -1) logmsg(WHO,111,FATAL,"unable to get local address"); if (ip6_isv4mapped(iplocal)) { env("PROTO","TCP6"); ipstr[ip4_fmt(ipstr,iplocal + 12)] = 0; } else { env("PROTO","TCP6"); if (flagtcpenv && netif) env("TCP6INTERFACE",socket_getifname(netif)); ipstr[ip6_fmt(ipstr,iplocal)] = 0; } env("SSLLOCALIP",ipstr); if (flagtcpenv) env("TCPLOCALIP",ipstr); strnum[fmt_ulong(strnum,portlocal)] = 0; env("SSLLOCALPORT",strnum); if (flagtcpenv) env("TCPLOCALPORT",strnum); x = forcelocal; if (!x) if (dns_name(&tmp,iplocal) >= 0) { if (!stralloc_0(&tmp)) nomem(); x = tmp.s; } env("SSLLOCALHOST",x); if (flagtcpenv) env("TCPLOCALHOST",x); /* Remote */ if (socket_remote(s,ipremote,&portremote,&netif) == -1) logmsg(WHO,111,FATAL,"unable to get remote address"); if (ip6_isv4mapped(ipremote)) ipstr[ip4_fmt(ipstr,ipremote + 12)] = 0; else ipstr[ip6_fmt(ipstr,ipremote)] = 0; env("SSLREMOTEIP",ipstr); if (flagtcpenv) env("TCPREMOTEIP",ipstr); strnum[fmt_ulong(strnum,portremote)] = 0; env("SSLREMOTEPORT",strnum); if (flagtcpenv) env("TCPREMOTEPORT",strnum); x = 0; if (flagremotehost) if (dns_name(&tmp,ipremote) >= 0) { if (!stralloc_0(&tmp)) nomem(); x = tmp.s; } env("SSLREMOTEHOST",x); if (flagtcpenv) env("TCPREMOTEHOST",x); x = 0; if (flagremoteinfo) if (remoteinfo(&tmp,ipremote,portremote,iplocal,portlocal,itimeout,netif) == 0) { if (!stralloc_0(&tmp)) nomem(); x = tmp.s; } env("SSLREMOTEINFO",x); if (flagtcpenv) env("TCPREMOTEINFO",x); /* Context */ ctx = ssl_client(); ssl_errstr(); if (!ctx) logmsg(WHO,111,FATAL,"unable to create TLS context"); switch (ssl_certkey(ctx,certfile,keyfile,passwd_cb)) { case -1: logmsg(WHO,111,ERROR,"unable to load certificate"); case -2: logmsg(WHO,111,ERROR,"unable to load key pair"); case -3: logmsg(WHO,111,ERROR,"key does not match certificate"); default: break; } if (flagservercert && !ssl_ca(ctx,cafile,cadir,verifydepth)) logmsg(WHO,111,ERROR,"unable to load CA list"); if (!ssl_ciphers(ctx,ciphers)) logmsg(WHO,111,ERROR,"unable to set cipher list"); ssl = ssl_new(ctx,s); if (!ssl) logmsg(WHO,111,FATAL,"unable to create TLS instance"); if (flagsni) if (!SSL_set_tlsext_host_name(ssl,hostname)) logmsg(WHO,111,FATAL,B("unable to set TLS SNI extensions for hostname: ",(char *)hostname)); for (cloop = 0; cloop < 2; ++cloop) { if (!ssl_timeoutconn(ssl,ctimeout[cloop])) goto SSLCONNECTED; if (!cloop && ctimeout[1]) continue; logmsg(WHO,111,FATAL,"unable to TLS connect"); } _exit(111); SSLCONNECTED: ndelay_off(s); if (flagservercert) switch(ssl_verify(ssl,hostname,&certname)) { case -1: logmsg(WHO,110,ERROR,"no server certificate"); case -2: logmsg(WHO,110,ERROR,"missing credentials (CA) or unable to validate server certificate"); case -3: if (!stralloc_0(&certname)) nomem(); if (flagname) logmsg(WHO,110,ERROR,B("server hostname does not match certificate: ",(char *)hostname," <=> ",certname.s)); default: break; } if (verbosity >= 2) log_who(WHO,B("tls connected to: ",ipstr," port: ",strnum)); if (!flagdelay) socket_tcpnodelay(s); /* if it fails, bummer */ if (pipe(pi) == -1) logmsg(WHO,111,FATAL,"unable to create pipe"); if (pipe(po) == -1) logmsg(WHO,111,FATAL,"unable to create pipe"); if (pi[0] == 7) { if (pipe(pt) == -1) logmsg(WHO,111,FATAL,"unable to create pipe"); close(pi[0]); close(pi[1]); pi[0] = pt[0]; pi[1] = pt[1]; } if (po[1] == 6) { if (pipe(pt) == -1) logmsg(WHO,111,FATAL,"unable to create pipe"); close(po[0]); close(po[1]); po[0] = pt[0]; po[1] = pt[1]; } switch (opt = fork()) { case -1: logmsg(WHO,111,FATAL,"unable to fork"); case 0: break; default: close(pi[0]); close(po[1]); if (ssl_io(ssl,pi[1],po[0],progtimeout)) { logmsg(WHO,110,DROP,"unable to speak TLS"); ssl_close(ssl); wait_pid(&wstat,opt); _exit(111); } ssl_close(ssl); if (wait_pid(&wstat,opt) > 0) _exit(wait_exitcode(wstat)); _exit(0); } ssl_close(ssl); close(pi[1]); close(po[0]); if (flagsslenv && !ssl_client_env(ssl,0)) nomem(); if (fd_move(6,pi[0]) == -1) logmsg(WHO,111,FATAL,"unable to set up descriptor 6"); if (fd_move(7,po[1]) == -1) logmsg(WHO,111,FATAL,"unable to set up descriptor 7"); sig_uncatch(sig_pipe); pathexec(argv); logmsg(WHO,111,FATAL,B("unable to run: ",*argv)); return 0; /* never happens, but avoids compile warning */ }