]> Cypherpunks.ru repositories - ucspi.git/blob - cmd/tlss/main.go
Raised copyright years
[ucspi.git] / cmd / tlss / main.go
1 /*
2 ucspi/cmd/tlsc -- UCSPI TLS server
3 Copyright (C) 2021-2022 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 package main
19
20 import (
21         "crypto/tls"
22         "crypto/x509"
23         "flag"
24         "fmt"
25         "io"
26         "log"
27         "os"
28         "os/exec"
29
30         "go.cypherpunks.ru/ucspi"
31 )
32
33 func main() {
34         crtPath := flag.String("cert", "cert.pem", "Path to server X.509 certificate")
35         prvPath := flag.String("key", "prv.pem", "Path to server PKCS#8 private key")
36         casPath := flag.String("client-ca", "", "Require client authentication, path to CA certificates file")
37         flag.Usage = func() {
38                 fmt.Fprintf(os.Stderr, `Usage: tcpserver host port tlss [-client-ca CAs.pem]
39         -cert cert.pem -key prv.pem program [args...]
40
41 `)
42                 flag.PrintDefaults()
43         }
44         flag.Parse()
45         log.SetFlags(log.Lshortfile)
46
47         crtRaw, _, err := ucspi.CertificateFromFile(*crtPath)
48         if err != nil {
49                 log.Fatalln(err)
50         }
51         prv, err := ucspi.PrivateKeyFromFile(*prvPath)
52         if err != nil {
53                 log.Fatalln(err)
54         }
55         var cas *x509.CertPool
56         if *casPath != "" {
57                 _, cas, err = ucspi.CertPoolFromFile(*casPath)
58                 if err != nil {
59                         log.Fatalln(err)
60                 }
61         }
62
63         cfg := &tls.Config{
64                 Certificates: []tls.Certificate{{
65                         Certificate: [][]byte{crtRaw},
66                         PrivateKey:  prv,
67                 }},
68                 ClientCAs: cas,
69         }
70         if *casPath != "" {
71                 cfg.ClientAuth = tls.RequireAndVerifyClientCert
72         }
73
74         conn, _ := ucspi.NewConn(os.Stdin, os.Stdout)
75         tlsConn := tls.Server(conn, cfg)
76         if err = tlsConn.Handshake(); err != nil {
77                 log.Fatalln(err)
78         }
79         var dn string
80         if *casPath != "" {
81                 dn = tlsConn.ConnectionState().PeerCertificates[0].Subject.String()
82         }
83
84         rr, rw, err := os.Pipe()
85         if err != nil {
86                 log.Fatalln(err)
87         }
88         wr, ww, err := os.Pipe()
89         if err != nil {
90                 log.Fatalln(err)
91         }
92         args := flag.Args()
93         cmd := exec.Command(args[0], args[1:]...)
94         cmd.Stdin = rr
95         cmd.Stdout = ww
96         cmd.Stderr = os.Stderr
97         cmd.Env = append(os.Environ(), "PROTO=TLS")
98         if dn != "" {
99                 cmd.Env = append(cmd.Env, "TLSREMOTEDN="+dn)
100         }
101
102         if err = cmd.Start(); err != nil {
103                 log.Fatalln(err)
104         }
105         worker := make(chan struct{})
106         go func() {
107                 io.Copy(rw, tlsConn)
108                 rw.Close()
109         }()
110         go func() {
111                 io.Copy(tlsConn, wr)
112                 tlsConn.Close()
113                 close(worker)
114         }()
115         err = cmd.Wait()
116         ww.Close()
117         <-worker
118         if err != nil {
119                 log.Fatalln(err)
120         }
121 }