| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620 |
- // Copyright (C) 2017. See AUTHORS.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package openssl
- // #include "shim.h"
- import "C"
- import (
- "errors"
- "fmt"
- "io"
- "net"
- "runtime"
- "sync"
- "time"
- "unsafe"
- "github.com/spacemonkeygo/openssl/utils"
- )
- var (
- zeroReturn = errors.New("zero return")
- wantRead = errors.New("want read")
- wantWrite = errors.New("want write")
- tryAgain = errors.New("try again")
- )
- type Conn struct {
- *SSL
- conn net.Conn
- ctx *Ctx // for gc
- into_ssl *readBio
- from_ssl *writeBio
- is_shutdown bool
- mtx sync.Mutex
- want_read_future *utils.Future
- }
- type VerifyResult int
- const (
- Ok VerifyResult = C.X509_V_OK
- UnableToGetIssuerCert VerifyResult = C.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT
- UnableToGetCrl VerifyResult = C.X509_V_ERR_UNABLE_TO_GET_CRL
- UnableToDecryptCertSignature VerifyResult = C.X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE
- UnableToDecryptCrlSignature VerifyResult = C.X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE
- UnableToDecodeIssuerPublicKey VerifyResult = C.X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY
- CertSignatureFailure VerifyResult = C.X509_V_ERR_CERT_SIGNATURE_FAILURE
- CrlSignatureFailure VerifyResult = C.X509_V_ERR_CRL_SIGNATURE_FAILURE
- CertNotYetValid VerifyResult = C.X509_V_ERR_CERT_NOT_YET_VALID
- CertHasExpired VerifyResult = C.X509_V_ERR_CERT_HAS_EXPIRED
- CrlNotYetValid VerifyResult = C.X509_V_ERR_CRL_NOT_YET_VALID
- CrlHasExpired VerifyResult = C.X509_V_ERR_CRL_HAS_EXPIRED
- ErrorInCertNotBeforeField VerifyResult = C.X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD
- ErrorInCertNotAfterField VerifyResult = C.X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD
- ErrorInCrlLastUpdateField VerifyResult = C.X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD
- ErrorInCrlNextUpdateField VerifyResult = C.X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD
- OutOfMem VerifyResult = C.X509_V_ERR_OUT_OF_MEM
- DepthZeroSelfSignedCert VerifyResult = C.X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
- SelfSignedCertInChain VerifyResult = C.X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN
- UnableToGetIssuerCertLocally VerifyResult = C.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
- UnableToVerifyLeafSignature VerifyResult = C.X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
- CertChainTooLong VerifyResult = C.X509_V_ERR_CERT_CHAIN_TOO_LONG
- CertRevoked VerifyResult = C.X509_V_ERR_CERT_REVOKED
- InvalidCa VerifyResult = C.X509_V_ERR_INVALID_CA
- PathLengthExceeded VerifyResult = C.X509_V_ERR_PATH_LENGTH_EXCEEDED
- InvalidPurpose VerifyResult = C.X509_V_ERR_INVALID_PURPOSE
- CertUntrusted VerifyResult = C.X509_V_ERR_CERT_UNTRUSTED
- CertRejected VerifyResult = C.X509_V_ERR_CERT_REJECTED
- SubjectIssuerMismatch VerifyResult = C.X509_V_ERR_SUBJECT_ISSUER_MISMATCH
- AkidSkidMismatch VerifyResult = C.X509_V_ERR_AKID_SKID_MISMATCH
- AkidIssuerSerialMismatch VerifyResult = C.X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH
- KeyusageNoCertsign VerifyResult = C.X509_V_ERR_KEYUSAGE_NO_CERTSIGN
- UnableToGetCrlIssuer VerifyResult = C.X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER
- UnhandledCriticalExtension VerifyResult = C.X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION
- KeyusageNoCrlSign VerifyResult = C.X509_V_ERR_KEYUSAGE_NO_CRL_SIGN
- UnhandledCriticalCrlExtension VerifyResult = C.X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION
- InvalidNonCa VerifyResult = C.X509_V_ERR_INVALID_NON_CA
- ProxyPathLengthExceeded VerifyResult = C.X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED
- KeyusageNoDigitalSignature VerifyResult = C.X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE
- ProxyCertificatesNotAllowed VerifyResult = C.X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED
- InvalidExtension VerifyResult = C.X509_V_ERR_INVALID_EXTENSION
- InvalidPolicyExtension VerifyResult = C.X509_V_ERR_INVALID_POLICY_EXTENSION
- NoExplicitPolicy VerifyResult = C.X509_V_ERR_NO_EXPLICIT_POLICY
- UnnestedResource VerifyResult = C.X509_V_ERR_UNNESTED_RESOURCE
- ApplicationVerification VerifyResult = C.X509_V_ERR_APPLICATION_VERIFICATION
- )
- func newSSL(ctx *C.SSL_CTX) (*C.SSL, error) {
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
- ssl := C.SSL_new(ctx)
- if ssl == nil {
- return nil, errorFromErrorQueue()
- }
- return ssl, nil
- }
- func newConn(conn net.Conn, ctx *Ctx) (*Conn, error) {
- ssl, err := newSSL(ctx.ctx)
- if err != nil {
- return nil, err
- }
- into_ssl := &readBio{}
- from_ssl := &writeBio{}
- if ctx.GetMode()&ReleaseBuffers > 0 {
- into_ssl.release_buffers = true
- from_ssl.release_buffers = true
- }
- into_ssl_cbio := into_ssl.MakeCBIO()
- from_ssl_cbio := from_ssl.MakeCBIO()
- if into_ssl_cbio == nil || from_ssl_cbio == nil {
- // these frees are null safe
- C.BIO_free(into_ssl_cbio)
- C.BIO_free(from_ssl_cbio)
- C.SSL_free(ssl)
- return nil, errors.New("failed to allocate memory BIO")
- }
- // the ssl object takes ownership of these objects now
- C.SSL_set_bio(ssl, into_ssl_cbio, from_ssl_cbio)
- s := &SSL{ssl: ssl}
- C.SSL_set_ex_data(s.ssl, get_ssl_idx(), unsafe.Pointer(s))
- c := &Conn{
- SSL: s,
- conn: conn,
- ctx: ctx,
- into_ssl: into_ssl,
- from_ssl: from_ssl}
- runtime.SetFinalizer(c, func(c *Conn) {
- c.into_ssl.Disconnect(into_ssl_cbio)
- c.from_ssl.Disconnect(from_ssl_cbio)
- C.SSL_free(c.ssl)
- })
- return c, nil
- }
- // Client wraps an existing stream connection and puts it in the connect state
- // for any subsequent handshakes.
- //
- // IMPORTANT NOTE: if you use this method instead of Dial to construct an SSL
- // connection, you are responsible for verifying the peer's hostname.
- // Otherwise, you are vulnerable to MITM attacks.
- //
- // Client also does not set up SNI for you like Dial does.
- //
- // Client connections probably won't work for you unless you set a verify
- // location or add some certs to the certificate store of the client context
- // you're using. This library is not nice enough to use the system certificate
- // store by default for you yet.
- func Client(conn net.Conn, ctx *Ctx) (*Conn, error) {
- c, err := newConn(conn, ctx)
- if err != nil {
- return nil, err
- }
- C.SSL_set_connect_state(c.ssl)
- return c, nil
- }
- // Server wraps an existing stream connection and puts it in the accept state
- // for any subsequent handshakes.
- func Server(conn net.Conn, ctx *Ctx) (*Conn, error) {
- c, err := newConn(conn, ctx)
- if err != nil {
- return nil, err
- }
- C.SSL_set_accept_state(c.ssl)
- return c, nil
- }
- func (c *Conn) GetCtx() *Ctx { return c.ctx }
- func (c *Conn) CurrentCipher() (string, error) {
- p := C.X_SSL_get_cipher_name(c.ssl)
- if p == nil {
- return "", errors.New("Session not established")
- }
- return C.GoString(p), nil
- }
- func (c *Conn) fillInputBuffer() error {
- for {
- n, err := c.into_ssl.ReadFromOnce(c.conn)
- if n == 0 && err == nil {
- continue
- }
- if err == io.EOF {
- c.into_ssl.MarkEOF()
- return c.Close()
- }
- return err
- }
- }
- func (c *Conn) flushOutputBuffer() error {
- _, err := c.from_ssl.WriteTo(c.conn)
- return err
- }
- func (c *Conn) getErrorHandler(rv C.int, errno error) func() error {
- errcode := C.SSL_get_error(c.ssl, rv)
- switch errcode {
- case C.SSL_ERROR_ZERO_RETURN:
- return func() error {
- c.Close()
- return io.ErrUnexpectedEOF
- }
- case C.SSL_ERROR_WANT_READ:
- go c.flushOutputBuffer()
- if c.want_read_future != nil {
- want_read_future := c.want_read_future
- return func() error {
- _, err := want_read_future.Get()
- return err
- }
- }
- c.want_read_future = utils.NewFuture()
- want_read_future := c.want_read_future
- return func() (err error) {
- defer func() {
- c.mtx.Lock()
- c.want_read_future = nil
- c.mtx.Unlock()
- want_read_future.Set(nil, err)
- }()
- err = c.fillInputBuffer()
- if err != nil {
- return err
- }
- return tryAgain
- }
- case C.SSL_ERROR_WANT_WRITE:
- return func() error {
- err := c.flushOutputBuffer()
- if err != nil {
- return err
- }
- return tryAgain
- }
- case C.SSL_ERROR_SYSCALL:
- var err error
- if C.ERR_peek_error() == 0 {
- switch rv {
- case 0:
- err = errors.New("protocol-violating EOF")
- case -1:
- err = errno
- default:
- err = errorFromErrorQueue()
- }
- } else {
- err = errorFromErrorQueue()
- }
- return func() error { return err }
- default:
- err := errorFromErrorQueue()
- return func() error { return err }
- }
- }
- func (c *Conn) handleError(errcb func() error) error {
- if errcb != nil {
- return errcb()
- }
- return nil
- }
- func (c *Conn) handshake() func() error {
- c.mtx.Lock()
- defer c.mtx.Unlock()
- if c.is_shutdown {
- return func() error { return io.ErrUnexpectedEOF }
- }
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
- rv, errno := C.SSL_do_handshake(c.ssl)
- if rv > 0 {
- return nil
- }
- return c.getErrorHandler(rv, errno)
- }
- // Handshake performs an SSL handshake. If a handshake is not manually
- // triggered, it will run before the first I/O on the encrypted stream.
- func (c *Conn) Handshake() error {
- err := tryAgain
- for err == tryAgain {
- err = c.handleError(c.handshake())
- }
- go c.flushOutputBuffer()
- return err
- }
- // PeerCertificate returns the Certificate of the peer with which you're
- // communicating. Only valid after a handshake.
- func (c *Conn) PeerCertificate() (*Certificate, error) {
- c.mtx.Lock()
- defer c.mtx.Unlock()
- if c.is_shutdown {
- return nil, errors.New("connection closed")
- }
- x := C.SSL_get_peer_certificate(c.ssl)
- if x == nil {
- return nil, errors.New("no peer certificate found")
- }
- cert := &Certificate{x: x}
- runtime.SetFinalizer(cert, func(cert *Certificate) {
- C.X509_free(cert.x)
- })
- return cert, nil
- }
- // loadCertificateStack loads up a stack of x509 certificates and returns them,
- // handling memory ownership.
- func (c *Conn) loadCertificateStack(sk *C.struct_stack_st_X509) (
- rv []*Certificate) {
- sk_num := int(C.X_sk_X509_num(sk))
- rv = make([]*Certificate, 0, sk_num)
- for i := 0; i < sk_num; i++ {
- x := C.X_sk_X509_value(sk, C.int(i))
- // ref holds on to the underlying connection memory so we don't need to
- // worry about incrementing refcounts manually or freeing the X509
- rv = append(rv, &Certificate{x: x, ref: c})
- }
- return rv
- }
- // PeerCertificateChain returns the certificate chain of the peer. If called on
- // the client side, the stack also contains the peer's certificate; if called
- // on the server side, the peer's certificate must be obtained separately using
- // PeerCertificate.
- func (c *Conn) PeerCertificateChain() (rv []*Certificate, err error) {
- c.mtx.Lock()
- defer c.mtx.Unlock()
- if c.is_shutdown {
- return nil, errors.New("connection closed")
- }
- sk := C.SSL_get_peer_cert_chain(c.ssl)
- if sk == nil {
- return nil, errors.New("no peer certificates found")
- }
- return c.loadCertificateStack(sk), nil
- }
- type ConnectionState struct {
- Certificate *Certificate
- CertificateError error
- CertificateChain []*Certificate
- CertificateChainError error
- SessionReused bool
- }
- func (c *Conn) ConnectionState() (rv ConnectionState) {
- rv.Certificate, rv.CertificateError = c.PeerCertificate()
- rv.CertificateChain, rv.CertificateChainError = c.PeerCertificateChain()
- rv.SessionReused = c.SessionReused()
- return
- }
- func (c *Conn) shutdown() func() error {
- c.mtx.Lock()
- defer c.mtx.Unlock()
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
- rv, errno := C.SSL_shutdown(c.ssl)
- if rv > 0 {
- return nil
- }
- if rv == 0 {
- // The OpenSSL docs say that in this case, the shutdown is not
- // finished, and we should call SSL_shutdown() a second time, if a
- // bidirectional shutdown is going to be performed. Further, the
- // output of SSL_get_error may be misleading, as an erroneous
- // SSL_ERROR_SYSCALL may be flagged even though no error occurred.
- // So, TODO: revisit bidrectional shutdown, possibly trying again.
- // Note: some broken clients won't engage in bidirectional shutdown
- // without tickling them to close by sending a TCP_FIN packet, or
- // shutting down the write-side of the connection.
- return nil
- } else {
- return c.getErrorHandler(rv, errno)
- }
- }
- func (c *Conn) shutdownLoop() error {
- err := tryAgain
- shutdown_tries := 0
- for err == tryAgain {
- shutdown_tries = shutdown_tries + 1
- err = c.handleError(c.shutdown())
- if err == nil {
- return c.flushOutputBuffer()
- }
- if err == tryAgain && shutdown_tries >= 2 {
- return errors.New("shutdown requested a third time?")
- }
- }
- if err == io.ErrUnexpectedEOF {
- err = nil
- }
- return err
- }
- // Close shuts down the SSL connection and closes the underlying wrapped
- // connection.
- func (c *Conn) Close() error {
- c.mtx.Lock()
- if c.is_shutdown {
- c.mtx.Unlock()
- return nil
- }
- c.is_shutdown = true
- c.mtx.Unlock()
- var errs utils.ErrorGroup
- errs.Add(c.shutdownLoop())
- errs.Add(c.conn.Close())
- return errs.Finalize()
- }
- func (c *Conn) read(b []byte) (int, func() error) {
- if len(b) == 0 {
- return 0, nil
- }
- c.mtx.Lock()
- defer c.mtx.Unlock()
- if c.is_shutdown {
- return 0, func() error { return io.EOF }
- }
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
- rv, errno := C.SSL_read(c.ssl, unsafe.Pointer(&b[0]), C.int(len(b)))
- if rv > 0 {
- return int(rv), nil
- }
- return 0, c.getErrorHandler(rv, errno)
- }
- // Read reads up to len(b) bytes into b. It returns the number of bytes read
- // and an error if applicable. io.EOF is returned when the caller can expect
- // to see no more data.
- func (c *Conn) Read(b []byte) (n int, err error) {
- if len(b) == 0 {
- return 0, nil
- }
- err = tryAgain
- for err == tryAgain {
- n, errcb := c.read(b)
- err = c.handleError(errcb)
- if err == nil {
- go c.flushOutputBuffer()
- return n, nil
- }
- if err == io.ErrUnexpectedEOF {
- err = io.EOF
- }
- }
- return 0, err
- }
- func (c *Conn) write(b []byte) (int, func() error) {
- if len(b) == 0 {
- return 0, nil
- }
- c.mtx.Lock()
- defer c.mtx.Unlock()
- if c.is_shutdown {
- err := errors.New("connection closed")
- return 0, func() error { return err }
- }
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
- rv, errno := C.SSL_write(c.ssl, unsafe.Pointer(&b[0]), C.int(len(b)))
- if rv > 0 {
- return int(rv), nil
- }
- return 0, c.getErrorHandler(rv, errno)
- }
- // Write will encrypt the contents of b and write it to the underlying stream.
- // Performance will be vastly improved if the size of b is a multiple of
- // SSLRecordSize.
- func (c *Conn) Write(b []byte) (written int, err error) {
- if len(b) == 0 {
- return 0, nil
- }
- err = tryAgain
- for err == tryAgain {
- n, errcb := c.write(b)
- err = c.handleError(errcb)
- if err == nil {
- return n, c.flushOutputBuffer()
- }
- }
- return 0, err
- }
- // VerifyHostname pulls the PeerCertificate and calls VerifyHostname on the
- // certificate.
- func (c *Conn) VerifyHostname(host string) error {
- cert, err := c.PeerCertificate()
- if err != nil {
- return err
- }
- return cert.VerifyHostname(host)
- }
- // LocalAddr returns the underlying connection's local address
- func (c *Conn) LocalAddr() net.Addr {
- return c.conn.LocalAddr()
- }
- // RemoteAddr returns the underlying connection's remote address
- func (c *Conn) RemoteAddr() net.Addr {
- return c.conn.RemoteAddr()
- }
- // SetDeadline calls SetDeadline on the underlying connection.
- func (c *Conn) SetDeadline(t time.Time) error {
- return c.conn.SetDeadline(t)
- }
- // SetReadDeadline calls SetReadDeadline on the underlying connection.
- func (c *Conn) SetReadDeadline(t time.Time) error {
- return c.conn.SetReadDeadline(t)
- }
- // SetWriteDeadline calls SetWriteDeadline on the underlying connection.
- func (c *Conn) SetWriteDeadline(t time.Time) error {
- return c.conn.SetWriteDeadline(t)
- }
- func (c *Conn) UnderlyingConn() net.Conn {
- return c.conn
- }
- func (c *Conn) SetTlsExtHostName(name string) error {
- cname := C.CString(name)
- defer C.free(unsafe.Pointer(cname))
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
- if C.X_SSL_set_tlsext_host_name(c.ssl, cname) == 0 {
- return errorFromErrorQueue()
- }
- return nil
- }
- func (c *Conn) VerifyResult() VerifyResult {
- return VerifyResult(C.SSL_get_verify_result(c.ssl))
- }
- func (c *Conn) SessionReused() bool {
- return C.X_SSL_session_reused(c.ssl) == 1
- }
- func (c *Conn) GetSession() ([]byte, error) {
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
- // get1 increases the refcount of the session, so we have to free it.
- session := (*C.SSL_SESSION)(C.SSL_get1_session(c.ssl))
- if session == nil {
- return nil, errors.New("failed to get session")
- }
- defer C.SSL_SESSION_free(session)
- // get the size of the encoding
- slen := C.i2d_SSL_SESSION(session, nil)
- buf := (*C.uchar)(C.malloc(C.size_t(slen)))
- defer C.free(unsafe.Pointer(buf))
- // this modifies the value of buf (seriously), so we have to pass in a temp
- // var so that we can actually read the bytes from buf.
- tmp := buf
- slen2 := C.i2d_SSL_SESSION(session, &tmp)
- if slen != slen2 {
- return nil, errors.New("session had different lengths")
- }
- return C.GoBytes(unsafe.Pointer(buf), slen), nil
- }
- func (c *Conn) setSession(session []byte) error {
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
- ptr := (*C.uchar)(&session[0])
- s := C.d2i_SSL_SESSION(nil, &ptr, C.long(len(session)))
- if s == nil {
- return fmt.Errorf("unable to load session: %s", errorFromErrorQueue())
- }
- defer C.SSL_SESSION_free(s)
- ret := C.SSL_set_session(c.ssl, s)
- if ret != 1 {
- return fmt.Errorf("unable to set session: %s", errorFromErrorQueue())
- }
- return nil
- }
|