2 * Copyright (C) 2020 Pascal Knecht
3 * Copyright (C) 2020 Tobias Brunner
4 * Copyright (C) 2010 Martin Willi
6 * Copyright (C) secunet Security Networks AG
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 #include <sys/types.h>
22 #include <sys/socket.h>
28 #include <utils/debug.h>
29 #include <tls_socket.h>
30 #include <networking/host.h>
31 #include <credentials/sets/mem_cred.h>
34 * Print usage information
36 static void usage(FILE *out
, char *cmd
)
38 fprintf(out
, "usage:\n");
39 fprintf(out
, " %s --connect <address> --port <port> [--key <key] [--cert <file>] [--cacert <file>]+ [--times <n>]\n", cmd
);
40 fprintf(out
, " %s --listen <address> --port <port> --key <key> --cert <file> [--cacert <file>]+ [--auth-optional] [--times <n>]\n", cmd
);
42 fprintf(out
, "options:\n");
43 fprintf(out
, " --help print help and exit\n");
44 fprintf(out
, " --connect <address> connect to a server on dns name or ip address\n");
45 fprintf(out
, " --listen <address> listen on dns name or ip address\n");
46 fprintf(out
, " --port <port> specify the port to use\n");
47 fprintf(out
, " --cert <file> certificate to authenticate itself\n");
48 fprintf(out
, " --key <file> private key to authenticate itself\n");
49 fprintf(out
, " --cacert <file> certificate to verify other peer\n");
50 fprintf(out
, " --identity <id> optional remote identity to enforce\n");
51 fprintf(out
, " --auth-optional don't enforce client authentication\n");
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");
65 * Check, as client, if we have a client certificate with private key
67 static identification_t
*find_client_id()
69 identification_t
*client
= NULL
, *keyid
;
70 enumerator_t
*enumerator
;
73 private_key_t
*privkey
;
76 enumerator
= lib
->credmgr
->create_cert_enumerator(lib
->credmgr
,
77 CERT_X509
, KEY_ANY
, NULL
, FALSE
);
78 while (enumerator
->enumerate(enumerator
, &cert
))
80 pubkey
= cert
->get_public_key(cert
);
83 if (pubkey
->get_fingerprint(pubkey
, KEYID_PUBKEY_SHA1
, &chunk
))
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
);
91 client
= cert
->get_subject(cert
);
92 client
= client
->clone(client
);
93 privkey
->destroy(privkey
);
96 pubkey
->destroy(pubkey
);
103 enumerator
->destroy(enumerator
);
111 static int run_client(host_t
*host
, identification_t
*server
,
112 identification_t
*client
, int times
, tls_cache_t
*cache
,
113 tls_version_t min_version
, tls_version_t max_version
,
119 while (times
== -1 || times
-- > 0)
121 DBG2(DBG_TLS
, "connecting to %#H", host
);
122 fd
= socket(host
->get_family(host
), SOCK_STREAM
, 0);
125 DBG1(DBG_TLS
, "opening socket failed: %s", strerror(errno
));
128 if (connect(fd
, host
->get_sockaddr(host
),
129 *host
->get_sockaddr_len(host
)) == -1)
131 DBG1(DBG_TLS
, "connecting to %#H failed: %s", host
, strerror(errno
));
135 tls
= tls_socket_create(FALSE
, server
, client
, fd
, cache
, min_version
,
142 res
= tls
->splice(tls
, 0, 1) ? 0 : 1;
156 static int serve(host_t
*host
, identification_t
*server
, identification_t
*client
,
157 int times
, tls_cache_t
*cache
, tls_version_t min_version
,
158 tls_version_t max_version
, tls_flag_t flags
)
163 fd
= socket(AF_INET
, SOCK_STREAM
, 0);
166 DBG1(DBG_TLS
, "opening socket failed: %s", strerror(errno
));
169 if (bind(fd
, host
->get_sockaddr(host
),
170 *host
->get_sockaddr_len(host
)) == -1)
172 DBG1(DBG_TLS
, "binding to %#H failed: %s", host
, strerror(errno
));
176 if (listen(fd
, 1) == -1)
178 DBG1(DBG_TLS
, "listen to %#H failed: %m", host
, strerror(errno
));
183 while (times
== -1 || times
-- > 0)
185 cfd
= accept(fd
, host
->get_sockaddr(host
), host
->get_sockaddr_len(host
));
188 DBG1(DBG_TLS
, "accept failed: %s", strerror(errno
));
192 DBG1(DBG_TLS
, "%#H connected", host
);
194 tls
= tls_socket_create(TRUE
, server
, client
, cfd
, cache
, min_version
,
201 tls
->splice(tls
, 0, 1);
202 DBG1(DBG_TLS
, "%#H disconnected", host
);
211 * In-Memory credential set
213 static mem_cred_t
*creds
;
216 * Load certificate from file
218 static bool load_certificate(char *filename
)
222 cert
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
, CERT_X509
,
223 BUILD_FROM_FILE
, filename
, BUILD_END
);
226 DBG1(DBG_TLS
, "loading certificate from '%s' failed", filename
);
229 creds
->add_cert(creds
, TRUE
, cert
);
234 * Load private key from file
236 static bool load_key(char *filename
)
240 key
= lib
->creds
->create(lib
->creds
, CRED_PRIVATE_KEY
, KEY_ANY
,
241 BUILD_FROM_FILE
, filename
, BUILD_END
);
244 DBG1(DBG_TLS
, "loading key from '%s' failed", filename
);
247 creds
->add_key(creds
, key
);
254 static level_t tls_level
= 1;
256 static void dbg_tls(debug_t group
, level_t level
, char *fmt
, ...)
258 if ((group
== DBG_TLS
&& level
<= tls_level
) || level
<= 1)
263 vfprintf(stderr
, fmt
, args
);
264 fprintf(stderr
, "\n");
272 static void cleanup()
274 lib
->credmgr
->remove_set(lib
->credmgr
, &creds
->set
);
275 creds
->destroy(creds
);
286 library_init(NULL
, "tls_test");
290 plugins
= getenv("PLUGINS") ?: PLUGINS
;
291 lib
->plugins
->load(lib
->plugins
, plugins
);
293 creds
= mem_cred_create();
294 lib
->credmgr
->add_set(lib
->credmgr
, &creds
->set
);
299 int main(int argc
, char *argv
[])
301 char *address
= NULL
;
303 int port
= 0, times
= -1, res
, family
= AF_UNSPEC
;
304 identification_t
*server
, *client
= NULL
, *identity
= NULL
;
305 tls_version_t min_version
= TLS_SUPPORTED_MIN
, max_version
= TLS_SUPPORTED_MAX
;
306 tls_flag_t flags
= TLS_FLAG_ENCRYPTION_OPTIONAL
;
314 struct option long_opts
[] = {
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 {"identity", required_argument
, NULL
, 'i' },
330 {"debug", required_argument
, NULL
, 'd' },
333 switch (getopt_long(argc
, argv
, "", long_opts
, NULL
))
338 usage(stdout
, argv
[0]);
341 if (!load_certificate(optarg
))
347 if (!load_key(optarg
))
353 if (!load_certificate(optarg
))
357 client
= identification_create_from_encoding(ID_ANY
, chunk_empty
);
360 identity
= identification_create_from_string(optarg
);
372 usage(stderr
, argv
[0]);
381 times
= atoi(optarg
);
384 tls_level
= atoi(optarg
);
393 if (!enum_from_name(tls_numeric_version_names
, optarg
,
396 fprintf(stderr
, "unknown minimum TLS version: %s\n", optarg
);
401 if (!enum_from_name(tls_numeric_version_names
, optarg
,
404 fprintf(stderr
, "unknown maximum TLS version: %s\n", optarg
);
409 if (!enum_from_name(tls_numeric_version_names
, optarg
,
412 fprintf(stderr
, "unknown TLS version: %s\n", optarg
);
415 max_version
= min_version
;
418 flags
|= TLS_FLAG_CLIENT_AUTH_OPTIONAL
;
421 usage(stderr
, argv
[0]);
426 if (!port
|| !address
)
428 usage(stderr
, argv
[0]);
431 host
= host_create_from_dns(address
, family
, port
);
434 DBG1(DBG_TLS
, "resolving hostname %s failed", address
);
437 server
= identification_create_from_string(address
);
438 cache
= tls_cache_create(100, 30);
441 res
= serve(host
, server
, identity
?: client
, times
, cache
, min_version
,
447 client
= find_client_id();
448 res
= run_client(host
, identity
?: server
, client
, times
, cache
, min_version
,
452 cache
->destroy(cache
);
454 server
->destroy(server
);
455 DESTROY_IF(identity
);