| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727 |
- .\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.42)
- .\"
- .\" Standard preamble:
- .\" ========================================================================
- .de Sp \" Vertical space (when we can't use .PP)
- .if t .sp .5v
- .if n .sp
- ..
- .de Vb \" Begin verbatim text
- .ft CW
- .nf
- .ne \\$1
- ..
- .de Ve \" End verbatim text
- .ft R
- .fi
- ..
- .\" Set up some character translations and predefined strings. \*(-- will
- .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
- .\" double quote, and \*(R" will give a right double quote. \*(C+ will
- .\" give a nicer C++. Capital omega is used to do unbreakable dashes and
- .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
- .\" nothing in troff, for use with C<>.
- .tr \(*W-
- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
- .ie n \{\
- . ds -- \(*W-
- . ds PI pi
- . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
- . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
- . ds L" ""
- . ds R" ""
- . ds C` ""
- . ds C' ""
- 'br\}
- .el\{\
- . ds -- \|\(em\|
- . ds PI \(*p
- . ds L" ``
- . ds R" ''
- . ds C`
- . ds C'
- 'br\}
- .\"
- .\" Escape single quotes in literal strings from groff's Unicode transform.
- .ie \n(.g .ds Aq \(aq
- .el .ds Aq '
- .\"
- .\" If the F register is >0, we'll generate index entries on stderr for
- .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
- .\" entries marked with X<> in POD. Of course, you'll have to process the
- .\" output yourself in some meaningful fashion.
- .\"
- .\" Avoid warning from groff about undefined register 'F'.
- .de IX
- ..
- .nr rF 0
- .if \n(.g .if rF .nr rF 1
- .if (\n(rF:(\n(.g==0)) \{\
- . if \nF \{\
- . de IX
- . tm Index:\\$1\t\\n%\t"\\$2"
- ..
- . if !\nF==2 \{\
- . nr % 0
- . nr F 2
- . \}
- . \}
- .\}
- .rr rF
- .\"
- .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
- .\" Fear. Run. Save yourself. No user-serviceable parts.
- . \" fudge factors for nroff and troff
- .if n \{\
- . ds #H 0
- . ds #V .8m
- . ds #F .3m
- . ds #[ \f1
- . ds #] \fP
- .\}
- .if t \{\
- . ds #H ((1u-(\\\\n(.fu%2u))*.13m)
- . ds #V .6m
- . ds #F 0
- . ds #[ \&
- . ds #] \&
- .\}
- . \" simple accents for nroff and troff
- .if n \{\
- . ds ' \&
- . ds ` \&
- . ds ^ \&
- . ds , \&
- . ds ~ ~
- . ds /
- .\}
- .if t \{\
- . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
- . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
- . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
- . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
- . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
- . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
- .\}
- . \" troff and (daisy-wheel) nroff accents
- .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
- .ds 8 \h'\*(#H'\(*b\h'-\*(#H'
- .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
- .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
- .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
- .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
- .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
- .ds ae a\h'-(\w'a'u*4/10)'e
- .ds Ae A\h'-(\w'A'u*4/10)'E
- . \" corrections for vroff
- .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
- .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
- . \" for low resolution devices (crt and lpr)
- .if \n(.H>23 .if \n(.V>19 \
- \{\
- . ds : e
- . ds 8 ss
- . ds o a
- . ds d- d\h'-1'\(ga
- . ds D- D\h'-1'\(hy
- . ds th \o'bp'
- . ds Th \o'LP'
- . ds ae ae
- . ds Ae AE
- .\}
- .rm #[ #] #H #V #F C
- .\" ========================================================================
- .\"
- .IX Title "OSSL-GUIDE-TLS-CLIENT-BLOCK 7ossl"
- .TH OSSL-GUIDE-TLS-CLIENT-BLOCK 7ossl "2024-09-03" "3.3.2" "OpenSSL"
- .\" For nroff, turn off justification. Always turn off hyphenation; it makes
- .\" way too many mistakes in technical documents.
- .if n .ad l
- .nh
- .SH "NAME"
- ossl\-guide\-tls\-client\-block
- \&\- OpenSSL Guide: Writing a simple blocking TLS client
- .SH "SIMPLE BLOCKING TLS CLIENT EXAMPLE"
- .IX Header "SIMPLE BLOCKING TLS CLIENT EXAMPLE"
- This page will present various source code samples demonstrating how to write
- a simple \s-1TLS\s0 client application which connects to a server, sends an \s-1HTTP/1.0\s0
- request to it, and reads back the response.
- .PP
- We use a blocking socket for the purposes of this example. This means that
- attempting to read data from a socket that has no data available on it to read
- will block (and the function will not return), until data becomes available.
- For example, this can happen if we have sent our request, but we are still
- waiting for the server's response. Similarly any attempts to write to a socket
- that is not able to write at the moment will block until writing is possible.
- .PP
- This blocking behaviour simplifies the implementation of a client because you do
- not have to worry about what happens if data is not yet available. The
- application will simply wait until it is available.
- .PP
- The complete source code for this example blocking \s-1TLS\s0 client is available in
- the \fBdemos/guide\fR directory of the OpenSSL source distribution in the file
- \&\fBtls\-client\-block.c\fR. It is also available online at
- <https://github.com/openssl/openssl/blob/master/demos/guide/tls\-client\-block.c>.
- .PP
- We assume that you already have OpenSSL installed on your system; that you
- already have some fundamental understanding of OpenSSL concepts and \s-1TLS\s0 (see
- \&\fBossl\-guide\-libraries\-introduction\fR\|(7) and \fBossl\-guide\-tls\-introduction\fR\|(7));
- and that you know how to write and build C code and link it against the
- libcrypto and libssl libraries that are provided by OpenSSL. It also assumes
- that you have a basic understanding of \s-1TCP/IP\s0 and sockets.
- .SS "Creating the \s-1SSL_CTX\s0 and \s-1SSL\s0 objects"
- .IX Subsection "Creating the SSL_CTX and SSL objects"
- The first step is to create an \fB\s-1SSL_CTX\s0\fR object for our client. We use the
- \&\fBSSL_CTX_new\fR\|(3) function for this purpose. We could alternatively use
- \&\fBSSL_CTX_new_ex\fR\|(3) if we want to associate the \fB\s-1SSL_CTX\s0\fR with a particular
- \&\fB\s-1OSSL_LIB_CTX\s0\fR (see \fBossl\-guide\-libraries\-introduction\fR\|(7) to learn about
- \&\fB\s-1OSSL_LIB_CTX\s0\fR). We pass as an argument the return value of the function
- \&\fBTLS_client_method\fR\|(3). You should use this method whenever you are writing a
- \&\s-1TLS\s0 client. This method will automatically use \s-1TLS\s0 version negotiation to select
- the highest version of the protocol that is mutually supported by both the
- client and the server.
- .PP
- .Vb 10
- \& /*
- \& * Create an SSL_CTX which we can use to create SSL objects from. We
- \& * want an SSL_CTX for creating clients so we use TLS_client_method()
- \& * here.
- \& */
- \& ctx = SSL_CTX_new(TLS_client_method());
- \& if (ctx == NULL) {
- \& printf("Failed to create the SSL_CTX\en");
- \& goto end;
- \& }
- .Ve
- .PP
- Since we are writing a client we must ensure that we verify the server's
- certificate. We do this by calling the \fBSSL_CTX_set_verify\fR\|(3) function and
- pass the \fB\s-1SSL_VERIFY_PEER\s0\fR value to it. The final argument to this function
- is a callback that you can optionally supply to override the default handling
- for certificate verification. Most applications do not need to do this so this
- can safely be set to \s-1NULL\s0 to get the default handling.
- .PP
- .Vb 6
- \& /*
- \& * Configure the client to abort the handshake if certificate
- \& * verification fails. Virtually all clients should do this unless you
- \& * really know what you are doing.
- \& */
- \& SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
- .Ve
- .PP
- In order for certificate verification to be successful you must have configured
- where the trusted certificate store to be used is located (see
- \&\fBossl\-guide\-tls\-introduction\fR\|(7)). In most cases you just want to use the
- default store so we call \fBSSL_CTX_set_default_verify_paths\fR\|(3).
- .PP
- .Vb 5
- \& /* Use the default trusted certificate store */
- \& if (!SSL_CTX_set_default_verify_paths(ctx)) {
- \& printf("Failed to set the default trusted certificate store\en");
- \& goto end;
- \& }
- .Ve
- .PP
- We would also like to restrict the \s-1TLS\s0 versions that we are willing to accept to
- TLSv1.2 or above. \s-1TLS\s0 protocol versions earlier than that are generally to be
- avoided where possible. We can do that using
- \&\fBSSL_CTX_set_min_proto_version\fR\|(3):
- .PP
- .Vb 8
- \& /*
- \& * TLSv1.1 or earlier are deprecated by IETF and are generally to be
- \& * avoided if possible. We require a minimum TLS version of TLSv1.2.
- \& */
- \& if (!SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION)) {
- \& printf("Failed to set the minimum TLS protocol version\en");
- \& goto end;
- \& }
- .Ve
- .PP
- That is all the setup that we need to do for the \fB\s-1SSL_CTX\s0\fR, so next we need to
- create an \fB\s-1SSL\s0\fR object to represent the \s-1TLS\s0 connection. In a real application
- we might expect to be creating more than one \s-1TLS\s0 connection over time. In that
- case we would expect to reuse the \fB\s-1SSL_CTX\s0\fR that we already created each time.
- There is no need to repeat those steps. In fact it is best not to since certain
- internal resources are cached in the \fB\s-1SSL_CTX\s0\fR. You will get better performance
- by reusing an existing \fB\s-1SSL_CTX\s0\fR instead of creating a new one each time.
- .PP
- Creating the \fB\s-1SSL\s0\fR object is a simple matter of calling the \fB\fBSSL_new\fB\|(3)\fR
- function and passing the \fB\s-1SSL_CTX\s0\fR we created as an argument.
- .PP
- .Vb 6
- \& /* Create an SSL object to represent the TLS connection */
- \& ssl = SSL_new(ctx);
- \& if (ssl == NULL) {
- \& printf("Failed to create the SSL object\en");
- \& goto end;
- \& }
- .Ve
- .SS "Creating the socket and \s-1BIO\s0"
- .IX Subsection "Creating the socket and BIO"
- \&\s-1TLS\s0 data is transmitted over an underlying transport layer. Normally a \s-1TCP\s0
- socket. It is the application's responsibility for ensuring that the socket is
- created and associated with an \s-1SSL\s0 object (via a \s-1BIO\s0).
- .PP
- Socket creation for use by a client is typically a 2 step process, i.e.
- constructing the socket; and connecting the socket.
- .PP
- How to construct a socket is platform specific \- but most platforms (including
- Windows) provide a \s-1POSIX\s0 compatible interface via the \fIsocket\fR function, e.g.
- to create an IPv4 \s-1TCP\s0 socket:
- .PP
- .Vb 1
- \& int sock;
- \&
- \& sock = socket(AF_INET, SOCK_STREAM, 0);
- \& if (sock == \-1)
- \& return NULL;
- .Ve
- .PP
- Once the socket is constructed it must be connected to the remote server. Again
- the details are platform specific but most platforms (including Windows)
- provide the \s-1POSIX\s0 compatible \fIconnect\fR function. For example:
- .PP
- .Vb 2
- \& struct sockaddr_in serveraddr;
- \& struct hostent *server;
- \&
- \& server = gethostbyname("www.openssl.org");
- \& if (server == NULL) {
- \& close(sock);
- \& return NULL;
- \& }
- \&
- \& memset(&serveraddr, 0, sizeof(serveraddr));
- \& serveraddr.sin_family = server\->h_addrtype;
- \& serveraddr.sin_port = htons(443);
- \& memcpy(&serveraddr.sin_addr.s_addr, server\->h_addr, server\->h_length);
- \&
- \& if (connect(sock, (struct sockaddr *)&serveraddr,
- \& sizeof(serveraddr)) == \-1) {
- \& close(sock);
- \& return NULL;
- \& }
- .Ve
- .PP
- OpenSSL provides portable helper functions to do these tasks which also
- integrate into the OpenSSL error system to log error data, e.g.
- .PP
- .Vb 3
- \& int sock = \-1;
- \& BIO_ADDRINFO *res;
- \& const BIO_ADDRINFO *ai = NULL;
- \&
- \& /*
- \& * Lookup IP address info for the server.
- \& */
- \& if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_STREAM, 0,
- \& &res))
- \& return NULL;
- \&
- \& /*
- \& * Loop through all the possible addresses for the server and find one
- \& * we can connect to.
- \& */
- \& for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) {
- \& /*
- \& * Create a TCP socket. We could equally use non\-OpenSSL calls such
- \& * as "socket" here for this and the subsequent connect and close
- \& * functions. But for portability reasons and also so that we get
- \& * errors on the OpenSSL stack in the event of a failure we use
- \& * OpenSSL\*(Aqs versions of these functions.
- \& */
- \& sock = BIO_socket(BIO_ADDRINFO_family(ai), SOCK_STREAM, 0, 0);
- \& if (sock == \-1)
- \& continue;
- \&
- \& /* Connect the socket to the server\*(Aqs address */
- \& if (!BIO_connect(sock, BIO_ADDRINFO_address(ai), BIO_SOCK_NODELAY)) {
- \& BIO_closesocket(sock);
- \& sock = \-1;
- \& continue;
- \& }
- \&
- \& /* We have a connected socket so break out of the loop */
- \& break;
- \& }
- \&
- \& /* Free the address information resources we allocated earlier */
- \& BIO_ADDRINFO_free(res);
- .Ve
- .PP
- See \fBBIO_lookup_ex\fR\|(3), \fBBIO_socket\fR\|(3), \fBBIO_connect\fR\|(3),
- \&\fBBIO_closesocket\fR\|(3), \fBBIO_ADDRINFO_next\fR\|(3), \fBBIO_ADDRINFO_address\fR\|(3) and
- \&\fBBIO_ADDRINFO_free\fR\|(3) for further information on the functions used here. In
- the above example code the \fBhostname\fR and \fBport\fR variables are strings, e.g.
- \&\*(L"www.example.com\*(R" and \*(L"443\*(R". Note also the use of the family variable, which
- can take the values of \s-1AF_INET\s0 or \s-1AF_INET6\s0 based on the command line \-6 option,
- to allow specific connections to an ipv4 or ipv6 enabled host.
- .PP
- Sockets created using the methods described above will automatically be blocking
- sockets \- which is exactly what we want for this example.
- .PP
- Once the socket has been created and connected we need to associate it with a
- \&\s-1BIO\s0 object:
- .PP
- .Vb 1
- \& BIO *bio;
- \&
- \& /* Create a BIO to wrap the socket */
- \& bio = BIO_new(BIO_s_socket());
- \& if (bio == NULL) {
- \& BIO_closesocket(sock);
- \& return NULL;
- \& }
- \&
- \& /*
- \& * Associate the newly created BIO with the underlying socket. By
- \& * passing BIO_CLOSE here the socket will be automatically closed when
- \& * the BIO is freed. Alternatively you can use BIO_NOCLOSE, in which
- \& * case you must close the socket explicitly when it is no longer
- \& * needed.
- \& */
- \& BIO_set_fd(bio, sock, BIO_CLOSE);
- .Ve
- .PP
- See \fBBIO_new\fR\|(3), \fBBIO_s_socket\fR\|(3) and \fBBIO_set_fd\fR\|(3) for further
- information on these functions.
- .PP
- Finally we associate the \fB\s-1SSL\s0\fR object we created earlier with the \fB\s-1BIO\s0\fR using
- the \fBSSL_set_bio\fR\|(3) function. Note that this passes ownership of the \fB\s-1BIO\s0\fR
- object to the \fB\s-1SSL\s0\fR object. Once ownership is passed the \s-1SSL\s0 object is
- responsible for its management and will free it automatically when the \fB\s-1SSL\s0\fR is
- freed. So, once \fBSSL_set_bio\fR\|(3) has been been called, you should not call
- \&\fBBIO_free\fR\|(3) on the \fB\s-1BIO\s0\fR.
- .PP
- .Vb 1
- \& SSL_set_bio(ssl, bio, bio);
- .Ve
- .SS "Setting the server's hostname"
- .IX Subsection "Setting the server's hostname"
- We have already connected our underlying socket to the server, but the client
- still needs to know the server's hostname. It uses this information for 2 key
- purposes and we need to set the hostname for each one.
- .PP
- Firstly, the server's hostname is included in the initial ClientHello message
- sent by the client. This is known as the Server Name Indication (\s-1SNI\s0). This is
- important because it is common for multiple hostnames to be fronted by a single
- server that handles requests for all of them. In other words a single server may
- have multiple hostnames associated with it and it is important to indicate which
- one we want to connect to. Without this information we may get a handshake
- failure, or we may get connected to the \*(L"default\*(R" server which may not be the
- one we were expecting.
- .PP
- To set the \s-1SNI\s0 hostname data we call the \fBSSL_set_tlsext_host_name\fR\|(3) function
- like this:
- .PP
- .Vb 8
- \& /*
- \& * Tell the server during the handshake which hostname we are attempting
- \& * to connect to in case the server supports multiple hosts.
- \& */
- \& if (!SSL_set_tlsext_host_name(ssl, hostname)) {
- \& printf("Failed to set the SNI hostname\en");
- \& goto end;
- \& }
- .Ve
- .PP
- Here the \f(CW\*(C`hostname\*(C'\fR argument is a string representing the hostname of the
- server, e.g. \*(L"www.example.com\*(R".
- .PP
- Secondly, we need to tell OpenSSL what hostname we expect to see in the
- certificate coming back from the server. This is almost always the same one that
- we asked for in the original request. This is important because, without this,
- we do not verify that the hostname in the certificate is what we expect it to be
- and any certificate is acceptable unless your application explicitly checks this
- itself. We do this via the \fBSSL_set1_host\fR\|(3) function:
- .PP
- .Vb 10
- \& /*
- \& * Ensure we check during certificate verification that the server has
- \& * supplied a certificate for the hostname that we were expecting.
- \& * Virtually all clients should do this unless you really know what you
- \& * are doing.
- \& */
- \& if (!SSL_set1_host(ssl, hostname)) {
- \& printf("Failed to set the certificate verification hostname");
- \& goto end;
- \& }
- .Ve
- .PP
- All of the above steps must happen before we attempt to perform the handshake
- otherwise they will have no effect.
- .SS "Performing the handshake"
- .IX Subsection "Performing the handshake"
- Before we can start sending or receiving application data over a \s-1TLS\s0 connection
- the \s-1TLS\s0 handshake must be performed. We can do this explicitly via the
- \&\fBSSL_connect\fR\|(3) function.
- .PP
- .Vb 12
- \& /* Do the handshake with the server */
- \& if (SSL_connect(ssl) < 1) {
- \& printf("Failed to connect to the server\en");
- \& /*
- \& * If the failure is due to a verification error we can get more
- \& * information about it from SSL_get_verify_result().
- \& */
- \& if (SSL_get_verify_result(ssl) != X509_V_OK)
- \& printf("Verify error: %s\en",
- \& X509_verify_cert_error_string(SSL_get_verify_result(ssl)));
- \& goto end;
- \& }
- .Ve
- .PP
- The \fBSSL_connect\fR\|(3) function can return 1, 0 or less than 0. Only a return
- value of 1 is considered a success. For a simple blocking client we only need
- to concern ourselves with whether the call was successful or not. Anything else
- indicates that we have failed to connect to the server.
- .PP
- A common cause of failures at this stage is due to a problem verifying the
- server's certificate. For example if the certificate has expired, or it is not
- signed by a \s-1CA\s0 in our trusted certificate store. We can use the
- \&\fBSSL_get_verify_result\fR\|(3) function to find out more information about the
- verification failure. A return value of \fBX509_V_OK\fR indicates that the
- verification was successful (so the connection error must be due to some other
- cause). Otherwise we use the \fBX509_verify_cert_error_string\fR\|(3) function to get
- a human readable error message.
- .SS "Sending and receiving data"
- .IX Subsection "Sending and receiving data"
- Once the handshake is complete we are able to send and receive application data.
- Exactly what data is sent and in what order is usually controlled by some
- application level protocol. In this example we are using \s-1HTTP 1.0\s0 which is a
- very simple request and response protocol. The client sends a request to the
- server. The server sends the response data and then immediately closes down the
- connection.
- .PP
- To send data to the server we use the \fBSSL_write_ex\fR\|(3) function and to receive
- data from the server we use the \fBSSL_read_ex\fR\|(3) function. In \s-1HTTP 1.0\s0 the
- client always writes data first. Our \s-1HTTP\s0 request will include the hostname that
- we are connecting to. For simplicity, we write the \s-1HTTP\s0 request in three
- chunks. First we write the start of the request. Secondly we write the hostname
- we are sending the request to. Finally we send the end of the request.
- .PP
- .Vb 3
- \& size_t written;
- \& const char *request_start = "GET / HTTP/1.0\er\enConnection: close\er\enHost: ";
- \& const char *request_end = "\er\en\er\en";
- \&
- \& /* Write an HTTP GET request to the peer */
- \& if (!SSL_write_ex(ssl, request_start, strlen(request_start), &written)) {
- \& printf("Failed to write start of HTTP request\en");
- \& goto end;
- \& }
- \& if (!SSL_write_ex(ssl, hostname, strlen(hostname), &written)) {
- \& printf("Failed to write hostname in HTTP request\en");
- \& goto end;
- \& }
- \& if (!SSL_write_ex(ssl, request_end, strlen(request_end), &written)) {
- \& printf("Failed to write end of HTTP request\en");
- \& goto end;
- \& }
- .Ve
- .PP
- The \fBSSL_write_ex\fR\|(3) function returns 0 if it fails and 1 if it is successful.
- If it is successful then we can proceed to waiting for a response from the
- server.
- .PP
- .Vb 2
- \& size_t readbytes;
- \& char buf[160];
- \&
- \& /*
- \& * Get up to sizeof(buf) bytes of the response. We keep reading until the
- \& * server closes the connection.
- \& */
- \& while (SSL_read_ex(ssl, buf, sizeof(buf), &readbytes)) {
- \& /*
- \& * OpenSSL does not guarantee that the returned data is a string or
- \& * that it is NUL terminated so we use fwrite() to write the exact
- \& * number of bytes that we read. The data could be non\-printable or
- \& * have NUL characters in the middle of it. For this simple example
- \& * we\*(Aqre going to print it to stdout anyway.
- \& */
- \& fwrite(buf, 1, readbytes, stdout);
- \& }
- \& /* In case the response didn\*(Aqt finish with a newline we add one now */
- \& printf("\en");
- .Ve
- .PP
- We use the \fBSSL_read_ex\fR\|(3) function to read the response. We don't know
- exactly how much data we are going to receive back so we enter a loop reading
- blocks of data from the server and printing each block that we receive to the
- screen. The loop ends as soon as \fBSSL_read_ex\fR\|(3) returns 0 \- meaning that it
- failed to read any data.
- .PP
- A failure to read data could mean that there has been some error, or it could
- simply mean that server has sent all the data that it wants to send and has
- indicated that it has finished by sending a \*(L"close_notify\*(R" alert. This alert is
- a \s-1TLS\s0 protocol level message indicating that the endpoint has finished sending
- all of its data and it will not send any more. Both of these conditions result
- in a 0 return value from \fBSSL_read_ex\fR\|(3) and we need to use the function
- \&\fBSSL_get_error\fR\|(3) to determine the cause of the 0 return value.
- .PP
- .Vb 10
- \& /*
- \& * Check whether we finished the while loop above normally or as the
- \& * result of an error. The 0 argument to SSL_get_error() is the return
- \& * code we received from the SSL_read_ex() call. It must be 0 in order
- \& * to get here. Normal completion is indicated by SSL_ERROR_ZERO_RETURN.
- \& */
- \& if (SSL_get_error(ssl, 0) != SSL_ERROR_ZERO_RETURN) {
- \& /*
- \& * Some error occurred other than a graceful close down by the
- \& * peer
- \& */
- \& printf ("Failed reading remaining data\en");
- \& goto end;
- \& }
- .Ve
- .PP
- If \fBSSL_get_error\fR\|(3) returns \fB\s-1SSL_ERROR_ZERO_RETURN\s0\fR then we know that the
- server has finished sending its data. Otherwise an error has occurred.
- .SS "Shutting down the connection"
- .IX Subsection "Shutting down the connection"
- Once we have finished reading data from the server then we are ready to close
- the connection down. We do this via the \fBSSL_shutdown\fR\|(3) function which has
- the effect of sending a \s-1TLS\s0 protocol level message (a \*(L"close_notify\*(R" alert) to
- the server saying that we have finished writing data:
- .PP
- .Vb 10
- \& /*
- \& * The peer already shutdown gracefully (we know this because of the
- \& * SSL_ERROR_ZERO_RETURN above). We should do the same back.
- \& */
- \& ret = SSL_shutdown(ssl);
- \& if (ret < 1) {
- \& /*
- \& * ret < 0 indicates an error. ret == 0 would be unexpected here
- \& * because that means "we\*(Aqve sent a close_notify and we\*(Aqre waiting
- \& * for one back". But we already know we got one from the peer
- \& * because of the SSL_ERROR_ZERO_RETURN above.
- \& */
- \& printf("Error shutting down\en");
- \& goto end;
- \& }
- .Ve
- .PP
- The \fBSSL_shutdown\fR\|(3) function will either return 1, 0, or less than 0. A
- return value of 1 is a success, and a return value less than 0 is an error. More
- precisely a return value of 1 means that we have sent a \*(L"close_notify\*(R" alert to
- the server, and that we have also received one back. A return value of 0 means
- that we have sent a \*(L"close_notify\*(R" alert to the server, but we have not yet
- received one back. Usually in this scenario you would call \fBSSL_shutdown\fR\|(3)
- again which (with a blocking socket) would block until the \*(L"close_notify\*(R" is
- received. However in this case we already know that the server has sent us a
- \&\*(L"close_notify\*(R" because of the \s-1SSL_ERROR_ZERO_RETURN\s0 that we received from the
- call to \fBSSL_read_ex\fR\|(3). So this scenario should never happen in practice. We
- just treat it as an error in this example.
- .SS "Final clean up"
- .IX Subsection "Final clean up"
- Before the application exits we have to clean up some memory that we allocated.
- If we are exiting due to an error we might also want to display further
- information about that error if it is available to the user:
- .PP
- .Vb 10
- \& /* Success! */
- \& res = EXIT_SUCCESS;
- \& end:
- \& /*
- \& * If something bad happened then we will dump the contents of the
- \& * OpenSSL error stack to stderr. There might be some useful diagnostic
- \& * information there.
- \& */
- \& if (res == EXIT_FAILURE)
- \& ERR_print_errors_fp(stderr);
- \&
- \& /*
- \& * Free the resources we allocated. We do not free the BIO object here
- \& * because ownership of it was immediately transferred to the SSL object
- \& * via SSL_set_bio(). The BIO will be freed when we free the SSL object.
- \& */
- \& SSL_free(ssl);
- \& SSL_CTX_free(ctx);
- \& return res;
- .Ve
- .PP
- To display errors we make use of the \fBERR_print_errors_fp\fR\|(3) function which
- simply dumps out the contents of any errors on the OpenSSL error stack to the
- specified location (in this case \fIstderr\fR).
- .PP
- We need to free up the \fB\s-1SSL\s0\fR object that we created for the connection via the
- \&\fBSSL_free\fR\|(3) function. Also, since we are not going to be creating any more
- \&\s-1TLS\s0 connections we must also free up the \fB\s-1SSL_CTX\s0\fR via a call to
- \&\fBSSL_CTX_free\fR\|(3).
- .SH "TROUBLESHOOTING"
- .IX Header "TROUBLESHOOTING"
- There are a number of things that might go wrong when running the demo
- application. This section describes some common things you might encounter.
- .SS "Failure to connect the underlying socket"
- .IX Subsection "Failure to connect the underlying socket"
- This could occur for numerous reasons. For example if there is a problem in the
- network route between the client and the server; or a firewall is blocking the
- communication; or the server is not in \s-1DNS.\s0 Check the network configuration.
- .SS "Verification failure of the server certificate"
- .IX Subsection "Verification failure of the server certificate"
- A verification failure of the server certificate would result in a failure when
- running the \fBSSL_connect\fR\|(3) function. \fBERR_print_errors_fp\fR\|(3) would display
- an error which would look something like this:
- .PP
- .Vb 2
- \& Verify error: unable to get local issuer certificate
- \& 40E74AF1F47F0000:error:0A000086:SSL routines:tls_post_process_server_certificate:certificate verify failed:ssl/statem/statem_clnt.c:2069:
- .Ve
- .PP
- A server certificate verification failure could be caused for a number of
- reasons. For example
- .IP "Failure to correctly setup the trusted certificate store" 4
- .IX Item "Failure to correctly setup the trusted certificate store"
- See the page \fBossl\-guide\-tls\-introduction\fR\|(7) and check that your trusted
- certificate store is correctly configured
- .IP "Unrecognised \s-1CA\s0" 4
- .IX Item "Unrecognised CA"
- If the \s-1CA\s0 used by the server's certificate is not in the trusted certificate
- store for the client then this will cause a verification failure during
- connection. Often this can occur if the server is using a self-signed
- certificate (i.e. a test certificate that has not been signed by a \s-1CA\s0 at all).
- .IP "Missing intermediate CAs" 4
- .IX Item "Missing intermediate CAs"
- This is a server misconfiguration where the client has the relevant root \s-1CA\s0 in
- its trust store, but the server has not supplied all of the intermediate \s-1CA\s0
- certificates between that root \s-1CA\s0 and the server's own certificate. Therefore
- a trust chain cannot be established.
- .IP "Mismatched hostname" 4
- .IX Item "Mismatched hostname"
- If for some reason the hostname of the server that the client is expecting does
- not match the hostname in the certificate then this will cause verification to
- fail.
- .IP "Expired certificate" 4
- .IX Item "Expired certificate"
- The date that the server's certificate is valid to has passed.
- .PP
- The \*(L"unable to get local issuer certificate\*(R" we saw in the example above means
- that we have been unable to find the issuer of the server's certificate (or one
- of its intermediate \s-1CA\s0 certificates) in our trusted certificate store (e.g.
- because the trusted certificate store is misconfigured, or there are missing
- intermediate CAs, or the issuer is simply unrecognised).
- .SH "FURTHER READING"
- .IX Header "FURTHER READING"
- See \fBossl\-guide\-tls\-client\-non\-block\fR\|(7) to read a tutorial on how to modify
- the client developed on this page to support a nonblocking socket.
- .PP
- See \fBossl\-guide\-quic\-client\-block\fR\|(7) to read a tutorial on how to modify the
- client developed on this page to support \s-1QUIC\s0 instead of \s-1TLS.\s0
- .SH "SEE ALSO"
- .IX Header "SEE ALSO"
- \&\fBossl\-guide\-introduction\fR\|(7), \fBossl\-guide\-libraries\-introduction\fR\|(7),
- \&\fBossl\-guide\-libssl\-introduction\fR\|(7), \fBossl\-guide\-tls\-introduction\fR\|(7),
- \&\fBossl\-guide\-tls\-client\-non\-block\fR\|(7), \fBossl\-guide\-quic\-client\-block\fR\|(7)
- .SH "COPYRIGHT"
- .IX Header "COPYRIGHT"
- Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
- .PP
- Licensed under the Apache License 2.0 (the \*(L"License\*(R"). You may not use
- this file except in compliance with the License. You can obtain a copy
- in the file \s-1LICENSE\s0 in the source distribution or at
- <https://www.openssl.org/source/license.html>.
|