openssl / openssl

TLS/SSL and crypto library
https://www.openssl.org
Apache License 2.0
25.87k stars 10.14k forks source link

Error 1408F10B - SSL routines:ssl3_get_record:wrong version number:ssl/record/ssl3_record.c #19969

Closed delasy closed 1 year ago

delasy commented 1 year ago

Hello, I'm using OpenSSL on daily basis and things were working good until I tried to download file from AWS S3 behind AWS CloudFront with Amazon issued certificate. I feel like I'm missing some piece of code that will make it working, not sure what it is though.

What I tried:

Ways I tried to solve it:

On all platforms I tried, it shows error:

140704456943808:error:1408F10B:SSL routines:ssl3_get_record:wrong version number:ssl/record/ssl3_record.c:

Also I tried:

In example below it successfully connects to api.thelang.io but fails for cdn.thelang.io. api.thelang.io is hosted on AWS EC2 with Let's Encrypt issued certificate, but SSL_connect doesn't like cdn.thelang.io for some reason.

Code example I'm trying to make working:

#include <stdbool.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define THE_EOL "\n"

bool lib_openssl_init = false;

int create_fd (char *hostname, char *port) {
  struct addrinfo *addr = NULL;
  struct addrinfo hints;
  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_INET;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_protocol = IPPROTO_TCP;
  if (getaddrinfo(hostname, port, &hints, &addr) != 0) {
    fprintf(stderr, "Error: failed to resolve hostname address" THE_EOL);
    exit(EXIT_FAILURE);
  }
  int fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
  if (fd == -1) {
    fprintf(stderr, "Error: failed to create socket" THE_EOL);
    exit(EXIT_FAILURE);
  }
  if (connect(fd, addr->ai_addr, addr->ai_addrlen) == -1) {
    fprintf(stderr, "Error: failed to connect to socket" THE_EOL);
    exit(EXIT_FAILURE);
  }
  freeaddrinfo(addr);
  return fd;
}

SSL *create_ssl (int fd) {
  if (!lib_openssl_init) {
    SSL_library_init();
    lib_openssl_init = true;
  }

  SSL_CTX *ctx = SSL_CTX_new(TLS_method());
  if (ctx == NULL) {
    ERR_print_errors_fp(stderr);
    fprintf(stderr, "Error: failed to create SSL context" THE_EOL);
    exit(EXIT_FAILURE);
  }

  SSL *ssl = SSL_new(ctx);
  SSL_set_fd(ssl, fd);

  if (SSL_connect(ssl) != 1) {
    ERR_print_errors_fp(stderr);
    fprintf(stderr, "Error: failed to connect to socket with SSL" THE_EOL);
    exit(EXIT_FAILURE);
  }

  return ssl;
}

void ssl_write (SSL *ssl, char *data) {
  if (SSL_write(ssl, data, strlen(data)) == -1) {
    ERR_print_errors_fp(stderr);
    fprintf(stderr, "Error: failed to write SSL data" THE_EOL);
    exit(EXIT_FAILURE);
  }
}

void request (char *data, char *hostname, char *port) {
  int fd = create_fd(hostname, port);
  SSL *ssl = create_ssl(fd);
  ssl_write(ssl, request);
  SSL_shutdown(ssl);

  printf("Success" THE_EOL);
}

int main () {
  request("GET / HTTP/1.1\r\n\r\n", "api.thelang.io", "443");
  request("GET / HTTP/1.1\r\n\r\n", "cdn.thelang.io", "443");
}
delasy commented 1 year ago

Also I tried pretty much every TLS client method with SSL_CTX_new, makes no difference

t-j-h commented 1 year ago
  ssl_write(ssl, request);
should be
  ssl_write(ssl, data);

TLS_client_method() would be the right method to use. And cdn.thelang.io requires TLSv1.2 or above to connect.

works: openssl s_client -state -debug -connect cdn.thelang.io:443 openssl s_client -tls1_2 -state -debug -connect cdn.thelang.io:443

fails: openssl s_client -tls1 -state -debug -connect cdn.thelang.io:443

