1 package wallet
2 3 import (
4 "crypto/tls"
5 "errors"
6 "fmt"
7 "io/ioutil"
8 "net"
9 "os"
10 "path/filepath"
11 "runtime"
12 "strings"
13 "time"
14 15 "github.com/p9c/p9/pkg/util"
16 "github.com/p9c/p9/pod/config"
17 "github.com/p9c/p9/pod/state"
18 )
19 20 type listenFunc func(net string, laddr string) (net.Listener, error)
21 22 // GenerateRPCKeyPair generates a new RPC TLS keypair and writes the cert and possibly also the key in PEM format to the
23 // paths specified by the config. If successful, the new keypair is returned.
24 func GenerateRPCKeyPair(config *config.Config, writeKey bool) (tls.Certificate, error) {
25 D.Ln("generating TLS certificates")
26 // Create directories for cert and key files if they do not yet exist.
27 D.Ln("rpc tls ", *config.RPCCert, " ", *config.RPCKey)
28 certDir, _ := filepath.Split(config.RPCCert.V())
29 keyDir, _ := filepath.Split(config.RPCKey.V())
30 e := os.MkdirAll(certDir, 0700)
31 if e != nil {
32 return tls.Certificate{}, e
33 }
34 e = os.MkdirAll(keyDir, 0700)
35 if e != nil {
36 return tls.Certificate{}, e
37 }
38 // Generate cert pair.
39 org := "pod/wallet autogenerated cert"
40 validUntil := time.Now().Add(time.Hour * 24 * 365 * 10)
41 cert, key, e := util.NewTLSCertPair(org, validUntil, nil)
42 if e != nil {
43 return tls.Certificate{}, e
44 }
45 keyPair, e := tls.X509KeyPair(cert, key)
46 if e != nil {
47 return tls.Certificate{}, e
48 }
49 // Write cert and (potentially) the key files.
50 e = ioutil.WriteFile(config.RPCCert.V(), cert, 0600)
51 if e != nil {
52 rmErr := os.Remove(config.RPCCert.V())
53 if rmErr != nil {
54 E.Ln("cannot remove written certificates:", rmErr)
55 }
56 return tls.Certificate{}, e
57 }
58 e = ioutil.WriteFile(config.CAFile.V(), cert, 0600)
59 if e != nil {
60 rmErr := os.Remove(config.RPCCert.V())
61 if rmErr != nil {
62 E.Ln("cannot remove written certificates:", rmErr)
63 }
64 return tls.Certificate{}, e
65 }
66 if writeKey {
67 e = ioutil.WriteFile(config.RPCKey.V(), key, 0600)
68 if e != nil {
69 rmErr := os.Remove(config.RPCCert.V())
70 if rmErr != nil {
71 E.Ln("cannot remove written certificates:", rmErr)
72 }
73 rmErr = os.Remove(config.CAFile.V())
74 if rmErr != nil {
75 E.Ln("cannot remove written certificates:", rmErr)
76 }
77 return tls.Certificate{}, e
78 }
79 }
80 I.Ln("done generating TLS certificates")
81 return keyPair, nil
82 }
83 84 // makeListeners splits the normalized listen addresses into IPv4 and IPv6 addresses and creates new net.Listeners for
85 // each with the passed listen func. Invalid addresses are logged and skipped.
86 func makeListeners(normalizedListenAddrs []string, listen listenFunc) []net.Listener {
87 ipv4Addrs := make([]string, 0, len(normalizedListenAddrs)*2)
88 // ipv6Addrs := make([]string, 0, len(normalizedListenAddrs)*2)
89 for _, addr := range normalizedListenAddrs {
90 var host string
91 var e error
92 host, _, e = net.SplitHostPort(addr)
93 if e != nil {
94 // Shouldn't happen due to already being normalized.
95 E.F(
96 "`%s` is not a normalized listener address", addr,
97 )
98 continue
99 }
100 // Empty host or host of * on plan9 is both IPv4 and IPv6.
101 if host == "" || (host == "*" && runtime.GOOS == "plan9") {
102 ipv4Addrs = append(ipv4Addrs, addr)
103 // ipv6Addrs = append(ipv6Addrs, addr)
104 continue
105 }
106 // Remove the IPv6 zone from the host, if present. The zone prevents ParseIP from correctly parsing the IP
107 // address. ResolveIPAddr is intentionally not used here due to the possibility of leaking a DNS query over Tor
108 // if the host is a hostname and not an IP address.
109 zoneIndex := strings.Index(host, "%")
110 if zoneIndex != -1 {
111 host = host[:zoneIndex]
112 }
113 ip := net.ParseIP(host)
114 switch {
115 case ip == nil:
116 W.F("`%s` is not a valid IP address", host)
117 case ip.To4() == nil:
118 // ipv6Addrs = append(ipv6Addrs, addr)
119 default:
120 ipv4Addrs = append(ipv4Addrs, addr)
121 }
122 }
123 listeners := make(
124 []net.Listener, 0,
125 // len(ipv6Addrs)+
126 len(ipv4Addrs),
127 )
128 for _, addr := range ipv4Addrs {
129 listener, e := listen("tcp4", addr)
130 if e != nil {
131 W.F(
132 "Can't listen on %s: %v", addr, e,
133 )
134 continue
135 }
136 listeners = append(listeners, listener)
137 }
138 // for _, addr := range ipv6Addrs {
139 // listener, e := listen("tcp6", addr)
140 // if e != nil {
141 // Warnf(
142 // "Can't listen on %s: %v", addr, e,
143 // )
144 // continue
145 // }
146 // listeners = append(listeners, listener)
147 // }
148 return listeners
149 }
150 151 // OpenRPCKeyPair creates or loads the RPC TLS keypair specified by the
152 // application config. This function respects the pod.Config.OneTimeTLSKey
153 // setting.
154 func OpenRPCKeyPair(config *config.Config) (tls.Certificate, error) {
155 // Chk for existence of the TLS key file. If one time TLS keys are enabled but a
156 // key already exists, this function should error since it's possible that a
157 // persistent certificate was copied to a remote machine. Otherwise, generate a
158 // new keypair when the key is missing. When generating new persistent keys,
159 // overwriting an existing cert is acceptable if the previous execution used a
160 // one time TLS key. Otherwise, both the cert and key should be read from disk.
161 // If the cert is missing, the read error will occur in LoadX509KeyPair.
162 _, e := os.Stat(config.RPCKey.V())
163 keyExists := !os.IsNotExist(e)
164 switch {
165 case config.OneTimeTLSKey.True() && keyExists:
166 if e = fmt.Errorf(
167 "one time TLS keys are enabled, but TLS key `%s` already exists", config.RPCKey.V(),
168 ); E.Chk(e) {
169 }
170 return tls.Certificate{}, e
171 case config.OneTimeTLSKey.True():
172 return GenerateRPCKeyPair(config, false)
173 case !keyExists:
174 return GenerateRPCKeyPair(config, true)
175 default:
176 return tls.LoadX509KeyPair(config.RPCCert.V(), config.RPCKey.V())
177 }
178 }
179 func startRPCServers(cx *state.State, walletLoader *Loader) (*Server, error) {
180 T.Ln("startRPCServers")
181 var (
182 legacyServer *Server
183 walletListen = net.Listen
184 keyPair tls.Certificate
185 e error
186 )
187 if cx.Config.ClientTLS.False() {
188 I.Ln("server TLS is disabled - only legacy RPC may be used")
189 } else {
190 keyPair, e = OpenRPCKeyPair(cx.Config)
191 if e != nil {
192 return nil, e
193 }
194 // Change the standard net.Listen function to the tls one.
195 tlsConfig := &tls.Config{
196 Certificates: []tls.Certificate{keyPair},
197 MinVersion: tls.VersionTLS12,
198 NextProtos: []string{"h2"}, // HTTP/2 over TLS
199 InsecureSkipVerify: cx.Config.TLSSkipVerify.True(),
200 }
201 walletListen = func(net string, laddr string) (net.Listener, error) {
202 return tls.Listen(net, laddr, tlsConfig)
203 }
204 }
205 if cx.Config.Username.V() == "" || cx.Config.Password.V() == "" {
206 I.Ln("legacy RPC server disabled (requires username and password)")
207 } else if len(cx.Config.WalletRPCListeners.S()) != 0 {
208 listeners := makeListeners(cx.Config.WalletRPCListeners.S(), walletListen)
209 if len(listeners) == 0 {
210 e := errors.New("failed to create listeners for legacy RPC server")
211 return nil, e
212 }
213 opts := Options{
214 Username: cx.Config.Username.V(),
215 Password: cx.Config.Password.V(),
216 MaxPOSTClients: int64(cx.Config.WalletRPCMaxClients.V()),
217 MaxWebsocketClients: int64(cx.Config.WalletRPCMaxWebsockets.V()),
218 }
219 legacyServer = NewServer(&opts, walletLoader, listeners, nil)
220 }
221 // Error when no legacy RPC servers can be started.
222 if legacyServer == nil {
223 return nil, errors.New("no suitable RPC services can be started")
224 }
225 return legacyServer, nil
226 }
227 228 // startWalletRPCServices associates each of the (optionally-nil) RPC servers with a wallet to enable remote wallet
229 // access. For the legacy JSON-RPC server it enables methods that require a loaded wallet.
230 func startWalletRPCServices(wallet *Wallet, legacyServer *Server) {
231 if legacyServer != nil {
232 D.Ln("starting legacy wallet rpc server")
233 legacyServer.RegisterWallet(wallet)
234 }
235 }
236