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