delasy commented 1 year ago

Thanks for fixing typo in code, I reformatted a lot of code to provide example, might have missed something. As I wrote in comment above I tried TLS_client_method, TLSv1_client_method, TLSv1_1_client_method, TLSv1_2_client_method, even DTLSv1_2_client_method. No difference in results between all these methods.

I just did test with clean openssl-v3.0.7 on Ubuntu 22.04 amd64

With this code (fixed request problem mentioned above and used TLSv1_2_client_method):

Code ```c #include #include #include #include #define THE_EOL "\n" bool lib_openssl_init = false; int create_fd (char *hostname, char *port) { struct addrinfo *addr = NULL; struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; if (getaddrinfo(hostname, port, &hints, &addr) != 0) { fprintf(stderr, "Error: failed to resolve hostname address" THE_EOL); exit(EXIT_FAILURE); } int fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); if (fd == -1) { fprintf(stderr, "Error: failed to create socket" THE_EOL); exit(EXIT_FAILURE); } if (connect(fd, addr->ai_addr, addr->ai_addrlen) == -1) { fprintf(stderr, "Error: failed to connect to socket" THE_EOL); exit(EXIT_FAILURE); } freeaddrinfo(addr); return fd; } SSL *create_ssl (int fd) { if (!lib_openssl_init) { SSL_library_init(); lib_openssl_init = true; } SSL_CTX *ctx = SSL_CTX_new(TLSv1_2_client_method()); if (ctx == NULL) { ERR_print_errors_fp(stderr); fprintf(stderr, "Error: failed to create SSL context" THE_EOL); exit(EXIT_FAILURE); } SSL *ssl = SSL_new(ctx); SSL_set_fd(ssl, fd); if (SSL_connect(ssl) != 1) { ERR_print_errors_fp(stderr); fprintf(stderr, "Error: failed to connect to socket with SSL" THE_EOL); exit(EXIT_FAILURE); } return ssl; } void ssl_write (SSL *ssl, char *data) { if (SSL_write(ssl, data, strlen(data)) == -1) { ERR_print_errors_fp(stderr); fprintf(stderr, "Error: failed to write SSL data" THE_EOL); exit(EXIT_FAILURE); } } void request (char *data, char *hostname, char *port) { int fd = create_fd(hostname, port); SSL *ssl = create_ssl(fd); ssl_write(ssl, data); SSL_shutdown(ssl); printf("Success" THE_EOL); } int main () { request("GET / HTTP/1.1\r\n\r\n", "api.thelang.io", "443"); request("GET / HTTP/1.1\r\n\r\n", "cdn.thelang.io", "443"); } ```

With this setup

