charon-cmd: add --eap-identity and --xauth-username options
[strongswan.git] / src / charon-cmd / cmd / cmd_connection.c
1 /*
2 * Copyright (C) 2013 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * Copyright (C) 2013 Martin Willi
6 * Copyright (C) 2013 revosec AG
7 *
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>.
12 *
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
16 * for more details.
17 */
18
19 #include "cmd_connection.h"
20
21 #include <signal.h>
22 #include <unistd.h>
23
24 #include <utils/debug.h>
25 #include <processing/jobs/callback_job.h>
26 #include <threading/thread.h>
27 #include <daemon.h>
28
29 typedef enum profile_t profile_t;
30 typedef struct private_cmd_connection_t private_cmd_connection_t;
31
32 /**
33 * Connection profiles we support
34 */
35 enum profile_t {
36 PROF_UNDEF,
37 PROF_V2_PUB,
38 PROF_V2_EAP,
39 PROF_V2_PUB_EAP,
40 PROF_V1_PUB,
41 PROF_V1_PUB_AM,
42 PROF_V1_XAUTH,
43 PROF_V1_XAUTH_AM,
44 PROF_V1_XAUTH_PSK,
45 PROF_V1_XAUTH_PSK_AM,
46 PROF_V1_HYBRID,
47 PROF_V1_HYBRID_AM,
48 };
49
50 ENUM(profile_names, PROF_V2_PUB, PROF_V1_HYBRID_AM,
51 "ikev2-pub",
52 "ikev2-eap",
53 "ikev2-pub-eap",
54 "ikev1-pub",
55 "ikev1-pub-am",
56 "ikev1-xauth",
57 "ikev1-xauth-am",
58 "ikev1-xauth-psk",
59 "ikev1-xauth-psk-am",
60 "ikev1-hybrid",
61 "ikev1-hybrid-am",
62 );
63
64 /**
65 * Private data of an cmd_connection_t object.
66 */
67 struct private_cmd_connection_t {
68
69 /**
70 * Public cmd_connection_t interface.
71 */
72 cmd_connection_t public;
73
74 /**
75 * Process ID to terminate on failure
76 */
77 pid_t pid;
78
79 /**
80 * List of local traffic selectors
81 */
82 linked_list_t *local_ts;
83
84 /**
85 * List of remote traffic selectors
86 */
87 linked_list_t *remote_ts;
88
89 /**
90 * Hostname to connect to
91 */
92 char *host;
93
94 /**
95 * Server identity, or NULL to use host
96 */
97 char *server;
98
99 /**
100 * Local identity
101 */
102 char *identity;
103
104 /**
105 * XAuth/EAP identity
106 */
107 char *xautheap;
108
109 /**
110 * Is a private key configured
111 */
112 bool key_seen;
113
114 /**
115 * Selected connection profile
116 */
117 profile_t profile;
118 };
119
120 /**
121 * Shut down application
122 */
123 static void terminate(pid_t pid)
124 {
125 kill(pid, SIGUSR1);
126 }
127
128 /**
129 * Create peer config with associated ike config
130 */
131 static peer_cfg_t* create_peer_cfg(private_cmd_connection_t *this)
132 {
133 ike_cfg_t *ike_cfg;
134 peer_cfg_t *peer_cfg;
135 u_int16_t local_port, remote_port = IKEV2_UDP_PORT;
136 ike_version_t version = IKE_ANY;
137 bool aggressive = FALSE;
138
139 switch (this->profile)
140 {
141 case PROF_UNDEF:
142 case PROF_V2_PUB:
143 case PROF_V2_EAP:
144 case PROF_V2_PUB_EAP:
145 version = IKEV2;
146 break;
147 case PROF_V1_PUB_AM:
148 case PROF_V1_XAUTH_AM:
149 case PROF_V1_XAUTH_PSK_AM:
150 case PROF_V1_HYBRID_AM:
151 aggressive = TRUE;
152 /* FALL */
153 case PROF_V1_PUB:
154 case PROF_V1_XAUTH:
155 case PROF_V1_XAUTH_PSK:
156 case PROF_V1_HYBRID:
157 version = IKEV1;
158 break;
159 }
160
161 local_port = charon->socket->get_port(charon->socket, FALSE);
162 if (local_port != IKEV2_UDP_PORT)
163 {
164 remote_port = IKEV2_NATT_PORT;
165 }
166 ike_cfg = ike_cfg_create(version, TRUE, FALSE, "0.0.0.0", FALSE, local_port,
167 this->host, FALSE, remote_port, FRAGMENTATION_NO, 0);
168 ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
169 peer_cfg = peer_cfg_create("cmd", ike_cfg,
170 CERT_SEND_IF_ASKED, UNIQUE_REPLACE, 1, /* keyingtries */
171 36000, 0, /* rekey 10h, reauth none */
172 600, 600, /* jitter, over 10min */
173 TRUE, aggressive, /* mobike, aggressive */
174 30, 0, /* DPD delay, timeout */
175 FALSE, NULL, NULL); /* mediation */
176 peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0));
177
178 return peer_cfg;
179 }
180
181 /**
182 * Add a single auth cfg of given class to peer cfg
183 */
184 static void add_auth_cfg(private_cmd_connection_t *this, peer_cfg_t *peer_cfg,
185 bool local, auth_class_t class)
186 {
187 identification_t *id;
188 auth_cfg_t *auth;
189
190 auth = auth_cfg_create();
191 auth->add(auth, AUTH_RULE_AUTH_CLASS, class);
192 if (local)
193 {
194 id = identification_create_from_string(this->identity);
195 if (this->xautheap)
196 {
197 switch (class)
198 {
199 case AUTH_CLASS_EAP:
200 auth->add(auth, AUTH_RULE_EAP_IDENTITY,
201 identification_create_from_string(this->xautheap));
202 break;
203 case AUTH_CLASS_XAUTH:
204 auth->add(auth, AUTH_RULE_XAUTH_IDENTITY,
205 identification_create_from_string(this->xautheap));
206 break;
207 default:
208 break;
209 }
210 }
211 }
212 else
213 {
214 if (this->server)
215 {
216 id = identification_create_from_string(this->server);
217 }
218 else
219 {
220 id = identification_create_from_string(this->host);
221 }
222 auth->add(auth, AUTH_RULE_IDENTITY_LOOSE, TRUE);
223 }
224 auth->add(auth, AUTH_RULE_IDENTITY, id);
225 peer_cfg->add_auth_cfg(peer_cfg, auth, local);
226 }
227
228 /**
229 * Attach authentication configs to peer config
230 */
231 static bool add_auth_cfgs(private_cmd_connection_t *this, peer_cfg_t *peer_cfg)
232 {
233 if (this->profile == PROF_UNDEF)
234 {
235 if (this->key_seen)
236 {
237 this->profile = PROF_V2_PUB;
238 }
239 else
240 {
241 this->profile = PROF_V2_EAP;
242 }
243 }
244 switch (this->profile)
245 {
246 case PROF_V2_PUB:
247 case PROF_V2_PUB_EAP:
248 case PROF_V1_PUB:
249 case PROF_V1_XAUTH:
250 case PROF_V1_PUB_AM:
251 case PROF_V1_XAUTH_AM:
252 if (!this->key_seen)
253 {
254 DBG1(DBG_CFG, "missing private key for profile %N",
255 profile_names, this->profile);
256 return FALSE;
257 }
258 break;
259 default:
260 break;
261 }
262
263 switch (this->profile)
264 {
265 case PROF_V2_PUB:
266 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
267 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_ANY);
268 break;
269 case PROF_V2_EAP:
270 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_EAP);
271 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_ANY);
272 break;
273 case PROF_V2_PUB_EAP:
274 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
275 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_EAP);
276 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_ANY);
277 break;
278 case PROF_V1_PUB:
279 case PROF_V1_PUB_AM:
280 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
281 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PUBKEY);
282 break;
283 case PROF_V1_XAUTH:
284 case PROF_V1_XAUTH_AM:
285 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
286 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_XAUTH);
287 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PUBKEY);
288 break;
289 case PROF_V1_XAUTH_PSK:
290 case PROF_V1_XAUTH_PSK_AM:
291 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PSK);
292 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_XAUTH);
293 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PSK);
294 break;
295 case PROF_V1_HYBRID:
296 case PROF_V1_HYBRID_AM:
297 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_XAUTH);
298 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PUBKEY);
299 break;
300 default:
301 return FALSE;
302 }
303 return TRUE;
304 }
305
306 /**
307 * Attach child config to peer config
308 */
309 static child_cfg_t* create_child_cfg(private_cmd_connection_t *this)
310 {
311 child_cfg_t *child_cfg;
312 traffic_selector_t *ts;
313 lifetime_cfg_t lifetime = {
314 .time = {
315 .life = 10800 /* 3h */,
316 .rekey = 10200 /* 2h50min */,
317 .jitter = 300 /* 5min */
318 }
319 };
320
321 child_cfg = child_cfg_create("cmd", &lifetime,
322 NULL, FALSE, MODE_TUNNEL, /* updown, hostaccess */
323 ACTION_NONE, ACTION_NONE, ACTION_NONE, FALSE,
324 0, 0, NULL, NULL, 0);
325 child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
326 while (this->local_ts->remove_first(this->local_ts, (void**)&ts) == SUCCESS)
327 {
328 child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
329 }
330 if (this->remote_ts->get_count(this->remote_ts) == 0)
331 {
332 /* add a 0.0.0.0/0 TS for remote side if none given */
333 ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE,
334 "0.0.0.0", 0, "255.255.255.255", 65535);
335 this->remote_ts->insert_last(this->remote_ts, ts);
336 }
337 while (this->remote_ts->remove_first(this->remote_ts,
338 (void**)&ts) == SUCCESS)
339 {
340 child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
341 }
342
343 return child_cfg;
344 }
345
346 /**
347 * Initiate the configured connection
348 */
349 static job_requeue_t initiate(private_cmd_connection_t *this)
350 {
351 peer_cfg_t *peer_cfg;
352 child_cfg_t *child_cfg;
353 pid_t pid = this->pid;
354
355 if (!this->host)
356 {
357 DBG1(DBG_CFG, "unable to initiate, missing --host option");
358 terminate(pid);
359 return JOB_REQUEUE_NONE;
360 }
361 if (!this->identity)
362 {
363 DBG1(DBG_CFG, "unable to initiate, missing --identity option");
364 terminate(pid);
365 return JOB_REQUEUE_NONE;
366 }
367
368 peer_cfg = create_peer_cfg(this);
369
370 if (!add_auth_cfgs(this, peer_cfg))
371 {
372 peer_cfg->destroy(peer_cfg);
373 terminate(pid);
374 return JOB_REQUEUE_NONE;
375 }
376
377 child_cfg = create_child_cfg(this);
378 peer_cfg->add_child_cfg(peer_cfg, child_cfg->get_ref(child_cfg));
379
380 if (charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
381 controller_cb_empty, NULL, 0) != SUCCESS)
382 {
383 terminate(pid);
384 }
385 return JOB_REQUEUE_NONE;
386 }
387
388 /**
389 * Create a traffic selector from string, add to list
390 */
391 static void add_ts(private_cmd_connection_t *this,
392 linked_list_t *list, char *string)
393 {
394 traffic_selector_t *ts;
395
396 ts = traffic_selector_create_from_cidr(string, 0, 0, 65535);
397 if (!ts)
398 {
399 DBG1(DBG_CFG, "invalid traffic selector: %s", string);
400 exit(1);
401 }
402 list->insert_last(list, ts);
403 }
404
405 /**
406 * Parse profile name identifier
407 */
408 static void set_profile(private_cmd_connection_t *this, char *name)
409 {
410 int profile;
411
412 profile = enum_from_name(profile_names, name);
413 if (profile == -1)
414 {
415 DBG1(DBG_CFG, "unknown connection profile: %s", name);
416 exit(1);
417 }
418 this->profile = profile;
419 }
420
421 METHOD(cmd_connection_t, handle, bool,
422 private_cmd_connection_t *this, cmd_option_type_t opt, char *arg)
423 {
424 switch (opt)
425 {
426 case CMD_OPT_HOST:
427 this->host = arg;
428 break;
429 case CMD_OPT_REMOTE_IDENTITY:
430 this->server = arg;
431 break;
432 case CMD_OPT_IDENTITY:
433 this->identity = arg;
434 break;
435 case CMD_OPT_EAP_IDENTITY:
436 case CMD_OPT_XAUTH_USER:
437 this->xautheap = arg;
438 break;
439 case CMD_OPT_RSA:
440 case CMD_OPT_AGENT:
441 case CMD_OPT_PKCS12:
442 this->key_seen = TRUE;
443 break;
444 case CMD_OPT_LOCAL_TS:
445 add_ts(this, this->local_ts, arg);
446 break;
447 case CMD_OPT_REMOTE_TS:
448 add_ts(this, this->remote_ts, arg);
449 break;
450 case CMD_OPT_PROFILE:
451 set_profile(this, arg);
452 break;
453 default:
454 return FALSE;
455 }
456 return TRUE;
457 }
458
459 METHOD(cmd_connection_t, destroy, void,
460 private_cmd_connection_t *this)
461 {
462 this->local_ts->destroy_offset(this->local_ts,
463 offsetof(traffic_selector_t, destroy));
464 this->remote_ts->destroy_offset(this->remote_ts,
465 offsetof(traffic_selector_t, destroy));
466 free(this);
467 }
468
469 /**
470 * See header
471 */
472 cmd_connection_t *cmd_connection_create()
473 {
474 private_cmd_connection_t *this;
475
476 INIT(this,
477 .public = {
478 .handle = _handle,
479 .destroy = _destroy,
480 },
481 .pid = getpid(),
482 .local_ts = linked_list_create(),
483 .remote_ts = linked_list_create(),
484 .profile = PROF_UNDEF,
485 );
486
487 /* always include the virtual IP in traffic selector list */
488 this->local_ts->insert_last(this->local_ts,
489 traffic_selector_create_dynamic(0, 0, 65535));
490
491 /* queue job, gets initiated as soon as we are up and running */
492 lib->processor->queue_job(lib->processor,
493 (job_t*)callback_job_create_with_prio(
494 (callback_job_cb_t)initiate, this, NULL,
495 (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
496
497 return &this->public;
498 }