Skip to content

Commit cc4484a

Browse files
authored
Remove Init fallback logic and provide CheckVersion instead (#66)
* remove Init fallback logic and provide CheckVersion instead * do not require GO_OPENSSL_VERSION_OVERRIDE for testing * remove spurious comment * ci: avoid running duplicated address sanitizer tests
1 parent edc462d commit cc4484a

File tree

7 files changed

+90
-79
lines changed

7 files changed

+90
-79
lines changed

.github/workflows/test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
run: |
4141
ok=true
4242
for t in $(go test ./... -list=. | grep '^Test'); do
43-
go test ./... -gcflags=all=-d=checkptr -asan -run $t -v || ok=false
43+
go test ./... -gcflags=all=-d=checkptr -asan -run ^$t$ -v || ok=false
4444
done
4545
$ok
4646
env:

openssl/goopenssl.c

+20
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,26 @@ FOR_ALL_OPENSSL_FUNCTIONS
2727
#undef DEFINEFUNC_RENAMED_1_1
2828
#undef DEFINEFUNC_RENAMED_3_0
2929

30+
int
31+
go_openssl_fips_enabled(void* handle)
32+
{
33+
// For OpenSSL 1.x.
34+
int (*FIPS_mode)(void);
35+
FIPS_mode = (int (*)(void))dlsym(handle, "FIPS_mode");
36+
if (FIPS_mode != NULL)
37+
return FIPS_mode();
38+
39+
// For OpenSSL 3.x.
40+
int (*EVP_default_properties_is_fips_enabled)(void*);
41+
int (*OSSL_PROVIDER_available)(void*, const char*);
42+
EVP_default_properties_is_fips_enabled = (int (*)(void*))dlsym(handle, "EVP_default_properties_is_fips_enabled");
43+
OSSL_PROVIDER_available = (int (*)(void*, const char*))dlsym(handle, "OSSL_PROVIDER_available");
44+
if (EVP_default_properties_is_fips_enabled != NULL && OSSL_PROVIDER_available != NULL &&
45+
EVP_default_properties_is_fips_enabled(NULL) == 1 && OSSL_PROVIDER_available(NULL, "fips") == 1)
46+
return 1;
47+
48+
return 0;
49+
}
3050

3151
// Load all the functions stored in FOR_ALL_OPENSSL_FUNCTIONS
3252
// and assign them to their corresponding function pointer

openssl/goopenssl.h

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ go_openssl_do_leak_check(void)
1919
#endif
2020
}
2121

22+
int go_openssl_fips_enabled(void* handle);
2223
int go_openssl_version_major(void* handle);
2324
int go_openssl_version_minor(void* handle);
2425
int go_openssl_version_patch(void* handle);

openssl/init.go

+4-35
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,17 @@ import (
1111
"unsafe"
1212
)
1313