sudo apt-get update
sudo apt-get install -y build-essential cmake clang
git clone --depth=1 --branch openssl-3.0.7 https://github.com/openssl/openssl.git
pushd openssl
CC=clang perl Configure no-tests --prefix="$PWD/build"
make
make install_sw
popd
vim main.c
clang main.c "$PWD/openssl/build/lib64/libssl.so.3" "$PWD/openssl/build/lib64/libcrypto.so.3" -w -o a.out -I"$PWD/openssl/build/include"
./a.out
perl configdata.pm --dump ```txt Command line (with current working directory = .): /usr/bin/perl ./Configure --prefix=/home/ubuntu/openssl/build no-tests Perl information: /usr/bin/perl 5.34.0 for x86_64-linux-gnu-thread-multi Enabled features: afalgeng aria asm async autoalginit autoerrinit autoload-config bf blake2 bulk cached-fetch camellia capieng cast chacha cmac cmp cms comp ct deprecated des dgram dh dsa dso dtls dynamic-engine ec ec2m ecdh ecdsa engine err filenames gost idea legacy loadereng makedepend md4 mdc2 module multiblock nextprotoneg ocb ocsp padlockeng pic pinshared poly1305 posix-io psk rc2 rc4 rdrand rfc3779 rmd160 scrypt secure-memory seed shared siphash siv sm2 sm3 sm4 sock srp srtp sse2 ssl ssl-trace static-engine stdio threads tls ts ui-console whirlpool tls1 tls1-method tls1_1 tls1_1-method tls1_2 tls1_2-method tls1_3 dtls1 dtls1-method dtls1_2 dtls1_2-method Disabled features: acvp-tests [cascade] OPENSSL_NO_ACVP_TESTS asan [default] OPENSSL_NO_ASAN buildtest-c++ [default] crypto-mdebug [default] OPENSSL_NO_CRYPTO_MDEBUG devcryptoeng [default] OPENSSL_NO_DEVCRYPTOENG ec_nistp_64_gcc_128 [default] OPENSSL_NO_EC_NISTP_64_GCC_128 egd [default] OPENSSL_NO_EGD external-tests [default] OPENSSL_NO_EXTERNAL_TESTS fips [default] fips-securitychecks [cascade] OPENSSL_NO_FIPS_SECURITYCHECKS fuzz-afl [default] OPENSSL_NO_FUZZ_AFL fuzz-libfuzzer [default] OPENSSL_NO_FUZZ_LIBFUZZER ktls [default] OPENSSL_NO_KTLS md2 [default] OPENSSL_NO_MD2 (skip crypto/md2) msan [default] OPENSSL_NO_MSAN rc5 [default] OPENSSL_NO_RC5 (skip crypto/rc5) sctp [default] OPENSSL_NO_SCTP tests [option] OPENSSL_NO_TESTS trace [default] OPENSSL_NO_TRACE ubsan [default] OPENSSL_NO_UBSAN unit-test [default] OPENSSL_NO_UNIT_TEST uplink [no uplink_arch] OPENSSL_NO_UPLINK weak-ssl-ciphers [default] OPENSSL_NO_WEAK_SSL_CIPHERS zlib [default] zlib-dynamic [default] ssl3 [default] OPENSSL_NO_SSL3 ssl3-method [default] OPENSSL_NO_SSL3_METHOD Config target attributes: AR => "ar", ARFLAGS => "qc", CC => "clang", CFLAGS => "-Wall -O3", CXX => "clang++", CXXFLAGS => "-Wall -O3", HASHBANGPERL => "/usr/bin/env perl", RANLIB => "ranlib", RC => "windres", asm_arch => "x86_64", bn_ops => "SIXTY_FOUR_BIT_LONG", build_file => "Makefile", build_scheme => [ "unified", "unix" ], cflags => "-pthread -m64", cppflags => "", cxxflags => "-std=c++11 -pthread -m64", defines => [ "OPENSSL_BUILDING_OPENSSL" ], disable => [ ], dso_ldflags => "-Wl,-z,defs", dso_scheme => "dlfcn", enable => [ "afalgeng" ], ex_libs => "-ldl -pthread", includes => [ ], lflags => "", lib_cflags => "", lib_cppflags => "-DOPENSSL_USE_NODELETE -DL_ENDIAN", lib_defines => [ ], module_cflags => "-fPIC", module_cxxflags => undef, module_ldflags => "-Wl,-znodelete -shared -Wl,-Bsymbolic", multilib => "64", perl_platform => "Unix", perlasm_scheme => "elf", shared_cflag => "-fPIC", shared_defflag => "-Wl,--version-script=", shared_defines => [ ], shared_ldflag => "-Wl,-znodelete -shared -Wl,-Bsymbolic", shared_rcflag => "", shared_sonameflag => "-Wl,-soname=", shared_target => "linux-shared", thread_defines => [ ], thread_scheme => "pthreads", unistd => "", Recorded environment: AR = ARFLAGS = AS = ASFLAGS = BUILDFILE = CC = clang CFLAGS = CPP = CPPDEFINES = CPPFLAGS = CPPINCLUDES = CROSS_COMPILE = CXX = CXXFLAGS = HASHBANGPERL = LD = LDFLAGS = LDLIBS = MT = MTFLAGS = OPENSSL_LOCAL_CONFIG_DIR = PERL = RANLIB = RC = RCFLAGS = RM = WINDRES = __CNF_CFLAGS = __CNF_CPPDEFINES = __CNF_CPPFLAGS = __CNF_CPPINCLUDES = __CNF_CXXFLAGS = __CNF_LDFLAGS = __CNF_LDLIBS = Makevars: AR = ar ARFLAGS = qc CC = clang CFLAGS = -Wall -O3 CPPDEFINES = CPPFLAGS = CPPINCLUDES = CXX = clang++ CXXFLAGS = -Wall -O3 HASHBANGPERL = /usr/bin/env perl LDFLAGS = LDLIBS = PERL = /usr/bin/perl RANLIB = ranlib RC = windres RCFLAGS = NOTE: These variables only represent the configuration view. The build file template may have processed these variables further, please have a look at the build file for more exact data: Makefile build file: Makefile build file templates: Configurations/common0.tmpl Configurations/unix-Makefile.tmpl ```

