Path: blob/main/contrib/bearssl/samples/server_basic.c
39507 views
/*1* Copyright (c) 2016 Thomas Pornin <[email protected]>2*3* Permission is hereby granted, free of charge, to any person obtaining4* a copy of this software and associated documentation files (the5* "Software"), to deal in the Software without restriction, including6* without limitation the rights to use, copy, modify, merge, publish,7* distribute, sublicense, and/or sell copies of the Software, and to8* permit persons to whom the Software is furnished to do so, subject to9* the following conditions:10*11* The above copyright notice and this permission notice shall be12* included in all copies or substantial portions of the Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,15* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF16* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND17* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS18* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN19* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN20* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE21* SOFTWARE.22*/2324#include <stdio.h>25#include <stdlib.h>26#include <string.h>27#include <stdint.h>28#include <errno.h>29#include <signal.h>3031#include <sys/types.h>32#include <sys/socket.h>33#include <netdb.h>34#include <netinet/in.h>35#include <arpa/inet.h>36#include <unistd.h>3738#include "bearssl.h"3940/*41* This sample code can use three possible certificate chains:42* -- A full-RSA chain (server key is RSA, certificates are signed with RSA)43* -- A full-EC chain (server key is EC, certificates are signed with ECDSA)44* -- A mixed chain (server key is EC, certificates are signed with RSA)45*46* The macros below define which chain is selected. This impacts the list47* of supported cipher suites.48*49* Other macros, which can be defined (with a non-zero value):50*51* SERVER_PROFILE_MIN_FS52* Select a "minimal" profile with forward security (ECDHE cipher53* suite).54*55* SERVER_PROFILE_MIN_NOFS56* Select a "minimal" profile without forward security (RSA or ECDH57* cipher suite, but not ECDHE).58*59* SERVER_CHACHA2060* If SERVER_PROFILE_MIN_FS is selected, then this macro selects61* a cipher suite with ChaCha20+Poly1305; otherwise, AES/GCM is62* used. This macro has no effect otherwise, since there is no63* non-forward secure cipher suite that uses ChaCha20+Poly1305.64*/6566#if !(SERVER_RSA || SERVER_EC || SERVER_MIXED)67#define SERVER_RSA 168#define SERVER_EC 069#define SERVER_MIXED 070#endif7172#if SERVER_RSA73#include "chain-rsa.h"74#include "key-rsa.h"75#define SKEY RSA76#elif SERVER_EC77#include "chain-ec.h"78#include "key-ec.h"79#define SKEY EC80#elif SERVER_MIXED81#include "chain-ec+rsa.h"82#include "key-ec.h"83#define SKEY EC84#else85#error Must use one of RSA, EC or MIXED chains.86#endif8788/*89* Create a server socket bound to the specified host and port. If 'host'90* is NULL, this will bind "generically" (all addresses).91*92* Returned value is the server socket descriptor, or -1 on error.93*/94static int95host_bind(const char *host, const char *port)96{97struct addrinfo hints, *si, *p;98int fd;99int err;100101memset(&hints, 0, sizeof hints);102hints.ai_family = PF_UNSPEC;103hints.ai_socktype = SOCK_STREAM;104err = getaddrinfo(host, port, &hints, &si);105if (err != 0) {106fprintf(stderr, "ERROR: getaddrinfo(): %s\n",107gai_strerror(err));108return -1;109}110fd = -1;111for (p = si; p != NULL; p = p->ai_next) {112struct sockaddr *sa;113struct sockaddr_in sa4;114struct sockaddr_in6 sa6;115size_t sa_len;116void *addr;117char tmp[INET6_ADDRSTRLEN + 50];118int opt;119120sa = (struct sockaddr *)p->ai_addr;121if (sa->sa_family == AF_INET) {122sa4 = *(struct sockaddr_in *)sa;123sa = (struct sockaddr *)&sa4;124sa_len = sizeof sa4;125addr = &sa4.sin_addr;126if (host == NULL) {127sa4.sin_addr.s_addr = INADDR_ANY;128}129} else if (sa->sa_family == AF_INET6) {130sa6 = *(struct sockaddr_in6 *)sa;131sa = (struct sockaddr *)&sa6;132sa_len = sizeof sa6;133addr = &sa6.sin6_addr;134if (host == NULL) {135sa6.sin6_addr = in6addr_any;136}137} else {138addr = NULL;139sa_len = p->ai_addrlen;140}141if (addr != NULL) {142inet_ntop(p->ai_family, addr, tmp, sizeof tmp);143} else {144sprintf(tmp, "<unknown family: %d>",145(int)sa->sa_family);146}147fprintf(stderr, "binding to: %s\n", tmp);148fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);149if (fd < 0) {150perror("socket()");151continue;152}153opt = 1;154setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt);155opt = 0;156setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof opt);157if (bind(fd, sa, sa_len) < 0) {158perror("bind()");159close(fd);160continue;161}162break;163}164if (p == NULL) {165freeaddrinfo(si);166fprintf(stderr, "ERROR: failed to bind\n");167return -1;168}169freeaddrinfo(si);170if (listen(fd, 5) < 0) {171perror("listen()");172close(fd);173return -1;174}175fprintf(stderr, "bound.\n");176return fd;177}178179/*180* Accept a single client on the provided server socket. This is blocking.181* On error, this returns -1.182*/183static int184accept_client(int server_fd)185{186int fd;187struct sockaddr sa;188socklen_t sa_len;189char tmp[INET6_ADDRSTRLEN + 50];190const char *name;191192sa_len = sizeof sa;193fd = accept(server_fd, &sa, &sa_len);194if (fd < 0) {195perror("accept()");196return -1;197}198name = NULL;199switch (sa.sa_family) {200case AF_INET:201name = inet_ntop(AF_INET,202&((struct sockaddr_in *)&sa)->sin_addr,203tmp, sizeof tmp);204break;205case AF_INET6:206name = inet_ntop(AF_INET6,207&((struct sockaddr_in6 *)&sa)->sin6_addr,208tmp, sizeof tmp);209break;210}211if (name == NULL) {212sprintf(tmp, "<unknown: %lu>", (unsigned long)sa.sa_family);213name = tmp;214}215fprintf(stderr, "accepting connection from: %s\n", name);216return fd;217}218219/*220* Low-level data read callback for the simplified SSL I/O API.221*/222static int223sock_read(void *ctx, unsigned char *buf, size_t len)224{225for (;;) {226ssize_t rlen;227228rlen = read(*(int *)ctx, buf, len);229if (rlen <= 0) {230if (rlen < 0 && errno == EINTR) {231continue;232}233return -1;234}235return (int)rlen;236}237}238239/*240* Low-level data write callback for the simplified SSL I/O API.241*/242static int243sock_write(void *ctx, const unsigned char *buf, size_t len)244{245for (;;) {246ssize_t wlen;247248wlen = write(*(int *)ctx, buf, len);249if (wlen <= 0) {250if (wlen < 0 && errno == EINTR) {251continue;252}253return -1;254}255return (int)wlen;256}257}258259/*260* Sample HTTP response to send.261*/262static const char *HTTP_RES =263"HTTP/1.0 200 OK\r\n"264"Content-Length: 46\r\n"265"Connection: close\r\n"266"Content-Type: text/html; charset=iso-8859-1\r\n"267"\r\n"268"<html>\r\n"269"<body>\r\n"270"<p>Test!</p>\r\n"271"</body>\r\n"272"</html>\r\n";273274/*275* Main program: this is a simple program that expects 1 argument: a276* port number. This will start a simple network server on that port,277* that expects incoming SSL clients. It handles only one client at a278* time (handling several would require threads, sub-processes, or279* multiplexing with select()/poll(), all of which being possible).280*281* For each client, the server will wait for two successive newline282* characters (ignoring CR characters, so CR+LF is accepted), then283* produce a sample static HTTP response. This is very crude, but284* sufficient for explanatory purposes.285*/286int287main(int argc, char *argv[])288{289const char *port;290int fd;291292if (argc != 2) {293return EXIT_FAILURE;294}295port = argv[1];296297/*298* Ignore SIGPIPE to avoid crashing in case of abrupt socket close.299*/300signal(SIGPIPE, SIG_IGN);301302/*303* Open the server socket.304*/305fd = host_bind(NULL, port);306if (fd < 0) {307return EXIT_FAILURE;308}309310/*311* Process each client, one at a time.312*/313for (;;) {314int cfd;315br_ssl_server_context sc;316unsigned char iobuf[BR_SSL_BUFSIZE_BIDI];317br_sslio_context ioc;318int lcwn, err;319320cfd = accept_client(fd);321if (cfd < 0) {322return EXIT_FAILURE;323}324325/*326* Initialise the context with the cipher suites and327* algorithms. This depends on the server key type328* (and, for EC keys, the signature algorithm used by329* the CA to sign the server's certificate).330*331* Depending on the defined macros, we may select one of332* the "minimal" profiles. Key exchange algorithm depends333* on the key type:334* RSA key: RSA or ECDHE_RSA335* EC key, cert signed with ECDSA: ECDH_ECDSA or ECDHE_ECDSA336* EC key, cert signed with RSA: ECDH_RSA or ECDHE_ECDSA337*/338#if SERVER_RSA339#if SERVER_PROFILE_MIN_FS340#if SERVER_CHACHA20341br_ssl_server_init_mine2c(&sc, CHAIN, CHAIN_LEN, &SKEY);342#else343br_ssl_server_init_mine2g(&sc, CHAIN, CHAIN_LEN, &SKEY);344#endif345#elif SERVER_PROFILE_MIN_NOFS346br_ssl_server_init_minr2g(&sc, CHAIN, CHAIN_LEN, &SKEY);347#else348br_ssl_server_init_full_rsa(&sc, CHAIN, CHAIN_LEN, &SKEY);349#endif350#elif SERVER_EC351#if SERVER_PROFILE_MIN_FS352#if SERVER_CHACHA20353br_ssl_server_init_minf2c(&sc, CHAIN, CHAIN_LEN, &SKEY);354#else355br_ssl_server_init_minf2g(&sc, CHAIN, CHAIN_LEN, &SKEY);356#endif357#elif SERVER_PROFILE_MIN_NOFS358br_ssl_server_init_minv2g(&sc, CHAIN, CHAIN_LEN, &SKEY);359#else360br_ssl_server_init_full_ec(&sc, CHAIN, CHAIN_LEN,361BR_KEYTYPE_EC, &SKEY);362#endif363#else /* SERVER_MIXED */364#if SERVER_PROFILE_MIN_FS365#if SERVER_CHACHA20366br_ssl_server_init_minf2c(&sc, CHAIN, CHAIN_LEN, &SKEY);367#else368br_ssl_server_init_minf2g(&sc, CHAIN, CHAIN_LEN, &SKEY);369#endif370#elif SERVER_PROFILE_MIN_NOFS371br_ssl_server_init_minu2g(&sc, CHAIN, CHAIN_LEN, &SKEY);372#else373br_ssl_server_init_full_ec(&sc, CHAIN, CHAIN_LEN,374BR_KEYTYPE_RSA, &SKEY);375#endif376#endif377/*378* Set the I/O buffer to the provided array. We379* allocated a buffer large enough for full-duplex380* behaviour with all allowed sizes of SSL records,381* hence we set the last argument to 1 (which means382* "split the buffer into separate input and output383* areas").384*/385br_ssl_engine_set_buffer(&sc.eng, iobuf, sizeof iobuf, 1);386387/*388* Reset the server context, for a new handshake.389*/390br_ssl_server_reset(&sc);391392/*393* Initialise the simplified I/O wrapper context.394*/395br_sslio_init(&ioc, &sc.eng, sock_read, &cfd, sock_write, &cfd);396397/*398* Read bytes until two successive LF (or CR+LF) are received.399*/400lcwn = 0;401for (;;) {402unsigned char x;403404if (br_sslio_read(&ioc, &x, 1) < 0) {405goto client_drop;406}407if (x == 0x0D) {408continue;409}410if (x == 0x0A) {411if (lcwn) {412break;413}414lcwn = 1;415} else {416lcwn = 0;417}418}419420/*421* Write a response and close the connection.422*/423br_sslio_write_all(&ioc, HTTP_RES, strlen(HTTP_RES));424br_sslio_close(&ioc);425426client_drop:427err = br_ssl_engine_last_error(&sc.eng);428if (err == 0) {429fprintf(stderr, "SSL closed (correctly).\n");430} else {431fprintf(stderr, "SSL error: %d\n", err);432}433close(cfd);434}435}436437438