]> git.ipfire.org Git - people/ms/strongswan.git/blob - src/charon-cmd/cmd/cmd_connection.c
8b42befe964c07603a4d3074c175f13b0e028c3a
[people/ms/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 auth->add(auth, AUTH_RULE_IDENTITY_LOOSE, TRUE);
184 }
185 auth->add(auth, AUTH_RULE_IDENTITY, id);
186 peer_cfg->add_auth_cfg(peer_cfg, auth, local);
187 }
188
189 /**
190 * Attach authentication configs to peer config
191 */
192 static bool add_auth_cfgs(private_cmd_connection_t *this, peer_cfg_t *peer_cfg)
193 {
194 if (this->profile == PROF_UNDEF)
195 {
196 if (this->key_seen)
197 {
198 this->profile = PROF_V2_PUB;
199 }
200 else
201 {
202 this->profile = PROF_V2_EAP;
203 }
204 }
205 switch (this->profile)
206 {
207 case PROF_V2_PUB:
208 case PROF_V2_PUB_EAP:
209 case PROF_V1_PUB:
210 case PROF_V1_XAUTH:
211 if (!this->key_seen)
212 {
213 DBG1(DBG_CFG, "missing private key for profile %N",
214 profile_names, this->profile);
215 return FALSE;
216 }
217 break;
218 default:
219 break;
220 }
221
222 switch (this->profile)
223 {
224 case PROF_V2_PUB:
225 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
226 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_ANY);
227 break;
228 case PROF_V2_EAP:
229 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_EAP);
230 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_ANY);
231 break;
232 case PROF_V2_PUB_EAP:
233 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
234 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_EAP);
235 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_ANY);
236 break;
237 case PROF_V1_PUB:
238 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
239 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PUBKEY);
240 break;
241 case PROF_V1_XAUTH:
242 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PUBKEY);
243 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_XAUTH);
244 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PUBKEY);
245 break;
246 case PROF_V1_XAUTH_PSK:
247 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_PSK);
248 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_XAUTH);
249 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PSK);
250 break;
251 case PROF_V1_HYBRID:
252 add_auth_cfg(this, peer_cfg, TRUE, AUTH_CLASS_XAUTH);
253 add_auth_cfg(this, peer_cfg, FALSE, AUTH_CLASS_PUBKEY);
254 break;
255 default:
256 return FALSE;
257 }
258 return TRUE;
259 }
260
261 /**
262 * Attach child config to peer config
263 */
264 static child_cfg_t* create_child_cfg(private_cmd_connection_t *this)
265 {
266 child_cfg_t *child_cfg;
267 traffic_selector_t *ts;
268 lifetime_cfg_t lifetime = {
269 .time = {
270 .life = 10800 /* 3h */,
271 .rekey = 10200 /* 2h50min */,
272 .jitter = 300 /* 5min */
273 }
274 };
275
276 child_cfg = child_cfg_create("cmd", &lifetime,
277 NULL, FALSE, MODE_TUNNEL, /* updown, hostaccess */
278 ACTION_NONE, ACTION_NONE, ACTION_NONE, FALSE,
279 0, 0, NULL, NULL, 0);
280 child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
281 while (this->local_ts->remove_first(this->local_ts, (void**)&ts) == SUCCESS)
282 {
283 child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
284 }
285 if (this->remote_ts->get_count(this->remote_ts) == 0)
286 {
287 /* add a 0.0.0.0/0 TS for remote side if none given */
288 ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE,
289 "0.0.0.0", 0, "255.255.255.255", 65535);
290 this->remote_ts->insert_last(this->remote_ts, ts);
291 }
292 while (this->remote_ts->remove_first(this->remote_ts,
293 (void**)&ts) == SUCCESS)
294 {
295 child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
296 }
297
298 return child_cfg;
299 }
300
301 /**
302 * Initiate the configured connection
303 */
304 static job_requeue_t initiate(private_cmd_connection_t *this)
305 {
306 peer_cfg_t *peer_cfg;
307 child_cfg_t *child_cfg;
308
309 if (!this->host)
310 {
311 DBG1(DBG_CFG, "unable to initiate, missing --host option");
312 terminate(this);
313 return JOB_REQUEUE_NONE;
314 }
315 if (!this->identity)
316 {
317 DBG1(DBG_CFG, "unable to initiate, missing --identity option");
318 terminate(this);
319 return JOB_REQUEUE_NONE;
320 }
321
322 peer_cfg = create_peer_cfg(this);
323
324 if (!add_auth_cfgs(this, peer_cfg))
325 {
326 peer_cfg->destroy(peer_cfg);
327 terminate(this);
328 return JOB_REQUEUE_NONE;
329 }
330
331 child_cfg = create_child_cfg(this);
332 peer_cfg->add_child_cfg(peer_cfg, child_cfg->get_ref(child_cfg));
333
334 if (charon->controller->initiate(charon->controller, peer_cfg, child_cfg,
335 controller_cb_empty, NULL, 0) != SUCCESS)
336 {
337 terminate(this);
338 }
339 return JOB_REQUEUE_NONE;
340 }
341
342 /**
343 * Create a traffic selector from string, add to list
344 */
345 static void add_ts(private_cmd_connection_t *this,
346 linked_list_t *list, char *string)
347 {
348 traffic_selector_t *ts;
349
350 ts = traffic_selector_create_from_cidr(string, 0, 0, 65535);
351 if (!ts)
352 {
353 DBG1(DBG_CFG, "invalid traffic selector: %s", string);
354 exit(1);
355 }
356 list->insert_last(list, ts);
357 }
358
359 /**
360 * Parse profile name identifier
361 */
362 static void set_profile(private_cmd_connection_t *this, char *name)
363 {
364 int profile;
365
366 profile = enum_from_name(profile_names, name);
367 if (profile == -1)
368 {
369 DBG1(DBG_CFG, "unknown connection profile: %s", name);
370 exit(1);
371 }
372 this->profile = profile;
373 }
374
375 METHOD(cmd_connection_t, handle, bool,
376 private_cmd_connection_t *this, cmd_option_type_t opt, char *arg)
377 {
378 switch (opt)
379 {
380 case CMD_OPT_HOST:
381 this->host = arg;
382 break;
383 case CMD_OPT_REMOTE_IDENTITY:
384 this->server = arg;
385 break;
386 case CMD_OPT_IDENTITY:
387 this->identity = arg;
388 break;
389 case CMD_OPT_RSA:
390 case CMD_OPT_AGENT:
391 this->key_seen = TRUE;
392 break;
393 case CMD_OPT_LOCAL_TS:
394 add_ts(this, this->local_ts, arg);
395 break;
396 case CMD_OPT_REMOTE_TS:
397 add_ts(this, this->remote_ts, arg);
398 break;
399 case CMD_OPT_PROFILE:
400 set_profile(this, arg);
401 break;
402 default:
403 return FALSE;
404 }
405 return TRUE;
406 }
407
408 METHOD(cmd_connection_t, destroy, void,
409 private_cmd_connection_t *this)
410 {
411 this->local_ts->destroy_offset(this->local_ts,
412 offsetof(traffic_selector_t, destroy));
413 this->remote_ts->destroy_offset(this->remote_ts,
414 offsetof(traffic_selector_t, destroy));
415 free(this);
416 }
417
418 /**
419 * See header
420 */
421 cmd_connection_t *cmd_connection_create()
422 {
423 private_cmd_connection_t *this;
424
425 INIT(this,
426 .public = {
427 .handle = _handle,
428 .destroy = _destroy,
429 },
430 .pid = getpid(),
431 .local_ts = linked_list_create(),
432 .remote_ts = linked_list_create(),
433 .profile = PROF_UNDEF,
434 );
435
436 /* always include the virtual IP in traffic selector list */
437 this->local_ts->insert_last(this->local_ts,
438 traffic_selector_create_dynamic(0, 0, 65535));
439
440 /* queue job, gets initiated as soon as we are up and running */
441 lib->processor->queue_job(lib->processor,
442 (job_t*)callback_job_create_with_prio(
443 (callback_job_cb_t)initiate, this, NULL,
444 (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
445
446 return &this->public;
447 }