diff options
Diffstat (limited to 'src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl')
40 files changed, 6832 insertions, 0 deletions
diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/LICENSE b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/LICENSE new file mode 100644 index 00000000000..37ec93a14fd --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/README.md b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/README.md new file mode 100644 index 00000000000..6bd3383a0e8 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/README.md @@ -0,0 +1,26 @@ +# OpenSSL bindings for Go + +Please see http://godoc.org/github.com/spacemonkeygo/openssl for more info + +### License + +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. + +### Using on Windows +1. Install [mingw-w64](http://mingw-w64.sourceforge.net/) +2. Install [pkg-config-lite](http://sourceforge.net/projects/pkgconfiglite) +3. Build (or install precompiled) openssl for mingw32-w64 +4. Set __PKG\_CONFIG\_PATH__ to the directory containing openssl.pc + (i.e. c:\mingw64\mingw64\lib\pkgconfig) diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/bio.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/bio.go new file mode 100644 index 00000000000..1be93aaa23a --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/bio.go @@ -0,0 +1,355 @@ +// 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 <string.h> +#include <openssl/bio.h> + +extern int cbioNew(BIO *b); +static int cbioFree(BIO *b) { + return 1; +} + +extern int writeBioWrite(BIO *b, char *buf, int size); +extern long writeBioCtrl(BIO *b, int cmd, long arg1, void *arg2); +static int writeBioPuts(BIO *b, const char *str) { + return writeBioWrite(b, (char*)str, (int)strlen(str)); +} + +extern int readBioRead(BIO *b, char *buf, int size); +extern long readBioCtrl(BIO *b, int cmd, long arg1, void *arg2); + +static BIO_METHOD writeBioMethod = { + BIO_TYPE_SOURCE_SINK, + "Go Write BIO", + (int (*)(BIO *, const char *, int))writeBioWrite, + NULL, + writeBioPuts, + NULL, + writeBioCtrl, + cbioNew, + cbioFree, + NULL}; + +static BIO_METHOD* BIO_s_writeBio() { return &writeBioMethod; } + +static BIO_METHOD readBioMethod = { + BIO_TYPE_SOURCE_SINK, + "Go Read BIO", + NULL, + readBioRead, + NULL, + NULL, + readBioCtrl, + cbioNew, + cbioFree, + NULL}; + +static BIO_METHOD* BIO_s_readBio() { return &readBioMethod; } +*/ +import "C" + +import ( + "errors" + "io" + "reflect" + "sync" + "unsafe" +) + +const ( + SSLRecordSize = 16 * 1024 +) + +func nonCopyGoBytes(ptr uintptr, length int) []byte { + var slice []byte + header := (*reflect.SliceHeader)(unsafe.Pointer(&slice)) + header.Cap = length + header.Len = length + header.Data = ptr + return slice +} + +func nonCopyCString(data *C.char, size C.int) []byte { + return nonCopyGoBytes(uintptr(unsafe.Pointer(data)), int(size)) +} + +//export cbioNew +func cbioNew(b *C.BIO) C.int { + b.shutdown = 1 + b.init = 1 + b.num = -1 + b.ptr = nil + b.flags = 0 + return 1 +} + +type writeBio struct { + data_mtx sync.Mutex + op_mtx sync.Mutex + buf []byte + release_buffers bool +} + +func loadWritePtr(b *C.BIO) *writeBio { + return (*writeBio)(unsafe.Pointer(b.ptr)) +} + +func bioClearRetryFlags(b *C.BIO) { + // from BIO_clear_retry_flags and BIO_clear_flags + b.flags &= ^(C.BIO_FLAGS_RWS | C.BIO_FLAGS_SHOULD_RETRY) +} + +func bioSetRetryRead(b *C.BIO) { + // from BIO_set_retry_read and BIO_set_flags + b.flags |= (C.BIO_FLAGS_READ | C.BIO_FLAGS_SHOULD_RETRY) +} + +//export writeBioWrite +func writeBioWrite(b *C.BIO, data *C.char, size C.int) (rc C.int) { + defer func() { + if err := recover(); err != nil { + logger.Critf("openssl: writeBioWrite panic'd: %v", err) + rc = -1 + } + }() + ptr := loadWritePtr(b) + if ptr == nil || data == nil || size < 0 { + return -1 + } + ptr.data_mtx.Lock() + defer ptr.data_mtx.Unlock() + bioClearRetryFlags(b) + ptr.buf = append(ptr.buf, nonCopyCString(data, size)...) + return size +} + +//export writeBioCtrl +func writeBioCtrl(b *C.BIO, cmd C.int, arg1 C.long, arg2 unsafe.Pointer) ( + rc C.long) { + defer func() { + if err := recover(); err != nil { + logger.Critf("openssl: writeBioCtrl panic'd: %v", err) + rc = -1 + } + }() + switch cmd { + case C.BIO_CTRL_WPENDING: + return writeBioPending(b) + case C.BIO_CTRL_DUP, C.BIO_CTRL_FLUSH: + return 1 + default: + return 0 + } +} + +func writeBioPending(b *C.BIO) C.long { + ptr := loadWritePtr(b) + if ptr == nil { + return 0 + } + ptr.data_mtx.Lock() + defer ptr.data_mtx.Unlock() + return C.long(len(ptr.buf)) +} + +func (b *writeBio) WriteTo(w io.Writer) (rv int64, err error) { + b.op_mtx.Lock() + defer b.op_mtx.Unlock() + + // write whatever data we currently have + b.data_mtx.Lock() + data := b.buf + b.data_mtx.Unlock() + + if len(data) == 0 { + return 0, nil + } + n, err := w.Write(data) + + // subtract however much data we wrote from the buffer + b.data_mtx.Lock() + b.buf = b.buf[:copy(b.buf, b.buf[n:])] + if b.release_buffers && len(b.buf) == 0 { + b.buf = nil + } + b.data_mtx.Unlock() + + return int64(n), err +} + +func (self *writeBio) Disconnect(b *C.BIO) { + if loadWritePtr(b) == self { + b.ptr = nil + } +} + +func (b *writeBio) MakeCBIO() *C.BIO { + rv := C.BIO_new(C.BIO_s_writeBio()) + rv.ptr = unsafe.Pointer(b) + return rv +} + +type readBio struct { + data_mtx sync.Mutex + op_mtx sync.Mutex + buf []byte + eof bool + release_buffers bool +} + +func loadReadPtr(b *C.BIO) *readBio { + return (*readBio)(unsafe.Pointer(b.ptr)) +} + +//export readBioRead +func readBioRead(b *C.BIO, data *C.char, size C.int) (rc C.int) { + defer func() { + if err := recover(); err != nil { + logger.Critf("openssl: readBioRead panic'd: %v", err) + rc = -1 + } + }() + ptr := loadReadPtr(b) + if ptr == nil || size < 0 { + return -1 + } + ptr.data_mtx.Lock() + defer ptr.data_mtx.Unlock() + bioClearRetryFlags(b) + if len(ptr.buf) == 0 { + if ptr.eof { + return 0 + } + bioSetRetryRead(b) + return -1 + } + if size == 0 || data == nil { + return C.int(len(ptr.buf)) + } + n := copy(nonCopyCString(data, size), ptr.buf) + ptr.buf = ptr.buf[:copy(ptr.buf, ptr.buf[n:])] + if ptr.release_buffers && len(ptr.buf) == 0 { + ptr.buf = nil + } + return C.int(n) +} + +//export readBioCtrl +func readBioCtrl(b *C.BIO, cmd C.int, arg1 C.long, arg2 unsafe.Pointer) ( + rc C.long) { + + defer func() { + if err := recover(); err != nil { + logger.Critf("openssl: readBioCtrl panic'd: %v", err) + rc = -1 + } + }() + switch cmd { + case C.BIO_CTRL_PENDING: + return readBioPending(b) + case C.BIO_CTRL_DUP, C.BIO_CTRL_FLUSH: + return 1 + default: + return 0 + } +} + +func readBioPending(b *C.BIO) C.long { + ptr := loadReadPtr(b) + if ptr == nil { + return 0 + } + ptr.data_mtx.Lock() + defer ptr.data_mtx.Unlock() + return C.long(len(ptr.buf)) +} + +func (b *readBio) ReadFromOnce(r io.Reader) (n int, err error) { + b.op_mtx.Lock() + defer b.op_mtx.Unlock() + + // make sure we have a destination that fits at least one SSL record + b.data_mtx.Lock() + if cap(b.buf) < len(b.buf)+SSLRecordSize { + new_buf := make([]byte, len(b.buf), len(b.buf)+SSLRecordSize) + copy(new_buf, b.buf) + b.buf = new_buf + } + dst := b.buf[len(b.buf):cap(b.buf)] + dst_slice := b.buf + b.data_mtx.Unlock() + + n, err = r.Read(dst) + b.data_mtx.Lock() + defer b.data_mtx.Unlock() + if n > 0 { + if len(dst_slice) != len(b.buf) { + // someone shrunk the buffer, so we read in too far ahead and we + // need to slide backwards + copy(b.buf[len(b.buf):len(b.buf)+n], dst) + } + b.buf = b.buf[:len(b.buf)+n] + } + return n, err +} + +func (b *readBio) MakeCBIO() *C.BIO { + rv := C.BIO_new(C.BIO_s_readBio()) + rv.ptr = unsafe.Pointer(b) + return rv +} + +func (self *readBio) Disconnect(b *C.BIO) { + if loadReadPtr(b) == self { + b.ptr = nil + } +} + +func (b *readBio) MarkEOF() { + b.data_mtx.Lock() + defer b.data_mtx.Unlock() + b.eof = true +} + +type anyBio C.BIO + +func asAnyBio(b *C.BIO) *anyBio { return (*anyBio)(b) } + +func (b *anyBio) Read(buf []byte) (n int, err error) { + if len(buf) == 0 { + return 0, nil + } + n = int(C.BIO_read((*C.BIO)(b), unsafe.Pointer(&buf[0]), C.int(len(buf)))) + if n <= 0 { + return 0, io.EOF + } + return n, nil +} + +func (b *anyBio) Write(buf []byte) (written int, err error) { + if len(buf) == 0 { + return 0, nil + } + n := int(C.BIO_write((*C.BIO)(b), unsafe.Pointer(&buf[0]), + C.int(len(buf)))) + if n != len(buf) { + return n, errors.New("BIO write failed") + } + return n, nil +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/build.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/build.go new file mode 100644 index 00000000000..dd72651d3ea --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/build.go @@ -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. + +// +build cgo + +package openssl + +// #cgo linux pkg-config: openssl +// #cgo windows CFLAGS: -DWIN32_LEAN_AND_MEAN +// #cgo darwin CFLAGS: -Wno-deprecated-declarations +// #cgo darwin LDFLAGS: -lssl -lcrypto +import "C" diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/cert.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/cert.go new file mode 100644 index 00000000000..61637c649fa --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/cert.go @@ -0,0 +1,407 @@ +// 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/conf.h> +// #include <openssl/ssl.h> +// #include <openssl/x509v3.h> +// +// void OPENSSL_free_not_a_macro(void *ref) { OPENSSL_free(ref); } +// +import "C" + +import ( + "errors" + "io/ioutil" + "math/big" + "runtime" + "time" + "unsafe" +) + +type EVP_MD int + +const ( + EVP_NULL EVP_MD = iota + EVP_MD5 EVP_MD = iota + EVP_SHA EVP_MD = iota + EVP_SHA1 EVP_MD = iota + EVP_DSS EVP_MD = iota + EVP_DSS1 EVP_MD = iota + EVP_MDC2 EVP_MD = iota + EVP_RIPEMD160 EVP_MD = iota + EVP_SHA224 EVP_MD = iota + EVP_SHA256 EVP_MD = iota + EVP_SHA384 EVP_MD = iota + EVP_SHA512 EVP_MD = iota +) + +type Certificate struct { + x *C.X509 + Issuer *Certificate + ref interface{} + pubKey PublicKey +} + +type CertificateInfo struct { + Serial *big.Int + Issued time.Duration + Expires time.Duration + Country string + Organization string + CommonName string +} + +type Name struct { + name *C.X509_NAME +} + +// Allocate and return a new Name object. +func NewName() (*Name, error) { + n := C.X509_NAME_new() + if n == nil { + return nil, errors.New("could not create x509 name") + } + name := &Name{name: n} + runtime.SetFinalizer(name, func(n *Name) { + C.X509_NAME_free(n.name) + }) + return name, nil +} + +// AddTextEntry appends a text entry to an X509 NAME. +func (n *Name) AddTextEntry(field, value string) error { + cfield := C.CString(field) + defer C.free(unsafe.Pointer(cfield)) + cvalue := (*C.uchar)(unsafe.Pointer(C.CString(value))) + defer C.free(unsafe.Pointer(cvalue)) + ret := C.X509_NAME_add_entry_by_txt( + n.name, cfield, C.MBSTRING_ASC, cvalue, -1, -1, 0) + if ret != 1 { + return errors.New("failed to add x509 name text entry") + } + return nil +} + +// AddTextEntries allows adding multiple entries to a name in one call. +func (n *Name) AddTextEntries(entries map[string]string) error { + for f, v := range entries { + if err := n.AddTextEntry(f, v); err != nil { + return err + } + } + return nil +} + +// GetEntry returns a name entry based on NID. If no entry, then ("", false) is +// returned. +func (n *Name) GetEntry(nid NID) (entry string, ok bool) { + entrylen := C.X509_NAME_get_text_by_NID(n.name, C.int(nid), nil, 0) + if entrylen == -1 { + return "", false + } + buf := (*C.char)(C.malloc(C.size_t(entrylen + 1))) + defer C.free(unsafe.Pointer(buf)) + C.X509_NAME_get_text_by_NID(n.name, C.int(nid), buf, entrylen+1) + return C.GoStringN(buf, entrylen), true +} + +// NewCertificate generates a basic certificate based +// on the provided CertificateInfo struct +func NewCertificate(info *CertificateInfo, key PublicKey) (*Certificate, error) { + c := &Certificate{x: C.X509_new()} + runtime.SetFinalizer(c, func(c *Certificate) { + C.X509_free(c.x) + }) + + name, err := c.GetSubjectName() + if err != nil { + return nil, err + } + err = name.AddTextEntries(map[string]string{ + "C": info.Country, + "O": info.Organization, + "CN": info.CommonName, + }) + if err != nil { + return nil, err + } + // self-issue for now + if err := c.SetIssuerName(name); err != nil { + return nil, err + } + if err := c.SetSerial(info.Serial); err != nil { + return nil, err + } + if err := c.SetIssueDate(info.Issued); err != nil { + return nil, err + } + if err := c.SetExpireDate(info.Expires); err != nil { + return nil, err + } + if err := c.SetPubKey(key); err != nil { + return nil, err + } + return c, nil +} + +func (c *Certificate) GetSubjectName() (*Name, error) { + n := C.X509_get_subject_name(c.x) + if n == nil { + return nil, errors.New("failed to get subject name") + } + return &Name{name: n}, nil +} + +func (c *Certificate) GetIssuerName() (*Name, error) { + n := C.X509_get_issuer_name(c.x) + if n == nil { + return nil, errors.New("failed to get issuer name") + } + return &Name{name: n}, nil +} + +func (c *Certificate) SetSubjectName(name *Name) error { + if C.X509_set_subject_name(c.x, name.name) != 1 { + return errors.New("failed to set subject name") + } + return nil +} + +// SetIssuer updates the stored Issuer cert +// and the internal x509 Issuer Name of a certificate. +// The stored Issuer reference is used when adding extensions. +func (c *Certificate) SetIssuer(issuer *Certificate) error { + name, err := issuer.GetSubjectName() + if err != nil { + return err + } + if err = c.SetIssuerName(name); err != nil { + return err + } + c.Issuer = issuer + return nil +} + +// SetIssuerName populates the issuer name of a certificate. +// Use SetIssuer instead, if possible. +func (c *Certificate) SetIssuerName(name *Name) error { + if C.X509_set_issuer_name(c.x, name.name) != 1 { + return errors.New("failed to set subject name") + } + return nil +} + +// SetSerial sets the serial of a certificate. +func (c *Certificate) SetSerial(serial *big.Int) error { + sno := C.ASN1_INTEGER_new() + defer C.ASN1_INTEGER_free(sno) + bn := C.BN_new() + defer C.BN_free(bn) + + serialBytes := serial.Bytes() + if bn = C.BN_bin2bn((*C.uchar)(unsafe.Pointer(&serialBytes[0])), C.int(len(serialBytes)), bn); bn == nil { + return errors.New("failed to set serial") + } + if sno = C.BN_to_ASN1_INTEGER(bn, sno); sno == nil { + return errors.New("failed to set serial") + } + if C.X509_set_serialNumber(c.x, sno) != 1 { + return errors.New("failed to set serial") + } + return nil +} + +// SetIssueDate sets the certificate issue date relative to the current time. +func (c *Certificate) SetIssueDate(when time.Duration) error { + offset := C.long(when / time.Second) + result := C.X509_gmtime_adj(c.x.cert_info.validity.notBefore, offset) + if result == nil { + return errors.New("failed to set issue date") + } + return nil +} + +// SetExpireDate sets the certificate issue date relative to the current time. +func (c *Certificate) SetExpireDate(when time.Duration) error { + offset := C.long(when / time.Second) + result := C.X509_gmtime_adj(c.x.cert_info.validity.notAfter, offset) + if result == nil { + return errors.New("failed to set expire date") + } + return nil +} + +// SetPubKey assigns a new public key to a certificate. +func (c *Certificate) SetPubKey(pubKey PublicKey) error { + c.pubKey = pubKey + if C.X509_set_pubkey(c.x, pubKey.evpPKey()) != 1 { + return errors.New("failed to set public key") + } + return nil +} + +// Sign a certificate using a private key and a digest name. +// Accepted digest names are 'sha256', 'sha384', and 'sha512'. +func (c *Certificate) Sign(privKey PrivateKey, digest EVP_MD) error { + switch digest { + case EVP_SHA256: + case EVP_SHA384: + case EVP_SHA512: + default: + return errors.New("Unsupported digest" + + "You're probably looking for 'EVP_SHA256' or 'EVP_SHA512'.") + } + return c.insecureSign(privKey, digest) +} + +func (c *Certificate) insecureSign(privKey PrivateKey, digest EVP_MD) error { + var md *C.EVP_MD + switch digest { + // please don't use these digest functions + case EVP_NULL: + md = C.EVP_md_null() + case EVP_MD5: + md = C.EVP_md5() + case EVP_SHA: + md = C.EVP_sha() + case EVP_SHA1: + md = C.EVP_sha1() + case EVP_DSS: + md = C.EVP_dss() + case EVP_DSS1: + md = C.EVP_dss1() + case EVP_RIPEMD160: + md = C.EVP_ripemd160() + case EVP_SHA224: + md = C.EVP_sha224() + // you actually want one of these + case EVP_SHA256: + md = C.EVP_sha256() + case EVP_SHA384: + md = C.EVP_sha384() + case EVP_SHA512: + md = C.EVP_sha512() + } + if C.X509_sign(c.x, privKey.evpPKey(), md) <= 0 { + return errors.New("failed to sign certificate") + } + return nil +} + +// Add an extension to a certificate. +// Extension constants are NID_* as found in openssl. +func (c *Certificate) AddExtension(nid NID, value string) error { + issuer := c + if c.Issuer != nil { + issuer = c.Issuer + } + var ctx C.X509V3_CTX + C.X509V3_set_ctx(&ctx, c.x, issuer.x, nil, nil, 0) + ex := C.X509V3_EXT_conf_nid(nil, &ctx, C.int(nid), C.CString(value)) + if ex == nil { + return errors.New("failed to create x509v3 extension") + } + defer C.X509_EXTENSION_free(ex) + if C.X509_add_ext(c.x, ex, -1) <= 0 { + return errors.New("failed to add x509v3 extension") + } + return nil +} + +// Wraps AddExtension using a map of NID to text extension. +// Will return without finishing if it encounters an error. +func (c *Certificate) AddExtensions(extensions map[NID]string) error { + for nid, value := range extensions { + if err := c.AddExtension(nid, value); err != nil { + return err + } + } + return nil +} + +// LoadCertificateFromPEM loads an X509 certificate from a PEM-encoded block. +func LoadCertificateFromPEM(pem_block []byte) (*Certificate, error) { + if len(pem_block) == 0 { + return nil, errors.New("empty pem block") + } + runtime.LockOSThread() + defer runtime.UnlockOSThread() + bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]), + C.int(len(pem_block))) + cert := C.PEM_read_bio_X509(bio, nil, nil, nil) + C.BIO_free(bio) + if cert == nil { + return nil, errorFromErrorQueue() + } + x := &Certificate{x: cert} + runtime.SetFinalizer(x, func(x *Certificate) { + C.X509_free(x.x) + }) + return x, nil +} + +// MarshalPEM converts the X509 certificate to PEM-encoded format +func (c *Certificate) MarshalPEM() (pem_block []byte, err error) { + bio := C.BIO_new(C.BIO_s_mem()) + if bio == nil { + return nil, errors.New("failed to allocate memory BIO") + } + defer C.BIO_free(bio) + if int(C.PEM_write_bio_X509(bio, c.x)) != 1 { + return nil, errors.New("failed dumping certificate") + } + return ioutil.ReadAll(asAnyBio(bio)) +} + +// PublicKey returns the public key embedded in the X509 certificate. +func (c *Certificate) PublicKey() (PublicKey, error) { + pkey := C.X509_get_pubkey(c.x) + if pkey == nil { + return nil, errors.New("no public key found") + } + key := &pKey{key: pkey} + runtime.SetFinalizer(key, func(key *pKey) { + C.EVP_PKEY_free(key.key) + }) + return key, nil +} + +// GetSerialNumberHex returns the certificate's serial number in hex format +func (c *Certificate) GetSerialNumberHex() (serial string) { + asn1_i := C.X509_get_serialNumber(c.x) + bignum := C.ASN1_INTEGER_to_BN(asn1_i, nil) + hex := C.BN_bn2hex(bignum) + serial = C.GoString(hex) + C.BN_free(bignum) + C.OPENSSL_free_not_a_macro(unsafe.Pointer(hex)) + return +} + +func (c *Certificate) X509NamePrintEx() (out []byte, err error) { + bio := C.BIO_new(C.BIO_s_mem()) + if bio == nil { + return nil, errors.New("failed to allocate memory BIO") + } + defer C.BIO_free(bio) + name := C.X509_get_subject_name(c.x) + // TODO, pass in flags instead of using this hardcoded one + if int(C.X509_NAME_print_ex(bio, name, 0, C.XN_FLAG_RFC2253)) < 0 { + return nil, errors.New("failed formatting subject") + } + return ioutil.ReadAll(asAnyBio(bio)) +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/cert_test.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/cert_test.go new file mode 100644 index 00000000000..c32883ba4eb --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/cert_test.go @@ -0,0 +1,139 @@ +// Copyright (C) 2014 Ryan Hileman +// +// 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 ( + "math/big" + "testing" + "time" +) + +func TestCertGenerate(t *testing.T) { + key, err := GenerateRSAKey(2048) + if err != nil { + t.Fatal(err) + } + info := &CertificateInfo{ + Serial: big.NewInt(int64(1)), + Issued: 0, + Expires: 24 * time.Hour, + Country: "US", + Organization: "Test", + CommonName: "localhost", + } + cert, err := NewCertificate(info, key) + if err != nil { + t.Fatal(err) + } + if err := cert.Sign(key, EVP_SHA256); err != nil { + t.Fatal(err) + } +} + +func TestCAGenerate(t *testing.T) { + cakey, err := GenerateRSAKey(2048) + if err != nil { + t.Fatal(err) + } + info := &CertificateInfo{ + Serial: big.NewInt(int64(1)), + Issued: 0, + Expires: 24 * time.Hour, + Country: "US", + Organization: "Test CA", + CommonName: "CA", + } + ca, err := NewCertificate(info, cakey) + if err != nil { + t.Fatal(err) + } + if err := ca.AddExtensions(map[NID]string{ + NID_basic_constraints: "critical,CA:TRUE", + NID_key_usage: "critical,keyCertSign,cRLSign", + NID_subject_key_identifier: "hash", + NID_netscape_cert_type: "sslCA", + }); err != nil { + t.Fatal(err) + } + if err := ca.Sign(cakey, EVP_SHA256); err != nil { + t.Fatal(err) + } + key, err := GenerateRSAKey(2048) + if err != nil { + t.Fatal(err) + } + info = &CertificateInfo{ + Serial: big.NewInt(int64(1)), + Issued: 0, + Expires: 24 * time.Hour, + Country: "US", + Organization: "Test", + CommonName: "localhost", + } + cert, err := NewCertificate(info, key) + if err != nil { + t.Fatal(err) + } + if err := cert.AddExtensions(map[NID]string{ + NID_basic_constraints: "critical,CA:FALSE", + NID_key_usage: "keyEncipherment", + NID_ext_key_usage: "serverAuth", + }); err != nil { + t.Fatal(err) + } + if err := cert.SetIssuer(ca); err != nil { + t.Fatal(err) + } + if err := cert.Sign(cakey, EVP_SHA256); err != nil { + t.Fatal(err) + } +} + +func TestCertGetNameEntry(t *testing.T) { + key, err := GenerateRSAKey(2048) + if err != nil { + t.Fatal(err) + } + info := &CertificateInfo{ + Serial: big.NewInt(int64(1)), + Issued: 0, + Expires: 24 * time.Hour, + Country: "US", + Organization: "Test", + CommonName: "localhost", + } + cert, err := NewCertificate(info, key) + if err != nil { + t.Fatal(err) + } + name, err := cert.GetSubjectName() + if err != nil { + t.Fatal(err) + } + entry, ok := name.GetEntry(NID_commonName) + if !ok { + t.Fatal("no common name") + } + if entry != "localhost" { + t.Fatalf("expected localhost; got %q", entry) + } + entry, ok = name.GetEntry(NID_localityName) + if ok { + t.Fatal("did not expect a locality name") + } + if entry != "" { + t.Fatalf("entry should be empty; got %q", entry) + } +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/ciphers.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/ciphers.go new file mode 100644 index 00000000000..12662707f54 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/ciphers.go @@ -0,0 +1,355 @@ +// 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/evp.h> +// +// int EVP_CIPHER_block_size_not_a_macro(EVP_CIPHER *c) { +// return EVP_CIPHER_block_size(c); +// } +// +// int EVP_CIPHER_key_length_not_a_macro(EVP_CIPHER *c) { +// return EVP_CIPHER_key_length(c); +// } +// +// int EVP_CIPHER_iv_length_not_a_macro(EVP_CIPHER *c) { +// return EVP_CIPHER_iv_length(c); +// } +// +// int EVP_CIPHER_nid_not_a_macro(EVP_CIPHER *c) { +// return EVP_CIPHER_nid(c); +// } +// +// int EVP_CIPHER_CTX_block_size_not_a_macro(EVP_CIPHER_CTX *ctx) { +// return EVP_CIPHER_CTX_block_size(ctx); +// } +// +// int EVP_CIPHER_CTX_key_length_not_a_macro(EVP_CIPHER_CTX *ctx) { +// return EVP_CIPHER_CTX_key_length(ctx); +// } +// +// int EVP_CIPHER_CTX_iv_length_not_a_macro(EVP_CIPHER_CTX *ctx) { +// return EVP_CIPHER_CTX_iv_length(ctx); +// } +// +// const EVP_CIPHER *EVP_CIPHER_CTX_cipher_not_a_macro(EVP_CIPHER_CTX *ctx) { +// return EVP_CIPHER_CTX_cipher(ctx); +// } +import "C" + +import ( + "errors" + "fmt" + "runtime" + "unsafe" +) + +const ( + GCM_TAG_MAXLEN = 16 +) + +type CipherCtx interface { + Cipher() *Cipher + BlockSize() int + KeySize() int + IVSize() int +} + +type Cipher struct { + ptr *C.EVP_CIPHER +} + +func (c *Cipher) Nid() NID { + return NID(C.EVP_CIPHER_nid_not_a_macro(c.ptr)) +} + +func (c *Cipher) ShortName() (string, error) { + return Nid2ShortName(c.Nid()) +} + +func (c *Cipher) BlockSize() int { + return int(C.EVP_CIPHER_block_size_not_a_macro(c.ptr)) +} + +func (c *Cipher) KeySize() int { + return int(C.EVP_CIPHER_key_length_not_a_macro(c.ptr)) +} + +func (c *Cipher) IVSize() int { + return int(C.EVP_CIPHER_iv_length_not_a_macro(c.ptr)) +} + +func Nid2ShortName(nid NID) (string, error) { + sn := C.OBJ_nid2sn(C.int(nid)) + if sn == nil { + return "", fmt.Errorf("NID %d not found", nid) + } + return C.GoString(sn), nil +} + +func GetCipherByName(name string) (*Cipher, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + p := C.EVP_get_cipherbyname(cname) + if p == nil { + return nil, fmt.Errorf("Cipher %v not found", name) + } + // we can consider ciphers to use static mem; don't need to free + return &Cipher{ptr: p}, nil +} + +func GetCipherByNid(nid NID) (*Cipher, error) { + sn, err := Nid2ShortName(nid) + if err != nil { + return nil, err + } + return GetCipherByName(sn) +} + +type cipherCtx struct { + ctx *C.EVP_CIPHER_CTX +} + +func newCipherCtx() (*cipherCtx, error) { + cctx := C.EVP_CIPHER_CTX_new() + if cctx == nil { + return nil, errors.New("failed to allocate cipher context") + } + ctx := &cipherCtx{cctx} + runtime.SetFinalizer(ctx, func(ctx *cipherCtx) { + C.EVP_CIPHER_CTX_free(ctx.ctx) + }) + return ctx, nil +} + +func (ctx *cipherCtx) applyKeyAndIV(key, iv []byte) error { + var kptr, iptr *C.uchar + if key != nil { + if len(key) != ctx.KeySize() { + return fmt.Errorf("bad key size (%d bytes instead of %d)", + len(key), ctx.KeySize()) + } + kptr = (*C.uchar)(&key[0]) + } + if iv != nil { + if len(iv) != ctx.IVSize() { + return fmt.Errorf("bad IV size (%d bytes instead of %d)", + len(iv), ctx.IVSize()) + } + iptr = (*C.uchar)(&iv[0]) + } + if kptr != nil || iptr != nil { + var res C.int + if ctx.ctx.encrypt != 0 { + res = C.EVP_EncryptInit_ex(ctx.ctx, nil, nil, kptr, iptr) + } else { + res = C.EVP_DecryptInit_ex(ctx.ctx, nil, nil, kptr, iptr) + } + if 1 != res { + return errors.New("failed to apply key/IV") + } + } + return nil +} + +func (ctx *cipherCtx) Cipher() *Cipher { + return &Cipher{ptr: C.EVP_CIPHER_CTX_cipher_not_a_macro(ctx.ctx)} +} + +func (ctx *cipherCtx) BlockSize() int { + return int(C.EVP_CIPHER_CTX_block_size_not_a_macro(ctx.ctx)) +} + +func (ctx *cipherCtx) KeySize() int { + return int(C.EVP_CIPHER_CTX_key_length_not_a_macro(ctx.ctx)) +} + +func (ctx *cipherCtx) IVSize() int { + return int(C.EVP_CIPHER_CTX_iv_length_not_a_macro(ctx.ctx)) +} + +func (ctx *cipherCtx) setCtrl(code, arg int) error { + res := C.EVP_CIPHER_CTX_ctrl(ctx.ctx, C.int(code), C.int(arg), nil) + if res != 1 { + return fmt.Errorf("failed to set code %d to %d [result %d]", + code, arg, res) + } + return nil +} + +func (ctx *cipherCtx) setCtrlBytes(code, arg int, value []byte) error { + res := C.EVP_CIPHER_CTX_ctrl(ctx.ctx, C.int(code), C.int(arg), + unsafe.Pointer(&value[0])) + if res != 1 { + return fmt.Errorf("failed to set code %d with arg %d to %x [result %d]", + code, arg, value, res) + } + return nil +} + +func (ctx *cipherCtx) getCtrlInt(code, arg int) (int, error) { + var returnVal C.int + res := C.EVP_CIPHER_CTX_ctrl(ctx.ctx, C.int(code), C.int(arg), + unsafe.Pointer(&returnVal)) + if res != 1 { + return 0, fmt.Errorf("failed to get code %d with arg %d [result %d]", + code, arg, res) + } + return int(returnVal), nil +} + +func (ctx *cipherCtx) getCtrlBytes(code, arg, expectsize int) ([]byte, error) { + returnVal := make([]byte, expectsize) + res := C.EVP_CIPHER_CTX_ctrl(ctx.ctx, C.int(code), C.int(arg), + unsafe.Pointer(&returnVal[0])) + if res != 1 { + return nil, fmt.Errorf("failed to get code %d with arg %d [result %d]", + code, arg, res) + } + return returnVal, nil +} + +type EncryptionCipherCtx interface { + CipherCtx + + // pass in plaintext, get back ciphertext. can be called + // multiple times as needed + EncryptUpdate(input []byte) ([]byte, error) + + // call after all plaintext has been passed in; may return + // additional ciphertext if needed to finish off a block + // or extra padding information + EncryptFinal() ([]byte, error) +} + +type DecryptionCipherCtx interface { + CipherCtx + + // pass in ciphertext, get back plaintext. can be called + // multiple times as needed + DecryptUpdate(input []byte) ([]byte, error) + + // call after all ciphertext has been passed in; may return + // additional plaintext if needed to finish off a block + DecryptFinal() ([]byte, error) +} + +type encryptionCipherCtx struct { + *cipherCtx +} + +type decryptionCipherCtx struct { + *cipherCtx +} + +func newEncryptionCipherCtx(c *Cipher, e *Engine, key, iv []byte) ( + *encryptionCipherCtx, error) { + if c == nil { + return nil, errors.New("null cipher not allowed") + } + ctx, err := newCipherCtx() + if err != nil { + return nil, err + } + var eptr *C.ENGINE + if e != nil { + eptr = e.e + } + if 1 != C.EVP_EncryptInit_ex(ctx.ctx, c.ptr, eptr, nil, nil) { + return nil, errors.New("failed to initialize cipher context") + } + err = ctx.applyKeyAndIV(key, iv) + if err != nil { + return nil, err + } + return &encryptionCipherCtx{cipherCtx: ctx}, nil +} + +func newDecryptionCipherCtx(c *Cipher, e *Engine, key, iv []byte) ( + *decryptionCipherCtx, error) { + if c == nil { + return nil, errors.New("null cipher not allowed") + } + ctx, err := newCipherCtx() + if err != nil { + return nil, err + } + var eptr *C.ENGINE + if e != nil { + eptr = e.e + } + if 1 != C.EVP_DecryptInit_ex(ctx.ctx, c.ptr, eptr, nil, nil) { + return nil, errors.New("failed to initialize cipher context") + } + err = ctx.applyKeyAndIV(key, iv) + if err != nil { + return nil, err + } + return &decryptionCipherCtx{cipherCtx: ctx}, nil +} + +func NewEncryptionCipherCtx(c *Cipher, e *Engine, key, iv []byte) ( + EncryptionCipherCtx, error) { + return newEncryptionCipherCtx(c, e, key, iv) +} + +func NewDecryptionCipherCtx(c *Cipher, e *Engine, key, iv []byte) ( + DecryptionCipherCtx, error) { + return newDecryptionCipherCtx(c, e, key, iv) +} + +func (ctx *encryptionCipherCtx) EncryptUpdate(input []byte) ([]byte, error) { + outbuf := make([]byte, len(input)+ctx.BlockSize()) + outlen := C.int(len(outbuf)) + res := C.EVP_EncryptUpdate(ctx.ctx, (*C.uchar)(&outbuf[0]), &outlen, + (*C.uchar)(&input[0]), C.int(len(input))) + if res != 1 { + return nil, fmt.Errorf("failed to encrypt [result %d]", res) + } + return outbuf[:outlen], nil +} + +func (ctx *decryptionCipherCtx) DecryptUpdate(input []byte) ([]byte, error) { + outbuf := make([]byte, len(input)+ctx.BlockSize()) + outlen := C.int(len(outbuf)) + res := C.EVP_DecryptUpdate(ctx.ctx, (*C.uchar)(&outbuf[0]), &outlen, + (*C.uchar)(&input[0]), C.int(len(input))) + if res != 1 { + return nil, fmt.Errorf("failed to decrypt [result %d]", res) + } + return outbuf[:outlen], nil +} + +func (ctx *encryptionCipherCtx) EncryptFinal() ([]byte, error) { + outbuf := make([]byte, ctx.BlockSize()) + var outlen C.int + if 1 != C.EVP_EncryptFinal_ex(ctx.ctx, (*C.uchar)(&outbuf[0]), &outlen) { + return nil, errors.New("encryption failed") + } + return outbuf[:outlen], nil +} + +func (ctx *decryptionCipherCtx) DecryptFinal() ([]byte, error) { + outbuf := make([]byte, ctx.BlockSize()) + var outlen C.int + if 1 != C.EVP_DecryptFinal_ex(ctx.ctx, (*C.uchar)(&outbuf[0]), &outlen) { + // this may mean the tag failed to verify- all previous plaintext + // returned must be considered faked and invalid + return nil, errors.New("decryption failed") + } + return outbuf[:outlen], nil +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/ciphers_test.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/ciphers_test.go new file mode 100644 index 00000000000..d1d430b1e15 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/ciphers_test.go @@ -0,0 +1,307 @@ +// 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 !darwin + +package openssl + +import ( + "bytes" + "fmt" + "strings" + "testing" +) + +func expectError(t *testing.T, err error, msg string) { + if err == nil { + t.Fatalf("Expected error containing %#v, but got none", msg) + } + if !strings.Contains(err.Error(), msg) { + t.Fatalf("Expected error containing %#v, but got %s", msg, err) + } +} + +func TestBadInputs(t *testing.T) { + _, err := NewGCMEncryptionCipherCtx(256, nil, + []byte("abcdefghijklmnopqrstuvwxyz"), nil) + expectError(t, err, "bad key size") + _, err = NewGCMEncryptionCipherCtx(128, nil, + []byte("abcdefghijklmnopqrstuvwxyz"), nil) + expectError(t, err, "bad key size") + _, err = NewGCMEncryptionCipherCtx(200, nil, + []byte("abcdefghijklmnopqrstuvwxy"), nil) + expectError(t, err, "unknown block size") + c, err := GetCipherByName("AES-128-CBC") + if err != nil { + t.Fatal("Could not look up AES-128-CBC") + } + _, err = NewEncryptionCipherCtx(c, nil, []byte("abcdefghijklmnop"), + []byte("abc")) + expectError(t, err, "bad IV size") +} + +func doEncryption(key, iv, aad, plaintext []byte, blocksize, bufsize int) ( + ciphertext, tag []byte, err error) { + ectx, err := NewGCMEncryptionCipherCtx(blocksize, nil, key, iv) + if err != nil { + return nil, nil, fmt.Errorf("Failed making GCM encryption ctx: %s", err) + } + err = ectx.ExtraData(aad) + if err != nil { + return nil, nil, fmt.Errorf("Failed to add authenticated data: %s", + err) + } + plainb := bytes.NewBuffer(plaintext) + cipherb := new(bytes.Buffer) + for plainb.Len() > 0 { + moar, err := ectx.EncryptUpdate(plainb.Next(bufsize)) + if err != nil { + return nil, nil, fmt.Errorf("Failed to perform an encryption: %s", + err) + } + cipherb.Write(moar) + } + moar, err := ectx.EncryptFinal() + if err != nil { + return nil, nil, fmt.Errorf("Failed to finalize encryption: %s", err) + } + cipherb.Write(moar) + tag, err = ectx.GetTag() + if err != nil { + return nil, nil, fmt.Errorf("Failed to get GCM tag: %s", err) + } + return cipherb.Bytes(), tag, nil +} + +func doDecryption(key, iv, aad, ciphertext, tag []byte, blocksize, + bufsize int) (plaintext []byte, err error) { + dctx, err := NewGCMDecryptionCipherCtx(blocksize, nil, key, iv) + if err != nil { + return nil, fmt.Errorf("Failed making GCM decryption ctx: %s", err) + } + aadbuf := bytes.NewBuffer(aad) + for aadbuf.Len() > 0 { + err = dctx.ExtraData(aadbuf.Next(bufsize)) + if err != nil { + return nil, fmt.Errorf("Failed to add authenticated data: %s", err) + } + } + plainb := new(bytes.Buffer) + cipherb := bytes.NewBuffer(ciphertext) + for cipherb.Len() > 0 { + moar, err := dctx.DecryptUpdate(cipherb.Next(bufsize)) + if err != nil { + return nil, fmt.Errorf("Failed to perform a decryption: %s", err) + } + plainb.Write(moar) + } + err = dctx.SetTag(tag) + if err != nil { + return nil, fmt.Errorf("Failed to set expected GCM tag: %s", err) + } + moar, err := dctx.DecryptFinal() + if err != nil { + return nil, fmt.Errorf("Failed to finalize decryption: %s", err) + } + plainb.Write(moar) + return plainb.Bytes(), nil +} + +func checkEqual(t *testing.T, output []byte, original string) { + output_s := string(output) + if output_s != original { + t.Fatalf("output != original! %#v != %#v", output_s, original) + } +} + +func TestGCM(t *testing.T) { + aad := []byte("foo bar baz") + key := []byte("nobody can guess this i'm sure..") // len=32 + iv := []byte("just a bunch of bytes") + plaintext := "Long long ago, in a land far away..." + + blocksizes_to_test := []int{256, 192, 128} + + // best for this to have no common factors with blocksize, so that the + // buffering layer inside the CIPHER_CTX gets exercised + bufsize := 33 + + if len(plaintext)%8 == 0 { + plaintext += "!" // make sure padding is exercised + } + + for _, bsize := range blocksizes_to_test { + subkey := key[:bsize/8] + ciphertext, tag, err := doEncryption(subkey, iv, aad, []byte(plaintext), + bsize, bufsize) + if err != nil { + t.Fatalf("Encryption with b=%d: %s", bsize, err) + } + plaintext_out, err := doDecryption(subkey, iv, aad, ciphertext, tag, + bsize, bufsize) + if err != nil { + t.Fatalf("Decryption with b=%d: %s", bsize, err) + } + checkEqual(t, plaintext_out, plaintext) + } +} + +func TestGCMWithNoAAD(t *testing.T) { + key := []byte("0000111122223333") + iv := []byte("9999") + plaintext := "ABORT ABORT ABORT DANGAR" + + ciphertext, tag, err := doEncryption(key, iv, nil, []byte(plaintext), + 128, 32) + if err != nil { + t.Fatal("Encryption failure:", err) + } + plaintext_out, err := doDecryption(key, iv, nil, ciphertext, tag, 128, 129) + if err != nil { + t.Fatal("Decryption failure:", err) + } + checkEqual(t, plaintext_out, plaintext) +} + +func TestBadTag(t *testing.T) { + key := []byte("abcdefghijklmnop") + iv := []byte("v7239qjfv3qr793fuaj") + plaintext := "The red rooster has flown the coop I REPEAT" + + "the red rooster has flown the coop!!1!" + + ciphertext, tag, err := doEncryption(key, iv, nil, []byte(plaintext), + 128, 32) + if err != nil { + t.Fatal("Encryption failure:", err) + } + // flip the last bit + tag[len(tag)-1] ^= 1 + plaintext_out, err := doDecryption(key, iv, nil, ciphertext, tag, 128, 129) + if err == nil { + t.Fatal("Expected error for bad tag, but got none") + } + // flip it back, try again just to make sure + tag[len(tag)-1] ^= 1 + plaintext_out, err = doDecryption(key, iv, nil, ciphertext, tag, 128, 129) + if err != nil { + t.Fatal("Decryption failure:", err) + } + checkEqual(t, plaintext_out, plaintext) +} + +func TestBadCiphertext(t *testing.T) { + key := []byte("hard boiled eggs & bacon") + iv := []byte("x") // it's not a very /good/ IV, is it + aad := []byte("mu") + plaintext := "Roger roger bingo charlie, we have a niner fourteen tango" + + ciphertext, tag, err := doEncryption(key, iv, aad, []byte(plaintext), + 192, 1) + if err != nil { + t.Fatal("Encryption failure:", err) + } + // flip the last bit + ciphertext[len(ciphertext)-1] ^= 1 + plaintext_out, err := doDecryption(key, iv, aad, ciphertext, tag, 192, 192) + if err == nil { + t.Fatal("Expected error for bad ciphertext, but got none") + } + // flip it back, try again just to make sure + ciphertext[len(ciphertext)-1] ^= 1 + plaintext_out, err = doDecryption(key, iv, aad, ciphertext, tag, 192, 192) + if err != nil { + t.Fatal("Decryption failure:", err) + } + checkEqual(t, plaintext_out, plaintext) +} + +func TestBadAAD(t *testing.T) { + key := []byte("Ive got a lovely buncha coconuts") + iv := []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab") + aad := []byte("Hi i am a plain") + plaintext := "Whatever." + + ciphertext, tag, err := doEncryption(key, iv, aad, []byte(plaintext), + 256, 256) + if err != nil { + t.Fatal("Encryption failure:", err) + } + // flip the last bit + aad[len(aad)-1] ^= 1 + plaintext_out, err := doDecryption(key, iv, aad, ciphertext, tag, 256, 256) + if err == nil { + t.Fatal("Expected error for bad AAD, but got none") + } + // flip it back, try again just to make sure + aad[len(aad)-1] ^= 1 + plaintext_out, err = doDecryption(key, iv, aad, ciphertext, tag, 256, 256) + if err != nil { + t.Fatal("Decryption failure:", err) + } + checkEqual(t, plaintext_out, plaintext) +} + +func TestNonAuthenticatedEncryption(t *testing.T) { + key := []byte("never gonna give you up, never g") + iv := []byte("onna let you dow") + plaintext1 := "n, never gonna run around" + plaintext2 := " and desert you" + + cipher, err := GetCipherByName("aes-256-cbc") + if err != nil { + t.Fatal("Could not get cipher: ", err) + } + + eCtx, err := NewEncryptionCipherCtx(cipher, nil, key, iv) + if err != nil { + t.Fatal("Could not create encryption context: ", err) + } + cipherbytes, err := eCtx.EncryptUpdate([]byte(plaintext1)) + if err != nil { + t.Fatal("EncryptUpdate(plaintext1) failure: ", err) + } + ciphertext := string(cipherbytes) + cipherbytes, err = eCtx.EncryptUpdate([]byte(plaintext2)) + if err != nil { + t.Fatal("EncryptUpdate(plaintext2) failure: ", err) + } + ciphertext += string(cipherbytes) + cipherbytes, err = eCtx.EncryptFinal() + if err != nil { + t.Fatal("EncryptFinal() failure: ", err) + } + ciphertext += string(cipherbytes) + + dCtx, err := NewDecryptionCipherCtx(cipher, nil, key, iv) + if err != nil { + t.Fatal("Could not create decryption context: ", err) + } + plainbytes, err := dCtx.DecryptUpdate([]byte(ciphertext[:15])) + if err != nil { + t.Fatal("DecryptUpdate(ciphertext part 1) failure: ", err) + } + plainOutput := string(plainbytes) + plainbytes, err = dCtx.DecryptUpdate([]byte(ciphertext[15:])) + if err != nil { + t.Fatal("DecryptUpdate(ciphertext part 2) failure: ", err) + } + plainOutput += string(plainbytes) + plainbytes, err = dCtx.DecryptFinal() + if err != nil { + t.Fatal("DecryptFinal() failure: ", err) + } + plainOutput += string(plainbytes) + + checkEqual(t, []byte(plainOutput), plaintext1+plaintext2) +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/conn.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/conn.go new file mode 100644 index 00000000000..afc73a50ae3 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/conn.go @@ -0,0 +1,625 @@ +// 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 <stdlib.h> +#include <openssl/ssl.h> +#include <openssl/conf.h> +#include <openssl/err.h> + +int sk_X509_num_not_a_macro(STACK_OF(X509) *sk) { return sk_X509_num(sk); } +X509 *sk_X509_value_not_a_macro(STACK_OF(X509)* sk, int i) { + return sk_X509_value(sk, i); +} +const char * SSL_get_cipher_name_not_a_macro(const SSL *ssl) { + return SSL_get_cipher_name(ssl); +} +static int SSL_session_reused_not_a_macro(SSL *ssl) { + return SSL_session_reused(ssl); +} +*/ +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 { + conn net.Conn + ssl *C.SSL + 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) + + c := &Conn{ + conn: conn, + ssl: ssl, + 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) CurrentCipher() (string, error) { + p := C.SSL_get_cipher_name_not_a_macro(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 +} + +// 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") + } + sk_num := int(C.sk_X509_num_not_a_macro(sk)) + rv = make([]*Certificate, 0, sk_num) + for i := 0; i < sk_num; i++ { + x := C.sk_X509_value_not_a_macro(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, nil +} + +// GetVerifyResult gets result of peer certificate verification +// SSL_get_verify_result() returns the result of the verification of the X509 +// certificate presented by the peer, if any. See +// https://www.openssl.org/docs/ssl/SSL_get_verify_result.html +func (c *Conn) GetVerifyResults() error { + result := C.SSL_get_verify_result(c.ssl) + if int(result) != 0 { + return errors.New(C.GoString( + C.X509_verify_cert_error_string(result))) + } + return 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) VerifyResult() VerifyResult { + return VerifyResult(C.SSL_get_verify_result(c.ssl)) +} + +func (c *Conn) SessionReused() bool { + return C.SSL_session_reused_not_a_macro(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 +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/ctx.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/ctx.go new file mode 100644 index 00000000000..74422f290a3 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/ctx.go @@ -0,0 +1,831 @@ +// 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> +#include <openssl/x509.h> + +static long SSL_CTX_set_options_not_a_macro(SSL_CTX* ctx, long options) { + return SSL_CTX_set_options(ctx, options); +} + +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); +} + +static long SSL_CTX_get_mode_not_a_macro(SSL_CTX* ctx) { + return SSL_CTX_get_mode(ctx); +} + +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); +} + +static long SSL_CTX_sess_set_cache_size_not_a_macro(SSL_CTX* ctx, long t) { + return SSL_CTX_sess_set_cache_size(ctx, t); +} + +static long SSL_CTX_sess_get_cache_size_not_a_macro(SSL_CTX* ctx) { + return SSL_CTX_sess_get_cache_size(ctx); +} + +static long SSL_CTX_set_timeout_not_a_macro(SSL_CTX* ctx, long t) { + return SSL_CTX_set_timeout(ctx, t); +} + +static long SSL_CTX_get_timeout_not_a_macro(SSL_CTX* ctx) { + return SSL_CTX_get_timeout(ctx); +} + +static int CRYPTO_add_not_a_macro(int *pointer,int amount,int type) { + return CRYPTO_add(pointer, amount, type); +} + +static long SSL_CTX_add_extra_chain_cert_not_a_macro(SSL_CTX* ctx, X509 *cert) { + return SSL_CTX_add_extra_chain_cert(ctx, cert); +} + +#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() { +#if OPENSSL_VERSION_NUMBER > 0x1000100fL && defined(TLS1_1_VERSION) && !defined(OPENSSL_SYSNAME_MACOSX) + return TLSv1_1_method(); +#else + return NULL; +#endif +} + +static const SSL_METHOD *OUR_TLSv1_2_method() { +#if OPENSSL_VERSION_NUMBER > 0x1000100fL && defined(TLS1_2_VERSION) && !defined(OPENSSL_SYSNAME_MACOSX) + return TLSv1_2_method(); +#else + return NULL; +#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); + +typedef STACK_OF(X509_NAME) *STACK_OF_X509_NAME_not_a_macro; + +static void sk_X509_NAME_pop_free_not_a_macro(STACK_OF_X509_NAME_not_a_macro st) { + sk_X509_NAME_pop_free(st, X509_NAME_free); +} + +extern int password_cb(char *buf, int size, int rwflag, void *password); +*/ +import "C" + +import ( + "errors" + "fmt" + "io/ioutil" + "os" + "runtime" + "time" + "unsafe" + + "github.com/spacemonkeygo/spacelog" +) + +var ( + ssl_ctx_idx = C.SSL_CTX_get_ex_new_index(0, nil, nil, nil, nil) + + logger = spacelog.GetLogger() +) + +type Ctx struct { + ctx *C.SSL_CTX + cert *Certificate + chain []*Certificate + key PrivateKey + verify_cb VerifyCallback + sni_cb TLSExtServernameCallback +} + +//export get_ssl_ctx_idx +func get_ssl_ctx_idx() C.int { + return ssl_ctx_idx +} + +func newCtx(method *C.SSL_METHOD) (*Ctx, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + ctx := C.SSL_CTX_new(method) + if ctx == nil { + 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) + }) + return c, nil +} + +type SSLVersion int + +const ( + SSLv3 SSLVersion = 0x02 // Vulnerable to "POODLE" attack. + TLSv1 SSLVersion = 0x03 + TLSv1_1 SSLVersion = 0x04 + TLSv1_2 SSLVersion = 0x05 + + // Make sure to disable SSLv2 and SSLv3 if you use this. SSLv3 is vulnerable + // to the "POODLE" attack, and SSLv2 is what, just don't even. + AnyVersion SSLVersion = 0x06 +) + +// NewCtxWithVersion creates an SSL context that is specific to the provided +// SSL version. See http://www.openssl.org/docs/ssl/SSL_CTX_new.html for more. +func NewCtxWithVersion(version SSLVersion) (*Ctx, error) { + var method *C.SSL_METHOD + switch version { + case TLSv1: + method = C.TLSv1_method() + case TLSv1_1: + method = C.OUR_TLSv1_1_method() + case TLSv1_2: + method = C.OUR_TLSv1_2_method() + case AnyVersion: + method = C.SSLv23_method() + } + if method == nil { + return nil, errors.New("unknown ssl/tls version") + } + return newCtx(method) +} + +// NewCtx creates a context that supports any TLS version 1.0 and newer. +func NewCtx() (*Ctx, error) { + c, err := NewCtxWithVersion(AnyVersion) + if err == nil { + c.SetOptions(NoSSLv2 | NoSSLv3) + } + return c, err +} + +// NewCtxFromFiles calls NewCtx, loads the provided files, and configures the +// context to use them. +func NewCtxFromFiles(cert_file string, key_file string) (*Ctx, error) { + ctx, err := NewCtx() + if err != nil { + return nil, err + } + + cert_bytes, err := ioutil.ReadFile(cert_file) + if err != nil { + return nil, err + } + + certs := SplitPEM(cert_bytes) + if len(certs) == 0 { + return nil, fmt.Errorf("No PEM certificate found in '%s'", cert_file) + } + first, certs := certs[0], certs[1:] + cert, err := LoadCertificateFromPEM(first) + if err != nil { + return nil, err + } + + err = ctx.UseCertificate(cert) + if err != nil { + return nil, err + } + + for _, pem := range certs { + cert, err := LoadCertificateFromPEM(pem) + if err != nil { + return nil, err + } + err = ctx.AddChainCertificate(cert) + if err != nil { + return nil, err + } + } + + key_bytes, err := ioutil.ReadFile(key_file) + if err != nil { + return nil, err + } + + key, err := LoadPrivateKeyFromPEM(key_bytes) + if err != nil { + return nil, err + } + + err = ctx.UsePrivateKey(key) + if err != nil { + return nil, err + } + + return ctx, nil +} + +// EllipticCurve repesents the ASN.1 OID of an elliptic curve. +// see https://www.openssl.org/docs/apps/ecparam.html for a list of implemented curves. +type EllipticCurve int + +const ( + // P-256: X9.62/SECG curve over a 256 bit prime field + Prime256v1 EllipticCurve = C.NID_X9_62_prime256v1 + // P-384: NIST/SECG curve over a 384 bit prime field + Secp384r1 EllipticCurve = C.NID_secp384r1 +) + +// UseCertificate configures the context to present the given certificate to +// peers. +func (c *Ctx) UseCertificate(cert *Certificate) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + c.cert = cert + if int(C.SSL_CTX_use_certificate(c.ctx, cert.x)) != 1 { + return errorFromErrorQueue() + } + return nil +} + +// UseCertificateChainFromFile loads a certificate chain from file into ctx. +// The certificates must be in PEM format and must be sorted starting with the +// subject's certificate (actual client or server certificate), followed by +// intermediate CA certificates if applicable, and ending at the highest level +// (root) CA. See +// https://www.openssl.org/docs/ssl/SSL_CTX_use_certificate.html +func (c *Ctx) UseCertificateChainFile(cert_file string) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + var c_cert_file *C.char + if cert_file != "" { + c_cert_file = C.CString(cert_file) + defer C.free(unsafe.Pointer(c_cert_file)) + } + if int(C.SSL_CTX_use_certificate_chain_file(c.ctx, c_cert_file)) != 1 { + return errorFromErrorQueue() + } + return nil +} + +// UsePrivateKeyFile adds the first private key found in file to the *Ctx, c. The +// formatting type of the certificate must be specified from the known types +// FiletypePEM, and FiletypeASN1 +func (c *Ctx) UsePrivateKeyFile(key_file string, file_type Filetypes) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + var c_key_file *C.char + if key_file != "" { + c_key_file = C.CString(key_file) + defer C.free(unsafe.Pointer(c_key_file)) + } + if int(C.SSL_CTX_use_PrivateKey_file(c.ctx, c_key_file, C.int(file_type))) != 1 { + return errorFromErrorQueue() + } + return nil +} + +func (c *Ctx) UsePrivateKeyFileWithPassword(key_file string, file_type Filetypes, password string) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + var c_key_file *C.char + + c_pwd := C.CString(password) + defer C.free(unsafe.Pointer(c_pwd)) + C.SSL_CTX_set_default_passwd_cb_userdata(c.ctx, unsafe.Pointer(c_pwd)) + C.SSL_CTX_set_default_passwd_cb(c.ctx, (*C.pem_password_cb)(C.password_cb)) + + if key_file != "" { + c_key_file = C.CString(key_file) + defer C.free(unsafe.Pointer(c_key_file)) + } + if int(C.SSL_CTX_use_PrivateKey_file(c.ctx, c_key_file, C.int(file_type))) != 1 { + return errorFromErrorQueue() + } + return nil +} + +// CheckPrivateKey verifies that the private key agrees with the corresponding +// public key in the certificate +func (c *Ctx) CheckPrivateKey() error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + if int(C.SSL_CTX_check_private_key(c.ctx)) != 1 { + return errorFromErrorQueue() + } + return nil +} + +type StackOfX509Name struct { + stack C.STACK_OF_X509_NAME_not_a_macro + // shared indicates weather we are the sole owner of this pointer, and implies + // weather we should or shouldn't free the underlying data structure + // when this go data structure goes out of scope + shared bool +} + +// LoadClientCAFile reads certificates from file and returns a StackOfX509Name +// with the subject names found. See +// https://www.openssl.org/docs/ssl/SSL_load_client_CA_file.html +func LoadClientCAFile(ca_file string) (*StackOfX509Name, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + var c_ca_file *C.char + if ca_file != "" { + c_ca_file = C.CString(ca_file) + defer C.free(unsafe.Pointer(c_ca_file)) + } + stack := C.SSL_load_client_CA_file(c_ca_file) + if stack == nil { + return nil, errorFromErrorQueue() + } + caList := StackOfX509Name{ + stack: stack, + shared: false, + } + runtime.SetFinalizer(&caList, func(c *StackOfX509Name) { + if !c.shared { + C.sk_X509_NAME_pop_free_not_a_macro(c.stack) + } + }) + return &caList, nil +} + +// SetClientCAList sets the list of CAs sent to the client when requesting a +// client certificate for Ctx. See +// https://www.openssl.org/docs/ssl/SSL_CTX_set_client_CA_list.html +func (c *Ctx) SetClientCAList(caList *StackOfX509Name) { + C.SSL_CTX_set_client_CA_list(c.ctx, caList.stack) + caList.shared = true +} + +// AddChainCertificate adds a certificate to the chain presented in the +// handshake. +func (c *Ctx) AddChainCertificate(cert *Certificate) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + c.chain = append(c.chain, cert) + if int(C.SSL_CTX_add_extra_chain_cert_not_a_macro(c.ctx, cert.x)) != 1 { + return errorFromErrorQueue() + } + // OpenSSL takes ownership via SSL_CTX_add_extra_chain_cert + runtime.SetFinalizer(cert, nil) + return nil +} + +// UsePrivateKey configures the context to use the given private key for SSL +// handshakes. +func (c *Ctx) UsePrivateKey(key PrivateKey) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + c.key = key + if int(C.SSL_CTX_use_PrivateKey(c.ctx, key.evpPKey())) != 1 { + return errorFromErrorQueue() + } + return nil +} + +type CertificateStore struct { + store *C.X509_STORE + // for GC + ctx *Ctx + certs []*Certificate +} + +// Allocate a new, empty CertificateStore +func NewCertificateStore() (*CertificateStore, error) { + s := C.X509_STORE_new() + if s == nil { + return nil, errors.New("failed to allocate X509_STORE") + } + store := &CertificateStore{store: s} + runtime.SetFinalizer(store, func(s *CertificateStore) { + C.X509_STORE_free(s.store) + }) + return store, nil +} + +// Parse a chained PEM file, loading all certificates into the Store. +func (s *CertificateStore) LoadCertificatesFromPEM(data []byte) error { + pems := SplitPEM(data) + for _, pem := range pems { + cert, err := LoadCertificateFromPEM(pem) + if err != nil { + return err + } + err = s.AddCertificate(cert) + if err != nil { + return err + } + } + return nil +} + +// GetCertificateStore returns the context's certificate store that will be +// used for peer validation. +func (c *Ctx) GetCertificateStore() *CertificateStore { + // we don't need to dealloc the cert store pointer here, because it points + // to a ctx internal. so we do need to keep the ctx around + return &CertificateStore{ + store: C.SSL_CTX_get_cert_store(c.ctx), + ctx: c} +} + +// AddCertificate marks the provided Certificate as a trusted certificate in +// the given CertificateStore. +func (s *CertificateStore) AddCertificate(cert *Certificate) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + s.certs = append(s.certs, cert) + if int(C.X509_STORE_add_cert(s.store, cert.x)) != 1 { + return errorFromErrorQueue() + } + return nil +} + +type X509VerificationFlag int + +func (s *CertificateStore) SetFlags(flags X509VerificationFlag) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + if int(C.X509_STORE_set_flags(s.store, C.ulong(flags))) != 1 { + return errorFromErrorQueue() + } + return nil +} + +// See https://www.openssl.org/docs/crypto/X509_VERIFY_PARAM_set_flags.html +const ( + CBIssuerCheck X509VerificationFlag = C.X509_V_FLAG_CB_ISSUER_CHECK + UseCheckTime X509VerificationFlag = C.X509_V_FLAG_USE_CHECK_TIME + CRLCheck X509VerificationFlag = C.X509_V_FLAG_CRL_CHECK + CRLCheckAll X509VerificationFlag = C.X509_V_FLAG_CRL_CHECK_ALL + IgnoreCritical X509VerificationFlag = C.X509_V_FLAG_IGNORE_CRITICAL + X509Strict X509VerificationFlag = C.X509_V_FLAG_X509_STRICT + AllowProxyCerts X509VerificationFlag = C.X509_V_FLAG_ALLOW_PROXY_CERTS + PolicyCheck X509VerificationFlag = C.X509_V_FLAG_POLICY_CHECK + ExplicitPolicy X509VerificationFlag = C.X509_V_FLAG_EXPLICIT_POLICY + InhibitAny X509VerificationFlag = C.X509_V_FLAG_INHIBIT_ANY + InhibitMap X509VerificationFlag = C.X509_V_FLAG_INHIBIT_MAP + NotifyPolicy X509VerificationFlag = C.X509_V_FLAG_NOTIFY_POLICY + // ExtendedCRLSupport X509VerificationFlag = C.X509_V_FLAG_EXTENDED_CRL_SUPPORT + // UseDeltas X509VerificationFlag = C.X509_V_FLAG_USE_DELTAS + // CheckSsSignature X509VerificationFlag = C.X509_V_FLAG_CHECK_SS_SIGNATURE + // TrustedFirst X509VerificationFlag = C.X509_V_FLAG_TRUSTED_FIRST + PolicyMask X509VerificationFlag = C.X509_V_FLAG_POLICY_MASK +) + +type CertificateStoreLookup struct { + lookup *C.X509_LOOKUP + store *CertificateStore +} + +// an X509LookupMethod is required to build a a CertificateStoreLookup in a +// CertificateStore. The X509LookupMethod indicates the type or functionality +// of the CertificateStoreLookup +type X509LookupMethod *C.X509_LOOKUP_METHOD + +// CertificateStoreLookups with X509LookupFile methods look for certs in a file +func X509LookupFile() X509LookupMethod { + return X509LookupMethod(C.X509_LOOKUP_file()) +} + +// CertificateStoreLookups with X509LookupHashDir methods look for certs in a +// directory +func X509LookupHashDir() X509LookupMethod { + return X509LookupMethod(C.X509_LOOKUP_hash_dir()) +} + +// AddLookup creates a CertificateStoreLookup of type X509LookupMethod in the +// CertificateStore +func (s *CertificateStore) AddLookup(method X509LookupMethod) (*CertificateStoreLookup, error) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + var lookup *C.X509_LOOKUP + lookup = C.X509_STORE_add_lookup(s.store, method) + if lookup != nil { + return &CertificateStoreLookup{ + lookup: lookup, + store: s, + }, nil + } + return nil, errorFromErrorQueue() +} + +// LoadCRLFile adds a file to a CertificateStoreLookup in the +// CertificateStore +// I suspect that the CertificateStoreLookup needs to have been created with +// X509LookupFile as the lookup method +func (l *CertificateStoreLookup) LoadCRLFile(crl_file string) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + var c_crl_file *C.char + if crl_file != "" { + c_crl_file = C.CString(crl_file) + defer C.free(unsafe.Pointer(c_crl_file)) + } + if int(C.X509_load_crl_file(l.lookup, c_crl_file, C.X509_FILETYPE_PEM)) != 1 { + return errorFromErrorQueue() + } + return nil +} + +type CertificateStoreCtx struct { + ctx *C.X509_STORE_CTX + ssl_ctx *Ctx +} + +func (self *CertificateStoreCtx) VerifyResult() VerifyResult { + return VerifyResult(C.X509_STORE_CTX_get_error(self.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(cert *Certificate) { + C.X509_free(cert.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 +// more. +func (c *Ctx) LoadVerifyLocations(ca_file string, ca_path string) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + var c_ca_file, c_ca_path *C.char + if ca_file != "" { + c_ca_file = C.CString(ca_file) + defer C.free(unsafe.Pointer(c_ca_file)) + } + if ca_path != "" { + c_ca_path = C.CString(ca_path) + defer C.free(unsafe.Pointer(c_ca_path)) + } + if C.SSL_CTX_load_verify_locations(c.ctx, c_ca_file, c_ca_path) != 1 { + return errorFromErrorQueue() + } + return nil +} + +type Options uint + +const ( + // NoCompression is only valid if you are using OpenSSL 1.0.1 or newer + NoCompression Options = C.SSL_OP_NO_COMPRESSION + NoSSLv2 Options = C.SSL_OP_NO_SSLv2 + NoSSLv3 Options = C.SSL_OP_NO_SSLv3 + NoTLSv1 Options = C.SSL_OP_NO_TLSv1 + CipherServerPreference Options = C.SSL_OP_CIPHER_SERVER_PREFERENCE + NoSessionResumptionOrRenegotiation Options = C.SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION + OpAll Options = C.SSL_OP_ALL +) + +// SetOptions sets context options. See +// http://www.openssl.org/docs/ssl/SSL_CTX_set_options.html +func (c *Ctx) SetOptions(options Options) Options { + return Options(C.SSL_CTX_set_options_not_a_macro( + c.ctx, C.long(options))) +} + +func (c *Ctx) ClearOptions(options Options) Options { + return Options(C.SSL_CTX_clear_options_not_a_macro( + 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 ( + // ReleaseBuffers is only valid if you are using OpenSSL 1.0.1 or newer + ReleaseBuffers Modes = C.SSL_MODE_RELEASE_BUFFERS + AutoRetry Modes = C.SSL_MODE_AUTO_RETRY +) + +// SetMode sets context modes. See +// http://www.openssl.org/docs/ssl/SSL_CTX_set_mode.html +func (c *Ctx) SetMode(modes Modes) Modes { + return Modes(C.SSL_CTX_set_mode_not_a_macro(c.ctx, C.long(modes))) +} + +// GetMode returns context modes. See +// http://www.openssl.org/docs/ssl/SSL_CTX_set_mode.html +func (c *Ctx) GetMode() Modes { + return Modes(C.SSL_CTX_get_mode_not_a_macro(c.ctx)) +} + +type VerifyOptions int + +const ( + VerifyNone VerifyOptions = C.SSL_VERIFY_NONE + VerifyPeer VerifyOptions = C.SSL_VERIFY_PEER + VerifyFailIfNoPeerCert VerifyOptions = C.SSL_VERIFY_FAIL_IF_NO_PEER_CERT + VerifyClientOnce VerifyOptions = C.SSL_VERIFY_CLIENT_ONCE +) + +type Filetypes int + +const ( + FiletypePEM Filetypes = C.SSL_FILETYPE_PEM + FiletypeASN1 Filetypes = C.SSL_FILETYPE_ASN1 +) + +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 { + logger.Critf("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, 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) GetVerifyCallback() VerifyCallback { + return c.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 +// verification logic is willing to follow a certificate chain. See +// https://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html +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 + +func (c *Ctx) SetSessionId(session_id []byte) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + var ptr *C.uchar + if len(session_id) > 0 { + ptr = (*C.uchar)(unsafe.Pointer(&session_id[0])) + } + if int(C.SSL_CTX_set_session_id_context(c.ctx, ptr, + C.uint(len(session_id)))) == 0 { + return errorFromErrorQueue() + } + return nil +} + +// SetCipherList sets the list of available ciphers. The format of the list is +// described at http://www.openssl.org/docs/apps/ciphers.html, but see +// http://www.openssl.org/docs/ssl/SSL_CTX_set_cipher_list.html for more. +func (c *Ctx) SetCipherList(list string) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + clist := C.CString(list) + defer C.free(unsafe.Pointer(clist)) + if int(C.SSL_CTX_set_cipher_list(c.ctx, clist)) == 0 { + return errorFromErrorQueue() + } + return nil +} + +type SessionCacheModes int + +const ( + SessionCacheOff SessionCacheModes = C.SSL_SESS_CACHE_OFF + SessionCacheClient SessionCacheModes = C.SSL_SESS_CACHE_CLIENT + SessionCacheServer SessionCacheModes = C.SSL_SESS_CACHE_SERVER + SessionCacheBoth SessionCacheModes = C.SSL_SESS_CACHE_BOTH + NoAutoClear SessionCacheModes = C.SSL_SESS_CACHE_NO_AUTO_CLEAR + NoInternalLookup SessionCacheModes = C.SSL_SESS_CACHE_NO_INTERNAL_LOOKUP + NoInternalStore SessionCacheModes = C.SSL_SESS_CACHE_NO_INTERNAL_STORE + NoInternal SessionCacheModes = C.SSL_SESS_CACHE_NO_INTERNAL +) + +// SetSessionCacheMode enables or disables session caching. See +// http://www.openssl.org/docs/ssl/SSL_CTX_set_session_cache_mode.html +func (c *Ctx) SetSessionCacheMode(modes SessionCacheModes) SessionCacheModes { + return SessionCacheModes( + C.SSL_CTX_set_session_cache_mode_not_a_macro(c.ctx, C.long(modes))) +} + +// Set session cache timeout. Returns previously set value. +// See https://www.openssl.org/docs/ssl/SSL_CTX_set_timeout.html +func (c *Ctx) SetTimeout(t time.Duration) time.Duration { + prev := C.SSL_CTX_set_timeout_not_a_macro(c.ctx, C.long(t/time.Second)) + return time.Duration(prev) * time.Second +} + +// Get session cache timeout. +// See https://www.openssl.org/docs/ssl/SSL_CTX_set_timeout.html +func (c *Ctx) GetTimeout() time.Duration { + return time.Duration(C.SSL_CTX_get_timeout_not_a_macro(c.ctx)) * time.Second +} + +// Set session cache size. Returns previously set value. +// https://www.openssl.org/docs/ssl/SSL_CTX_sess_set_cache_size.html +func (c *Ctx) SessSetCacheSize(t int) int { + return int(C.SSL_CTX_sess_set_cache_size_not_a_macro(c.ctx, C.long(t))) +} + +// Get session cache size. +// https://www.openssl.org/docs/ssl/SSL_CTX_sess_set_cache_size.html +func (c *Ctx) SessGetCacheSize() int { + return int(C.SSL_CTX_sess_get_cache_size_not_a_macro(c.ctx)) +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/ctx_test.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/ctx_test.go new file mode 100644 index 00000000000..9644e518bf3 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/ctx_test.go @@ -0,0 +1,48 @@ +// Copyright (C) 2014 Ryan Hileman +// +// 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 ( + "testing" + "time" +) + +func TestCtxTimeoutOption(t *testing.T) { + ctx, _ := NewCtx() + oldTimeout1 := ctx.GetTimeout() + newTimeout1 := oldTimeout1 + (time.Duration(99) * time.Second) + oldTimeout2 := ctx.SetTimeout(newTimeout1) + newTimeout2 := ctx.GetTimeout() + if oldTimeout1 != oldTimeout2 { + t.Error("SetTimeout() returns something undocumented") + } + if newTimeout1 != newTimeout2 { + t.Error("SetTimeout() does not save anything to ctx") + } +} + +func TestCtxSessCacheSizeOption(t *testing.T) { + ctx, _ := NewCtx() + oldSize1 := ctx.SessGetCacheSize() + newSize1 := oldSize1 + 42 + oldSize2 := ctx.SessSetCacheSize(newSize1) + newSize2 := ctx.SessGetCacheSize() + if oldSize1 != oldSize2 { + t.Error("SessSetCacheSize() returns something undocumented") + } + if newSize1 != newSize2 { + t.Error("SessSetCacheSize() does not save anything to ctx") + } +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/dhparam.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/dhparam.go new file mode 100644 index 00000000000..a698645c1ec --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/dhparam.go @@ -0,0 +1,65 @@ +// +build cgo + +package openssl + +/* +#include <openssl/crypto.h> +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/conf.h> +#include <openssl/dh.h> + +static long SSL_CTX_set_tmp_dh_not_a_macro(SSL_CTX* ctx, DH *dh) { + return SSL_CTX_set_tmp_dh(ctx, dh); +} +static long PEM_read_DHparams_not_a_macro(SSL_CTX* ctx, DH *dh) { + return SSL_CTX_set_tmp_dh(ctx, dh); +} +*/ +import "C" + +import ( + "errors" + "runtime" + "unsafe" +) + +type DH struct { + dh *C.struct_dh_st +} + +// LoadDHParametersFromPEM loads the Diffie-Hellman parameters from +// a PEM-encoded block. +func LoadDHParametersFromPEM(pem_block []byte) (*DH, error) { + if len(pem_block) == 0 { + return nil, errors.New("empty pem block") + } + bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]), + C.int(len(pem_block))) + if bio == nil { + return nil, errors.New("failed creating bio") + } + defer C.BIO_free(bio) + + params := C.PEM_read_bio_DHparams(bio, nil, nil, nil) + if params == nil { + return nil, errors.New("failed reading dh parameters") + } + dhparams := &DH{dh: params} + runtime.SetFinalizer(dhparams, func(dhparams *DH) { + C.DH_free(dhparams.dh) + }) + return dhparams, nil +} + +// SetDHParameters sets the DH group (DH parameters) used to +// negotiate an emphemeral DH key during handshaking. +func (c *Ctx) SetDHParameters(dh *DH) error { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if int(C.SSL_CTX_set_tmp_dh_not_a_macro(c.ctx, dh.dh)) != 1 { + return errorFromErrorQueue() + } + return nil +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/digest.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/digest.go new file mode 100644 index 00000000000..44d4d001b13 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/digest.go @@ -0,0 +1,53 @@ +// Copyright (C) 2015 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/evp.h> +import "C" + +import ( + "fmt" + "unsafe" +) + +// Digest represents and openssl message digest. +type Digest struct { + ptr *C.EVP_MD +} + +// GetDigestByName returns the Digest with the name or nil and an error if the +// digest was not found. +func GetDigestByName(name string) (*Digest, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + p := C.EVP_get_digestbyname(cname) + if p == nil { + return nil, fmt.Errorf("Digest %v not found", name) + } + // we can consider digests to use static mem; don't need to free + return &Digest{ptr: p}, nil +} + +// GetDigestByName returns the Digest with the NID or nil and an error if the +// digest was not found. +func GetDigestByNid(nid NID) (*Digest, error) { + sn, err := Nid2ShortName(nid) + if err != nil { + return nil, err + } + return GetDigestByName(sn) +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/engine.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/engine.go new file mode 100644 index 00000000000..7a175b70f7c --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/engine.go @@ -0,0 +1,52 @@ +// 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/engine.h" +*/ +import "C" + +import ( + "fmt" + "runtime" + "unsafe" +) + +type Engine struct { + e *C.ENGINE +} + +func EngineById(name string) (*Engine, error) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + e := &Engine{ + e: C.ENGINE_by_id(cname), + } + if e.e == nil { + return nil, fmt.Errorf("engine %s missing", name) + } + if C.ENGINE_init(e.e) == 0 { + C.ENGINE_free(e.e) + return nil, fmt.Errorf("engine %s not initialized", name) + } + runtime.SetFinalizer(e, func(e *Engine) { + C.ENGINE_finish(e.e) + C.ENGINE_free(e.e) + }) + return e, nil +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/fips.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/fips.go new file mode 100644 index 00000000000..cc463f17a18 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/fips.go @@ -0,0 +1,22 @@ +// +build cgo +// +build -darwin + +package openssl + +/* +#include <openssl/ssl.h> +*/ +import "C" + +func FIPSModeSet(mode bool) error { + var r C.int + if mode { + r = C.FIPS_mode_set(1) + } else { + r = C.FIPS_mode_set(0) + } + if r != 1 { + return errorFromErrorQueue() + } + return nil +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/hostname.c b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/hostname.c new file mode 100644 index 00000000000..9a610292067 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/hostname.c @@ -0,0 +1,367 @@ +/* Go-OpenSSL notice: + This file is required for all OpenSSL versions prior to 1.1.0. This simply + provides the new 1.1.0 X509_check_* methods for hostname validation if they + don't already exist. + */ + +#include <openssl/x509.h> + +#ifndef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT + +/* portions from x509v3.h and v3_utl.c */ +/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL + * project. + */ +/* ==================================================================== + * Copyright (c) 1999-2003 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* X509 v3 extension utilities */ + +#include <stdlib.h> +#include <openssl/ssl.h> +#include <openssl/conf.h> +#include <openssl/x509v3.h> + +#define X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT 0x1 +#define X509_CHECK_FLAG_NO_WILDCARDS 0x2 + +typedef int (*equal_fn)(const unsigned char *pattern, size_t pattern_len, + const unsigned char *subject, size_t subject_len); + +/* Compare while ASCII ignoring case. */ +static int equal_nocase(const unsigned char *pattern, size_t pattern_len, + const unsigned char *subject, size_t subject_len) + { + if (pattern_len != subject_len) + return 0; + while (pattern_len) + { + unsigned char l = *pattern; + unsigned char r = *subject; + /* The pattern must not contain NUL characters. */ + if (l == 0) + return 0; + if (l != r) + { + if ('A' <= l && l <= 'Z') + l = (l - 'A') + 'a'; + if ('A' <= r && r <= 'Z') + r = (r - 'A') + 'a'; + if (l != r) + return 0; + } + ++pattern; + ++subject; + --pattern_len; + } + return 1; + } + +/* Compare using memcmp. */ +static int equal_case(const unsigned char *pattern, size_t pattern_len, + const unsigned char *subject, size_t subject_len) +{ + /* The pattern must not contain NUL characters. */ + if (memchr(pattern, '\0', pattern_len) != NULL) + return 0; + if (pattern_len != subject_len) + return 0; + return !memcmp(pattern, subject, pattern_len); +} + +/* RFC 5280, section 7.5, requires that only the domain is compared in + a case-insensitive manner. */ +static int equal_email(const unsigned char *a, size_t a_len, + const unsigned char *b, size_t b_len) + { + size_t i = a_len; + if (a_len != b_len) + return 0; + /* We search backwards for the '@' character, so that we do + not have to deal with quoted local-parts. The domain part + is compared in a case-insensitive manner. */ + while (i > 0) + { + --i; + if (a[i] == '@' || b[i] == '@') + { + if (!equal_nocase(a + i, a_len - i, + b + i, a_len - i)) + return 0; + break; + } + } + if (i == 0) + i = a_len; + return equal_case(a, i, b, i); + } + +/* Compare the prefix and suffix with the subject, and check that the + characters in-between are valid. */ +static int wildcard_match(const unsigned char *prefix, size_t prefix_len, + const unsigned char *suffix, size_t suffix_len, + const unsigned char *subject, size_t subject_len) + { + const unsigned char *wildcard_start; + const unsigned char *wildcard_end; + const unsigned char *p; + if (subject_len < prefix_len + suffix_len) + return 0; + if (!equal_nocase(prefix, prefix_len, subject, prefix_len)) + return 0; + wildcard_start = subject + prefix_len; + wildcard_end = subject + (subject_len - suffix_len); + if (!equal_nocase(wildcard_end, suffix_len, suffix, suffix_len)) + return 0; + /* The wildcard must match at least one character. */ + if (wildcard_start == wildcard_end) + return 0; + /* Check that the part matched by the wildcard contains only + permitted characters and only matches a single label. */ + for (p = wildcard_start; p != wildcard_end; ++p) + if (!(('0' <= *p && *p <= '9') || + ('A' <= *p && *p <= 'Z') || + ('a' <= *p && *p <= 'z') || + *p == '-')) + return 0; + return 1; + } + +/* Checks if the memory region consistens of [0-9A-Za-z.-]. */ +static int valid_domain_characters(const unsigned char *p, size_t len) + { + while (len) + { + if (!(('0' <= *p && *p <= '9') || + ('A' <= *p && *p <= 'Z') || + ('a' <= *p && *p <= 'z') || + *p == '-' || *p == '.')) + return 0; + ++p; + --len; + } + return 1; + } + +/* Find the '*' in a wildcard pattern. If no such character is found + or the pattern is otherwise invalid, returns NULL. */ +static const unsigned char *wildcard_find_star(const unsigned char *pattern, + size_t pattern_len) + { + const unsigned char *star = memchr(pattern, '*', pattern_len); + size_t dot_count = 0; + const unsigned char *suffix_start; + size_t suffix_length; + if (star == NULL) + return NULL; + suffix_start = star + 1; + suffix_length = (pattern + pattern_len) - (star + 1); + if (!(valid_domain_characters(pattern, star - pattern) && + valid_domain_characters(suffix_start, suffix_length))) + return NULL; + /* Check that the suffix matches at least two labels. */ + while (suffix_length) + { + if (*suffix_start == '.') + ++dot_count; + ++suffix_start; + --suffix_length; + } + if (dot_count < 2) + return NULL; + return star; + } + +/* Compare using wildcards. */ +static int equal_wildcard(const unsigned char *pattern, size_t pattern_len, + const unsigned char *subject, size_t subject_len) + { + const unsigned char *star = wildcard_find_star(pattern, pattern_len); + if (star == NULL) + return equal_nocase(pattern, pattern_len, + subject, subject_len); + return wildcard_match(pattern, star - pattern, + star + 1, (pattern + pattern_len) - star - 1, + subject, subject_len); + } + +/* Compare an ASN1_STRING to a supplied string. If they match + * return 1. If cmp_type > 0 only compare if string matches the + * type, otherwise convert it to UTF8. + */ + +static int do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal, + const unsigned char *b, size_t blen) + { + if (!a->data || !a->length) + return 0; + if (cmp_type > 0) + { + if (cmp_type != a->type) + return 0; + if (cmp_type == V_ASN1_IA5STRING) + return equal(a->data, a->length, b, blen); + if (a->length == (int)blen && !memcmp(a->data, b, blen)) + return 1; + else + return 0; + } + else + { + int astrlen, rv; + unsigned char *astr; + astrlen = ASN1_STRING_to_UTF8(&astr, a); + if (astrlen < 0) + return -1; + rv = equal(astr, astrlen, b, blen); + OPENSSL_free(astr); + return rv; + } + } + +static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen, + unsigned int flags, int check_type) + { + STACK_OF(GENERAL_NAME) *gens = NULL; + X509_NAME *name = NULL; + int i; + int cnid; + int alt_type; + equal_fn equal; + if (check_type == GEN_EMAIL) + { + cnid = NID_pkcs9_emailAddress; + alt_type = V_ASN1_IA5STRING; + equal = equal_email; + } + else if (check_type == GEN_DNS) + { + cnid = NID_commonName; + alt_type = V_ASN1_IA5STRING; + if (flags & X509_CHECK_FLAG_NO_WILDCARDS) + equal = equal_nocase; + else + equal = equal_wildcard; + } + else + { + cnid = 0; + alt_type = V_ASN1_OCTET_STRING; + equal = equal_case; + } + + if (chklen == 0) + chklen = strlen((const char *)chk); + + gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL); + if (gens) + { + int rv = 0; + for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) + { + GENERAL_NAME *gen; + ASN1_STRING *cstr; + gen = sk_GENERAL_NAME_value(gens, i); + if(gen->type != check_type) + continue; + if (check_type == GEN_EMAIL) + cstr = gen->d.rfc822Name; + else if (check_type == GEN_DNS) + cstr = gen->d.dNSName; + else + cstr = gen->d.iPAddress; + if (do_check_string(cstr, alt_type, equal, chk, chklen)) + { + rv = 1; + break; + } + } + GENERAL_NAMES_free(gens); + if (rv) + return 1; + if (!(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT) || !cnid) + return 0; + } + i = -1; + name = X509_get_subject_name(x); + while((i = X509_NAME_get_index_by_NID(name, cnid, i)) >= 0) + { + X509_NAME_ENTRY *ne; + ASN1_STRING *str; + ne = X509_NAME_get_entry(name, i); + str = X509_NAME_ENTRY_get_data(ne); + if (do_check_string(str, -1, equal, chk, chklen)) + return 1; + } + return 0; + } + +int _X509_check_host(X509 *x, const unsigned char *chk, size_t chklen, + unsigned int flags) + { + return do_x509_check(x, chk, chklen, flags, GEN_DNS); + } + +int _X509_check_email(X509 *x, const unsigned char *chk, size_t chklen, + unsigned int flags) + { + return do_x509_check(x, chk, chklen, flags, GEN_EMAIL); + } + +int _X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen, + unsigned int flags) + { + return do_x509_check(x, chk, chklen, flags, GEN_IPADD); + } + +#endif diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/hostname.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/hostname.go new file mode 100644 index 00000000000..c1d1202fb65 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/hostname.go @@ -0,0 +1,127 @@ +// 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/ssl.h> +#include <openssl/conf.h> +#include <openssl/x509.h> + +#ifndef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT +#define X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT 0x1 +#define X509_CHECK_FLAG_NO_WILDCARDS 0x2 + +extern int _X509_check_host(X509 *x, const unsigned char *chk, size_t chklen, + unsigned int flags); +extern int _X509_check_email(X509 *x, const unsigned char *chk, size_t chklen, + unsigned int flags); +extern int _X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen, + unsigned int flags); +#endif +*/ +import "C" + +import ( + "errors" + "net" + "unsafe" +) + +var ( + ValidationError = errors.New("Host validation error") +) + +type CheckFlags int + +const ( + AlwaysCheckSubject CheckFlags = C.X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT + NoWildcards CheckFlags = C.X509_CHECK_FLAG_NO_WILDCARDS +) + +// CheckHost checks that the X509 certificate is signed for the provided +// host name. See http://www.openssl.org/docs/crypto/X509_check_host.html for +// more. Note that CheckHost does not check the IP field. See VerifyHostname. +// Specifically returns ValidationError if the Certificate didn't match but +// there was no internal error. +func (c *Certificate) CheckHost(host string, flags CheckFlags) error { + chost := unsafe.Pointer(C.CString(host)) + defer C.free(chost) + rv := C._X509_check_host(c.x, (*C.uchar)(chost), C.size_t(len(host)), + C.uint(flags)) + if rv > 0 { + return nil + } + if rv == 0 { + return ValidationError + } + return errors.New("hostname validation had an internal failure") +} + +// CheckEmail checks that the X509 certificate is signed for the provided +// email address. See http://www.openssl.org/docs/crypto/X509_check_host.html +// for more. +// Specifically returns ValidationError if the Certificate didn't match but +// there was no internal error. +func (c *Certificate) CheckEmail(email string, flags CheckFlags) error { + cemail := unsafe.Pointer(C.CString(email)) + defer C.free(cemail) + rv := C._X509_check_email(c.x, (*C.uchar)(cemail), C.size_t(len(email)), + C.uint(flags)) + if rv > 0 { + return nil + } + if rv == 0 { + return ValidationError + } + return errors.New("email validation had an internal failure") +} + +// CheckIP checks that the X509 certificate is signed for the provided +// IP address. See http://www.openssl.org/docs/crypto/X509_check_host.html +// for more. +// Specifically returns ValidationError if the Certificate didn't match but +// there was no internal error. +func (c *Certificate) CheckIP(ip net.IP, flags CheckFlags) error { + cip := unsafe.Pointer(&ip[0]) + rv := C._X509_check_ip(c.x, (*C.uchar)(cip), C.size_t(len(ip)), + C.uint(flags)) + if rv > 0 { + return nil + } + if rv == 0 { + return ValidationError + } + return errors.New("ip validation had an internal failure") +} + +// VerifyHostname is a combination of CheckHost and CheckIP. If the provided +// hostname looks like an IP address, it will be checked as an IP address, +// otherwise it will be checked as a hostname. +// Specifically returns ValidationError if the Certificate didn't match but +// there was no internal error. +func (c *Certificate) VerifyHostname(host string) error { + var ip net.IP + if len(host) >= 3 && host[0] == '[' && host[len(host)-1] == ']' { + ip = net.ParseIP(host[1 : len(host)-1]) + } else { + ip = net.ParseIP(host) + } + if ip != nil { + return c.CheckIP(ip, 0) + } + return c.CheckHost(host, 0) +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/http.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/http.go new file mode 100644 index 00000000000..e3be32c264a --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/http.go @@ -0,0 +1,61 @@ +// 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 ( + "net/http" +) + +// ListenAndServeTLS will take an http.Handler and serve it using OpenSSL over +// the given tcp address, configured to use the provided cert and key files. +func ListenAndServeTLS(addr string, cert_file string, key_file string, + handler http.Handler) error { + return ServerListenAndServeTLS( + &http.Server{Addr: addr, Handler: handler}, cert_file, key_file) +} + +// ServerListenAndServeTLS will take an http.Server and serve it using OpenSSL +// configured to use the provided cert and key files. +func ServerListenAndServeTLS(srv *http.Server, + cert_file, key_file string) error { + addr := srv.Addr + if addr == "" { + addr = ":https" + } + + ctx, err := NewCtxFromFiles(cert_file, key_file) + if err != nil { + return err + } + + l, err := Listen("tcp", addr, ctx) + if err != nil { + return err + } + + return srv.Serve(l) +} + +// TODO: http client integration +// holy crap, getting this integrated nicely with the Go stdlib HTTP client +// stack so that it does proxying, connection pooling, and most importantly +// hostname verification is really hard. So much stuff is hardcoded to just use +// the built-in TLS lib. I think to get this to work either some crazy +// hacktackery beyond me, an almost straight up fork of the HTTP client, or +// serious stdlib internal refactoring is necessary. +// even more so, good luck getting openssl to use the operating system default +// root certificates if the user doesn't provide any. sadlol +// NOTE: if you're going to try and write your own round tripper, at least use +// openssl.Dial, or equivalent logic diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/init.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/init.go new file mode 100644 index 00000000000..7663a480ed2 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/init.go @@ -0,0 +1,155 @@ +// 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 is a light wrapper around OpenSSL for Go. + +It strives to provide a near-drop-in replacement for the Go standard library +tls package, while allowing for: + +Performance + +OpenSSL is battle-tested and optimized C. While Go's built-in library shows +great promise, it is still young and in some places, inefficient. This simple +OpenSSL wrapper can often do at least 2x with the same cipher and protocol. + +On my lappytop, I get the following benchmarking speeds: + BenchmarkSHA1Large_openssl 1000 2611282 ns/op 401.56 MB/s + BenchmarkSHA1Large_stdlib 500 3963983 ns/op 264.53 MB/s + BenchmarkSHA1Small_openssl 1000000 3476 ns/op 0.29 MB/s + BenchmarkSHA1Small_stdlib 5000000 550 ns/op 1.82 MB/s + BenchmarkSHA256Large_openssl 200 8085314 ns/op 129.69 MB/s + BenchmarkSHA256Large_stdlib 100 18948189 ns/op 55.34 MB/s + BenchmarkSHA256Small_openssl 1000000 4262 ns/op 0.23 MB/s + BenchmarkSHA256Small_stdlib 1000000 1444 ns/op 0.69 MB/s + BenchmarkOpenSSLThroughput 100000 21634 ns/op 47.33 MB/s + BenchmarkStdlibThroughput 50000 58974 ns/op 17.36 MB/s + +Interoperability + +Many systems support OpenSSL with a variety of plugins and modules for things, +such as hardware acceleration in embedded devices. + +Greater flexibility and configuration + +OpenSSL allows for far greater configuration of corner cases and backwards +compatibility (such as support of SSLv2). You shouldn't be using SSLv2 if you +can help but, but sometimes you can't help it. + +Security + +Yeah yeah, Heartbleed. But according to the author of the standard library's +TLS implementation, Go's TLS library is vulnerable to timing attacks. And +whether or not OpenSSL received the appropriate amount of scrutiny +pre-Heartbleed, it sure is receiving it now. + +Usage + +Starting an HTTP server that uses OpenSSL is very easy. It's as simple as: + log.Fatal(openssl.ListenAndServeTLS( + ":8443", "my_server.crt", "my_server.key", myHandler)) + +Getting a net.Listener that uses OpenSSL is also easy: + ctx, err := openssl.NewCtxFromFiles("my_server.crt", "my_server.key") + if err != nil { + log.Fatal(err) + } + l, err := openssl.Listen("tcp", ":7777", ctx) + +Making a client connection is straightforward too: + ctx, err := NewCtx() + if err != nil { + log.Fatal(err) + } + err = ctx.LoadVerifyLocations("/etc/ssl/certs/ca-certificates.crt", "") + if err != nil { + log.Fatal(err) + } + conn, err := openssl.Dial("tcp", "localhost:7777", ctx, 0) + +Help wanted: To get this library to work with net/http's client, we +had to fork net/http. It would be nice if an alternate http client library +supported the generality needed to use OpenSSL instead of crypto/tls. +*/ +package openssl + +/* +#include <openssl/ssl.h> +#include <openssl/conf.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/engine.h> + +extern int Goopenssl_init_locks(); +extern void Goopenssl_thread_locking_callback(int, int, const char*, int); + +static int Goopenssl_init_threadsafety() { + // Set up OPENSSL thread safety callbacks. We only set the locking + // callback because the default id callback implementation is good + // enough for us. + int rc = Goopenssl_init_locks(); + if (rc == 0) { + CRYPTO_set_locking_callback(Goopenssl_thread_locking_callback); + } + return rc; +} + +static void OpenSSL_add_all_algorithms_not_a_macro() { + OpenSSL_add_all_algorithms(); +} + +*/ +import "C" + +import ( + "errors" + "fmt" + "strings" + "sync" +) + +var ( + sslMutexes []sync.Mutex +) + +func init() { + C.OPENSSL_config(nil) + C.ENGINE_load_builtin_engines() + C.SSL_load_error_strings() + C.SSL_library_init() + C.OpenSSL_add_all_algorithms_not_a_macro() + rc := C.Goopenssl_init_threadsafety() + if rc != 0 { + panic(fmt.Errorf("Goopenssl_init_locks failed with %d", rc)) + } +} + +// errorFromErrorQueue needs to run in the same OS thread as the operation +// that caused the possible error +func errorFromErrorQueue() error { + var errs []string + for { + err := C.ERR_get_error() + if err == 0 { + break + } + errs = append(errs, fmt.Sprintf("%s:%s:%s", + C.GoString(C.ERR_lib_error_string(err)), + C.GoString(C.ERR_func_error_string(err)), + C.GoString(C.ERR_reason_error_string(err)))) + } + return errors.New(fmt.Sprintf("SSL errors: %s", strings.Join(errs, "\n"))) +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/init_posix.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/init_posix.go new file mode 100644 index 00000000000..03ed0f01bd0 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/init_posix.go @@ -0,0 +1,64 @@ +// 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 linux darwin cgo +// +build !windows + +package openssl + +/* +#include <errno.h> +#include <openssl/crypto.h> +#include <pthread.h> + +pthread_mutex_t* goopenssl_locks; + +int Goopenssl_init_locks() { + int rc = 0; + int nlock; + int i; + int locks_needed = CRYPTO_num_locks(); + + goopenssl_locks = (pthread_mutex_t*)malloc( + sizeof(pthread_mutex_t) * locks_needed); + if (!goopenssl_locks) { + return ENOMEM; + } + for (nlock = 0; nlock < locks_needed; ++nlock) { + rc = pthread_mutex_init(&goopenssl_locks[nlock], NULL); + if (rc != 0) { + break; + } + } + + if (rc != 0) { + for (i = nlock - 1; i >= 0; --i) { + pthread_mutex_destroy(&goopenssl_locks[i]); + } + free(goopenssl_locks); + goopenssl_locks = NULL; + } + return rc; +} + +void Goopenssl_thread_locking_callback(int mode, int n, const char *file, + int line) { + if (mode & CRYPTO_LOCK) { + pthread_mutex_lock(&goopenssl_locks[n]); + } else { + pthread_mutex_unlock(&goopenssl_locks[n]); + } +} +*/ +import "C" diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/init_windows.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/init_windows.go new file mode 100644 index 00000000000..5eca9fa0eac --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/init_windows.go @@ -0,0 +1,60 @@ +// 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 windows cgo + +package openssl + +/* + +#cgo windows LDFLAGS: -lssleay32 -llibeay32 -L c:/openssl/bin +#cgo windows CFLAGS: -I"c:/openssl/include" + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <errno.h> +#include <openssl/crypto.h> +#include <windows.h> + +CRITICAL_SECTION* goopenssl_locks; + +int Goopenssl_init_locks() { + int rc = 0; + int nlock; + int i; + int locks_needed = CRYPTO_num_locks(); + + goopenssl_locks = (CRITICAL_SECTION*)malloc( + sizeof(*goopenssl_locks) * locks_needed); + if (!goopenssl_locks) { + return ENOMEM; + } + for (nlock = 0; nlock < locks_needed; ++nlock) { + InitializeCriticalSection(&goopenssl_locks[nlock]); + } + + return 0; +} + +void Goopenssl_thread_locking_callback(int mode, int n, const char *file, + int line) { + if (mode & CRYPTO_LOCK) { + EnterCriticalSection(&goopenssl_locks[n]); + } else { + LeaveCriticalSection(&goopenssl_locks[n]); + } +} +*/ +import "C" diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/key.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/key.go new file mode 100644 index 00000000000..c69a101631f --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/key.go @@ -0,0 +1,374 @@ +// 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/evp.h> +// #include <openssl/ssl.h> +// #include <openssl/conf.h> +// +// int EVP_SignInit_not_a_macro(EVP_MD_CTX *ctx, const EVP_MD *type) { +// return EVP_SignInit(ctx, type); +// } +// +// int EVP_SignUpdate_not_a_macro(EVP_MD_CTX *ctx, const void *d, +// unsigned int cnt) { +// return EVP_SignUpdate(ctx, d, cnt); +// } +// +// int EVP_VerifyInit_not_a_macro(EVP_MD_CTX *ctx, const EVP_MD *type) { +// return EVP_VerifyInit(ctx, type); +// } +// +// int EVP_VerifyUpdate_not_a_macro(EVP_MD_CTX *ctx, const void *d, +// unsigned int cnt) { +// return EVP_VerifyUpdate(ctx, d, cnt); +// } +// +// int EVP_PKEY_assign_charp(EVP_PKEY *pkey, int type, char *key) { +// return EVP_PKEY_assign(pkey, type, key); +// } +import "C" + +import ( + "errors" + "io/ioutil" + "runtime" + "unsafe" +) + +type Method *C.EVP_MD + +var ( + SHA256_Method Method = C.EVP_sha256() +) + +type PublicKey interface { + // Verifies the data signature using PKCS1.15 + VerifyPKCS1v15(method Method, data, sig []byte) error + + // MarshalPKIXPublicKeyPEM converts the public key to PEM-encoded PKIX + // format + MarshalPKIXPublicKeyPEM() (pem_block []byte, err error) + + // MarshalPKIXPublicKeyDER converts the public key to DER-encoded PKIX + // format + MarshalPKIXPublicKeyDER() (der_block []byte, err error) + + evpPKey() *C.EVP_PKEY +} + +type PrivateKey interface { + PublicKey + + // Signs the data using PKCS1.15 + SignPKCS1v15(Method, []byte) ([]byte, error) + + // MarshalPKCS1PrivateKeyPEM converts the private key to PEM-encoded PKCS1 + // format + MarshalPKCS1PrivateKeyPEM() (pem_block []byte, err error) + + // MarshalPKCS1PrivateKeyDER converts the private key to DER-encoded PKCS1 + // format + MarshalPKCS1PrivateKeyDER() (der_block []byte, err error) +} + +type pKey struct { + key *C.EVP_PKEY +} + +func (key *pKey) evpPKey() *C.EVP_PKEY { return key.key } + +func (key *pKey) SignPKCS1v15(method Method, data []byte) ([]byte, error) { + var ctx C.EVP_MD_CTX + C.EVP_MD_CTX_init(&ctx) + defer C.EVP_MD_CTX_cleanup(&ctx) + + if 1 != C.EVP_SignInit_not_a_macro(&ctx, method) { + return nil, errors.New("signpkcs1v15: failed to init signature") + } + if len(data) > 0 { + if 1 != C.EVP_SignUpdate_not_a_macro( + &ctx, unsafe.Pointer(&data[0]), C.uint(len(data))) { + return nil, errors.New("signpkcs1v15: failed to update signature") + } + } + sig := make([]byte, C.EVP_PKEY_size(key.key)) + var sigblen C.uint + if 1 != C.EVP_SignFinal(&ctx, + ((*C.uchar)(unsafe.Pointer(&sig[0]))), &sigblen, key.key) { + return nil, errors.New("signpkcs1v15: failed to finalize signature") + } + return sig[:sigblen], nil +} + +func (key *pKey) VerifyPKCS1v15(method Method, data, sig []byte) error { + var ctx C.EVP_MD_CTX + C.EVP_MD_CTX_init(&ctx) + defer C.EVP_MD_CTX_cleanup(&ctx) + + if 1 != C.EVP_VerifyInit_not_a_macro(&ctx, method) { + return errors.New("verifypkcs1v15: failed to init verify") + } + if len(data) > 0 { + if 1 != C.EVP_VerifyUpdate_not_a_macro( + &ctx, unsafe.Pointer(&data[0]), C.uint(len(data))) { + return errors.New("verifypkcs1v15: failed to update verify") + } + } + if 1 != C.EVP_VerifyFinal(&ctx, + ((*C.uchar)(unsafe.Pointer(&sig[0]))), C.uint(len(sig)), key.key) { + return errors.New("verifypkcs1v15: failed to finalize verify") + } + return nil +} + +func (key *pKey) MarshalPKCS1PrivateKeyPEM() (pem_block []byte, + err error) { + bio := C.BIO_new(C.BIO_s_mem()) + if bio == nil { + return nil, errors.New("failed to allocate memory BIO") + } + defer C.BIO_free(bio) + rsa := (*C.RSA)(C.EVP_PKEY_get1_RSA(key.key)) + if rsa == nil { + return nil, errors.New("failed getting rsa key") + } + defer C.RSA_free(rsa) + if int(C.PEM_write_bio_RSAPrivateKey(bio, rsa, nil, nil, C.int(0), nil, + nil)) != 1 { + return nil, errors.New("failed dumping private key") + } + return ioutil.ReadAll(asAnyBio(bio)) +} + +func (key *pKey) MarshalPKCS1PrivateKeyDER() (der_block []byte, + err error) { + bio := C.BIO_new(C.BIO_s_mem()) + if bio == nil { + return nil, errors.New("failed to allocate memory BIO") + } + defer C.BIO_free(bio) + rsa := (*C.RSA)(C.EVP_PKEY_get1_RSA(key.key)) + if rsa == nil { + return nil, errors.New("failed getting rsa key") + } + defer C.RSA_free(rsa) + if int(C.i2d_RSAPrivateKey_bio(bio, rsa)) != 1 { + return nil, errors.New("failed dumping private key der") + } + return ioutil.ReadAll(asAnyBio(bio)) +} + +func (key *pKey) MarshalPKIXPublicKeyPEM() (pem_block []byte, + err error) { + bio := C.BIO_new(C.BIO_s_mem()) + if bio == nil { + return nil, errors.New("failed to allocate memory BIO") + } + defer C.BIO_free(bio) + rsa := (*C.RSA)(C.EVP_PKEY_get1_RSA(key.key)) + if rsa == nil { + return nil, errors.New("failed getting rsa key") + } + defer C.RSA_free(rsa) + if int(C.PEM_write_bio_RSA_PUBKEY(bio, rsa)) != 1 { + return nil, errors.New("failed dumping public key pem") + } + return ioutil.ReadAll(asAnyBio(bio)) +} + +func (key *pKey) MarshalPKIXPublicKeyDER() (der_block []byte, + err error) { + bio := C.BIO_new(C.BIO_s_mem()) + if bio == nil { + return nil, errors.New("failed to allocate memory BIO") + } + defer C.BIO_free(bio) + rsa := (*C.RSA)(C.EVP_PKEY_get1_RSA(key.key)) + if rsa == nil { + return nil, errors.New("failed getting rsa key") + } + defer C.RSA_free(rsa) + if int(C.i2d_RSA_PUBKEY_bio(bio, rsa)) != 1 { + return nil, errors.New("failed dumping public key der") + } + return ioutil.ReadAll(asAnyBio(bio)) +} + +// LoadPrivateKeyFromPEM loads a private key from a PEM-encoded block. +func LoadPrivateKeyFromPEM(pem_block []byte) (PrivateKey, error) { + if len(pem_block) == 0 { + return nil, errors.New("empty pem block") + } + bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]), + C.int(len(pem_block))) + if bio == nil { + return nil, errors.New("failed creating bio") + } + defer C.BIO_free(bio) + + rsakey := C.PEM_read_bio_RSAPrivateKey(bio, nil, nil, nil) + if rsakey == nil { + return nil, errors.New("failed reading rsa key") + } + defer C.RSA_free(rsakey) + + // convert to PKEY + key := C.EVP_PKEY_new() + if key == nil { + return nil, errors.New("failed converting to evp_pkey") + } + if C.EVP_PKEY_set1_RSA(key, (*C.struct_rsa_st)(rsakey)) != 1 { + C.EVP_PKEY_free(key) + return nil, errors.New("failed converting to evp_pkey") + } + + p := &pKey{key: key} + runtime.SetFinalizer(p, func(p *pKey) { + C.EVP_PKEY_free(p.key) + }) + return p, nil +} + +// LoadPrivateKeyFromPEM loads a private key from a PEM-encoded block. +func LoadPrivateKeyFromPEMWidthPassword(pem_block []byte, password string) ( + PrivateKey, error) { + if len(pem_block) == 0 { + return nil, errors.New("empty pem block") + } + bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]), + C.int(len(pem_block))) + if bio == nil { + return nil, errors.New("failed creating bio") + } + defer C.BIO_free(bio) + cs := C.CString(password) + defer C.free(unsafe.Pointer(cs)) + rsakey := C.PEM_read_bio_RSAPrivateKey(bio, nil, nil, unsafe.Pointer(cs)) + if rsakey == nil { + return nil, errors.New("failed reading rsa key") + } + defer C.RSA_free(rsakey) + + // convert to PKEY + key := C.EVP_PKEY_new() + if key == nil { + return nil, errors.New("failed converting to evp_pkey") + } + if C.EVP_PKEY_set1_RSA(key, (*C.struct_rsa_st)(rsakey)) != 1 { + C.EVP_PKEY_free(key) + return nil, errors.New("failed converting to evp_pkey") + } + + p := &pKey{key: key} + runtime.SetFinalizer(p, func(p *pKey) { + C.EVP_PKEY_free(p.key) + }) + return p, nil +} + +// LoadPublicKeyFromPEM loads a public key from a PEM-encoded block. +func LoadPublicKeyFromPEM(pem_block []byte) (PublicKey, error) { + if len(pem_block) == 0 { + return nil, errors.New("empty pem block") + } + bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]), + C.int(len(pem_block))) + if bio == nil { + return nil, errors.New("failed creating bio") + } + defer C.BIO_free(bio) + + rsakey := C.PEM_read_bio_RSA_PUBKEY(bio, nil, nil, nil) + if rsakey == nil { + return nil, errors.New("failed reading rsa key") + } + defer C.RSA_free(rsakey) + + // convert to PKEY + key := C.EVP_PKEY_new() + if key == nil { + return nil, errors.New("failed converting to evp_pkey") + } + if C.EVP_PKEY_set1_RSA(key, (*C.struct_rsa_st)(rsakey)) != 1 { + C.EVP_PKEY_free(key) + return nil, errors.New("failed converting to evp_pkey") + } + + p := &pKey{key: key} + runtime.SetFinalizer(p, func(p *pKey) { + C.EVP_PKEY_free(p.key) + }) + return p, nil +} + +// LoadPublicKeyFromDER loads a public key from a DER-encoded block. +func LoadPublicKeyFromDER(der_block []byte) (PublicKey, error) { + if len(der_block) == 0 { + return nil, errors.New("empty der block") + } + bio := C.BIO_new_mem_buf(unsafe.Pointer(&der_block[0]), + C.int(len(der_block))) + if bio == nil { + return nil, errors.New("failed creating bio") + } + defer C.BIO_free(bio) + + rsakey := C.d2i_RSA_PUBKEY_bio(bio, nil) + if rsakey == nil { + return nil, errors.New("failed reading rsa key") + } + defer C.RSA_free(rsakey) + + // convert to PKEY + key := C.EVP_PKEY_new() + if key == nil { + return nil, errors.New("failed converting to evp_pkey") + } + if C.EVP_PKEY_set1_RSA(key, (*C.struct_rsa_st)(rsakey)) != 1 { + C.EVP_PKEY_free(key) + return nil, errors.New("failed converting to evp_pkey") + } + + p := &pKey{key: key} + runtime.SetFinalizer(p, func(p *pKey) { + C.EVP_PKEY_free(p.key) + }) + return p, nil +} + +// GenerateRSAKey generates a new RSA private key with an exponent of 3. +func GenerateRSAKey(bits int) (PrivateKey, error) { + exponent := 3 + rsa := C.RSA_generate_key(C.int(bits), C.ulong(exponent), nil, nil) + if rsa == nil { + return nil, errors.New("failed to generate RSA key") + } + key := C.EVP_PKEY_new() + if key == nil { + return nil, errors.New("failed to allocate EVP_PKEY") + } + if C.EVP_PKEY_assign_charp(key, C.EVP_PKEY_RSA, (*C.char)(unsafe.Pointer(rsa))) != 1 { + C.EVP_PKEY_free(key) + return nil, errors.New("failed to assign RSA key") + } + p := &pKey{key: key} + runtime.SetFinalizer(p, func(p *pKey) { + C.EVP_PKEY_free(p.key) + }) + return p, nil +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/key_test.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/key_test.go new file mode 100644 index 00000000000..54752d381bf --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/key_test.go @@ -0,0 +1,149 @@ +// 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 ( + "bytes" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "encoding/hex" + pem_pkg "encoding/pem" + "io/ioutil" + "testing" +) + +func TestMarshal(t *testing.T) { + key, err := LoadPrivateKeyFromPEM(keyBytes) + if err != nil { + t.Fatal(err) + } + cert, err := LoadCertificateFromPEM(certBytes) + if err != nil { + t.Fatal(err) + } + + pem, err := cert.MarshalPEM() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(pem, certBytes) { + ioutil.WriteFile("generated", pem, 0644) + ioutil.WriteFile("hardcoded", certBytes, 0644) + t.Fatal("invalid cert pem bytes") + } + + pem, err = key.MarshalPKCS1PrivateKeyPEM() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(pem, keyBytes) { + ioutil.WriteFile("generated", pem, 0644) + ioutil.WriteFile("hardcoded", keyBytes, 0644) + t.Fatal("invalid private key pem bytes") + } + tls_cert, err := tls.X509KeyPair(certBytes, keyBytes) + if err != nil { + t.Fatal(err) + } + tls_key, ok := tls_cert.PrivateKey.(*rsa.PrivateKey) + if !ok { + t.Fatal("FASDFASDF") + } + _ = tls_key + + der, err := key.MarshalPKCS1PrivateKeyDER() + if err != nil { + t.Fatal(err) + } + tls_der := x509.MarshalPKCS1PrivateKey(tls_key) + if !bytes.Equal(der, tls_der) { + t.Fatal("invalid private key der bytes: %s\n v.s. %s\n", + hex.Dump(der), hex.Dump(tls_der)) + } + + der, err = key.MarshalPKIXPublicKeyDER() + if err != nil { + t.Fatal(err) + } + tls_der, err = x509.MarshalPKIXPublicKey(&tls_key.PublicKey) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(der, tls_der) { + ioutil.WriteFile("generated", []byte(hex.Dump(der)), 0644) + ioutil.WriteFile("hardcoded", []byte(hex.Dump(tls_der)), 0644) + t.Fatal("invalid public key der bytes") + } + + pem, err = key.MarshalPKIXPublicKeyPEM() + if err != nil { + t.Fatal(err) + } + tls_pem := pem_pkg.EncodeToMemory(&pem_pkg.Block{ + Type: "PUBLIC KEY", Bytes: tls_der}) + if !bytes.Equal(pem, tls_pem) { + ioutil.WriteFile("generated", pem, 0644) + ioutil.WriteFile("hardcoded", tls_pem, 0644) + t.Fatal("invalid public key pem bytes") + } + + loaded_pubkey_from_pem, err := LoadPublicKeyFromPEM(pem) + if err != nil { + t.Fatal(err) + } + + loaded_pubkey_from_der, err := LoadPublicKeyFromDER(der) + if err != nil { + t.Fatal(err) + } + + new_der_from_pem, err := loaded_pubkey_from_pem.MarshalPKIXPublicKeyDER() + if err != nil { + t.Fatal(err) + } + + new_der_from_der, err := loaded_pubkey_from_der.MarshalPKIXPublicKeyDER() + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(new_der_from_der, tls_der) { + ioutil.WriteFile("generated", []byte(hex.Dump(new_der_from_der)), 0644) + ioutil.WriteFile("hardcoded", []byte(hex.Dump(tls_der)), 0644) + t.Fatal("invalid public key der bytes") + } + + if !bytes.Equal(new_der_from_pem, tls_der) { + ioutil.WriteFile("generated", []byte(hex.Dump(new_der_from_pem)), 0644) + ioutil.WriteFile("hardcoded", []byte(hex.Dump(tls_der)), 0644) + t.Fatal("invalid public key der bytes") + } +} + +func TestGenerate(t *testing.T) { + key, err := GenerateRSAKey(2048) + if err != nil { + t.Fatal(err) + } + _, err = key.MarshalPKIXPublicKeyPEM() + if err != nil { + t.Fatal(err) + } + _, err = key.MarshalPKCS1PrivateKeyPEM() + if err != nil { + t.Fatal(err) + } +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/net.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/net.go new file mode 100644 index 00000000000..3cdd040d4d4 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/net.go @@ -0,0 +1,134 @@ +// 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 ( + "errors" + "net" +) + +type listener struct { + net.Listener + ctx *Ctx +} + +func (l *listener) Accept() (c net.Conn, err error) { + c, err = l.Listener.Accept() + if err != nil { + return nil, err + } + ssl_c, err := Server(c, l.ctx) + if err != nil { + c.Close() + return nil, err + } + return ssl_c, nil +} + +// NewListener wraps an existing net.Listener such that all accepted +// connections are wrapped as OpenSSL server connections using the provided +// context ctx. +func NewListener(inner net.Listener, ctx *Ctx) net.Listener { + return &listener{ + Listener: inner, + ctx: ctx} +} + +// Listen is a wrapper around net.Listen that wraps incoming connections with +// an OpenSSL server connection using the provided context ctx. +func Listen(network, laddr string, ctx *Ctx) (net.Listener, error) { + if ctx == nil { + return nil, errors.New("no ssl context provided") + } + l, err := net.Listen(network, laddr) + if err != nil { + return nil, err + } + return NewListener(l, ctx), nil +} + +type DialFlags int + +const ( + InsecureSkipHostVerification DialFlags = 1 << iota + DisableSNI +) + +// Dial will connect to network/address and then wrap the corresponding +// underlying connection with an OpenSSL client connection using context ctx. +// If flags includes InsecureSkipHostVerification, the server certificate's +// hostname will not be checked to match the hostname in addr. Otherwise, flags +// should be 0. +// +// Dial 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 Dial(network, addr string, ctx *Ctx, flags DialFlags) (*Conn, error) { + return DialSession(network, addr, ctx, flags, nil) +} + +// DialSession will connect to network/address and then wrap the corresponding +// underlying connection with an OpenSSL client connection using context ctx. +// If flags includes InsecureSkipHostVerification, the server certificate's +// hostname will not be checked to match the hostname in addr. Otherwise, flags +// should be 0. +// +// Dial 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. +// +// If session is not nil it will be used to resume the tls state. The session +// can be retrieved from the GetSession method on the Conn. +func DialSession(network, addr string, ctx *Ctx, flags DialFlags, + session []byte) (*Conn, error) { + + host, _, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + if ctx == nil { + var err error + ctx, err = NewCtx() + if err != nil { + return nil, err + } + // TODO: use operating system default certificate chain? + } + c, err := net.Dial(network, addr) + if err != nil { + return nil, err + } + conn, err := Client(c, ctx) + if err != nil { + c.Close() + return nil, err + } + // XXX removed SNI + err = conn.Handshake() + if err != nil { + conn.Close() + return nil, err + } + if flags&InsecureSkipHostVerification == 0 { + err = conn.VerifyHostname(host) + if err != nil { + conn.Close() + return nil, err + } + } + return conn, nil +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/nid.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/nid.go new file mode 100644 index 00000000000..c80f237b605 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/nid.go @@ -0,0 +1,199 @@ +// Copyright (C) 2014 Ryan Hileman +// +// 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 + +type NID int + +const ( + NID_rsadsi NID = 1 + NID_pkcs NID = 2 + NID_md2 NID = 3 + NID_md5 NID = 4 + NID_rc4 NID = 5 + NID_rsaEncryption NID = 6 + NID_md2WithRSAEncryption NID = 7 + NID_md5WithRSAEncryption NID = 8 + NID_pbeWithMD2AndDES_CBC NID = 9 + NID_pbeWithMD5AndDES_CBC NID = 10 + NID_X500 NID = 11 + NID_X509 NID = 12 + NID_commonName NID = 13 + NID_countryName NID = 14 + NID_localityName NID = 15 + NID_stateOrProvinceName NID = 16 + NID_organizationName NID = 17 + NID_organizationalUnitName NID = 18 + NID_rsa NID = 19 + NID_pkcs7 NID = 20 + NID_pkcs7_data NID = 21 + NID_pkcs7_signed NID = 22 + NID_pkcs7_enveloped NID = 23 + NID_pkcs7_signedAndEnveloped NID = 24 + NID_pkcs7_digest NID = 25 + NID_pkcs7_encrypted NID = 26 + NID_pkcs3 NID = 27 + NID_dhKeyAgreement NID = 28 + NID_des_ecb NID = 29 + NID_des_cfb64 NID = 30 + NID_des_cbc NID = 31 + NID_des_ede NID = 32 + NID_des_ede3 NID = 33 + NID_idea_cbc NID = 34 + NID_idea_cfb64 NID = 35 + NID_idea_ecb NID = 36 + NID_rc2_cbc NID = 37 + NID_rc2_ecb NID = 38 + NID_rc2_cfb64 NID = 39 + NID_rc2_ofb64 NID = 40 + NID_sha NID = 41 + NID_shaWithRSAEncryption NID = 42 + NID_des_ede_cbc NID = 43 + NID_des_ede3_cbc NID = 44 + NID_des_ofb64 NID = 45 + NID_idea_ofb64 NID = 46 + NID_pkcs9 NID = 47 + NID_pkcs9_emailAddress NID = 48 + NID_pkcs9_unstructuredName NID = 49 + NID_pkcs9_contentType NID = 50 + NID_pkcs9_messageDigest NID = 51 + NID_pkcs9_signingTime NID = 52 + NID_pkcs9_countersignature NID = 53 + NID_pkcs9_challengePassword NID = 54 + NID_pkcs9_unstructuredAddress NID = 55 + NID_pkcs9_extCertAttributes NID = 56 + NID_netscape NID = 57 + NID_netscape_cert_extension NID = 58 + NID_netscape_data_type NID = 59 + NID_des_ede_cfb64 NID = 60 + NID_des_ede3_cfb64 NID = 61 + NID_des_ede_ofb64 NID = 62 + NID_des_ede3_ofb64 NID = 63 + NID_sha1 NID = 64 + NID_sha1WithRSAEncryption NID = 65 + NID_dsaWithSHA NID = 66 + NID_dsa_2 NID = 67 + NID_pbeWithSHA1AndRC2_CBC NID = 68 + NID_id_pbkdf2 NID = 69 + NID_dsaWithSHA1_2 NID = 70 + NID_netscape_cert_type NID = 71 + NID_netscape_base_url NID = 72 + NID_netscape_revocation_url NID = 73 + NID_netscape_ca_revocation_url NID = 74 + NID_netscape_renewal_url NID = 75 + NID_netscape_ca_policy_url NID = 76 + NID_netscape_ssl_server_name NID = 77 + NID_netscape_comment NID = 78 + NID_netscape_cert_sequence NID = 79 + NID_desx_cbc NID = 80 + NID_id_ce NID = 81 + NID_subject_key_identifier NID = 82 + NID_key_usage NID = 83 + NID_private_key_usage_period NID = 84 + NID_subject_alt_name NID = 85 + NID_issuer_alt_name NID = 86 + NID_basic_constraints NID = 87 + NID_crl_number NID = 88 + NID_certificate_policies NID = 89 + NID_authority_key_identifier NID = 90 + NID_bf_cbc NID = 91 + NID_bf_ecb NID = 92 + NID_bf_cfb64 NID = 93 + NID_bf_ofb64 NID = 94 + NID_mdc2 NID = 95 + NID_mdc2WithRSA NID = 96 + NID_rc4_40 NID = 97 + NID_rc2_40_cbc NID = 98 + NID_givenName NID = 99 + NID_surname NID = 100 + NID_initials NID = 101 + NID_uniqueIdentifier NID = 102 + NID_crl_distribution_points NID = 103 + NID_md5WithRSA NID = 104 + NID_serialNumber NID = 105 + NID_title NID = 106 + NID_description NID = 107 + NID_cast5_cbc NID = 108 + NID_cast5_ecb NID = 109 + NID_cast5_cfb64 NID = 110 + NID_cast5_ofb64 NID = 111 + NID_pbeWithMD5AndCast5_CBC NID = 112 + NID_dsaWithSHA1 NID = 113 + NID_md5_sha1 NID = 114 + NID_sha1WithRSA NID = 115 + NID_dsa NID = 116 + NID_ripemd160 NID = 117 + NID_ripemd160WithRSA NID = 119 + NID_rc5_cbc NID = 120 + NID_rc5_ecb NID = 121 + NID_rc5_cfb64 NID = 122 + NID_rc5_ofb64 NID = 123 + NID_rle_compression NID = 124 + NID_zlib_compression NID = 125 + NID_ext_key_usage NID = 126 + NID_id_pkix NID = 127 + NID_id_kp NID = 128 + NID_server_auth NID = 129 + NID_client_auth NID = 130 + NID_code_sign NID = 131 + NID_email_protect NID = 132 + NID_time_stamp NID = 133 + NID_ms_code_ind NID = 134 + NID_ms_code_com NID = 135 + NID_ms_ctl_sign NID = 136 + NID_ms_sgc NID = 137 + NID_ms_efs NID = 138 + NID_ns_sgc NID = 139 + NID_delta_crl NID = 140 + NID_crl_reason NID = 141 + NID_invalidity_date NID = 142 + NID_sxnet NID = 143 + NID_pbe_WithSHA1And128BitRC4 NID = 144 + NID_pbe_WithSHA1And40BitRC4 NID = 145 + NID_pbe_WithSHA1And3_Key_TripleDES_CBC NID = 146 + NID_pbe_WithSHA1And2_Key_TripleDES_CBC NID = 147 + NID_pbe_WithSHA1And128BitRC2_CBC NID = 148 + NID_pbe_WithSHA1And40BitRC2_CBC NID = 149 + NID_keyBag NID = 150 + NID_pkcs8ShroudedKeyBag NID = 151 + NID_certBag NID = 152 + NID_crlBag NID = 153 + NID_secretBag NID = 154 + NID_safeContentsBag NID = 155 + NID_friendlyName NID = 156 + NID_localKeyID NID = 157 + NID_x509Certificate NID = 158 + NID_sdsiCertificate NID = 159 + NID_x509Crl NID = 160 + NID_pbes2 NID = 161 + NID_pbmac1 NID = 162 + NID_hmacWithSHA1 NID = 163 + NID_id_qt_cps NID = 164 + NID_id_qt_unotice NID = 165 + NID_rc2_64_cbc NID = 166 + NID_SMIMECapabilities NID = 167 + NID_pbeWithMD2AndRC2_CBC NID = 168 + NID_pbeWithMD5AndRC2_CBC NID = 169 + NID_pbeWithSHA1AndDES_CBC NID = 170 + NID_ms_ext_req NID = 171 + NID_ext_req NID = 172 + NID_name NID = 173 + NID_dnQualifier NID = 174 + NID_id_pe NID = 175 + NID_id_ad NID = 176 + NID_info_access NID = 177 + NID_ad_OCSP NID = 178 + NID_ad_ca_issuers NID = 179 + NID_OCSP_sign NID = 180 +) diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/oracle_stubs.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/oracle_stubs.go new file mode 100644 index 00000000000..30492f3b9d8 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/oracle_stubs.go @@ -0,0 +1,162 @@ +// 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 + +import ( + "errors" + "net" + "time" +) + +const ( + SSLRecordSize = 16 * 1024 +) + +type Conn struct{} + +func Client(conn net.Conn, ctx *Ctx) (*Conn, error) +func Server(conn net.Conn, ctx *Ctx) (*Conn, error) + +func (c *Conn) Handshake() error +func (c *Conn) PeerCertificate() (*Certificate, error) +func (c *Conn) Close() error +func (c *Conn) Read(b []byte) (n int, err error) +func (c *Conn) Write(b []byte) (written int, err error) + +func (c *Conn) VerifyHostname(host string) error + +func (c *Conn) LocalAddr() net.Addr +func (c *Conn) RemoteAddr() net.Addr +func (c *Conn) SetDeadline(t time.Time) error +func (c *Conn) SetReadDeadline(t time.Time) error +func (c *Conn) SetWriteDeadline(t time.Time) error + +type Ctx struct{} + +type SSLVersion int + +const ( + SSLv3 SSLVersion = 0x02 + TLSv1 SSLVersion = 0x03 + TLSv1_1 SSLVersion = 0x04 + TLSv1_2 SSLVersion = 0x05 + AnyVersion SSLVersion = 0x06 +) + +func NewCtxWithVersion(version SSLVersion) (*Ctx, error) +func NewCtx() (*Ctx, error) +func NewCtxFromFiles(cert_file string, key_file string) (*Ctx, error) +func (c *Ctx) UseCertificate(cert *Certificate) error +func (c *Ctx) UsePrivateKey(key PrivateKey) error + +type CertificateStore struct{} + +func (c *Ctx) GetCertificateStore() *CertificateStore + +func (s *CertificateStore) AddCertificate(cert *Certificate) error + +func (c *Ctx) LoadVerifyLocations(ca_file string, ca_path string) error + +type Options int + +const ( + NoCompression Options = 0 + NoSSLv2 Options = 0 + NoSSLv3 Options = 0 + NoTLSv1 Options = 0 + CipherServerPreference Options = 0 + NoSessionResumptionOrRenegotiation Options = 0 + NoTicket Options = 0 +) + +func (c *Ctx) SetOptions(options Options) Options + +type Modes int + +const ( + ReleaseBuffers Modes = 0 +) + +func (c *Ctx) SetMode(modes Modes) Modes + +type VerifyOptions int + +const ( + VerifyNone VerifyOptions = 0 + VerifyPeer VerifyOptions = 0 + VerifyFailIfNoPeerCert VerifyOptions = 0 + VerifyClientOnce VerifyOptions = 0 +) + +func (c *Ctx) SetVerify(options VerifyOptions) +func (c *Ctx) SetVerifyDepth(depth int) +func (c *Ctx) SetSessionId(session_id []byte) error + +func (c *Ctx) SetCipherList(list string) error + +type SessionCacheModes int + +const ( + SessionCacheOff SessionCacheModes = 0 + SessionCacheClient SessionCacheModes = 0 + SessionCacheServer SessionCacheModes = 0 + SessionCacheBoth SessionCacheModes = 0 + NoAutoClear SessionCacheModes = 0 + NoInternalLookup SessionCacheModes = 0 + NoInternalStore SessionCacheModes = 0 + NoInternal SessionCacheModes = 0 +) + +func (c *Ctx) SetSessionCacheMode(modes SessionCacheModes) SessionCacheModes + +var ( + ValidationError = errors.New("Host validation error") +) + +type CheckFlags int + +const ( + AlwaysCheckSubject CheckFlags = 0 + NoWildcards CheckFlags = 0 +) + +func (c *Certificate) CheckHost(host string, flags CheckFlags) error +func (c *Certificate) CheckEmail(email string, flags CheckFlags) error +func (c *Certificate) CheckIP(ip net.IP, flags CheckFlags) error +func (c *Certificate) VerifyHostname(host string) error + +type PublicKey interface { + MarshalPKIXPublicKeyPEM() (pem_block []byte, err error) + MarshalPKIXPublicKeyDER() (der_block []byte, err error) + evpPKey() struct{} +} + +type PrivateKey interface { + PublicKey + MarshalPKCS1PrivateKeyPEM() (pem_block []byte, err error) + MarshalPKCS1PrivateKeyDER() (der_block []byte, err error) +} + +func LoadPrivateKeyFromPEM(pem_block []byte) (PrivateKey, error) + +type Certificate struct{} + +func LoadCertificateFromPEM(pem_block []byte) (*Certificate, error) + +func (c *Certificate) MarshalPEM() (pem_block []byte, err error) + +func (c *Certificate) PublicKey() (PublicKey, error) diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/password.c b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/password.c new file mode 100644 index 00000000000..db9582ca726 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/password.c @@ -0,0 +1,10 @@ +#include <openssl/ssl.h> +#include "_cgo_export.h" + +int password_cb(char *buf,int buf_len, int rwflag,void *userdata) { + char* pw = (char *)userdata; + int l = strlen(pw); + if (l + 1 > buf_len) return 0; + strcpy(buf,pw); + return l; +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/pem.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/pem.go new file mode 100644 index 00000000000..6dad5972dbd --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/pem.go @@ -0,0 +1,32 @@ +// Copyright (C) 2014 Ryan Hileman +// +// 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 ( + "regexp" +) + +var pemSplit *regexp.Regexp = regexp.MustCompile(`(?sm)` + + `(^-----[\s-]*?BEGIN.*?-----$` + + `.*?` + + `^-----[\s-]*?END.*?-----$)`) + +func SplitPEM(data []byte) [][]byte { + var results [][]byte + for _, block := range pemSplit.FindAll(data, -1) { + results = append(results, block) + } + return results +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/sha1.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/sha1.go new file mode 100644 index 00000000000..2592b6627d1 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/sha1.go @@ -0,0 +1,99 @@ +// 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 <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "openssl/evp.h" +*/ +import "C" + +import ( + "errors" + "runtime" + "unsafe" +) + +type SHA1Hash struct { + ctx C.EVP_MD_CTX + engine *Engine +} + +func NewSHA1Hash() (*SHA1Hash, error) { return NewSHA1HashWithEngine(nil) } + +func NewSHA1HashWithEngine(e *Engine) (*SHA1Hash, error) { + hash := &SHA1Hash{engine: e} + C.EVP_MD_CTX_init(&hash.ctx) + runtime.SetFinalizer(hash, func(hash *SHA1Hash) { hash.Close() }) + if err := hash.Reset(); err != nil { + return nil, err + } + return hash, nil +} + +func (s *SHA1Hash) Close() { + C.EVP_MD_CTX_cleanup(&s.ctx) +} + +func engineRef(e *Engine) *C.ENGINE { + if e == nil { + return nil + } + return e.e +} + +func (s *SHA1Hash) Reset() error { + if 1 != C.EVP_DigestInit_ex(&s.ctx, C.EVP_sha1(), engineRef(s.engine)) { + return errors.New("openssl: sha1: cannot init digest ctx") + } + return nil +} + +func (s *SHA1Hash) Write(p []byte) (n int, err error) { + if len(p) == 0 { + return 0, nil + } + if 1 != C.EVP_DigestUpdate(&s.ctx, unsafe.Pointer(&p[0]), + C.size_t(len(p))) { + return 0, errors.New("openssl: sha1: cannot update digest") + } + return len(p), nil +} + +func (s *SHA1Hash) Sum() (result [20]byte, err error) { + if 1 != C.EVP_DigestFinal_ex(&s.ctx, + (*C.uchar)(unsafe.Pointer(&result[0])), nil) { + return result, errors.New("openssl: sha1: cannot finalize ctx") + } + return result, s.Reset() +} + +func SHA1(data []byte) (result [20]byte, err error) { + hash, err := NewSHA1Hash() + if err != nil { + return result, err + } + defer hash.Close() + if _, err := hash.Write(data); err != nil { + return result, err + } + return hash.Sum() +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/sha1_test.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/sha1_test.go new file mode 100644 index 00000000000..37037e4468b --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/sha1_test.go @@ -0,0 +1,111 @@ +// 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 + +import ( + "crypto/rand" + "crypto/sha1" + "io" + "testing" +) + +func TestSHA1(t *testing.T) { + for i := 0; i < 100; i++ { + buf := make([]byte, 10*1024-i) + if _, err := io.ReadFull(rand.Reader, buf); err != nil { + t.Fatal(err) + } + + expected := sha1.Sum(buf) + got, err := SHA1(buf) + if err != nil { + t.Fatal(err) + } + + if expected != got { + t.Fatal("exp:%x got:%x", expected, got) + } + } +} + +func TestSHA1Writer(t *testing.T) { + ohash, err := NewSHA1Hash() + if err != nil { + t.Fatal(err) + } + hash := sha1.New() + + for i := 0; i < 100; i++ { + if err := ohash.Reset(); err != nil { + t.Fatal(err) + } + hash.Reset() + buf := make([]byte, 10*1024-i) + if _, err := io.ReadFull(rand.Reader, buf); err != nil { + t.Fatal(err) + } + + if _, err := ohash.Write(buf); err != nil { + t.Fatal(err) + } + if _, err := hash.Write(buf); err != nil { + t.Fatal(err) + } + + var got, exp [20]byte + + hash.Sum(exp[:0]) + got, err := ohash.Sum() + if err != nil { + t.Fatal(err) + } + + if got != exp { + t.Fatal("exp:%x got:%x", exp, got) + } + } +} + +type shafunc func([]byte) + +func benchmarkSHA1(b *testing.B, length int64, fn shafunc) { + buf := make([]byte, length) + if _, err := io.ReadFull(rand.Reader, buf); err != nil { + b.Fatal(err) + } + b.SetBytes(length) + b.ResetTimer() + for i := 0; i < b.N; i++ { + fn(buf) + } +} + +func BenchmarkSHA1Large_openssl(b *testing.B) { + benchmarkSHA1(b, 1024*1024, func(buf []byte) { SHA1(buf) }) +} + +func BenchmarkSHA1Large_stdlib(b *testing.B) { + benchmarkSHA1(b, 1024*1024, func(buf []byte) { sha1.Sum(buf) }) +} + +func BenchmarkSHA1Small_openssl(b *testing.B) { + benchmarkSHA1(b, 1, func(buf []byte) { SHA1(buf) }) +} + +func BenchmarkSHA1Small_stdlib(b *testing.B) { + benchmarkSHA1(b, 1, func(buf []byte) { sha1.Sum(buf) }) +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/sha256.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/sha256.go new file mode 100644 index 00000000000..6785b32f881 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/sha256.go @@ -0,0 +1,92 @@ +// 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 <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "openssl/evp.h" +*/ +import "C" + +import ( + "errors" + "runtime" + "unsafe" +) + +type SHA256Hash struct { + ctx C.EVP_MD_CTX + engine *Engine +} + +func NewSHA256Hash() (*SHA256Hash, error) { return NewSHA256HashWithEngine(nil) } + +func NewSHA256HashWithEngine(e *Engine) (*SHA256Hash, error) { + hash := &SHA256Hash{engine: e} + C.EVP_MD_CTX_init(&hash.ctx) + runtime.SetFinalizer(hash, func(hash *SHA256Hash) { hash.Close() }) + if err := hash.Reset(); err != nil { + return nil, err + } + return hash, nil +} + +func (s *SHA256Hash) Close() { + C.EVP_MD_CTX_cleanup(&s.ctx) +} + +func (s *SHA256Hash) Reset() error { + if 1 != C.EVP_DigestInit_ex(&s.ctx, C.EVP_sha256(), engineRef(s.engine)) { + return errors.New("openssl: sha256: cannot init digest ctx") + } + return nil +} + +func (s *SHA256Hash) Write(p []byte) (n int, err error) { + if len(p) == 0 { + return 0, nil + } + if 1 != C.EVP_DigestUpdate(&s.ctx, unsafe.Pointer(&p[0]), + C.size_t(len(p))) { + return 0, errors.New("openssl: sha256: cannot update digest") + } + return len(p), nil +} + +func (s *SHA256Hash) Sum() (result [32]byte, err error) { + if 1 != C.EVP_DigestFinal_ex(&s.ctx, + (*C.uchar)(unsafe.Pointer(&result[0])), nil) { + return result, errors.New("openssl: sha256: cannot finalize ctx") + } + return result, s.Reset() +} + +func SHA256(data []byte) (result [32]byte, err error) { + hash, err := NewSHA256Hash() + if err != nil { + return result, err + } + defer hash.Close() + if _, err := hash.Write(data); err != nil { + return result, err + } + return hash.Sum() +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/sha256_test.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/sha256_test.go new file mode 100644 index 00000000000..89df88afd44 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/sha256_test.go @@ -0,0 +1,109 @@ +// 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 + +import ( + "crypto/rand" + "crypto/sha256" + "io" + "testing" +) + +func TestSHA256(t *testing.T) { + for i := 0; i < 100; i++ { + buf := make([]byte, 10*1024-i) + if _, err := io.ReadFull(rand.Reader, buf); err != nil { + t.Fatal(err) + } + + expected := sha256.Sum256(buf) + got, err := SHA256(buf) + if err != nil { + t.Fatal(err) + } + + if expected != got { + t.Fatal("exp:%x got:%x", expected, got) + } + } +} + +func TestSHA256Writer(t *testing.T) { + ohash, err := NewSHA256Hash() + if err != nil { + t.Fatal(err) + } + hash := sha256.New() + + for i := 0; i < 100; i++ { + if err := ohash.Reset(); err != nil { + t.Fatal(err) + } + hash.Reset() + buf := make([]byte, 10*1024-i) + if _, err := io.ReadFull(rand.Reader, buf); err != nil { + t.Fatal(err) + } + + if _, err := ohash.Write(buf); err != nil { + t.Fatal(err) + } + if _, err := hash.Write(buf); err != nil { + t.Fatal(err) + } + + var got, exp [32]byte + + hash.Sum(exp[:0]) + got, err := ohash.Sum() + if err != nil { + t.Fatal(err) + } + + if got != exp { + t.Fatal("exp:%x got:%x", exp, got) + } + } +} + +func benchmarkSHA256(b *testing.B, length int64, fn shafunc) { + buf := make([]byte, length) + if _, err := io.ReadFull(rand.Reader, buf); err != nil { + b.Fatal(err) + } + b.SetBytes(length) + b.ResetTimer() + for i := 0; i < b.N; i++ { + fn(buf) + } +} + +func BenchmarkSHA256Large_openssl(b *testing.B) { + benchmarkSHA256(b, 1024*1024, func(buf []byte) { SHA256(buf) }) +} + +func BenchmarkSHA256Large_stdlib(b *testing.B) { + benchmarkSHA256(b, 1024*1024, func(buf []byte) { sha256.Sum256(buf) }) +} + +func BenchmarkSHA256Small_openssl(b *testing.B) { + benchmarkSHA256(b, 1, func(buf []byte) { SHA256(buf) }) +} + +func BenchmarkSHA256Small_stdlib(b *testing.B) { + benchmarkSHA256(b, 1, func(buf []byte) { sha256.Sum256(buf) }) +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/sni.c b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/sni.c new file mode 100644 index 00000000000..5398da869b8 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/sni.c @@ -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); +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/sni_test.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/sni_test.go new file mode 100644 index 00000000000..ee3b1a8bbaf --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/sni_test.go @@ -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") +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/ssl.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/ssl.go new file mode 100644 index 00000000000..d6120e15d99 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/ssl.go @@ -0,0 +1,167 @@ +// 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 + +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 +} + +// 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)) +} + +//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)) +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/ssl_test.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/ssl_test.go new file mode 100644 index 00000000000..f83225dec97 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/ssl_test.go @@ -0,0 +1,633 @@ +// 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 ( + "bytes" + "crypto/rand" + "crypto/tls" + "io" + "io/ioutil" + "net" + "sync" + "testing" + "time" + + "github.com/spacemonkeygo/openssl/utils" +) + +var ( + certBytes = []byte(`-----BEGIN CERTIFICATE----- +MIIDxDCCAqygAwIBAgIVAMcK/0VWQr2O3MNfJCydqR7oVELcMA0GCSqGSIb3DQEB +BQUAMIGQMUkwRwYDVQQDE0A1NjdjZGRmYzRjOWZiNTYwZTk1M2ZlZjA1N2M0NGFm +MDdiYjc4MDIzODIxYTA5NThiY2RmMGMwNzJhOTdiMThhMQswCQYDVQQGEwJVUzEN +MAsGA1UECBMEVXRhaDEQMA4GA1UEBxMHTWlkdmFsZTEVMBMGA1UEChMMU3BhY2Ug +TW9ua2V5MB4XDTEzMTIxNzE4MzgyMloXDTIzMTIxNTE4MzgyMlowgZAxSTBHBgNV +BAMTQDM4NTg3ODRkMjU1NTdiNTM1MWZmNjRmMmQzMTQ1ZjkwYTJlMTIzMDM4Y2Yz +Mjc1Yzg1OTM1MjcxYWIzMmNiMDkxCzAJBgNVBAYTAlVTMQ0wCwYDVQQIEwRVdGFo +MRAwDgYDVQQHEwdNaWR2YWxlMRUwEwYDVQQKEwxTcGFjZSBNb25rZXkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDdf3icNvFsrlrnNLi8SocscqlSbFq+ +pEvmhcSoqgDLqebnqu8Ld73HJJ74MGXEgRX8xZT5FinOML31CR6t9E/j3dqV6p+G +fdlFLe3IqtC0/bPVnCDBirBygBI4uCrMq+1VhAxPWclrDo7l9QRYbsExH9lfn+Ry +vxeNMZiOASasvVZNncY8E9usBGRdH17EfDL/TPwXqWOLyxSN5o54GTztjjy9w9CG +QP7jcCueKYyQJQCtEmnwc6P/q6/EPv5R6drBkX6loAPtmCUAkHqxkWOJrRq/v7Pw +zRYhfY+ZpVHGc7WEkDnLzRiUypr1C9oxvLKS10etZEIwEdKyOkSg2fdPAgMBAAGj +EzARMA8GA1UdEwEB/wQFMAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAEcz0RTTJ99l +HTK/zTyfV5VZEhtwqu6bwre/hD7lhI+1ji0DZYGIgCbJLKuZhj+cHn2h5nPhN7zE +M9tc4pn0TgeVS0SVFSe6TGnIFipNogvP17E+vXpDZcW/xn9kPKeVCZc1hlDt1W4Z +5q+ub3aUwuMwYs7bcArtDrumCmciJ3LFyNhebPi4mntb5ooeLFLaujEmVYyrQnpo +tWKC9sMlJmLm4yAso64Sv9KLS2T9ivJBNn0ZtougozBCCTqrqgZVjha+B2yjHe9f +sRkg/uxcJf7wC5Y0BLlp1+aPwdmZD87T3a1uQ1Ij93jmHG+2T9U20MklHAePOl0q +yTqdSPnSH1c= +-----END CERTIFICATE----- +`) + keyBytes = []byte(`-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA3X94nDbxbK5a5zS4vEqHLHKpUmxavqRL5oXEqKoAy6nm56rv +C3e9xySe+DBlxIEV/MWU+RYpzjC99QkerfRP493aleqfhn3ZRS3tyKrQtP2z1Zwg +wYqwcoASOLgqzKvtVYQMT1nJaw6O5fUEWG7BMR/ZX5/kcr8XjTGYjgEmrL1WTZ3G +PBPbrARkXR9exHwy/0z8F6lji8sUjeaOeBk87Y48vcPQhkD+43ArnimMkCUArRJp +8HOj/6uvxD7+UenawZF+paAD7ZglAJB6sZFjia0av7+z8M0WIX2PmaVRxnO1hJA5 +y80YlMqa9QvaMbyyktdHrWRCMBHSsjpEoNn3TwIDAQABAoIBAQCwgp6YzmgCFce3 +LBpzYmjqEM3CMzr1ZXRe1gbr6d4Mbu7leyBX4SpJAnP0kIzo1X2yG7ol7XWPLOST +2pqqQWFQ00EX6wsJYEy+hmVRXl5HfU3MUkkAMwd9l3Xt4UWqKPBPD5XHvmN2fvl9 +Y4388vXdseXGAGNK1eFs0TMjJuOtDxDyrmJcnxpJ7y/77y/Hb5rUa9DCvj8tkKHg +HmeIwQE0HhIFofj+qCYbqeVyjbPAaYZMrISXb2HmcyULKEOGRbMH24IzInKA0NxV +kdP9qmV8Y2bJ609Fft/y8Vpj31iEdq/OFXyobdVvnXMnaVyAetoaWy7AOTIQ2Cnw +wGbJ/F8BAoGBAN/pCnLQrWREeVMuFjf+MgYgCtRRaQ8EOVvjYcXXi0PhtOMFTAb7 +djqhlgmBOFsmeXcb8YRZsF+pNtu1xk5RJOquyKfK8j1rUdAJfoxGHiaUFI2/1i9E +zuXX/Ao0xNRkWMxMKuwYBmmt1fMuVo+1M8UEwFMdHRtgxe+/+eOV1J2PAoGBAP09 +7GLOYSYAI1OO3BN/bEVNau6tAxP5YShGmX2Qxy0+ooxHZ1V3D8yo6C0hSg+H+fPT +mjMgGcvaW6K+QyCdHDjgbk2hfdZ+Beq92JApPrH9gMV7MPhwHzgwjzDDio9OFxYY +3vjBQ2yX+9jvz9lkvq2NM3fqFqbsG6Et+5mCc6pBAoGBAI62bxVtEgbladrtdfXs +S6ABzkUzOl362EBL9iZuUnJKqstDtgiBQALwuLuIJA5cwHB9W/t6WuMt7CwveJy0 +NW5rRrNDtBAXlgad9o2bp135ZfxO+EoadjCi8B7lMUsaRkq4hWcDjRrQVJxxvXRN +DxkVBSw0Uzf+/0nnN3OqLODbAoGACCY+/isAC1YDzQOS53m5RT2pjEa7C6CB1Ob4 +t4a6MiWK25LMq35qXr6swg8JMBjDHWqY0r5ctievvTv8Mwd7SgVG526j+wwRKq2z +U2hQYS/0Peap+8S37Hn7kakpQ1VS/t4MBttJTSxS6XdGLAvG6xTZLCm3UuXUOcqe +ByGgkUECgYEAmop45kRi974g4MPvyLplcE4syb19ifrHj76gPRBi94Cp8jZosY1T +ucCCa4lOGgPtXJ0Qf1c8yq5vh4yqkQjrgUTkr+CFDGR6y4CxmNDQxEMYIajaIiSY +qmgvgyRayemfO2zR0CPgC6wSoGBth+xW6g+WA8y0z76ZSaWpFi8lVM4= +-----END RSA PRIVATE KEY----- +`) +) + +func NetPipe(t testing.TB) (net.Conn, net.Conn) { + l, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatal(err) + } + defer l.Close() + client_future := utils.NewFuture() + go func() { + client_future.Set(net.Dial(l.Addr().Network(), l.Addr().String())) + }() + var errs utils.ErrorGroup + server_conn, err := l.Accept() + errs.Add(err) + client_conn, err := client_future.Get() + errs.Add(err) + err = errs.Finalize() + if err != nil { + if server_conn != nil { + server_conn.Close() + } + if client_conn != nil { + client_conn.(net.Conn).Close() + } + t.Fatal(err) + } + return server_conn, client_conn.(net.Conn) +} + +type HandshakingConn interface { + net.Conn + Handshake() error +} + +func SimpleConnTest(t testing.TB, constructor func( + t testing.TB, conn1, conn2 net.Conn) (sslconn1, sslconn2 HandshakingConn)) { + server_conn, client_conn := NetPipe(t) + defer server_conn.Close() + defer client_conn.Close() + + data := "first test string\n" + + server, client := constructor(t, server_conn, client_conn) + defer close_both(server, client) + + var wg sync.WaitGroup + wg.Add(2) + go func() { + defer wg.Done() + + err := client.Handshake() + if err != nil { + t.Fatal(err) + } + + _, err = io.Copy(client, bytes.NewReader([]byte(data))) + if err != nil { + t.Fatal(err) + } + + err = client.Close() + if err != nil { + t.Fatal(err) + } + }() + go func() { + defer wg.Done() + + err := server.Handshake() + if err != nil { + t.Fatal(err) + } + + buf := bytes.NewBuffer(make([]byte, 0, len(data))) + _, err = io.CopyN(buf, server, int64(len(data))) + if err != nil { + t.Fatal(err) + } + if string(buf.Bytes()) != data { + t.Fatal("mismatched data") + } + + err = server.Close() + if err != nil { + t.Fatal(err) + } + }() + wg.Wait() +} + +func close_both(closer1, closer2 io.Closer) { + var wg sync.WaitGroup + wg.Add(2) + go func() { + defer wg.Done() + closer1.Close() + }() + go func() { + defer wg.Done() + closer2.Close() + }() + wg.Wait() +} + +func ClosingTest(t testing.TB, constructor func( + t testing.TB, conn1, conn2 net.Conn) (sslconn1, sslconn2 HandshakingConn)) { + + run_test := func(close_tcp bool, server_writes bool) { + server_conn, client_conn := NetPipe(t) + defer server_conn.Close() + defer client_conn.Close() + server, client := constructor(t, server_conn, client_conn) + defer close_both(server, client) + + var sslconn1, sslconn2 HandshakingConn + var conn1 net.Conn + if server_writes { + sslconn1 = server + conn1 = server_conn + sslconn2 = client + } else { + sslconn1 = client + conn1 = client_conn + sslconn2 = server + } + + var wg sync.WaitGroup + wg.Add(2) + go func() { + defer wg.Done() + _, err := sslconn1.Write([]byte("hello")) + if err != nil { + t.Fatal(err) + } + if close_tcp { + err = conn1.Close() + } else { + err = sslconn1.Close() + } + if err != nil { + t.Fatal(err) + } + }() + + go func() { + defer wg.Done() + data, err := ioutil.ReadAll(sslconn2) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(data, []byte("hello")) { + t.Fatal("bytes don't match") + } + }() + + wg.Wait() + } + + run_test(true, false) + run_test(false, false) + run_test(true, true) + run_test(false, true) +} + +func ThroughputBenchmark(b *testing.B, constructor func( + t testing.TB, conn1, conn2 net.Conn) (sslconn1, sslconn2 HandshakingConn)) { + server_conn, client_conn := NetPipe(b) + defer server_conn.Close() + defer client_conn.Close() + + server, client := constructor(b, server_conn, client_conn) + defer close_both(server, client) + + b.SetBytes(1024) + data := make([]byte, b.N*1024) + _, err := io.ReadFull(rand.Reader, data[:]) + if err != nil { + b.Fatal(err) + } + + b.ResetTimer() + var wg sync.WaitGroup + wg.Add(2) + go func() { + defer wg.Done() + _, err = io.Copy(client, bytes.NewReader([]byte(data))) + if err != nil { + b.Fatal(err) + } + }() + go func() { + defer wg.Done() + + buf := &bytes.Buffer{} + _, err = io.CopyN(buf, server, int64(len(data))) + if err != nil { + b.Fatal(err) + } + if !bytes.Equal(buf.Bytes(), data) { + b.Fatal("mismatched data") + } + }() + wg.Wait() + b.StopTimer() +} + +func StdlibConstructor(t testing.TB, server_conn, client_conn net.Conn) ( + server, client HandshakingConn) { + cert, err := tls.X509KeyPair(certBytes, keyBytes) + if err != nil { + t.Fatal(err) + } + config := &tls.Config{ + Certificates: []tls.Certificate{cert}, + InsecureSkipVerify: true, + CipherSuites: []uint16{tls.TLS_RSA_WITH_AES_128_CBC_SHA}} + server = tls.Server(server_conn, config) + client = tls.Client(client_conn, config) + 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 := LoadPrivateKeyFromPEM(keyBytes) + if err != nil { + t.Fatal(err) + } + err = ctx.UsePrivateKey(key) + if err != nil { + t.Fatal(err) + } + cert, err := LoadCertificateFromPEM(certBytes) + if err != nil { + t.Fatal(err) + } + err = ctx.UseCertificate(cert) + if err != nil { + t.Fatal(err) + } + err = ctx.SetCipherList("AES128-SHA") + if err != nil { + t.Fatal(err) + } + server, err = Server(server_conn, ctx) + if err != nil { + t.Fatal(err) + } + client, err = Client(client_conn, ctx) + if err != nil { + t.Fatal(err) + } + return server, client +} + +func StdlibOpenSSLConstructor(t testing.TB, server_conn, client_conn net.Conn) ( + server, client HandshakingConn) { + server_std, _ := StdlibConstructor(t, server_conn, client_conn) + _, client_ssl := OpenSSLConstructor(t, server_conn, client_conn) + return server_std, client_ssl +} + +func OpenSSLStdlibConstructor(t testing.TB, server_conn, client_conn net.Conn) ( + server, client HandshakingConn) { + _, client_std := StdlibConstructor(t, server_conn, client_conn) + server_ssl, _ := OpenSSLConstructor(t, server_conn, client_conn) + return server_ssl, client_std +} + +func TestStdlibSimple(t *testing.T) { + SimpleConnTest(t, StdlibConstructor) +} + +func TestOpenSSLSimple(t *testing.T) { + SimpleConnTest(t, OpenSSLConstructor) +} + +func TestStdlibClosing(t *testing.T) { + ClosingTest(t, StdlibConstructor) +} + +func TestOpenSSLClosing(t *testing.T) { + ClosingTest(t, OpenSSLConstructor) +} + +func BenchmarkStdlibThroughput(b *testing.B) { + ThroughputBenchmark(b, StdlibConstructor) +} + +func BenchmarkOpenSSLThroughput(b *testing.B) { + ThroughputBenchmark(b, OpenSSLConstructor) +} + +func TestStdlibOpenSSLSimple(t *testing.T) { + SimpleConnTest(t, StdlibOpenSSLConstructor) +} + +func TestOpenSSLStdlibSimple(t *testing.T) { + SimpleConnTest(t, OpenSSLStdlibConstructor) +} + +func TestStdlibOpenSSLClosing(t *testing.T) { + ClosingTest(t, StdlibOpenSSLConstructor) +} + +func TestOpenSSLStdlibClosing(t *testing.T) { + ClosingTest(t, OpenSSLStdlibConstructor) +} + +func BenchmarkStdlibOpenSSLThroughput(b *testing.B) { + ThroughputBenchmark(b, StdlibOpenSSLConstructor) +} + +func BenchmarkOpenSSLStdlibThroughput(b *testing.B) { + ThroughputBenchmark(b, OpenSSLStdlibConstructor) +} + +func FullDuplexRenegotiationTest(t testing.TB, constructor func( + t testing.TB, conn1, conn2 net.Conn) (sslconn1, sslconn2 HandshakingConn)) { + + server_conn, client_conn := NetPipe(t) + defer server_conn.Close() + defer client_conn.Close() + + times := 256 + data_len := 4 * SSLRecordSize + data1 := make([]byte, data_len) + _, err := io.ReadFull(rand.Reader, data1[:]) + if err != nil { + t.Fatal(err) + } + data2 := make([]byte, data_len) + _, err = io.ReadFull(rand.Reader, data1[:]) + if err != nil { + t.Fatal(err) + } + + server, client := constructor(t, server_conn, client_conn) + defer close_both(server, client) + + var wg sync.WaitGroup + + send_func := func(sender HandshakingConn, data []byte) { + defer wg.Done() + for i := 0; i < times; i++ { + if i == times/2 { + wg.Add(1) + go func() { + defer wg.Done() + err := sender.Handshake() + if err != nil { + t.Fatal(err) + } + }() + } + _, err := sender.Write(data) + if err != nil { + t.Fatal(err) + } + } + } + + recv_func := func(receiver net.Conn, data []byte) { + defer wg.Done() + + buf := make([]byte, len(data)) + for i := 0; i < times; i++ { + n, err := io.ReadFull(receiver, buf[:]) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(buf[:n], data) { + t.Fatal(err) + } + } + } + + wg.Add(4) + go recv_func(server, data1) + go send_func(client, data1) + go send_func(server, data2) + go recv_func(client, data2) + wg.Wait() +} + +func TestStdlibFullDuplexRenegotiation(t *testing.T) { + FullDuplexRenegotiationTest(t, StdlibConstructor) +} + +func TestOpenSSLFullDuplexRenegotiation(t *testing.T) { + FullDuplexRenegotiationTest(t, OpenSSLConstructor) +} + +func TestOpenSSLStdlibFullDuplexRenegotiation(t *testing.T) { + FullDuplexRenegotiationTest(t, OpenSSLStdlibConstructor) +} + +func TestStdlibOpenSSLFullDuplexRenegotiation(t *testing.T) { + FullDuplexRenegotiationTest(t, StdlibOpenSSLConstructor) +} + +func LotsOfConns(t *testing.T, payload_size int64, loops, clients int, + sleep time.Duration, newListener func(net.Listener) net.Listener, + newClient func(net.Conn) (net.Conn, error)) { + tcp_listener, err := net.Listen("tcp", "localhost:0") + if err != nil { + t.Fatal(err) + } + ssl_listener := newListener(tcp_listener) + go func() { + for { + conn, err := ssl_listener.Accept() + if err != nil { + t.Fatalf("failed accept: %s", err) + continue + } + go func() { + defer func() { + err = conn.Close() + if err != nil { + t.Fatalf("failed closing: %s", err) + } + }() + for i := 0; i < loops; i++ { + _, err := io.Copy(ioutil.Discard, + io.LimitReader(conn, payload_size)) + if err != nil { + t.Fatalf("failed reading: %s", err) + return + } + _, err = io.Copy(conn, io.LimitReader(rand.Reader, + payload_size)) + if err != nil { + t.Fatalf("failed writing: %s", err) + return + } + } + time.Sleep(sleep) + }() + } + }() + var wg sync.WaitGroup + for i := 0; i < clients; i++ { + tcp_client, err := net.Dial(tcp_listener.Addr().Network(), + tcp_listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + ssl_client, err := newClient(tcp_client) + if err != nil { + t.Fatal(err) + } + wg.Add(1) + go func(i int) { + defer func() { + err = ssl_client.Close() + if err != nil { + t.Fatalf("failed closing: %s", err) + } + wg.Done() + }() + for i := 0; i < loops; i++ { + _, err := io.Copy(ssl_client, io.LimitReader(rand.Reader, + payload_size)) + if err != nil { + t.Fatalf("failed writing: %s", err) + return + } + _, err = io.Copy(ioutil.Discard, + io.LimitReader(ssl_client, payload_size)) + if err != nil { + t.Fatalf("failed reading: %s", err) + return + } + } + time.Sleep(sleep) + }(i) + } + wg.Wait() +} + +func TestStdlibLotsOfConns(t *testing.T) { + tls_cert, err := tls.X509KeyPair(certBytes, keyBytes) + if err != nil { + t.Fatal(err) + } + tls_config := &tls.Config{ + Certificates: []tls.Certificate{tls_cert}, + InsecureSkipVerify: true, + CipherSuites: []uint16{tls.TLS_RSA_WITH_AES_128_CBC_SHA}} + LotsOfConns(t, 1024*64, 10, 100, 0*time.Second, + func(l net.Listener) net.Listener { + return tls.NewListener(l, tls_config) + }, func(c net.Conn) (net.Conn, error) { + return tls.Client(c, tls_config), nil + }) +} + +func TestOpenSSLLotsOfConns(t *testing.T) { + ctx, err := NewCtx() + if err != nil { + t.Fatal(err) + } + key, err := LoadPrivateKeyFromPEM(keyBytes) + if err != nil { + t.Fatal(err) + } + err = ctx.UsePrivateKey(key) + if err != nil { + t.Fatal(err) + } + cert, err := LoadCertificateFromPEM(certBytes) + if err != nil { + t.Fatal(err) + } + err = ctx.UseCertificate(cert) + if err != nil { + t.Fatal(err) + } + err = ctx.SetCipherList("AES128-SHA") + if err != nil { + t.Fatal(err) + } + LotsOfConns(t, 1024*64, 10, 100, 0*time.Second, + func(l net.Listener) net.Listener { + return NewListener(l, ctx) + }, func(c net.Conn) (net.Conn, error) { + return Client(c, ctx) + }) +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/utils/errors.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/utils/errors.go new file mode 100644 index 00000000000..bab314c95d7 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/utils/errors.go @@ -0,0 +1,50 @@ +// 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 utils + +import ( + "errors" + "strings" +) + +// ErrorGroup collates errors +type ErrorGroup struct { + Errors []error +} + +// Add adds an error to an existing error group +func (e *ErrorGroup) Add(err error) { + if err != nil { + e.Errors = append(e.Errors, err) + } +} + +// Finalize returns an error corresponding to the ErrorGroup state. If there's +// no errors in the group, finalize returns nil. If there's only one error, +// Finalize returns that error. Otherwise, Finalize will make a new error +// consisting of the messages from the constituent errors. +func (e *ErrorGroup) Finalize() error { + if len(e.Errors) == 0 { + return nil + } + if len(e.Errors) == 1 { + return e.Errors[0] + } + msgs := make([]string, 0, len(e.Errors)) + for _, err := range e.Errors { + msgs = append(msgs, err.Error()) + } + return errors.New(strings.Join(msgs, "\n")) +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/utils/future.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/utils/future.go new file mode 100644 index 00000000000..fa1bbbfb861 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/utils/future.go @@ -0,0 +1,79 @@ +// 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 utils + +import ( + "sync" +) + +// Future is a type that is essentially the inverse of a channel. With a +// channel, you have multiple senders and one receiver. With a future, you can +// have multiple receivers and one sender. Additionally, a future protects +// against double-sends. Since this is usually used for returning function +// results, we also capture and return error values as well. Use NewFuture +// to initialize. +type Future struct { + mutex *sync.Mutex + cond *sync.Cond + received bool + val interface{} + err error +} + +// NewFuture returns an initialized and ready Future. +func NewFuture() *Future { + mutex := &sync.Mutex{} + return &Future{ + mutex: mutex, + cond: sync.NewCond(mutex), + received: false, + val: nil, + err: nil, + } +} + +// Get blocks until the Future has a value set. +func (self *Future) Get() (interface{}, error) { + self.mutex.Lock() + defer self.mutex.Unlock() + for { + if self.received { + return self.val, self.err + } + self.cond.Wait() + } +} + +// Fired returns whether or not a value has been set. If Fired is true, Get +// won't block. +func (self *Future) Fired() bool { + self.mutex.Lock() + defer self.mutex.Unlock() + return self.received +} + +// Set provides the value to present and future Get calls. If Set has already +// been called, this is a no-op. +func (self *Future) Set(val interface{}, err error) { + self.mutex.Lock() + defer self.mutex.Unlock() + if self.received { + return + } + self.received = true + self.val = val + self.err = err + self.cond.Broadcast() +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/verify.c b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/verify.c new file mode 100644 index 00000000000..d55866c4cf0 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/verify.c @@ -0,0 +1,31 @@ +// 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" + +int verify_cb(int ok, X509_STORE_CTX* store) { + SSL* ssl = (SSL *)X509_STORE_CTX_get_app_data(store); + 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); +} + +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); +} diff --git a/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/version.go b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/version.go new file mode 100644 index 00000000000..8f3d392cde8 --- /dev/null +++ b/src/mongo/gotools/vendor/src/github.com/spacemonkeygo/openssl/version.go @@ -0,0 +1,22 @@ +// 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/opensslv.h> +import "C" + +const Version string = C.OPENSSL_VERSION_TEXT |