diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6db66d3..fd786dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,6 +45,8 @@ jobs: key: plan9port-${{ runner.os }}-v1 - name: Add plan9port to PATH run: echo "$PLAN9/bin" >> $GITHUB_PATH + - name: Install OpenSSL + run: sudo apt-get install -y libssl-dev pkg-config - name: Run tests run: mk test @@ -61,6 +63,8 @@ jobs: key: plan9port-${{ runner.os }}-v1 - name: Add plan9port to PATH run: echo "$PLAN9/bin" >> $GITHUB_PATH + - name: Install OpenSSL + run: sudo apt-get install -y libssl-dev pkg-config - name: Build o.thread run: mk o.thread diff --git a/mkfile b/mkfile index 5de4bab..149c284 100644 --- a/mkfile +++ b/mkfile @@ -51,8 +51,8 @@ runtest:V: $O.test ./$O.test $O.thread: $THREADOFILES $LIB - $LD $LDFLAGS -o $target $prereq + $LD -o $target $prereq $LDFLAGS $TESTOFILES: $TESTHFILES $O.test: $TESTOFILES $LIB - $LD $LDFLAGS -o $target $prereq + $LD -o $target $prereq $LDFLAGS diff --git a/opt.rc b/opt.rc index 46c723a..bb42aa1 100755 --- a/opt.rc +++ b/opt.rc @@ -1,11 +1,13 @@ #!/bin/rc - if(! ~ $#PLAN9 0) { # plan9port echo '<$PLAN9/src/mkhdr' echo 'MKONE=$PLAN9/src/mkone' echo 'TLSFILE=tlsp9p' echo 'BIN=$PLAN9/bin' + # OpenSSL for tlsp9p.c + echo 'CFLAGS=' `{pkg-config --cflags-only-I openssl} + echo 'LDFLAGS=' `{pkg-config --libs openssl} } if not { echo ' +#include /* before libc.h to avoid accept/listen macro conflicts */ #include #include #include +#include +#include #include "dat.h" #include "fns.h" -int +static int istrusted(uchar* cert, int certlen) { + uchar digest[SHA1dlen]; + Thumbprint* table; + + fmtinstall('H', encodefmt); + if(cert == nil || certlen <= 0){ + werrstr("server did not provide TLS certificate"); + return 0; + } + sha1(cert, certlen, digest, nil); + table = initThumbprints(unsharp("#9/lib/tls/rdp"), unsharp("#9/lib/tls/rdp.exclude")); + if(!table || !okThumbprint(digest, table)){ + werrstr("server certificate not recognized"); + fprint(2, "verify server certificate %.*H\n", SHA1dlen, digest); + fprint(2, "add thumbprint after verification:\n"); + fprint(2, "\techo 'x509 sha1=%.*H' >> $PLAN9/lib/tls/rdp\n", SHA1dlen, digest); + return 0; + } + freeThumbprints(table); return 1; } -/* lifted from /sys/src/cmd/upas/fs/imap4.c:/^starttls */ +static int +checkpeer(SSL* s) +{ + X509* peercrt; + uchar* cert; + int certlen, ok; + + peercrt = SSL_get_peer_certificate(s); + cert = nil; + certlen = 0; + if(peercrt != nil) + certlen = i2d_X509(peercrt, &cert); + X509_free(peercrt); + ok = istrusted(cert, certlen); + OPENSSL_free(cert); + return ok; +} + +static void +readtls(int pipefd, SSL* s) +{ + char buf[16384]; + int n; + + for(;;){ + n = SSL_read(s, buf, sizeof buf); + if(n <= 0) + break; + if(write(pipefd, buf, n) != n) + break; + } + SSL_shutdown(s); + SSL_free(s); + close(pipefd); + _exits(nil); +} + +static void +readlocal(int pipefd, SSL* s) +{ + char buf[16384]; + int n; + + for(;;){ + n = read(pipefd, buf, sizeof buf); + if(n <= 0) + break; + if(SSL_write(s, buf, n) <= 0) + break; + } + SSL_free(s); + close(pipefd); + _exits(nil); +} + int starttls(Rdp* r) { - TLSconn c; - int fd, sfd; + SSL_CTX* ctx; + SSL* s; + int p[2]; + static int inited; + + if(!inited){ + SSL_library_init(); + SSL_load_error_strings(); + inited = 1; + } - fd = r->fd; + ctx = SSL_CTX_new(TLS_client_method()); + if(ctx == nil) + goto ErrCtx; + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nil); + s = SSL_new(ctx); + SSL_CTX_free(ctx); + if(s == nil) + goto ErrNew; + if(SSL_set_fd(s, r->fd) != 1 || SSL_connect(s) != 1) + goto ErrConnect; + fprint(2, "tls: %s %s\n", SSL_get_version(s), SSL_get_cipher(s)); - memset(&c, 0, sizeof c); - sfd = tlsClient(fd, &c); - if(sfd < 0){ - werrstr("tlsClient: %r"); - return -1; + if(!checkpeer(s)) + goto ErrCert; + + if(socketpair(AF_UNIX, SOCK_STREAM, 0, p) < 0) + goto ErrSocket; + switch(fork()){ + case -1: + goto ErrFork; + case 0: + close(p[1]); + readtls(p[0], s); } - if(!istrusted(c.cert, c.certlen)){ - close(sfd); - return -1; + switch(fork()){ + case -1: + goto ErrFork; + case 0: + close(p[1]); + readlocal(p[0], s); } - /* BUG: free c.cert? */ close(r->fd); - r->fd = sfd; - return sfd; + close(p[0]); + r->fd = p[1]; + return p[1]; + +ErrFork: + werrstr("fork: %r"); + close(p[0]); + close(p[1]); + SSL_free(s); + return -1; +ErrSocket: + werrstr("socketpair: %r"); + SSL_free(s); + return -1; +ErrCert: + SSL_free(s); + return -1; +ErrConnect: + werrstr("SSL: %s", ERR_reason_error_string(ERR_get_error())); + SSL_free(s); + return -1; +ErrNew: + werrstr("SSL_new failed"); + return -1; +ErrCtx: + werrstr("SSL_CTX_new: %s", ERR_reason_error_string(ERR_get_error())); + return -1; }