/* ucspi/cmd/tlsc -- UCSPI TLS server Copyright (C) 2021 Sergey Matveev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package main import ( "crypto/tls" "crypto/x509" "flag" "fmt" "io" "log" "os" "os/exec" "go.cypherpunks.ru/ucspi" ) func main() { crtPath := flag.String("cert", "cert.pem", "Path to server X.509 certificate") prvPath := flag.String("key", "prv.pem", "Path to server PKCS#8 private key") casPath := flag.String("client-ca", "", "Require client authentication, path to CA certificates file") flag.Usage = func() { fmt.Fprintf(os.Stderr, `Usage: tcpserver host port tlss [-client-ca CAs.pem] -cert cert.pem -key prv.pem program [args...] `) flag.PrintDefaults() } flag.Parse() log.SetFlags(log.Lshortfile) crtRaw, _, err := ucspi.CertificateFromFile(*crtPath) if err != nil { log.Fatalln(err) } prv, err := ucspi.PrivateKeyFromFile(*prvPath) if err != nil { log.Fatalln(err) } var cas *x509.CertPool if *casPath != "" { _, cas, err = ucspi.CertPoolFromFile(*casPath) if err != nil { log.Fatalln(err) } } cfg := &tls.Config{ Certificates: []tls.Certificate{{ Certificate: [][]byte{crtRaw}, PrivateKey: prv, }}, ClientCAs: cas, } if *casPath != "" { cfg.ClientAuth = tls.RequireAndVerifyClientCert } conn, _ := ucspi.NewConn(os.Stdin, os.Stdout) tlsConn := tls.Server(conn, cfg) if err = tlsConn.Handshake(); err != nil { log.Fatalln(err) } var dn string if *casPath != "" { dn = tlsConn.ConnectionState().PeerCertificates[0].Subject.String() } rr, rw, err := os.Pipe() if err != nil { log.Fatalln(err) } wr, ww, err := os.Pipe() if err != nil { log.Fatalln(err) } args := flag.Args() cmd := exec.Command(args[0], args[1:]...) cmd.Stdin = rr cmd.Stdout = ww cmd.Stderr = os.Stderr cmd.Env = append(os.Environ(), "PROTO=TLS") if dn != "" { cmd.Env = append(cmd.Env, "TLSREMOTEDN="+dn) } if err = cmd.Start(); err != nil { log.Fatalln(err) } worker := make(chan struct{}) go func() { io.Copy(rw, tlsConn) }() go func() { io.Copy(tlsConn, wr) tlsConn.Close() close(worker) }() err = cmd.Wait() ww.Close() <-worker if err != nil { log.Fatalln(err) } }