Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add SNI (RFC 6066) #17

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ static long SSL_CTX_clear_options_not_a_macro(SSL_CTX* ctx, long options) {
return SSL_CTX_clear_options(ctx, options);
}

static long SSL_CTX_get_options_not_a_macro(SSL_CTX* ctx) {
return SSL_CTX_get_options(ctx);
}

static long SSL_CTX_set_mode_not_a_macro(SSL_CTX* ctx, long modes) {
return SSL_CTX_set_mode(ctx, modes);
}
Expand All @@ -54,6 +58,11 @@ static long SSL_CTX_set_tmp_ecdh_not_a_macro(SSL_CTX* ctx, EC_KEY *key) {
return SSL_CTX_set_tmp_ecdh(ctx, key);
}

static long SSL_CTX_set_tlsext_servername_callback_not_a_macro(
SSL_CTX* ctx, int (*cb)(SSL *con, int *ad, void *args)) {
return SSL_CTX_set_tlsext_servername_callback(ctx, cb);
}

#ifndef SSL_MODE_RELEASE_BUFFERS
#define SSL_MODE_RELEASE_BUFFERS 0
#endif
Expand All @@ -78,6 +87,10 @@ static const SSL_METHOD *OUR_TLSv1_2_method() {
#endif
}

#if defined SSL_CTRL_SET_TLSEXT_HOSTNAME
extern int sni_cb(SSL *ssl_conn, int *ad, void *arg);
#endif

extern int verify_cb(int ok, X509_STORE_CTX* store);
*/
import "C"
Expand All @@ -102,6 +115,7 @@ var (
type Ctx struct {
ctx *C.SSL_CTX
verify_cb VerifyCallback
sni_cb TLSExtServernameCallback
}

//export get_ssl_ctx_idx
Expand Down Expand Up @@ -381,6 +395,12 @@ func (c *Ctx) ClearOptions(options Options) Options {
c.ctx, C.long(options)))
}

// GetOptions returns context options. See
// https://www.openssl.org/docs/ssl/SSL_CTX_set_options.html
func (c *Ctx) GetOptions() Options {
return Options(C.SSL_CTX_get_options_not_a_macro(c.ctx))
}

type Modes int

