1 /*
2  *  Copyright 2022-2024 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  *  Licensed under the Apache License 2.0 (the "License").  You may not use
5  *  this file except in compliance with the License.  You can obtain a copy
6  *  in the file LICENSE in the source distribution or at
7  *  https://www.openssl.org/source/license.html
8  */
9 
10 #include <stdio.h>
11 #include <string.h>
12 #include <signal.h>
13 #include <openssl/ssl.h>
14 #include <openssl/err.h>
15 #if !defined(OPENSSL_SYS_WINDOWS)
16 #include <unistd.h>
17 #include <sys/socket.h>
18 #include <arpa/inet.h>
19 #include <netinet/in.h>
20 
21 #define SOCKET int
22 #define closesocket(s) close(s)
23 
24 #else
25 #include <winsock.h>
26 #include <ws2tcpip.h>
27 #endif
28 
29 static const int server_port = 4433;
30 
31 typedef unsigned char   flag;
32 #define true            1
33 #define false           0
34 
35 /*
36  * This flag won't be useful until both accept/read (TCP & SSL) methods
37  * can be called with a timeout. TBD.
38  */
39 static volatile flag server_running = true;
40 
create_socket(flag isServer)41 static SOCKET create_socket(flag isServer)
42 {
43     SOCKET s;
44     int optval = 1;
45     struct sockaddr_in addr;
46 
47     s = socket(AF_INET, SOCK_STREAM, 0);
48     if (s < 0) {
49         perror("Unable to create socket");
50         exit(EXIT_FAILURE);
51     }
52 
53     if (isServer) {
54         addr.sin_family = AF_INET;
55         addr.sin_port = htons(server_port);
56         addr.sin_addr.s_addr = INADDR_ANY;
57 
58         /* Reuse the address; good for quick restarts */
59         if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *)&optval, sizeof(optval))
60                 < 0) {
61             perror("setsockopt(SO_REUSEADDR) failed");
62             exit(EXIT_FAILURE);
63         }
64 
65         if (bind(s, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
66             perror("Unable to bind");
67             exit(EXIT_FAILURE);
68         }
69 
70         if (listen(s, 1) < 0) {
71             perror("Unable to listen");
72             exit(EXIT_FAILURE);
73         }
74     }
75 
76     return s;
77 }
78 
create_context(flag isServer)79 static SSL_CTX *create_context(flag isServer)
80 {
81     const SSL_METHOD *method;
82     SSL_CTX *ctx;
83 
84     if (isServer)
85         method = TLS_server_method();
86     else
87         method = TLS_client_method();
88 
89     ctx = SSL_CTX_new(method);
90     if (ctx == NULL) {
91         perror("Unable to create SSL context");
92         ERR_print_errors_fp(stderr);
93         exit(EXIT_FAILURE);
94     }
95 
96     return ctx;
97 }
98 
configure_server_context(SSL_CTX * ctx)99 static void configure_server_context(SSL_CTX *ctx)
100 {
101     /* Set the key and cert */
102     if (SSL_CTX_use_certificate_chain_file(ctx, "cert.pem") <= 0) {
103         ERR_print_errors_fp(stderr);
104         exit(EXIT_FAILURE);
105     }
106 
107     if (SSL_CTX_use_PrivateKey_file(ctx, "key.pem", SSL_FILETYPE_PEM) <= 0) {
108         ERR_print_errors_fp(stderr);
109         exit(EXIT_FAILURE);
110     }
111 }
112 
configure_client_context(SSL_CTX * ctx)113 static void configure_client_context(SSL_CTX *ctx)
114 {
115     /*
116      * Configure the client to abort the handshake if certificate verification
117      * fails
118      */
119     SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
120     /*
121      * In a real application you would probably just use the default system certificate trust store and call:
122      *     SSL_CTX_set_default_verify_paths(ctx);
123      * In this demo though we are using a self-signed certificate, so the client must trust it directly.
124      */
125     if (!SSL_CTX_load_verify_locations(ctx, "cert.pem", NULL)) {
126         ERR_print_errors_fp(stderr);
127         exit(EXIT_FAILURE);
128     }
129 }
130 
usage(void)131 static void usage(void)
132 {
133     printf("Usage: sslecho s\n");
134     printf("       --or--\n");
135     printf("       sslecho c ip\n");
136     printf("       c=client, s=server, ip=dotted ip of server\n");
137     exit(EXIT_FAILURE);
138 }
139 
140 #define BUFFERSIZE 1024
main(int argc,char ** argv)141 int main(int argc, char **argv)
142 {
143     flag isServer;
144     int result;
145 
146     SSL_CTX *ssl_ctx = NULL;
147     SSL *ssl = NULL;
148 
149     SOCKET server_skt = -1;
150     SOCKET client_skt = -1;
151 
152     /* used by fgets */
153     char buffer[BUFFERSIZE];
154     char *txbuf;
155 
156     char rxbuf[128];
157     size_t rxcap = sizeof(rxbuf);
158     int rxlen;
159 
160     char *rem_server_ip = NULL;
161 
162     struct sockaddr_in addr;
163 #if defined(OPENSSL_SYS_CYGWIN) || defined(OPENSSL_SYS_WINDOWS)
164     int addr_len = sizeof(addr);
165 #else
166     unsigned int addr_len = sizeof(addr);
167 #endif
168 
169 #if !defined (OPENSSL_SYS_WINDOWS)
170     /* ignore SIGPIPE so that server can continue running when client pipe closes abruptly */
171     signal(SIGPIPE, SIG_IGN);
172 #endif
173 
174     /* Splash */
175     printf("\nsslecho : Simple Echo Client/Server : %s : %s\n\n", __DATE__,
176     __TIME__);
177 
178     /* Need to know if client or server */
179     if (argc < 2) {
180         usage();
181         /* NOTREACHED */
182     }
183     isServer = (argv[1][0] == 's') ? true : false;
184     /* If client get remote server address (could be 127.0.0.1) */
185     if (!isServer) {
186         if (argc != 3) {
187             usage();
188             /* NOTREACHED */
189         }
190         rem_server_ip = argv[2];
191     }
192 
193     /* Create context used by both client and server */
194     ssl_ctx = create_context(isServer);
195 
196     /* If server */
197     if (isServer) {
198 
199         printf("We are the server on port: %d\n\n", server_port);
200 
201         /* Configure server context with appropriate key files */
202         configure_server_context(ssl_ctx);
203 
204         /* Create server socket; will bind with server port and listen */
205         server_skt = create_socket(true);
206 
207         /*
208          * Loop to accept clients.
209          * Need to implement timeouts on TCP & SSL connect/read functions
210          * before we can catch a CTRL-C and kill the server.
211          */
212         while (server_running) {
213             /* Wait for TCP connection from client */
214             client_skt = accept(server_skt, (struct sockaddr*) &addr,
215                                 &addr_len);
216             if (client_skt < 0) {
217                 perror("Unable to accept");
218                 exit(EXIT_FAILURE);
219             }
220 
221             printf("Client TCP connection accepted\n");
222 
223             /* Create server SSL structure using newly accepted client socket */
224             ssl = SSL_new(ssl_ctx);
225             if (!SSL_set_fd(ssl, (int)client_skt)) {
226                 ERR_print_errors_fp(stderr);
227                 exit(EXIT_FAILURE);
228             }
229 
230             /* Wait for SSL connection from the client */
231             if (SSL_accept(ssl) <= 0) {
232                 ERR_print_errors_fp(stderr);
233                 server_running = false;
234             } else {
235 
236                 printf("Client SSL connection accepted\n\n");
237 
238                 /* Echo loop */
239                 while (true) {
240                     /* Get message from client; will fail if client closes connection */
241                     if ((rxlen = SSL_read(ssl, rxbuf, (int)rxcap)) <= 0) {
242                         if (rxlen == 0) {
243                             printf("Client closed connection\n");
244                         } else {
245                             printf("SSL_read returned %d\n", rxlen);
246                         }
247                         ERR_print_errors_fp(stderr);
248                         break;
249                     }
250                     /* Insure null terminated input */
251                     rxbuf[rxlen] = 0;
252                     /* Look for kill switch */
253                     if (strcmp(rxbuf, "kill\n") == 0) {
254                         /* Terminate...with extreme prejudice */
255                         printf("Server received 'kill' command\n");
256                         server_running = false;
257                         break;
258                     }
259                     /* Show received message */
260                     printf("Received: %s", rxbuf);
261                     /* Echo it back */
262                     if (SSL_write(ssl, rxbuf, rxlen) <= 0) {
263                         ERR_print_errors_fp(stderr);
264                     }
265                 }
266             }
267             if (server_running) {
268                 /* Cleanup for next client */
269                 SSL_shutdown(ssl);
270                 SSL_free(ssl);
271                 closesocket(client_skt);
272                 /*
273                  * Set client_skt to -1 to avoid double close when
274                  * server_running become false before next accept
275                  */
276                 client_skt = -1;
277             }
278         }
279         printf("Server exiting...\n");
280     }
281     /* Else client */
282     else {
283 
284         printf("We are the client\n\n");
285 
286         /* Configure client context so we verify the server correctly */
287         configure_client_context(ssl_ctx);
288 
289         /* Create "bare" socket */
290         client_skt = create_socket(false);
291         /* Set up connect address */
292         addr.sin_family = AF_INET;
293         inet_pton(AF_INET, rem_server_ip, &addr.sin_addr.s_addr);
294         addr.sin_port = htons(server_port);
295         /* Do TCP connect with server */
296         if (connect(client_skt, (struct sockaddr*) &addr, sizeof(addr)) != 0) {
297             perror("Unable to TCP connect to server");
298             goto exit;
299         } else {
300             printf("TCP connection to server successful\n");
301         }
302 
303         /* Create client SSL structure using dedicated client socket */
304         ssl = SSL_new(ssl_ctx);
305         if (!SSL_set_fd(ssl, (int)client_skt)) {
306             ERR_print_errors_fp(stderr);
307             goto exit;
308         }
309         /* Set hostname for SNI */
310         SSL_set_tlsext_host_name(ssl, rem_server_ip);
311         /* Configure server hostname check */
312         if (!SSL_set1_host(ssl, rem_server_ip)) {
313             ERR_print_errors_fp(stderr);
314             goto exit;
315         }
316 
317         /* Now do SSL connect with server */
318         if (SSL_connect(ssl) == 1) {
319 
320             printf("SSL connection to server successful\n\n");
321 
322             /* Loop to send input from keyboard */
323             while (true) {
324                 /* Get a line of input */
325                 memset(buffer, 0, BUFFERSIZE);
326                 txbuf = fgets(buffer, BUFFERSIZE, stdin);
327 
328                 /* Exit loop on error */
329                 if (txbuf == NULL) {
330                     break;
331                 }
332                 /* Exit loop if just a carriage return */
333                 if (txbuf[0] == '\n') {
334                     break;
335                 }
336                 /* Send it to the server */
337                 if ((result = SSL_write(ssl, txbuf, (int)strlen(txbuf))) <= 0) {
338                     printf("Server closed connection\n");
339                     ERR_print_errors_fp(stderr);
340                     break;
341                 }
342 
343                 /* Wait for the echo */
344                 rxlen = SSL_read(ssl, rxbuf, (int)rxcap);
345                 if (rxlen <= 0) {
346                     printf("Server closed connection\n");
347                     ERR_print_errors_fp(stderr);
348                     break;
349                 } else {
350                     /* Show it */
351                     rxbuf[rxlen] = 0;
352                     printf("Received: %s", rxbuf);
353                 }
354             }
355             printf("Client exiting...\n");
356         } else {
357 
358             printf("SSL connection to server failed\n\n");
359 
360             ERR_print_errors_fp(stderr);
361         }
362     }
363 exit:
364     /* Close up */
365     if (ssl != NULL) {
366         SSL_shutdown(ssl);
367         SSL_free(ssl);
368     }
369     SSL_CTX_free(ssl_ctx);
370 
371     if (client_skt != -1)
372         closesocket(client_skt);
373     if (server_skt != -1)
374         closesocket(server_skt);
375 
376     printf("sslecho exiting\n");
377 
378     return EXIT_SUCCESS;
379 }
380