And it still doesn't work

delasy commented 1 year ago

@t-j-h , on my machine (macOS 13.1 OpenSSL 1.1.1s) openssl s_client -state -debug -connect cdn.thelang.io:443 doesnt work

Output ```txt CONNECTED(00000006) write to 0x6000034a9030 [0x7feec5f214c0] (287 bytes => 287 (0x11F)) 0000 - 16 03 01 01 1a 01 00 01-16 03 03 0e b5 64 68 3c .............dh< 0010 - 58 68 8a 71 f9 93 63 f3-58 45 10 73 55 73 24 4c Xh.q..c.XE.sUs$L 0020 - 89 1f 37 f0 1e f2 0a 11-36 c3 0f 20 0a c0 45 fb ..7.....6.. ..E. 0030 - 6e 68 18 9c 26 02 46 ba-ce eb 8f 42 dd 19 49 22 nh..&.F....B..I" 0040 - 59 53 70 25 9b 20 33 4c-01 f3 9e 20 00 62 13 02 YSp%. 3L... .b.. 0050 - 13 03 13 01 c0 30 c0 2c-c0 28 c0 24 c0 14 c0 0a .....0.,.(.$.... 0060 - 00 9f 00 6b 00 39 cc a9-cc a8 cc aa ff 85 00 c4 ...k.9.......... 0070 - 00 88 00 81 00 9d 00 3d-00 35 00 c0 00 84 c0 2f .......=.5...../ 0080 - c0 2b c0 27 c0 23 c0 13-c0 09 00 9e 00 67 00 33 .+.'.#.......g.3 0090 - 00 be 00 45 00 9c 00 3c-00 2f 00 ba 00 41 c0 11 ...E...<./...A.. 00a0 - c0 07 00 05 00 04 c0 12-c0 08 00 16 00 0a 00 ff ................ 00b0 - 01 00 00 6b 00 2b 00 09-08 03 04 03 03 03 02 03 ...k.+.......... 00c0 - 01 00 33 00 26 00 24 00-1d 00 20 3f 99 af 86 62 ..3.&.$... ?...b 00d0 - 47 69 c4 36 96 40 41 14-44 21 a5 22 95 13 d9 8a Gi.6.@A.D!.".... 00e0 - 21 30 0a 93 49 4d e9 94-b7 25 76 00 0b 00 02 01 !0..IM...%v..... 00f0 - 00 00 0a 00 0a 00 08 00-1d 00 17 00 18 00 19 00 ................ 0100 - 23 00 00 00 0d 00 18 00-16 08 06 06 01 06 03 08 #............... 0110 - 05 05 01 05 03 08 04 04-01 04 03 02 01 02 03 ............... read from 0x6000034a9030 [0x600001da1df0] (5 bytes => 5 (0x5)) 0000 - 15 00 00 00 02 ..... write to 0x6000034a9030 [0x600001db96f0] (7 bytes => 7 (0x7)) 0000 - 15 03 01 00 02 02 46 ......F 140704456943808:error:1404B42E:SSL routines:ST_CONNECT:tlsv1 alert protocol version:/AppleInternal/Library/BuildRoots/aaefcfd1-5c95-11ed-8734-2e32217d8374/Library/Caches/com.apple.xbs/Sources/libressl/libressl-3.3/ssl/tls13_lib.c:151: --- no peer certificate available --- No client certificate CA names sent --- SSL handshake has read 5 bytes and written 294 bytes --- New, (NONE), Cipher is (NONE) Secure Renegotiation IS NOT supported Compression: NONE Expansion: NONE No ALPN negotiated SSL-Session: Protocol : TLSv1.3 Cipher : 0000 Session-ID: Session-ID-ctx: Master-Key: Start Time: 1672183716 Timeout : 7200 (sec) Verify return code: 0 (ok) --- ```