const (
Expand Down Expand Up @@ -451,6 +471,10 @@ func (c *Ctx) SetVerifyCallback(verify_cb VerifyCallback) {
c.SetVerify(c.VerifyMode(), verify_cb)
}

func (c *Ctx) GetVerifyCallback() VerifyCallback {
return c.verify_cb
}

func (c *Ctx) VerifyMode() VerifyOptions {
return VerifyOptions(C.SSL_CTX_get_verify_mode(c.ctx))
}
Expand All @@ -462,6 +486,23 @@ func (c *Ctx) SetVerifyDepth(depth int) {
C.SSL_CTX_set_verify_depth(c.ctx, C.int(depth))
}

// GetVerifyDepth controls how many certificates deep the certificate
// verification logic is willing to follow a certificate chain. See
// https://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
func (c *Ctx) GetVerifyDepth() int {
return int(C.SSL_CTX_get_verify_depth(c.ctx))
}

type TLSExtServernameCallback func(ssl *SSL) SSLTLSExtErr

// SetTLSExtServernameCallback sets callback function for Server Name Indication
// (SNI) rfc6066 (http://tools.ietf.org/html/rfc6066). See
// http://stackoverflow.com/questions/22373332/serving-multiple-domains-in-one-box-with-sni
func (c *Ctx) SetTLSExtServernameCallback(sni_cb TLSExtServernameCallback) {
c.sni_cb = sni_cb
C.SSL_CTX_set_tlsext_servername_callback_not_a_macro(c.ctx, (*[0]byte)(C.sni_cb))
}

func (c *Ctx) SetSessionId(session_id []byte) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
Expand Down
23 changes: 23 additions & 0 deletions sni.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (C) 2014 Space Monkey, Inc.
//
// 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.

#include <openssl/ssl.h>
#include "_cgo_export.h"
#include <stdio.h>

int sni_cb(SSL *con, int *ad, void *arg) {
SSL_CTX* ssl_ctx = ssl_ctx = SSL_get_SSL_CTX(con);
void* p = SSL_CTX_get_ex_data(ssl_ctx, get_ssl_ctx_idx());
return sni_cb_thunk(p, con, ad, arg);
}
23 changes: 23 additions & 0 deletions sni_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (C) 2014 Space Monkey, Inc.
//
// 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

import "fmt"

// We can implemant SNI rfc6066 (http://tools.ietf.org/html/rfc6066) on the server side using foolowing callback.
// You should implement context storage (tlsCtxStorage) by your self.
func ExampleSetTLSExtServernameCallback() {
fmt.Println("Hello")
}
191 changes: 191 additions & 0 deletions ssl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// Copyright (C) 2014 Space Monkey, Inc.
//
// 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.

// +build cgo

package openssl

/*
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/conf.h>

static long SSL_set_options_not_a_macro(SSL* ssl, long options) {
return SSL_set_options(ssl, options);
}

static long SSL_get_options_not_a_macro(SSL* ssl) {
return SSL_get_options(ssl);
}

static long SSL_clear_options_not_a_macro(SSL* ssl, long options) {
return SSL_clear_options(ssl, options);
}

extern int verify_ssl_cb(int ok, X509_STORE_CTX* store);
*/
import "C"

import (
"os"
"unsafe"
)

type SSLTLSExtErr int

const (
SSLTLSExtErrOK SSLTLSExtErr = C.SSL_TLSEXT_ERR_OK
SSLTLSExtErrAlertWarning SSLTLSExtErr = C.SSL_TLSEXT_ERR_ALERT_WARNING
SSLTLSEXTErrAlertFatal SSLTLSExtErr = C.SSL_TLSEXT_ERR_ALERT_FATAL
SSLTLSEXTErrNoAck SSLTLSExtErr = C.SSL_TLSEXT_ERR_NOACK
)

var (
ssl_idx = C.SSL_get_ex_new_index(0, nil, nil, nil, nil)
)

//export get_ssl_idx
func get_ssl_idx() C.int {
return ssl_idx
}

type SSL struct {
ssl *C.SSL
verify_cb VerifyCallback
}

//export verify_ssl_cb_thunk
func verify_ssl_cb_thunk(p unsafe.Pointer, ok C.int, ctx *C.X509_STORE_CTX) C.int {
defer func() {
if err := recover(); err != nil {
logger.Critf("openssl: verify callback panic'd: %v", err)
os.Exit(1)
}
}()
verify_cb := (*SSL)(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
}

// Wrapper around SSL_get_servername. Returns server name according to rfc6066
// http://tools.ietf.org/html/rfc6066.
func (s *SSL) GetServername() string {
return C.GoString(C.SSL_get_servername(s.ssl, C.TLSEXT_NAMETYPE_host_name))
}

// GetOptions returns SSL options. See
// https://www.openssl.org/docs/ssl/SSL_CTX_set_options.html
func (s *SSL) GetOptions() Options {
return Options(C.SSL_get_options_not_a_macro(s.ssl))
}

// SetOptions sets SSL options. See
// https://www.openssl.org/docs/ssl/SSL_CTX_set_options.html
func (s *SSL) SetOptions(options Options) Options {
return Options(C.SSL_set_options_not_a_macro(s.ssl, C.long(options)))
}

// ClearOptions clear SSL options. See
// https://www.openssl.org/docs/ssl/SSL_CTX_set_options.html
func (s *SSL) ClearOptions(options Options) Options {
return Options(C.SSL_clear_options_not_a_macro(s.ssl, C.long(options)))
}

// SetVerify controls peer verification settings. See
// http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
func (s *SSL) SetVerify(options VerifyOptions, verify_cb VerifyCallback) {
s.verify_cb = verify_cb
if verify_cb != nil {
C.SSL_set_verify(s.ssl, C.int(options), (*[0]byte)(C.verify_ssl_cb))
} else {
C.SSL_set_verify(s.ssl, C.int(options), nil)
}
}

// SetVerifyMode controls peer verification setting. See
// http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
func (s *SSL) SetVerifyMode(options VerifyOptions) {
s.SetVerify(options, s.verify_cb)
}

// SetVerifyCallback controls peer verification setting. See
// http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
func (s *SSL) SetVerifyCallback(verify_cb VerifyCallback) {
s.SetVerify(s.VerifyMode(), s.verify_cb)
}

// GetVerifyCallback returns callback function. See
// http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
func (s *SSL) GetVerifyCallback() VerifyCallback {
return s.verify_cb
}

// VerifyMode returns peer verification setting. See
// http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
func (s *SSL) VerifyMode() VerifyOptions {
return VerifyOptions(C.SSL_get_verify_mode(s.ssl))
}

// SetVerifyDepth controls how many certificates deep the certificate
// verification logic is willing to follow a certificate chain. See
// https://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
func (s *SSL) SetVerifyDepth(depth int) {
C.SSL_set_verify_depth(s.ssl, C.int(depth))
}

// GetVerifyDepth controls how many certificates deep the certificate
// verification logic is willing to follow a certificate chain. See
// https://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
func (s *SSL) GetVerifyDepth() int {
return int(C.SSL_get_verify_depth(s.ssl))
}

// SetSSLCtx changes context to new one. Useful for Server Name Indication (SNI)
// rfc6066 http://tools.ietf.org/html/rfc6066. See
// http://stackoverflow.com/questions/22373332/serving-multiple-domains-in-one-box-with-sni
func (s *SSL) SetSSLCtx(ctx *Ctx) {
/*
* SSL_set_SSL_CTX() only changes certs as of 1.0.0d
* adjust other things we care about
*/
C.SSL_set_SSL_CTX(s.ssl, ctx.ctx)
}

//export sni_cb_thunk
func sni_cb_thunk(p unsafe.Pointer, con *C.SSL, ad unsafe.Pointer, arg unsafe.Pointer) C.int {
defer func() {
if err := recover(); err != nil {
logger.Critf("openssl: verify callback sni panic'd: %v", err)
os.Exit(1)
}
}()

sni_cb := (*Ctx)(p).sni_cb

s := &SSL{ssl: con}
// This attaches a pointer to our SSL struct into the SNI callback.
C.SSL_set_ex_data(s.ssl, get_ssl_idx(), unsafe.Pointer(s))

// Note: this is ctx.sni_cb, not C.sni_cb
return C.int(sni_cb(s))
}
7 changes: 7 additions & 0 deletions verify.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,10 @@ int verify_cb(int ok, X509_STORE_CTX* store) {
// get the pointer to the go Ctx object and pass it back into the thunk
return verify_cb_thunk(p, ok, store);
}

int verify_ssl_cb(int ok, X509_STORE_CTX* store) {
SSL* ssl = (SSL *)X509_STORE_CTX_get_app_data(store);
void* p = SSL_get_ex_data(ssl, get_ssl_idx());
// get the pointer to the go Ctx object and pass it back into the thunk
return verify_ssl_cb_thunk(p, ok, store);
}