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