But command above works on Ubuntu 22.04 amd64 with OpenSSL 3.0.2, but the code still doesn't work it throws error:

400781E4A17F0000:error:0A00010B:SSL routines:ssl3_get_record:wrong version number:../ssl/record/ssl3_record.c:354:
t-j-h commented 1 year ago

You are missing a call to SSL_set_tlsext_host_name to let the underlying service know what the right backing host to connect to.

It is a subtle problem in that the backend server doesn't know what incoming service you are requesting but it has no way of actually telling you what you have done wrong at this level.

i.e. the server behind cdn.thelang.io isn't responding with a TLS handshake as it doesn't know you actually want to connect to cdn.thelang.io behind that front end server which is why it fails.

Your replacement code with it working is:

#include <stdbool.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define THE_EOL "\n"

bool lib_openssl_init = false;

int create_fd (char *hostname, char *port) {
  struct addrinfo *addr = NULL;
  struct addrinfo hints;
  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_INET;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_protocol = IPPROTO_TCP;
  if (getaddrinfo(hostname, port, &hints, &addr) != 0) {
    fprintf(stderr, "Error: failed to resolve hostname address" THE_EOL);
    exit(EXIT_FAILURE);
  }

  int fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
  if (fd == -1) {
    fprintf(stderr, "Error: failed to create socket" THE_EOL);
    exit(EXIT_FAILURE);
  }

  if (connect(fd, addr->ai_addr, addr->ai_addrlen) == -1) {
    fprintf(stderr, "Error: failed to connect to socket" THE_EOL);
    exit(EXIT_FAILURE);
  }
  freeaddrinfo(addr);
  return fd;
}

SSL *create_ssl (int fd,char *hostname) {
  if (!lib_openssl_init) {
    SSL_library_init();
    lib_openssl_init = true;
    OPENSSL_add_all_algorithms_conf();
  }

  ERR_clear_error();

  SSL_CTX *ctx = SSL_CTX_new(TLS_client_method());
  if (ctx == NULL) {
    ERR_print_errors_fp(stderr);
    fprintf(stderr, "Error: failed to create SSL context" THE_EOL);
    exit(EXIT_FAILURE);
  }

  SSL *ssl = SSL_new(ctx);
  SSL_set_fd(ssl, fd);

  SSL_set_tlsext_host_name(ssl,hostname);

  if (SSL_connect(ssl) != 1) {
    ERR_print_errors_fp(stderr);
    fprintf(stderr, "Error: failed to connect to socket with SSL" THE_EOL);
    exit(EXIT_FAILURE);
  }

  return ssl;
}

void ssl_write (SSL *ssl, char *data) {
  if (SSL_write(ssl, data, strlen(data)) == -1) {
    ERR_print_errors_fp(stderr);
    fprintf(stderr, "Error: failed to write SSL data" THE_EOL);
    exit(EXIT_FAILURE);
  }
}

void request (char *data, char *hostname, char *port) {
  int fd = create_fd(hostname, port);
  SSL *ssl = create_ssl(fd,hostname);
  ssl_write(ssl, data);
  SSL_shutdown(ssl);

  printf("Success" THE_EOL);
}

int main () {
  request("GET / HTTP/1.1\r\n\r\n", "api.thelang.io", "443");
  request("GET / HTTP/1.1\r\n\r\n", "cdn.thelang.io", "443");
}
delasy commented 1 year ago

@t-j-h Wow... that's definitely not something I would be able to solve myself, glad you are here to help. Thank you very much!