]>
Commit | Line | Data |
---|---|---|
f1a74a3c MW |
1 | /* |
2 | * Copyright (C) 2010 Martin Willi | |
3 | * Copyright (C) 2010 revosec AG | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License as published by the | |
7 | * Free Software Foundation; either version 2 of the License, or (at your | |
8 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
12 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
13 | * for more details. | |
14 | */ | |
15 | ||
16 | #include <unistd.h> | |
17 | #include <stdio.h> | |
18 | #include <sys/types.h> | |
19 | #include <sys/socket.h> | |
20 | #include <getopt.h> | |
6a5c86b7 MW |
21 | #include <errno.h> |
22 | #include <string.h> | |
f1a74a3c MW |
23 | |
24 | #include <library.h> | |
f05b4272 | 25 | #include <utils/debug.h> |
f1a74a3c | 26 | #include <tls_socket.h> |
2e7cc07e | 27 | #include <networking/host.h> |
f1a74a3c MW |
28 | #include <credentials/sets/mem_cred.h> |
29 | ||
30 | /** | |
31 | * Print usage information | |
32 | */ | |
33 | static void usage(FILE *out, char *cmd) | |
34 | { | |
35 | fprintf(out, "usage:\n"); | |
3f4300ed | 36 | fprintf(out, " %s --connect <address> --port <port> [--key <key] [--cert <file>]+ [--times <n>]\n", cmd); |
6a5c86b7 | 37 | fprintf(out, " %s --listen <address> --port <port> --key <key> [--cert <file>]+ [--times <n>]\n", cmd); |
f1a74a3c MW |
38 | } |
39 | ||
3f4300ed MW |
40 | /** |
41 | * Check, as client, if we have a client certificate with private key | |
42 | */ | |
43 | static identification_t *find_client_id() | |
44 | { | |
45 | identification_t *client = NULL, *keyid; | |
46 | enumerator_t *enumerator; | |
47 | certificate_t *cert; | |
48 | public_key_t *pubkey; | |
49 | private_key_t *privkey; | |
50 | chunk_t chunk; | |
51 | ||
52 | enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr, | |
53 | CERT_X509, KEY_ANY, NULL, FALSE); | |
54 | while (enumerator->enumerate(enumerator, &cert)) | |
55 | { | |
56 | pubkey = cert->get_public_key(cert); | |
57 | if (pubkey) | |
58 | { | |
59 | if (pubkey->get_fingerprint(pubkey, KEYID_PUBKEY_SHA1, &chunk)) | |
60 | { | |
61 | keyid = identification_create_from_encoding(ID_KEY_ID, chunk); | |
62 | privkey = lib->credmgr->get_private(lib->credmgr, | |
63 | pubkey->get_type(pubkey), keyid, NULL); | |
64 | keyid->destroy(keyid); | |
65 | if (privkey) | |
66 | { | |
67 | client = cert->get_subject(cert); | |
68 | client = client->clone(client); | |
69 | privkey->destroy(privkey); | |
70 | } | |
71 | } | |
72 | pubkey->destroy(pubkey); | |
73 | } | |
74 | if (client) | |
75 | { | |
76 | break; | |
77 | } | |
78 | } | |
79 | enumerator->destroy(enumerator); | |
80 | ||
81 | return client; | |
82 | } | |
83 | ||
f1a74a3c MW |
84 | /** |
85 | * Client routine | |
86 | */ | |
3f4300ed MW |
87 | static int run_client(host_t *host, identification_t *server, |
88 | identification_t *client, int times, tls_cache_t *cache) | |
f1a74a3c MW |
89 | { |
90 | tls_socket_t *tls; | |
6a5c86b7 | 91 | int fd, res; |
f1a74a3c | 92 | |
6a5c86b7 | 93 | while (times == -1 || times-- > 0) |
f1a74a3c | 94 | { |
6a5c86b7 MW |
95 | fd = socket(AF_INET, SOCK_STREAM, 0); |
96 | if (fd == -1) | |
97 | { | |
98 | DBG1(DBG_TLS, "opening socket failed: %s", strerror(errno)); | |
99 | return 1; | |
100 | } | |
101 | if (connect(fd, host->get_sockaddr(host), | |
102 | *host->get_sockaddr_len(host)) == -1) | |
103 | { | |
104 | DBG1(DBG_TLS, "connecting to %#H failed: %s", host, strerror(errno)); | |
105 | close(fd); | |
106 | return 1; | |
107 | } | |
e15f64cc | 108 | tls = tls_socket_create(FALSE, server, client, fd, cache, TLS_1_2, TRUE); |
6a5c86b7 MW |
109 | if (!tls) |
110 | { | |
111 | close(fd); | |
112 | return 1; | |
113 | } | |
f8b29069 | 114 | res = tls->splice(tls, 0, 1) ? 0 : 1; |
6a5c86b7 MW |
115 | tls->destroy(tls); |
116 | close(fd); | |
117 | if (res) | |
118 | { | |
119 | break; | |
120 | } | |
f1a74a3c | 121 | } |
f1a74a3c MW |
122 | return res; |
123 | } | |
124 | ||
125 | /** | |
126 | * Server routine | |
127 | */ | |
6a5c86b7 MW |
128 | static int serve(host_t *host, identification_t *server, |
129 | int times, tls_cache_t *cache) | |
f1a74a3c MW |
130 | { |
131 | tls_socket_t *tls; | |
6a5c86b7 | 132 | int fd, cfd; |
f1a74a3c | 133 | |
6a5c86b7 MW |
134 | fd = socket(AF_INET, SOCK_STREAM, 0); |
135 | if (fd == -1) | |
136 | { | |
137 | DBG1(DBG_TLS, "opening socket failed: %s", strerror(errno)); | |
138 | return 1; | |
139 | } | |
f1a74a3c MW |
140 | if (bind(fd, host->get_sockaddr(host), |
141 | *host->get_sockaddr_len(host)) == -1) | |
142 | { | |
6a5c86b7 MW |
143 | DBG1(DBG_TLS, "binding to %#H failed: %s", host, strerror(errno)); |
144 | close(fd); | |
f1a74a3c MW |
145 | return 1; |
146 | } | |
147 | if (listen(fd, 1) == -1) | |
148 | { | |
6a5c86b7 MW |
149 | DBG1(DBG_TLS, "listen to %#H failed: %m", host, strerror(errno)); |
150 | close(fd); | |
f1a74a3c MW |
151 | return 1; |
152 | } | |
153 | ||
6a5c86b7 | 154 | while (times == -1 || times-- > 0) |
f1a74a3c MW |
155 | { |
156 | cfd = accept(fd, host->get_sockaddr(host), host->get_sockaddr_len(host)); | |
157 | if (cfd == -1) | |
158 | { | |
6a5c86b7 MW |
159 | DBG1(DBG_TLS, "accept failed: %s", strerror(errno)); |
160 | close(fd); | |
f1a74a3c MW |
161 | return 1; |
162 | } | |
6a5c86b7 | 163 | DBG1(DBG_TLS, "%#H connected", host); |
f1a74a3c | 164 | |
e15f64cc | 165 | tls = tls_socket_create(TRUE, server, NULL, cfd, cache, TLS_1_2, TRUE); |
f1a74a3c MW |
166 | if (!tls) |
167 | { | |
6a5c86b7 | 168 | close(fd); |
f1a74a3c MW |
169 | return 1; |
170 | } | |
f8b29069 | 171 | tls->splice(tls, 0, 1); |
6a5c86b7 | 172 | DBG1(DBG_TLS, "%#H disconnected", host); |
f1a74a3c MW |
173 | tls->destroy(tls); |
174 | } | |
6a5c86b7 | 175 | close(fd); |
f1a74a3c MW |
176 | |
177 | return 0; | |
178 | } | |
179 | ||
180 | /** | |
181 | * In-Memory credential set | |
182 | */ | |
183 | static mem_cred_t *creds; | |
184 | ||
185 | /** | |
186 | * Load certificate from file | |
187 | */ | |
188 | static bool load_certificate(char *filename) | |
189 | { | |
190 | certificate_t *cert; | |
191 | ||
192 | cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, | |
193 | BUILD_FROM_FILE, filename, BUILD_END); | |
194 | if (!cert) | |
195 | { | |
6a5c86b7 | 196 | DBG1(DBG_TLS, "loading certificate from '%s' failed", filename); |
f1a74a3c MW |
197 | return FALSE; |
198 | } | |
199 | creds->add_cert(creds, TRUE, cert); | |
200 | return TRUE; | |
201 | } | |
202 | ||
203 | /** | |
204 | * Load private key from file | |
205 | */ | |
206 | static bool load_key(char *filename) | |
207 | { | |
208 | private_key_t *key; | |
209 | ||
210 | key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA, | |
211 | BUILD_FROM_FILE, filename, BUILD_END); | |
212 | if (!key) | |
213 | { | |
6a5c86b7 | 214 | DBG1(DBG_TLS, "loading key from '%s' failed", filename); |
f1a74a3c MW |
215 | return FALSE; |
216 | } | |
217 | creds->add_key(creds, key); | |
218 | return TRUE; | |
219 | } | |
220 | ||
fd0bde9a MW |
221 | /** |
222 | * TLS debug level | |
223 | */ | |
224 | static level_t tls_level = 1; | |
225 | ||
226 | static void dbg_tls(debug_t group, level_t level, char *fmt, ...) | |
227 | { | |
228 | if ((group == DBG_TLS && level <= tls_level) || level <= 1) | |
229 | { | |
230 | va_list args; | |
231 | ||
232 | va_start(args, fmt); | |
233 | vfprintf(stderr, fmt, args); | |
234 | fprintf(stderr, "\n"); | |
235 | va_end(args); | |
236 | } | |
237 | } | |
238 | ||
f1a74a3c MW |
239 | /** |
240 | * Cleanup | |
241 | */ | |
242 | static void cleanup() | |
243 | { | |
244 | lib->credmgr->remove_set(lib->credmgr, &creds->set); | |
245 | creds->destroy(creds); | |
246 | library_deinit(); | |
247 | } | |
248 | ||
249 | /** | |
250 | * Initialize library | |
251 | */ | |
252 | static void init() | |
253 | { | |
34d3bfcf | 254 | library_init(NULL, "tls_test"); |
fd0bde9a MW |
255 | |
256 | dbg = dbg_tls; | |
257 | ||
b18a5317 | 258 | lib->plugins->load(lib->plugins, PLUGINS); |
f1a74a3c MW |
259 | |
260 | creds = mem_cred_create(); | |
261 | lib->credmgr->add_set(lib->credmgr, &creds->set); | |
262 | ||
263 | atexit(cleanup); | |
264 | } | |
265 | ||
266 | int main(int argc, char *argv[]) | |
267 | { | |
268 | char *address = NULL; | |
6a5c86b7 MW |
269 | bool listen = FALSE; |
270 | int port = 0, times = -1, res; | |
3f4300ed | 271 | identification_t *server, *client; |
6a5c86b7 | 272 | tls_cache_t *cache; |
f1a74a3c MW |
273 | host_t *host; |
274 | ||
275 | init(); | |
276 | ||
277 | while (TRUE) | |
278 | { | |
279 | struct option long_opts[] = { | |
280 | {"help", no_argument, NULL, 'h' }, | |
281 | {"connect", required_argument, NULL, 'c' }, | |
282 | {"listen", required_argument, NULL, 'l' }, | |
283 | {"port", required_argument, NULL, 'p' }, | |
284 | {"cert", required_argument, NULL, 'x' }, | |
285 | {"key", required_argument, NULL, 'k' }, | |
6a5c86b7 | 286 | {"times", required_argument, NULL, 't' }, |
fd0bde9a | 287 | {"debug", required_argument, NULL, 'd' }, |
f1a74a3c MW |
288 | {0,0,0,0 } |
289 | }; | |
290 | switch (getopt_long(argc, argv, "", long_opts, NULL)) | |
291 | { | |
292 | case EOF: | |
293 | break; | |
294 | case 'h': | |
295 | usage(stdout, argv[0]); | |
296 | return 0; | |
297 | case 'x': | |
298 | if (!load_certificate(optarg)) | |
299 | { | |
300 | return 1; | |
301 | } | |
302 | continue; | |
303 | case 'k': | |
304 | if (!load_key(optarg)) | |
305 | { | |
306 | return 1; | |
307 | } | |
308 | continue; | |
309 | case 'l': | |
310 | listen = TRUE; | |
311 | /* fall */ | |
312 | case 'c': | |
313 | if (address) | |
314 | { | |
315 | usage(stderr, argv[0]); | |
316 | return 1; | |
317 | } | |
318 | address = optarg; | |
319 | continue; | |
320 | case 'p': | |
321 | port = atoi(optarg); | |
322 | continue; | |
6a5c86b7 MW |
323 | case 't': |
324 | times = atoi(optarg); | |
f1a74a3c | 325 | continue; |
fd0bde9a MW |
326 | case 'd': |
327 | tls_level = atoi(optarg); | |
328 | continue; | |
f1a74a3c MW |
329 | default: |
330 | usage(stderr, argv[0]); | |
331 | return 1; | |
332 | } | |
333 | break; | |
334 | } | |
335 | if (!port || !address) | |
336 | { | |
337 | usage(stderr, argv[0]); | |
338 | return 1; | |
339 | } | |
f1a74a3c MW |
340 | host = host_create_from_dns(address, 0, port); |
341 | if (!host) | |
342 | { | |
6a5c86b7 | 343 | DBG1(DBG_TLS, "resolving hostname %s failed", address); |
f1a74a3c MW |
344 | return 1; |
345 | } | |
346 | server = identification_create_from_string(address); | |
6a5c86b7 | 347 | cache = tls_cache_create(100, 30); |
f1a74a3c MW |
348 | if (listen) |
349 | { | |
6a5c86b7 | 350 | res = serve(host, server, times, cache); |
f1a74a3c MW |
351 | } |
352 | else | |
353 | { | |
3f4300ed MW |
354 | client = find_client_id(); |
355 | res = run_client(host, server, client, times, cache); | |
356 | DESTROY_IF(client); | |
f1a74a3c | 357 | } |
6a5c86b7 | 358 | cache->destroy(cache); |
f1a74a3c MW |
359 | host->destroy(host); |
360 | server->destroy(server); | |
361 | return res; | |
362 | } |