14-
// knownVersions is a list of supported and well-known libcrypto.so suffixes in decreasing version order.
15-
//
16-
// FreeBSD library version numbering does not directly align to the version of OpenSSL.
17-
// Its preferred search order is 11 -> 111.
18-
//
19-
// Some distributions use 1.0.0 and others (such as Debian) 1.0.2 to refer to the same OpenSSL 1.0.2 version.
20-
//
21-
// Fedora derived distros use different naming for the version 1.0.x.
22-
var knownVersions = [...]string{"3", "1.1", "11", "111", "1.0.2", "1.0.0", "10"}
23-
2414
// opensslInit loads and initialize OpenSSL.
2515
// If successful, it returns the major and minor OpenSSL version
2616
// as reported by the OpenSSL API.
2717
//
2818
// See Init() for details about version.
2919
func opensslInit(version string) (major, minor, patch int, err error) {
3020
// Load the OpenSSL shared library using dlopen.
31-
handle, err := loadLibrary(version)
32-
if err != nil {
33-
return 0, 0, 0, err
21+
handle := dlopen(version)
22+
if handle == nil {
23+
errstr := C.GoString(C.dlerror())
24+
return 0, 0, 0, errors.New("openssl: can't load libcrypto.so." + version + ": " + errstr)
3425
}
3526

3627
// Retrieve the loaded OpenSSL version and check if it is supported.
@@ -80,25 +71,3 @@ func dlopen(version string) unsafe.Pointer {
8071
defer C.free(unsafe.Pointer(cv))
8172
return C.dlopen(cv, C.RTLD_LAZY|C.RTLD_LOCAL)
8273
}
83-
84-
func loadLibrary(version string) (unsafe.Pointer, error) {
85-
if version != "" {
86-
// If version is specified try to load it or error out.
87-
handle := dlopen(version)
88-
if handle == nil {
89-
errstr := C.GoString(C.dlerror())
90-
return nil, errors.New("openssl: can't load libcrypto.so." + version + ": " + errstr)
91-
}
92-
return handle, nil
93-
}
94-
// If the version is not specified, try loading from the list
95-
// of well known versions.
96-
for _, v := range knownVersions {
97-
handle := dlopen(v)
98-
if handle == nil {
99-
continue
100-
}
101-
return handle, nil
102-
}
103-
return nil, errors.New("openssl: can't load libcrypto.so using any known version suffix")
104-
}

openssl/openssl.go

+32-41
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package openssl
66

77
// #include "goopenssl.h"
8+
// #include <dlfcn.h>
89
// #cgo LDFLAGS: -ldl
910
import "C"
1011
import (
@@ -30,17 +31,28 @@ var (
3031

3132
var nativeEndian binary.ByteOrder
3233

34+
// CheckVersion checks if the OpenSSL version can be loaded
35+
// and if the FIPS mode is enabled.
36+
// This function can be called before Init.
37+
func CheckVersion(version string) (exists, fips bool) {
38+
handle := dlopen(version)
39+
if handle == nil {
40+
return false, false
41+
}
42+
defer C.dlclose(handle)
43+
fips = C.go_openssl_fips_enabled(handle) == 1
44+
return true, fips
45+
}
46+
3347
// Init loads and initializes OpenSSL.
34-
// It must be called before any other OpenSSL call.
48+
// It must be called before any other OpenSSL call, except CheckVersion.
3549
//
3650
// Only the first call to Init is effective,
3751
// subsequent calls will return the same error result as the one from the first call.
3852
//
39-
// If version is not empty, its value will be appended to the OpenSSL shared library name
40-
// as a version suffix when calling dlopen. For example, `version=1.1.1k-fips`
41-
// makes Init look for the shared library libcrypto.so.1.1.1k-fips.
42-
// If version is empty, Init will try to load the OpenSSL shared library
43-
// using a list of supported and well-known version suffixes, going from higher to lower versions.
53+
// version will be appended to the OpenSSL shared library name as a version suffix
54+
// when calling dlopen. For example, `version=1.1.1k-fips` makes Init look for
55+
// the shared library libcrypto.so.1.1.1k-fips.
4456
func Init(version string) error {
4557
initOnce.Do(func() {
4658
buf := [2]byte{}
@@ -75,24 +87,8 @@ func VersionText() string {
7587
var (
7688
providerNameFips = C.CString("fips")
7789
providerNameDefault = C.CString("default")
78-
propFipsYes = C.CString("fips=yes")
79-
propFipsNo = C.CString("fips=no")
80-
algProve = C.CString("SHA2-256")
8190
)
8291

83-
// providerAvailable looks through provider's digests
84-
// checking if there is any that matches the props query.
85-
func providerAvailable(props *C.char) bool {
86-
C.go_openssl_ERR_set_mark()
87-
defer C.go_openssl_ERR_pop_to_mark()
88-
md := C.go_openssl_EVP_MD_fetch(nil, algProve, props)
89-
if md == nil {
90-
return false
91-
}
92-
C.go_openssl_EVP_MD_free(md)
93-
return true
94-
}
95-
9692
// FIPS returns true if OpenSSL is running in FIPS mode, else returns false.
9793
func FIPS() bool {
9894
switch vMajor {
@@ -107,54 +103,49 @@ func FIPS() bool {
107103
// it is only based on the default properties.
108104
// We can be sure that the FIPS provider is available if we can fetch an algorithm, e.g., SHA2-256,
109105
// explicitly setting `fips=yes`.
110-
return providerAvailable(propFipsYes)
106+
return C.go_openssl_OSSL_PROVIDER_available(nil, providerNameFips) == 1
111107
default:
112108
panic(errUnsupportedVersion())
113109
}
114110
}
115111

116112
// SetFIPS enables or disables FIPS mode.
117113
//
118-
// It implements the following provider fallback logic for OpenSSL 3:
119-
// - The "fips" provider is loaded if enabled=true and no loaded provider matches "fips=yes".
120-
// - The "default" provider is loaded if enabled=false and no loaded provider matches "fips=no".
121-
//
122-
// This logic allows advanced users to define their own providers that match "fips=yes" and "fips=no" using the OpenSSL config file.
114+
// For OpenSSL 3, the `fips` provider is loaded if enabled is true,
115+
// else the `default` provider is loaded.
123116
func SetFIPS(enabled bool) error {
117+
var mode C.int
118+
if enabled {
119+
mode = C.int(1)
120+
} else {
121+
mode = C.int(0)
122+
}
124123
switch vMajor {
125124
case 1:
126-
var mode C.int
127-
if enabled {
128-
mode = C.int(1)
129-
} else {
130-
mode = C.int(0)
131-
}
132125
if C.go_openssl_FIPS_mode_set(mode) != 1 {
133126
return newOpenSSLError("FIPS_mode_set")
134127
}
135128
return nil
136129
case 3:
137-
var props, provName *C.char
130+
var provName *C.char
138131
if enabled {
139-
props = propFipsYes
140132
provName = providerNameFips
141133
} else {
142-
props = propFipsNo
143134
provName = providerNameDefault
144135
}
145136
// Check if there is any provider that matches props.
146-
if !providerAvailable(props) {
137+
if C.go_openssl_OSSL_PROVIDER_available(nil, provName) != 1 {
147138
// If not, fallback to provName provider.
148139
if C.go_openssl_OSSL_PROVIDER_load(nil, provName) == nil {
149140
return newOpenSSLError("OSSL_PROVIDER_try_load")
150141
}
151142
// Make sure we now have a provider available.
152-
if !providerAvailable(props) {
143+
if C.go_openssl_OSSL_PROVIDER_available(nil, provName) != 1 {
153144
return fail("SetFIPS(" + strconv.FormatBool(enabled) + ") not supported")
154145
}
155146
}
156-
if C.go_openssl_EVP_set_default_properties(nil, props) != 1 {
157-
return newOpenSSLError("EVP_set_default_properties")
147+
if C.go_openssl_EVP_default_properties_enable_fips(nil, mode) != 1 {
148+
return newOpenSSLError("openssl: EVP_default_properties_enable_fips")
158149
}
159150
return nil
160151
default:

openssl/openssl_test.go

+30-1
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,26 @@ import (
1212
"github.com/golang-fips/openssl-fips/openssl"
1313
)
1414

15-
func TestMain(m *testing.M) {
15+
// getVersion returns the OpenSSL version to use for testing.
16+
func getVersion() string {
1617
v := os.Getenv("GO_OPENSSL_VERSION_OVERRIDE")
18+
if v != "" {
19+
return v
20+
}
21+
// Try to find a supported version of OpenSSL on the system.
22+
// This is useful for local testing, where the user may not
23+
// have GO_OPENSSL_VERSION_OVERRIDE set.
24+
for _, v = range [...]string{"3", "1.1.1", "1.1", "11", "111", "1.0.2", "1.0.0", "10"} {
25+
if ok, _ := openssl.CheckVersion(v); ok {
26+
return v
27+
}
28+
}
29+
return ""
30+
}
31+
32+
func TestMain(m *testing.M) {
33+
v := getVersion()
34+
fmt.Printf("Using libcrypto.so.%s\n", v)
1735
err := openssl.Init(v)
1836
if err != nil {
1937
// An error here could mean that this Linux distro does not have a supported OpenSSL version
@@ -28,3 +46,14 @@ func TestMain(m *testing.M) {
2846
openssl.CheckLeaks()
2947
os.Exit(status)
3048
}
49+
50+
func TestCheckVersion(t *testing.T) {
51+
v := getVersion()
52+
exists, fips := openssl.CheckVersion(v)
53+
if !exists {
54+
t.Fatalf("OpenSSL version %q not found", v)
55+
}
56+
if want := openssl.FIPS(); want != fips {
57+
t.Fatalf("FIPS mismatch: want %v, got %v", want, fips)
58+
}
59+
}

openssl/shims.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ DEFINEFUNC_1_1(int, OPENSSL_init_crypto, (uint64_t ops, const GO_OPENSSL_INIT_SE
170170
DEFINEFUNC_LEGACY_1(int, FIPS_mode, (void), ()) \
171171
DEFINEFUNC_LEGACY_1(int, FIPS_mode_set, (int r), (r)) \
172172
DEFINEFUNC_3_0(int, EVP_default_properties_is_fips_enabled, (GO_OSSL_LIB_CTX_PTR libctx), (libctx)) \
173-
DEFINEFUNC_3_0(int, EVP_set_default_properties, (GO_OSSL_LIB_CTX_PTR libctx, const char *propq), (libctx, propq)) \
173+
DEFINEFUNC_3_0(int, EVP_default_properties_enable_fips, (GO_OSSL_LIB_CTX_PTR libctx, int enable), (libctx, enable)) \
174+
DEFINEFUNC_3_0(int, OSSL_PROVIDER_available, (GO_OSSL_LIB_CTX_PTR libctx, const char *name), (libctx, name)) \
174175
DEFINEFUNC_3_0(GO_OSSL_PROVIDER_PTR, OSSL_PROVIDER_load, (GO_OSSL_LIB_CTX_PTR libctx, const char *name), (libctx, name)) \
175176
DEFINEFUNC_3_0(GO_EVP_MD_PTR, EVP_MD_fetch, (GO_OSSL_LIB_CTX_PTR ctx, const char *algorithm, const char *properties), (ctx, algorithm, properties)) \
176177
DEFINEFUNC_3_0(void, EVP_MD_free, (GO_EVP_MD_PTR md), (md)) \

0 commit comments

Comments
 (0)