diff options
author | william <william@25tandclement.com> | 2014-03-25 19:58:02 -0700 |
---|---|---|
committer | william <william@25tandclement.com> | 2014-03-25 19:58:02 -0700 |
commit | e28605d441ecd909c814ee1893b7dde07c7684d2 (patch) | |
tree | 500b56c3b35d179814bd103cf705c02f80cbb0ee /src/openssl.c | |
parent | 9975e282f13d63378e54081056c6597508c2ddb4 (diff) | |
download | luaossl-e28605d441ecd909c814ee1893b7dde07c7684d2.tar.gz luaossl-e28605d441ecd909c814ee1893b7dde07c7684d2.tar.bz2 luaossl-e28605d441ecd909c814ee1893b7dde07c7684d2.zip |
add DER input and output formats, plus 'autodetect' PEM-or-DER, for almost everything (except pk__tostring and pk_toPEM, FIXME)
Diffstat (limited to 'src/openssl.c')
-rw-r--r-- | src/openssl.c | 229 |
1 files changed, 167 insertions, 62 deletions
diff --git a/src/openssl.c b/src/openssl.c index 1554053..84d8079 100644 --- a/src/openssl.c +++ b/src/openssl.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <strings.h> /* strcasecmp(3) */ | 31 | #include <strings.h> /* strcasecmp(3) */ |
32 | #include <math.h> /* INFINITY fabs(3) floor(3) frexp(3) fmod(3) round(3) isfinite(3) */ | 32 | #include <math.h> /* INFINITY fabs(3) floor(3) frexp(3) fmod(3) round(3) isfinite(3) */ |
33 | #include <time.h> /* struct tm time_t strptime(3) */ | 33 | #include <time.h> /* struct tm time_t strptime(3) */ |
34 | #include <ctype.h> /* tolower(3) */ | ||
34 | 35 | ||
35 | #include <sys/types.h> | 36 | #include <sys/types.h> |
36 | #include <sys/stat.h> /* struct stat stat(2) */ | 37 | #include <sys/stat.h> /* struct stat stat(2) */ |
@@ -96,6 +97,8 @@ | |||
96 | #define stricmp(a, b) strcasecmp((a), (b)) | 97 | #define stricmp(a, b) strcasecmp((a), (b)) |
97 | #define strieq(a, b) (!stricmp((a), (b))) | 98 | #define strieq(a, b) (!stricmp((a), (b))) |
98 | 99 | ||
100 | #define xtolower(c) tolower((unsigned char)(c)) | ||
101 | |||
99 | #define SAY_(file, func, line, fmt, ...) \ | 102 | #define SAY_(file, func, line, fmt, ...) \ |
100 | fprintf(stderr, "%s:%d: " fmt "%s", __func__, __LINE__, __VA_ARGS__) | 103 | fprintf(stderr, "%s:%d: " fmt "%s", __func__, __LINE__, __VA_ARGS__) |
101 | 104 | ||
@@ -218,7 +221,7 @@ static void addclass(lua_State *L, const char *name, const luaL_Reg *methods, co | |||
218 | } /* addclass() */ | 221 | } /* addclass() */ |
219 | 222 | ||
220 | 223 | ||
221 | static int checkoption(struct lua_State *L, int index, const char *def, const char *opts[]) { | 224 | static int checkoption(struct lua_State *L, int index, const char *def, const char *const opts[]) { |
222 | const char *opt = (def)? luaL_optstring(L, index, def) : luaL_checkstring(L, index); | 225 | const char *opt = (def)? luaL_optstring(L, index, def) : luaL_checkstring(L, index); |
223 | int i; | 226 | int i; |
224 | 227 | ||
@@ -231,6 +234,34 @@ static int checkoption(struct lua_State *L, int index, const char *def, const ch | |||
231 | } /* checkoption() */ | 234 | } /* checkoption() */ |
232 | 235 | ||
233 | 236 | ||
237 | #define X509_ANY 0x01 | ||
238 | #define X509_PEM 0x02 | ||
239 | #define X509_DER 0x04 | ||
240 | #define X509_ALL (X509_PEM|X509_DER) | ||
241 | |||
242 | static int optencoding(lua_State *L, int index, const char *def, int allow) { | ||
243 | static const char *const opts[] = { "*", "pem", "der", NULL }; | ||
244 | int type = 0; | ||
245 | |||
246 | switch (checkoption(L, index, def, opts)) { | ||
247 | case 0: | ||
248 | type = X509_ANY; | ||
249 | break; | ||
250 | case 1: | ||
251 | type = X509_PEM; | ||
252 | break; | ||
253 | case 2: | ||
254 | type = X509_DER; | ||
255 | break; | ||
256 | } | ||
257 | |||
258 | if (!(type & allow)) | ||
259 | luaL_argerror(L, index, lua_pushfstring(L, "invalid option %s", luaL_checkstring(L, index))); | ||
260 | |||
261 | return type; | ||
262 | } /* optencoding() */ | ||
263 | |||
264 | |||
234 | static _Bool getfield(lua_State *L, int index, const char *k) { | 265 | static _Bool getfield(lua_State *L, int index, const char *k) { |
235 | lua_getfield(L, index, k); | 266 | lua_getfield(L, index, k); |
236 | 267 | ||
@@ -719,11 +750,12 @@ static BIO *getbio(lua_State *L) { | |||
719 | static int pk_new(lua_State *L) { | 750 | static int pk_new(lua_State *L) { |
720 | EVP_PKEY **ud; | 751 | EVP_PKEY **ud; |
721 | 752 | ||
722 | lua_settop(L, 1); | 753 | /* #1 table or key; if key, #2 format and #3 type */ |
754 | lua_settop(L, 3); | ||
723 | 755 | ||
724 | ud = prepsimple(L, PUBKEY_CLASS); | 756 | ud = prepsimple(L, PUBKEY_CLASS); |
725 | 757 | ||
726 | if (lua_istable(L, 1)) { | 758 | if (lua_istable(L, 1) || lua_isnil(L, 1)) { |
727 | int type = EVP_PKEY_RSA; | 759 | int type = EVP_PKEY_RSA; |
728 | unsigned bits = 1024; | 760 | unsigned bits = 1024; |
729 | unsigned exp = 65537; | 761 | unsigned exp = 65537; |
@@ -860,37 +892,69 @@ creat: | |||
860 | default: | 892 | default: |
861 | return luaL_error(L, "%d: unknown EVP base type (%d)", EVP_PKEY_type(type), type); | 893 | return luaL_error(L, "%d: unknown EVP base type (%d)", EVP_PKEY_type(type), type); |
862 | } /* switch() */ | 894 | } /* switch() */ |
863 | } else { | 895 | } else if (lua_isstring(L, 1)) { |
864 | const char *pem; | 896 | int type = optencoding(L, 2, "*", X509_ANY|X509_PEM|X509_DER); |
897 | int ispub = -1; | ||
898 | const char *opt, *data; | ||
865 | size_t len; | 899 | size_t len; |
866 | BIO *bio; | 900 | BIO *bio; |
867 | int ok; | 901 | int ok = 0; |
902 | |||
903 | /* check if specified publickey or privatekey */ | ||
904 | if ((opt = luaL_optstring(L, 3, NULL))) { | ||
905 | if (xtolower(opt[0]) == 'p' && xtolower(opt[1]) == 'u') { | ||
906 | ispub = 1; | ||
907 | } else if (xtolower(opt[0]) == 'p' && xtolower(opt[1]) == 'r') { | ||
908 | ispub = 0; | ||
909 | } else { | ||
910 | return luaL_argerror(L, 3, lua_pushfstring(L, "invalid option %s", opt)); | ||
911 | } | ||
912 | } | ||
868 | 913 | ||
869 | if (!(*ud = EVP_PKEY_new())) | 914 | data = luaL_checklstring(L, 1, &len); |
915 | |||
916 | if (!(bio = BIO_new_mem_buf((void *)data, len))) | ||
870 | return throwssl(L, "pubkey.new"); | 917 | return throwssl(L, "pubkey.new"); |
871 | 918 | ||
872 | switch (lua_type(L, 1)) { | 919 | if (type == X509_PEM || type == X509_ANY) { |
873 | case LUA_TSTRING: | 920 | if (ispub == 1 || ispub == -1) { |
874 | pem = luaL_checklstring(L, 1, &len); | 921 | ok = !!(*ud = PEM_read_bio_PUBKEY(bio, NULL, 0, "")); |
875 | 922 | ||
876 | if (!(bio = BIO_new_mem_buf((void *)pem, len))) | 923 | if (ok || (type == X509_PEM && ispub == 1)) |
877 | return throwssl(L, "pubkey.new"); | 924 | goto done; |
925 | } | ||
878 | 926 | ||
879 | if (strstr(pem, "PUBLIC KEY")) { | 927 | if (ispub == 0 || ispub == -1) { |
880 | ok = !!PEM_read_bio_PUBKEY(bio, ud, 0, 0); | 928 | ok = !!(*ud = PEM_read_bio_PrivateKey(bio, NULL, 0, "")); |
881 | } else { | 929 | |
882 | ok = !!PEM_read_bio_PrivateKey(bio, ud, 0, 0); | 930 | if (ok || (type == X509_PEM && ispub == 0)) |
931 | goto done; | ||
883 | } | 932 | } |
933 | } | ||
884 | 934 | ||
885 | BIO_free(bio); | 935 | if (type == X509_DER || type == X509_ANY) { |
936 | if (ispub == 1 || ispub == -1) { | ||
937 | ok = !!(*ud = d2i_PUBKEY_bio(bio, NULL)); | ||
886 | 938 | ||
887 | if (!ok) | 939 | if (ok || (type == X509_DER && ispub == 1)) |
888 | return throwssl(L, "pubkey.new"); | 940 | goto done; |
941 | } | ||
889 | 942 | ||
890 | break; | 943 | if (ispub == 0 || ispub == -1) { |
891 | default: | 944 | ok = !!(*ud = d2i_PrivateKey_bio(bio, NULL)); |
892 | return luaL_error(L, "%s: unknown key initializer", lua_typename(L, lua_type(L, 1))); | 945 | |
893 | } /* switch() */ | 946 | if (ok || (type == X509_DER && ispub == 0)) |
947 | goto done; | ||
948 | } | ||
949 | } | ||
950 | |||
951 | done: | ||
952 | BIO_free(bio); | ||
953 | |||
954 | if (!ok) | ||
955 | return throwssl(L, "pubkey.new"); | ||
956 | } else { | ||
957 | return luaL_error(L, "%s: unknown key initializer", lua_typename(L, lua_type(L, 1))); | ||
894 | } | 958 | } |
895 | 959 | ||
896 | return 1; | 960 | return 1; |
@@ -914,19 +978,24 @@ static int pk_type(lua_State *L) { | |||
914 | 978 | ||
915 | static int pk_setPublicKey(lua_State *L) { | 979 | static int pk_setPublicKey(lua_State *L) { |
916 | EVP_PKEY **key = luaL_checkudata(L, 1, PUBKEY_CLASS); | 980 | EVP_PKEY **key = luaL_checkudata(L, 1, PUBKEY_CLASS); |
917 | const char *pem; | 981 | const char *data; |
918 | size_t len; | 982 | size_t len; |
919 | BIO *bio; | 983 | BIO *bio; |
920 | int ok; | 984 | int type, ok = 0; |
921 | |||
922 | lua_settop(L, 2); | ||
923 | 985 | ||
924 | pem = luaL_checklstring(L, 2, &len); | 986 | data = luaL_checklstring(L, 2, &len); |
987 | type = optencoding(L, 3, "*", X509_ANY|X509_PEM|X509_DER); | ||
925 | 988 | ||
926 | if (!(bio = BIO_new_mem_buf((void *)pem, len))) | 989 | if (!(bio = BIO_new_mem_buf((void *)data, len))) |
927 | return throwssl(L, "pubkey.new"); | 990 | return throwssl(L, "pubkey.new"); |
928 | 991 | ||
929 | ok = !!PEM_read_bio_PUBKEY(bio, key, 0, 0); | 992 | if (type == X509_ANY || type == X509_PEM) { |
993 | ok = !!PEM_read_bio_PUBKEY(bio, key, 0, ""); | ||
994 | } | ||
995 | |||
996 | if (!ok && (type == X509_ANY || type == X509_DER)) { | ||
997 | ok = !!d2i_PUBKEY_bio(bio, key); | ||
998 | } | ||
930 | 999 | ||
931 | BIO_free(bio); | 1000 | BIO_free(bio); |
932 | 1001 | ||
@@ -941,19 +1010,24 @@ static int pk_setPublicKey(lua_State *L) { | |||
941 | 1010 | ||
942 | static int pk_setPrivateKey(lua_State *L) { | 1011 | static int pk_setPrivateKey(lua_State *L) { |
943 | EVP_PKEY **key = luaL_checkudata(L, 1, PUBKEY_CLASS); | 1012 | EVP_PKEY **key = luaL_checkudata(L, 1, PUBKEY_CLASS); |
944 | const char *pem; | 1013 | const char *data; |
945 | size_t len; | 1014 | size_t len; |
946 | BIO *bio; | 1015 | BIO *bio; |
947 | int ok; | 1016 | int type, ok = 0; |
948 | 1017 | ||
949 | lua_settop(L, 2); | 1018 | data = luaL_checklstring(L, 2, &len); |
1019 | type = optencoding(L, 3, "*", X509_ANY|X509_PEM|X509_DER); | ||
950 | 1020 | ||
951 | pem = luaL_checklstring(L, 2, &len); | 1021 | if (!(bio = BIO_new_mem_buf((void *)data, len))) |
952 | |||
953 | if (!(bio = BIO_new_mem_buf((void *)pem, len))) | ||
954 | return throwssl(L, "pubkey.new"); | 1022 | return throwssl(L, "pubkey.new"); |
955 | 1023 | ||
956 | ok = !!PEM_read_bio_PrivateKey(bio, key, 0, 0); | 1024 | if (type == X509_ANY || type == X509_PEM) { |
1025 | ok = !!PEM_read_bio_PrivateKey(bio, key, 0, ""); | ||
1026 | } | ||
1027 | |||
1028 | if (!ok && (type == X509_ANY || type == X509_DER)) { | ||
1029 | ok = !!d2i_PrivateKey_bio(bio, key); | ||
1030 | } | ||
957 | 1031 | ||
958 | BIO_free(bio); | 1032 | BIO_free(bio); |
959 | 1033 | ||
@@ -1027,7 +1101,7 @@ static int pk_toPEM(lua_State *L) { | |||
1027 | bio = getbio(L); | 1101 | bio = getbio(L); |
1028 | 1102 | ||
1029 | for (i = 2; i <= top; i++) { | 1103 | for (i = 2; i <= top; i++) { |
1030 | static const char *opts[] = { | 1104 | static const char *const opts[] = { |
1031 | "public", "PublicKey", | 1105 | "public", "PublicKey", |
1032 | "private", "PrivateKey", | 1106 | "private", "PrivateKey", |
1033 | // "params", "Parameters", | 1107 | // "params", "Parameters", |
@@ -1624,22 +1698,29 @@ int luaopen__openssl_x509_altname(lua_State *L) { | |||
1624 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | 1698 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
1625 | 1699 | ||
1626 | static int xc_new(lua_State *L) { | 1700 | static int xc_new(lua_State *L) { |
1627 | const char *pem; | 1701 | const char *data; |
1628 | size_t len; | 1702 | size_t len; |
1629 | X509 **ud; | 1703 | X509 **ud; |
1630 | 1704 | ||
1631 | lua_settop(L, 1); | 1705 | lua_settop(L, 2); |
1632 | 1706 | ||
1633 | ud = prepsimple(L, X509_CERT_CLASS); | 1707 | ud = prepsimple(L, X509_CERT_CLASS); |
1634 | 1708 | ||
1635 | if ((pem = luaL_optlstring(L, 1, NULL, &len))) { | 1709 | if ((data = luaL_optlstring(L, 1, NULL, &len))) { |
1710 | int type = optencoding(L, 2, "*", X509_ANY|X509_PEM|X509_DER); | ||
1636 | BIO *tmp; | 1711 | BIO *tmp; |
1637 | int ok; | 1712 | int ok = 0; |
1638 | 1713 | ||
1639 | if (!(tmp = BIO_new_mem_buf((char *)pem, len))) | 1714 | if (!(tmp = BIO_new_mem_buf((char *)data, len))) |
1640 | return throwssl(L, "x509.cert.new"); | 1715 | return throwssl(L, "x509.cert.new"); |
1641 | 1716 | ||
1642 | ok = !!PEM_read_bio_X509(tmp, ud, 0, ""); /* no password */ | 1717 | if (type == X509_PEM || type == X509_ANY) { |
1718 | ok = !!(*ud = PEM_read_bio_X509(tmp, NULL, 0, "")); /* no password */ | ||
1719 | } | ||
1720 | |||
1721 | if (!ok && (type == X509_DER || type == X509_ANY)) { | ||
1722 | ok = !!(*ud = d2i_X509_bio(tmp, NULL)); | ||
1723 | } | ||
1643 | 1724 | ||
1644 | BIO_free(tmp); | 1725 | BIO_free(tmp); |
1645 | 1726 | ||
@@ -2399,17 +2480,25 @@ static int xc_sign(lua_State *L) { | |||
2399 | 2480 | ||
2400 | static int xc__tostring(lua_State *L) { | 2481 | static int xc__tostring(lua_State *L) { |
2401 | X509 *crt = checksimple(L, 1, X509_CERT_CLASS); | 2482 | X509 *crt = checksimple(L, 1, X509_CERT_CLASS); |
2402 | int fmt = checkoption(L, 2, "pem", (const char *[]){ "pem", NULL }); | 2483 | int type = optencoding(L, 2, "pem", X509_PEM|X509_DER); |
2403 | BIO *bio = getbio(L); | 2484 | BIO *bio = getbio(L); |
2404 | char *pem; | 2485 | char *data; |
2405 | long len; | 2486 | long len; |
2406 | 2487 | ||
2407 | if (!PEM_write_bio_X509(bio, crt)) | 2488 | switch (type) { |
2408 | return throwssl(L, "x509.cert:__tostring"); | 2489 | case X509_PEM: |
2490 | if (!PEM_write_bio_X509(bio, crt)) | ||
2491 | return throwssl(L, "x509.cert:__tostring"); | ||
2492 | break; | ||
2493 | case X509_DER: | ||
2494 | if (!i2d_X509_bio(bio, crt)) | ||
2495 | return throwssl(L, "x509.cert:__tostring"); | ||
2496 | break; | ||
2497 | } /* switch() */ | ||
2409 | 2498 | ||
2410 | len = BIO_get_mem_data(bio, &pem); | 2499 | len = BIO_get_mem_data(bio, &data); |
2411 | 2500 | ||
2412 | lua_pushlstring(L, pem, len); | 2501 | lua_pushlstring(L, data, len); |
2413 | 2502 | ||
2414 | return 1; | 2503 | return 1; |
2415 | } /* xc__tostring() */ | 2504 | } /* xc__tostring() */ |
@@ -2486,26 +2575,34 @@ int luaopen__openssl_x509_cert(lua_State *L) { | |||
2486 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | 2575 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
2487 | 2576 | ||
2488 | static int xr_new(lua_State *L) { | 2577 | static int xr_new(lua_State *L) { |
2489 | const char *pem; | 2578 | const char *data; |
2490 | size_t len; | 2579 | size_t len; |
2491 | X509_REQ **ud; | 2580 | X509_REQ **ud; |
2492 | X509 *crt; | 2581 | X509 *crt; |
2493 | 2582 | ||
2494 | lua_settop(L, 1); | 2583 | lua_settop(L, 1); |
2584 | lua_settop(L, 2); | ||
2495 | 2585 | ||
2496 | ud = prepsimple(L, X509_CSR_CLASS); | 2586 | ud = prepsimple(L, X509_CSR_CLASS); |
2497 | 2587 | ||
2498 | if ((crt = testsimple(L, 1, X509_CERT_CLASS))) { | 2588 | if ((crt = testsimple(L, 1, X509_CERT_CLASS))) { |
2499 | if (!(*ud = X509_to_X509_REQ(crt, 0, 0))) | 2589 | if (!(*ud = X509_to_X509_REQ(crt, 0, 0))) |
2500 | return throwssl(L, "x509.csr.new"); | 2590 | return throwssl(L, "x509.csr.new"); |
2501 | } else if ((pem = luaL_optlstring(L, 1, NULL, &len))) { | 2591 | } else if ((data = luaL_optlstring(L, 1, NULL, &len))) { |
2592 | int type = optencoding(L, 2, "*", X509_ANY|X509_PEM|X509_DER); | ||
2502 | BIO *tmp; | 2593 | BIO *tmp; |
2503 | int ok; | 2594 | int ok = 0; |
2504 | 2595 | ||
2505 | if (!(tmp = BIO_new_mem_buf((char *)pem, len))) | 2596 | if (!(tmp = BIO_new_mem_buf((char *)data, len))) |
2506 | return throwssl(L, "x509.csr.new"); | 2597 | return throwssl(L, "x509.csr.new"); |
2507 | 2598 | ||
2508 | ok = !!PEM_read_bio_X509_REQ(tmp, ud, 0, ""); /* no password */ | 2599 | if (type == X509_PEM || type == X509_ANY) { |
2600 | ok = !!(*ud = PEM_read_bio_X509_REQ(tmp, NULL, 0, "")); /* no password */ | ||
2601 | } | ||
2602 | |||
2603 | if (!ok && (type == X509_DER || type == X509_ANY)) { | ||
2604 | ok = !!(*ud = d2i_X509_REQ_bio(tmp, NULL)); | ||
2605 | } | ||
2509 | 2606 | ||
2510 | BIO_free(tmp); | 2607 | BIO_free(tmp); |
2511 | 2608 | ||
@@ -2612,17 +2709,25 @@ static int xr_sign(lua_State *L) { | |||
2612 | 2709 | ||
2613 | static int xr__tostring(lua_State *L) { | 2710 | static int xr__tostring(lua_State *L) { |
2614 | X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS); | 2711 | X509_REQ *csr = checksimple(L, 1, X509_CSR_CLASS); |
2615 | int fmt = checkoption(L, 2, "pem", (const char *[]){ "pem", NULL }); | 2712 | int type = optencoding(L, 2, "pem", X509_PEM|X509_DER); |
2616 | BIO *bio = getbio(L); | 2713 | BIO *bio = getbio(L); |
2617 | char *pem; | 2714 | char *data; |
2618 | long len; | 2715 | long len; |
2619 | 2716 | ||
2620 | if (!PEM_write_bio_X509_REQ(bio, csr)) | 2717 | switch (type) { |
2621 | return throwssl(L, "x509.csr:__tostring"); | 2718 | case X509_PEM: |
2719 | if (!PEM_write_bio_X509_REQ(bio, csr)) | ||
2720 | return throwssl(L, "x509.csr:__tostring"); | ||
2721 | break; | ||
2722 | case X509_DER: | ||
2723 | if (!i2d_X509_REQ_bio(bio, csr)) | ||
2724 | return throwssl(L, "x509.csr:__tostring"); | ||
2725 | break; | ||
2726 | } /* switch() */ | ||
2622 | 2727 | ||
2623 | len = BIO_get_mem_data(bio, &pem); | 2728 | len = BIO_get_mem_data(bio, &data); |
2624 | 2729 | ||
2625 | lua_pushlstring(L, pem, len); | 2730 | lua_pushlstring(L, data, len); |
2626 | 2731 | ||
2627 | return 1; | 2732 | return 1; |
2628 | } /* xr__tostring() */ | 2733 | } /* xr__tostring() */ |
@@ -3063,7 +3168,7 @@ int luaopen__openssl_x509_store_context(lua_State *L) { | |||
3063 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | 3168 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
3064 | 3169 | ||
3065 | static int sx_new(lua_State *L) { | 3170 | static int sx_new(lua_State *L) { |
3066 | static const char *opts[] = { | 3171 | static const char *const opts[] = { |
3067 | "SSLv2", "SSLv3", "SSLv23", "SSL", "TLSv1", "TLS", NULL | 3172 | "SSLv2", "SSLv3", "SSLv23", "SSL", "TLSv1", "TLS", NULL |
3068 | }; | 3173 | }; |
3069 | /* later versions of SSL declare a const qualifier on the return type */ | 3174 | /* later versions of SSL declare a const qualifier on the return type */ |