xref: /openbsd/regress/lib/libtls/gotls/tls.go (revision 264ca280)
1// Package tls provides a Go interface to the libtls library.
2package tls
3
4/*
5#cgo LDFLAGS: -ltls -lssl -lcrypto
6
7#include <stdlib.h>
8
9#include <tls.h>
10
11typedef void *tls;
12*/
13import "C"
14
15import (
16	"errors"
17	"fmt"
18	"time"
19	"unsafe"
20)
21
22var (
23	errWantPollIn  = errors.New("want poll in")
24	errWantPollOut = errors.New("want poll out")
25)
26
27// TLSConfig provides configuration options for a TLS context.
28type TLSConfig struct {
29	caFile *C.char
30	tlsCfg *C.struct_tls_config
31}
32
33// TLS encapsulates the TLS context.
34type TLS struct {
35	cfg *TLSConfig
36	ctx *C.struct_tls
37}
38
39// Init initialises the TLS library.
40func Init() error {
41	if C.tls_init() != 0 {
42		return errors.New("initialisation failed")
43	}
44	return nil
45}
46
47// NewConfig returns a new TLS configuration.
48func NewConfig() (*TLSConfig, error) {
49	cfg := C.tls_config_new()
50	if cfg == nil {
51		return nil, errors.New("failed to allocate config")
52	}
53	return &TLSConfig{
54		tlsCfg: cfg,
55	}, nil
56}
57
58// SetCAFile sets the CA file to be used for connections.
59func (c *TLSConfig) SetCAFile(filename string) {
60	if c.caFile != nil {
61		C.free(unsafe.Pointer(c.caFile))
62	}
63	c.caFile = C.CString(filename)
64	C.tls_config_set_ca_file(c.tlsCfg, c.caFile)
65}
66
67// InsecureNoVerifyCert disables certificate verification for the connection.
68func (c *TLSConfig) InsecureNoVerifyCert() {
69	C.tls_config_insecure_noverifycert(c.tlsCfg)
70}
71
72// InsecureNoVerifyName disables server name verification for the connection.
73func (c *TLSConfig) InsecureNoVerifyName() {
74	C.tls_config_insecure_noverifyname(c.tlsCfg)
75}
76
77// SetSecure enables verification for the connection.
78func (c *TLSConfig) SetVerify() {
79	C.tls_config_verify(c.tlsCfg)
80}
81
82// Free frees resources associated with the TLS configuration.
83func (c *TLSConfig) Free() {
84	if c.tlsCfg == nil {
85		return
86	}
87	C.tls_config_free(c.tlsCfg)
88	c.tlsCfg = nil
89}
90
91// NewClient returns a new TLS client context, using the optional configuration.
92// If no configuration is specified the default configuration will be used.
93func NewClient(config *TLSConfig) (*TLS, error) {
94	var sslCfg *C.struct_tls_config
95	if config != nil {
96		sslCfg = config.tlsCfg
97	}
98	ctx := C.tls_client()
99	if ctx == nil {
100		return nil, errors.New("tls client failed")
101	}
102	if C.tls_configure(ctx, sslCfg) != 0 {
103		return nil, errors.New("tls configure failed")
104	}
105	return &TLS{
106		cfg: config,
107		ctx: ctx,
108	}, nil
109}
110
111// Error returns the error message from the TLS context.
112func (t *TLS) Error() string {
113	if msg := C.tls_error(t.ctx); msg != nil {
114		return C.GoString(msg)
115	}
116	return ""
117}
118
119// PeerCertProvided returns whether the peer provided a certificate.
120func (t *TLS) PeerCertProvided() bool {
121	return C.tls_peer_cert_provided(t.ctx) == 1
122}
123
124// PeerCertContainsName checks whether the peer certificate contains
125// the specified name.
126func (t *TLS) PeerCertContainsName(name string) bool {
127	n := C.CString(name)
128	defer C.free(unsafe.Pointer(n))
129	return C.tls_peer_cert_contains_name(t.ctx, n) == 1
130}
131
132// PeerCertIssuer returns the issuer of the peer certificate.
133func (t *TLS) PeerCertIssuer() (string, error) {
134	issuer := C.tls_peer_cert_issuer(t.ctx)
135	if issuer == nil {
136		return "", errors.New("no issuer returned")
137	}
138	return C.GoString(issuer), nil
139}
140
141// PeerCertSubject returns the subject of the peer certificate.
142func (t *TLS) PeerCertSubject() (string, error) {
143	subject := C.tls_peer_cert_subject(t.ctx)
144	if subject == nil {
145		return "", errors.New("no subject returned")
146	}
147	return C.GoString(subject), nil
148}
149
150// PeerCertHash returns a hash of the peer certificate.
151func (t *TLS) PeerCertHash() (string, error) {
152	hash := C.tls_peer_cert_hash(t.ctx)
153	if hash == nil {
154		return "", errors.New("no hash returned")
155	}
156	return C.GoString(hash), nil
157}
158
159// PeerCertNotBefore returns the notBefore time from the peer
160// certificate.
161func (t *TLS) PeerCertNotBefore() (time.Time, error) {
162	notBefore := C.tls_peer_cert_notbefore(t.ctx)
163	if notBefore == -1 {
164		return time.Time{}, errors.New("no notBefore time returned")
165	}
166	return time.Unix(int64(notBefore), 0), nil
167}
168
169// PeerCertNotAfter returns the notAfter time from the peer
170// certificate.
171func (t *TLS) PeerCertNotAfter() (time.Time, error) {
172	notAfter := C.tls_peer_cert_notafter(t.ctx)
173	if notAfter == -1 {
174		return time.Time{}, errors.New("no notAfter time")
175	}
176	return time.Unix(int64(notAfter), 0), nil
177}
178
179// ConnVersion returns the protocol version of the connection.
180func (t *TLS) ConnVersion() (string, error) {
181	ver := C.tls_conn_version(t.ctx)
182	if ver == nil {
183		return "", errors.New("no connection version")
184	}
185	return C.GoString(ver), nil
186}
187
188// ConnCipher returns the cipher suite used for the connection.
189func (t *TLS) ConnCipher() (string, error) {
190	cipher := C.tls_conn_cipher(t.ctx)
191	if cipher == nil {
192		return "", errors.New("no connection cipher")
193	}
194	return C.GoString(cipher), nil
195}
196
197// Connect attempts to establish an TLS connection to the specified host on
198// the given port. The host may optionally contain a colon separated port
199// value if the port string is specified as an empty string.
200func (t *TLS) Connect(host, port string) error {
201	h := C.CString(host)
202	var p *C.char
203	if port != "" {
204		p = C.CString(port)
205	}
206	defer C.free(unsafe.Pointer(h))
207	defer C.free(unsafe.Pointer(p))
208	if C.tls_connect(t.ctx, h, p) != 0 {
209		return fmt.Errorf("connect failed: %v", t.Error())
210	}
211	return nil
212}
213
214// Handshake attempts to complete the TLS handshake.
215func (t *TLS) Handshake() error {
216	ret := C.tls_handshake(t.ctx)
217	switch {
218	case ret == C.TLS_WANT_POLLIN:
219		return errWantPollIn
220	case ret == C.TLS_WANT_POLLOUT:
221		return errWantPollOut
222	case ret != 0:
223		return fmt.Errorf("handshake failed: %v", t.Error())
224	}
225	return nil
226}
227
228// Read reads data the TLS connection into the given buffer.
229func (t *TLS) Read(buf []byte) (int, error) {
230	ret := C.tls_read(t.ctx, unsafe.Pointer(&buf[0]), C.size_t(len(buf)))
231	switch {
232	case ret == C.TLS_WANT_POLLIN:
233		return -1, errWantPollIn
234	case ret == C.TLS_WANT_POLLOUT:
235		return -1, errWantPollOut
236	case ret < 0:
237		return -1, fmt.Errorf("read failed: %v", t.Error())
238	}
239	return int(ret), nil
240}
241
242// Write writes the given data to the TLS connection.
243func (t *TLS) Write(buf []byte) (int, error) {
244	p := C.CString(string(buf))
245	defer C.free(unsafe.Pointer(p))
246	ret := C.tls_write(t.ctx, unsafe.Pointer(p), C.size_t(len(buf)))
247	switch {
248	case ret == C.TLS_WANT_POLLIN:
249		return -1, errWantPollIn
250	case ret == C.TLS_WANT_POLLOUT:
251		return -1, errWantPollOut
252	case ret < 0:
253		return -1, fmt.Errorf("write failed: %v", t.Error())
254	}
255	return int(ret), nil
256}
257
258// Close closes the TLS connection.
259func (t *TLS) Close() error {
260	ret := C.tls_close(t.ctx)
261	switch {
262	case ret == C.TLS_WANT_POLLIN:
263		return errWantPollIn
264	case ret == C.TLS_WANT_POLLOUT:
265		return errWantPollOut
266	case ret != 0:
267		return fmt.Errorf("close failed: %v", t.Error())
268	}
269	return nil
270}
271
272// Free frees resources associated with the TLS context.
273func (t *TLS) Free() {
274	if t.ctx == nil {
275		return
276	}
277	C.tls_free(t.ctx)
278	t.ctx = nil
279}
280