]> git.ipfire.org Git - people/ms/strongswan.git/blame - scripts/tls_test.c
os_info: Parse /etc/os-release first
[people/ms/strongswan.git] / scripts / tls_test.c
CommitLineData
f1a74a3c 1/*
5e579ebe
PK
2 * Copyright (C) 2020 Pascal Knecht
3 * Copyright (C) 2020 Tobias Brunner
4 * HSR Hochschule fuer Technik Rapperswil
5 *
f1a74a3c
MW
6 * Copyright (C) 2010 Martin Willi
7 * Copyright (C) 2010 revosec AG
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 * for more details.
18 */
19
20#include <unistd.h>
21#include <stdio.h>
22#include <sys/types.h>
23#include <sys/socket.h>
24#include <getopt.h>
6a5c86b7
MW
25#include <errno.h>
26#include <string.h>
f1a74a3c
MW
27
28#include <library.h>
f05b4272 29#include <utils/debug.h>
f1a74a3c 30#include <tls_socket.h>
2e7cc07e 31#include <networking/host.h>
f1a74a3c
MW
32#include <credentials/sets/mem_cred.h>
33
34/**
35 * Print usage information
36 */
37static void usage(FILE *out, char *cmd)
38{
39 fprintf(out, "usage:\n");
299cc800 40 fprintf(out, " %s --connect <address> --port <port> [--key <key] [--cert <file>] [--cacert <file>]+ [--times <n>]\n", cmd);
82116dba 41 fprintf(out, " %s --listen <address> --port <port> --key <key> --cert <file> [--cacert <file>]+ [--auth-optional] [--times <n>]\n", cmd);
299cc800
PK
42 fprintf(out, "\n");
43 fprintf(out, "options:\n");
44 fprintf(out, " --help print help and exit\n");
45 fprintf(out, " --connect <address> connect to a server on dns name or ip address\n");
46 fprintf(out, " --listen <address> listen on dns name or ip address\n");
47 fprintf(out, " --port <port> specify the port to use\n");
48 fprintf(out, " --cert <file> certificate to authenticate itself\n");
49 fprintf(out, " --key <file> private key to authenticate itself\n");
50 fprintf(out, " --cacert <file> certificate to verify other peer\n");
82116dba 51 fprintf(out, " --auth-optional don't enforce client authentication\n");
299cc800
PK
52 fprintf(out, " --times <n> specify the amount of repeated connection establishments\n");
53 fprintf(out, " --ipv4 use IPv4\n");
54 fprintf(out, " --ipv6 use IPv6\n");
55 fprintf(out, " --min-version <version> specify the minimum TLS version, supported versions:\n");
56 fprintf(out, " 1.0 (default), 1.1, 1.2 and 1.3\n");
57 fprintf(out, " --max-version <version> specify the maximum TLS version, supported versions:\n");
58 fprintf(out, " 1.0, 1.1, 1.2 and 1.3 (default)\n");
59 fprintf(out, " --version <version> set one specific TLS version to use, supported versions:\n");
60 fprintf(out, " 1.0, 1.1, 1.2 and 1.3\n");
61 fprintf(out, " --debug <debug level> set debug level, default is 1\n");
f1a74a3c
MW
62}
63
3f4300ed
MW
64/**
65 * Check, as client, if we have a client certificate with private key
66 */
67static identification_t *find_client_id()
68{
69 identification_t *client = NULL, *keyid;
70 enumerator_t *enumerator;
71 certificate_t *cert;
72 public_key_t *pubkey;
73 private_key_t *privkey;
74 chunk_t chunk;
75
76 enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
77 CERT_X509, KEY_ANY, NULL, FALSE);
78 while (enumerator->enumerate(enumerator, &cert))
79 {
80 pubkey = cert->get_public_key(cert);
81 if (pubkey)
82 {
83 if (pubkey->get_fingerprint(pubkey, KEYID_PUBKEY_SHA1, &chunk))
84 {
85 keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
86 privkey = lib->credmgr->get_private(lib->credmgr,
87 pubkey->get_type(pubkey), keyid, NULL);
88 keyid->destroy(keyid);
89 if (privkey)
90 {
91 client = cert->get_subject(cert);
92 client = client->clone(client);
93 privkey->destroy(privkey);
94 }
95 }
96 pubkey->destroy(pubkey);
97 }
98 if (client)
99 {
100 break;
101 }
102 }
103 enumerator->destroy(enumerator);
104
105 return client;
106}
107
f1a74a3c
MW
108/**
109 * Client routine
110 */
3f4300ed 111static int run_client(host_t *host, identification_t *server,
8e35b1f1 112 identification_t *client, int times, tls_cache_t *cache,
82116dba
TB
113 tls_version_t min_version, tls_version_t max_version,
114 tls_flag_t flags)
f1a74a3c
MW
115{
116 tls_socket_t *tls;
6a5c86b7 117 int fd, res;
f1a74a3c 118
6a5c86b7 119 while (times == -1 || times-- > 0)
f1a74a3c 120 {
4099035a
TB
121 DBG2(DBG_TLS, "connecting to %#H", host);
122 fd = socket(host->get_family(host), SOCK_STREAM, 0);
6a5c86b7
MW
123 if (fd == -1)
124 {
125 DBG1(DBG_TLS, "opening socket failed: %s", strerror(errno));
126 return 1;
127 }
128 if (connect(fd, host->get_sockaddr(host),
129 *host->get_sockaddr_len(host)) == -1)
130 {
4099035a
TB
131 DBG1(DBG_TLS, "connecting to %#H failed: %s", host, strerror(errno));
132 close(fd);
133 return 1;
6a5c86b7 134 }
8e35b1f1 135 tls = tls_socket_create(FALSE, server, client, fd, cache, min_version,
82116dba 136 max_version, flags);
6a5c86b7
MW
137 if (!tls)
138 {
139 close(fd);
140 return 1;
141 }
f8b29069 142 res = tls->splice(tls, 0, 1) ? 0 : 1;
6a5c86b7
MW
143 tls->destroy(tls);
144 close(fd);
145 if (res)
146 {
147 break;
148 }
f1a74a3c 149 }
f1a74a3c
MW
150 return res;
151}
152
153/**
154 * Server routine
155 */
299cc800 156static int serve(host_t *host, identification_t *server, identification_t *client,
8e35b1f1 157 int times, tls_cache_t *cache, tls_version_t min_version,
82116dba 158 tls_version_t max_version, tls_flag_t flags)
f1a74a3c
MW
159{
160 tls_socket_t *tls;
6a5c86b7 161 int fd, cfd;
f1a74a3c 162
6a5c86b7
MW
163 fd = socket(AF_INET, SOCK_STREAM, 0);
164 if (fd == -1)
165 {
166 DBG1(DBG_TLS, "opening socket failed: %s", strerror(errno));
167 return 1;
168 }
f1a74a3c
MW
169 if (bind(fd, host->get_sockaddr(host),
170 *host->get_sockaddr_len(host)) == -1)
171 {
6a5c86b7
MW
172 DBG1(DBG_TLS, "binding to %#H failed: %s", host, strerror(errno));
173 close(fd);
f1a74a3c
MW
174 return 1;
175 }
176 if (listen(fd, 1) == -1)
177 {
6a5c86b7
MW
178 DBG1(DBG_TLS, "listen to %#H failed: %m", host, strerror(errno));
179 close(fd);
f1a74a3c
MW
180 return 1;
181 }
182
6a5c86b7 183 while (times == -1 || times-- > 0)
f1a74a3c
MW
184 {
185 cfd = accept(fd, host->get_sockaddr(host), host->get_sockaddr_len(host));
186 if (cfd == -1)
187 {
6a5c86b7
MW
188 DBG1(DBG_TLS, "accept failed: %s", strerror(errno));
189 close(fd);
f1a74a3c
MW
190 return 1;
191 }
6a5c86b7 192 DBG1(DBG_TLS, "%#H connected", host);
f1a74a3c 193
299cc800 194 tls = tls_socket_create(TRUE, server, client, cfd, cache, min_version,
82116dba 195 max_version, flags);
f1a74a3c
MW
196 if (!tls)
197 {
6a5c86b7 198 close(fd);
f1a74a3c
MW
199 return 1;
200 }
f8b29069 201 tls->splice(tls, 0, 1);
6a5c86b7 202 DBG1(DBG_TLS, "%#H disconnected", host);
f1a74a3c
MW
203 tls->destroy(tls);
204 }
6a5c86b7 205 close(fd);
f1a74a3c
MW
206
207 return 0;
208}
209
210/**
211 * In-Memory credential set
212 */
213static mem_cred_t *creds;
214
215/**
216 * Load certificate from file
217 */
218static bool load_certificate(char *filename)
219{
220 certificate_t *cert;
221
222 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
223 BUILD_FROM_FILE, filename, BUILD_END);
224 if (!cert)
225 {
6a5c86b7 226 DBG1(DBG_TLS, "loading certificate from '%s' failed", filename);
f1a74a3c
MW
227 return FALSE;
228 }
229 creds->add_cert(creds, TRUE, cert);
230 return TRUE;
231}
232
233/**
234 * Load private key from file
235 */
236static bool load_key(char *filename)
237{
238 private_key_t *key;
239
5e579ebe
PK
240 key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
241 BUILD_FROM_FILE, filename, BUILD_END);
f1a74a3c
MW
242 if (!key)
243 {
6a5c86b7 244 DBG1(DBG_TLS, "loading key from '%s' failed", filename);
f1a74a3c
MW
245 return FALSE;
246 }
247 creds->add_key(creds, key);
248 return TRUE;
249}
250
fd0bde9a
MW
251/**
252 * TLS debug level
253 */
254static level_t tls_level = 1;
255
256static void dbg_tls(debug_t group, level_t level, char *fmt, ...)
257{
258 if ((group == DBG_TLS && level <= tls_level) || level <= 1)
259 {
260 va_list args;
261
262 va_start(args, fmt);
263 vfprintf(stderr, fmt, args);
264 fprintf(stderr, "\n");
265 va_end(args);
266 }
267}
268
f1a74a3c
MW
269/**
270 * Cleanup
271 */
272static void cleanup()
273{
274 lib->credmgr->remove_set(lib->credmgr, &creds->set);
275 creds->destroy(creds);
276 library_deinit();
277}
278
279/**
280 * Initialize library
281 */
282static void init()
283{
06aad98f
TB
284 char *plugins;
285
34d3bfcf 286 library_init(NULL, "tls_test");
fd0bde9a
MW
287
288 dbg = dbg_tls;
289
06aad98f
TB
290 plugins = getenv("PLUGINS") ?: PLUGINS;
291 lib->plugins->load(lib->plugins, plugins);
f1a74a3c
MW
292
293 creds = mem_cred_create();
294 lib->credmgr->add_set(lib->credmgr, &creds->set);
295
296 atexit(cleanup);
297}
298
299int main(int argc, char *argv[])
300{
301 char *address = NULL;
6a5c86b7 302 bool listen = FALSE;
4099035a 303 int port = 0, times = -1, res, family = AF_UNSPEC;
299cc800 304 identification_t *server, *client = NULL;
663969dd 305 tls_version_t min_version = TLS_SUPPORTED_MIN, max_version = TLS_SUPPORTED_MAX;
82116dba 306 tls_flag_t flags = TLS_FLAG_ENCRYPTION_OPTIONAL;
6a5c86b7 307 tls_cache_t *cache;
f1a74a3c
MW
308 host_t *host;
309
310 init();
311
312 while (TRUE)
313 {
314 struct option long_opts[] = {
82116dba
TB
315 {"help", no_argument, NULL, 'h' },
316 {"connect", required_argument, NULL, 'c' },
317 {"listen", required_argument, NULL, 'l' },
318 {"port", required_argument, NULL, 'p' },
319 {"cert", required_argument, NULL, 'x' },
320 {"key", required_argument, NULL, 'k' },
321 {"cacert", required_argument, NULL, 'f' },
322 {"times", required_argument, NULL, 't' },
323 {"ipv4", no_argument, NULL, '4' },
324 {"ipv6", no_argument, NULL, '6' },
325 {"min-version", required_argument, NULL, 'm' },
326 {"max-version", required_argument, NULL, 'M' },
327 {"version", required_argument, NULL, 'v' },
328 {"auth-optional", no_argument, NULL, 'n' },
329 {"debug", required_argument, NULL, 'd' },
f1a74a3c
MW
330 {0,0,0,0 }
331 };
332 switch (getopt_long(argc, argv, "", long_opts, NULL))
333 {
334 case EOF:
335 break;
336 case 'h':
337 usage(stdout, argv[0]);
338 return 0;
339 case 'x':
340 if (!load_certificate(optarg))
341 {
342 return 1;
343 }
344 continue;
345 case 'k':
346 if (!load_key(optarg))
347 {
348 return 1;
349 }
350 continue;
299cc800
PK
351 case 'f':
352 if (!load_certificate(optarg))
353 {
354 return 1;
355 }
356 client = identification_create_from_encoding(ID_ANY, chunk_empty);
357 continue;
f1a74a3c
MW
358 case 'l':
359 listen = TRUE;
360 /* fall */
361 case 'c':
362 if (address)
363 {
364 usage(stderr, argv[0]);
365 return 1;
366 }
367 address = optarg;
368 continue;
369 case 'p':
370 port = atoi(optarg);
371 continue;
6a5c86b7
MW
372 case 't':
373 times = atoi(optarg);
f1a74a3c 374 continue;
fd0bde9a
MW
375 case 'd':
376 tls_level = atoi(optarg);
377 continue;
4099035a
TB
378 case '4':
379 family = AF_INET;
380 continue;
381 case '6':
382 family = AF_INET6;
383 continue;
8e35b1f1 384 case 'm':
663969dd
TB
385 if (!enum_from_name(tls_numeric_version_names, optarg,
386 &min_version))
8e35b1f1
TB
387 {
388 fprintf(stderr, "unknown minimum TLS version: %s\n", optarg);
389 return 1;
390 }
391 continue;
392 case 'M':
663969dd
TB
393 if (!enum_from_name(tls_numeric_version_names, optarg,
394 &max_version))
8e35b1f1
TB
395 {
396 fprintf(stderr, "unknown maximum TLS version: %s\n", optarg);
397 return 1;
398 }
399 continue;
400 case 'v':
663969dd
TB
401 if (!enum_from_name(tls_numeric_version_names, optarg,
402 &min_version))
8e35b1f1
TB
403 {
404 fprintf(stderr, "unknown TLS version: %s\n", optarg);
405 return 1;
406 }
407 max_version = min_version;
408 continue;
82116dba
TB
409 case 'n':
410 flags |= TLS_FLAG_CLIENT_AUTH_OPTIONAL;
411 continue;
f1a74a3c
MW
412 default:
413 usage(stderr, argv[0]);
414 return 1;
415 }
416 break;
417 }
418 if (!port || !address)
419 {
420 usage(stderr, argv[0]);
421 return 1;
422 }
4099035a 423 host = host_create_from_dns(address, family, port);
f1a74a3c
MW
424 if (!host)
425 {
4099035a
TB
426 DBG1(DBG_TLS, "resolving hostname %s failed", address);
427 return 1;
f1a74a3c
MW
428 }
429 server = identification_create_from_string(address);
6a5c86b7 430 cache = tls_cache_create(100, 30);
f1a74a3c
MW
431 if (listen)
432 {
82116dba
TB
433 res = serve(host, server, client, times, cache, min_version,
434 max_version, flags);
f1a74a3c
MW
435 }
436 else
437 {
299cc800 438 DESTROY_IF(client);
3f4300ed 439 client = find_client_id();
8e35b1f1 440 res = run_client(host, server, client, times, cache, min_version,
82116dba 441 max_version, flags);
3f4300ed 442 DESTROY_IF(client);
f1a74a3c 443 }
6a5c86b7 444 cache->destroy(cache);
f1a74a3c
MW
445 host->destroy(host);
446 server->destroy(server);
447 return res;
448}