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