diff --git a/conn.go b/conn.go index 8cc756a..b0ba2e0 100644 --- a/conn.go +++ b/conn.go @@ -35,6 +35,7 @@ var ( type Conn struct { conn net.Conn ssl *C.SSL + ctx *Ctx // for gc into_ssl *readBio from_ssl *writeBio is_shutdown bool @@ -77,6 +78,7 @@ func newConn(conn net.Conn, ctx *Ctx) (*Conn, error) { c := &Conn{ conn: conn, ssl: ssl, + ctx: ctx, into_ssl: into_ssl, from_ssl: from_ssl} runtime.SetFinalizer(c, func(c *Conn) { diff --git a/ctx.go b/ctx.go index 69314e3..db7a3bb 100644 --- a/ctx.go +++ b/ctx.go @@ -3,44 +3,71 @@ package openssl -// #include -// #include +//#include +//#include +//#include +//#include // -// long SSL_CTX_set_options_not_a_macro(SSL_CTX* ctx, long options) { -// return SSL_CTX_set_options(ctx, options); -// } +//static long SSL_CTX_set_options_not_a_macro(SSL_CTX* ctx, long options) { +// return SSL_CTX_set_options(ctx, options); +//} // -// long SSL_CTX_set_mode_not_a_macro(SSL_CTX* ctx, long modes) { -// return SSL_CTX_set_mode(ctx, modes); -// } +//static long SSL_CTX_set_mode_not_a_macro(SSL_CTX* ctx, long modes) { +// return SSL_CTX_set_mode(ctx, modes); +//} // -// long SSL_CTX_set_session_cache_mode_not_a_macro(SSL_CTX* ctx, long modes) { -// return SSL_CTX_set_session_cache_mode(ctx, modes); -// } +//static long SSL_CTX_set_session_cache_mode_not_a_macro(SSL_CTX* ctx, long modes) { +// return SSL_CTX_set_session_cache_mode(ctx, modes); +//} // -// #ifndef SSL_MODE_RELEASE_BUFFERS -// #define SSL_MODE_RELEASE_BUFFERS 0 -// #endif -// #ifndef SSL_OP_NO_COMPRESSION -// #define SSL_OP_NO_COMPRESSION 0 -// #endif -// #ifndef TLSv1_1_method -// const SSL_METHOD *TLSv1_1_method() { return NULL; } -// #endif -// #ifndef TLSv1_2_method -// const SSL_METHOD *TLSv1_2_method() { return NULL; } -// #endif +//static int CRYPTO_add_not_a_macro(int *pointer,int amount,int type) { +// return CRYPTO_add(pointer, amount, type); +//} +// +//#ifndef SSL_MODE_RELEASE_BUFFERS +//#define SSL_MODE_RELEASE_BUFFERS 0 +//#endif +//#ifndef SSL_OP_NO_COMPRESSION +//#define SSL_OP_NO_COMPRESSION 0 +//#endif +//static const SSL_METHOD *OUR_TLSv1_1_method() { +//#ifndef TLSv1_1_method +// return NULL; +//#endif +// return TLSv1_1_method(); +//} +//static const SSL_METHOD *OUR_TLSv1_2_method() { +//#ifndef TLSv1_2_method +// return NULL; +//#endif +// return TLSv1_2_method(); +//} +// +//extern int verify_cb(int ok, X509_STORE_CTX* store); import "C" import ( "errors" + "fmt" "io/ioutil" + "log" + "os" "runtime" "unsafe" ) +var ( + ssl_ctx_idx = C.SSL_CTX_get_ex_new_index(0, nil, nil, nil, nil) +) + type Ctx struct { - ctx *C.SSL_CTX + ctx *C.SSL_CTX + verify_cb VerifyCallback +} + +//export get_ssl_ctx_idx +func get_ssl_ctx_idx() C.int { + return ssl_ctx_idx } func newCtx(method *C.SSL_METHOD) (*Ctx, error) { @@ -51,6 +78,7 @@ func newCtx(method *C.SSL_METHOD) (*Ctx, error) { return nil, errorFromErrorQueue() } c := &Ctx{ctx: ctx} + C.SSL_CTX_set_ex_data(ctx, get_ssl_ctx_idx(), unsafe.Pointer(c)) runtime.SetFinalizer(c, func(c *Ctx) { C.SSL_CTX_free(c.ctx) }) @@ -77,9 +105,9 @@ func NewCtxWithVersion(version SSLVersion) (*Ctx, error) { case TLSv1: method = C.TLSv1_method() case TLSv1_1: - method = C.TLSv1_1_method() + method = C.OUR_TLSv1_1_method() case TLSv1_2: - method = C.TLSv1_2_method() + method = C.OUR_TLSv1_2_method() case AnyVersion: method = C.SSLv23_method() } @@ -187,6 +215,42 @@ func (s *CertificateStore) AddCertificate(cert *Certificate) error { return nil } +type CertificateStoreCtx struct { + ctx *C.X509_STORE_CTX + ssl_ctx *Ctx +} + +func (self *CertificateStoreCtx) Err() error { + code := C.X509_STORE_CTX_get_error(self.ctx) + if code == C.X509_V_OK { + return nil + } + return fmt.Errorf("openssl: %s", + C.GoString(C.X509_verify_cert_error_string(C.long(code)))) +} + +func (self *CertificateStoreCtx) Depth() int { + return int(C.X509_STORE_CTX_get_error_depth(self.ctx)) +} + +// the certicate returned is only valid for the lifetime of the underlying +// X509_STORE_CTX +func (self *CertificateStoreCtx) GetCurrentCert() *Certificate { + x509 := C.X509_STORE_CTX_get_current_cert(self.ctx) + if x509 == nil { + return nil + } + // add a ref + C.CRYPTO_add_not_a_macro(&x509.references, 1, C.CRYPTO_LOCK_X509) + cert := &Certificate{ + x: x509, + } + runtime.SetFinalizer(cert, func(c *Certificate) { + C.X509_free(c.x) + }) + return cert +} + // LoadVerifyLocations tells the context to trust all certificate authorities // provided in either the ca_file or the ca_path. // See http://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html for @@ -251,11 +315,50 @@ const ( VerifyClientOnce VerifyOptions = C.SSL_VERIFY_CLIENT_ONCE ) +type VerifyCallback func(ok bool, store *CertificateStoreCtx) bool + +//export verify_cb_thunk +func verify_cb_thunk(p unsafe.Pointer, ok C.int, ctx *C.X509_STORE_CTX) C.int { + defer func() { + if err := recover(); err != nil { + log.Printf("openssl: verify callback panic'd: %v", err) + os.Exit(1) + } + }() + verify_cb := (*Ctx)(p).verify_cb + // set up defaults just in case verify_cb is nil + if verify_cb != nil { + store := &CertificateStoreCtx{ctx: ctx} + if verify_cb(ok == 1, store) { + ok = 1 + } else { + ok = 0 + } + } + return ok +} + // SetVerify controls peer verification settings. See // http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html -func (c *Ctx) SetVerify(options VerifyOptions) { - // TODO: take a callback - C.SSL_CTX_set_verify(c.ctx, C.int(options), nil) +func (c *Ctx) SetVerify(options VerifyOptions, verify_cb VerifyCallback) { + c.verify_cb = verify_cb + if verify_cb != nil { + C.SSL_CTX_set_verify(c.ctx, C.int(options), (*[0]byte)(C.verify_cb)) + } else { + C.SSL_CTX_set_verify(c.ctx, C.int(options), nil) + } +} + +func (c *Ctx) SetVerifyMode(options VerifyOptions) { + c.SetVerify(options, c.verify_cb) +} + +func (c *Ctx) SetVerifyCallback(verify_cb VerifyCallback) { + c.SetVerify(c.VerifyMode(), verify_cb) +} + +func (c *Ctx) VerifyMode() VerifyOptions { + return VerifyOptions(C.SSL_CTX_get_verify_mode(c.ctx)) } // SetVerifyDepth controls how many certificates deep the certificate diff --git a/ssl_test.go b/ssl_test.go index 725ec26..3db15cc 100644 --- a/ssl_test.go +++ b/ssl_test.go @@ -291,12 +291,28 @@ func StdlibConstructor(t testing.TB, server_conn, client_conn net.Conn) ( return server, client } +func passThruVerify(t testing.TB) func(bool, *CertificateStoreCtx) bool { + x := func(ok bool, store *CertificateStoreCtx) bool { + cert := store.GetCurrentCert() + if cert == nil { + t.Fatalf("Could not obtain cert from store\n") + } + sn := cert.GetSerialNumberHex() + if len(sn) == 0 { + t.Fatalf("Could not obtain serial number from cert") + } + return ok + } + return x +} + func OpenSSLConstructor(t testing.TB, server_conn, client_conn net.Conn) ( server, client HandshakingConn) { ctx, err := NewCtx() if err != nil { t.Fatal(err) } + ctx.SetVerify(VerifyNone, passThruVerify(t)) key, err := LoadPrivateKey(keyBytes) if err != nil { t.Fatal(err) diff --git a/verify.c b/verify.c new file mode 100644 index 0000000..8df3bc5 --- /dev/null +++ b/verify.c @@ -0,0 +1,11 @@ +#include +#include "_cgo_export.h" +#include + +int verify_cb(int ok, X509_STORE_CTX* store) { + SSL* ssl = (SSL *)X509_STORE_CTX_get_app_data(store); + SSL_CTX* ssl_ctx = ssl_ctx = SSL_get_SSL_CTX(ssl); + void* p = SSL_CTX_get_ex_data(ssl_ctx, get_ssl_ctx_idx()); + // get the pointer to the go Ctx object and pass it back into the thunk + return verify_cb_thunk(p, ok, store); +}