36ff2dd44710c6f7d58934fcd649183fb5e6cb41
[strongswan.git] / src / libimcv / plugins / imc_scanner / imc_scanner.c
1 /*
2 * Copyright (C) 2011-2012 Andreas Steffen
3 * HSR Hochschule fuer Technik Rapperswil
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 "imc_scanner_state.h"
17
18 #include <imc/imc_agent.h>
19 #include <imc/imc_msg.h>
20 #include <ietf/ietf_attr.h>
21 #include <ietf/ietf_attr_attr_request.h>
22 #include <ietf/ietf_attr_port_filter.h>
23 #include <ietf/ietf_attr_remediation_instr.h>
24
25 #include <tncif_pa_subtypes.h>
26
27 #include <pen/pen.h>
28 #include <utils/lexparser.h>
29 #include <utils/debug.h>
30
31 #include <stdio.h>
32
33 /* IMC definitions */
34
35 static const char imc_name[] = "Scanner";
36
37 static pen_type_t msg_types[] = {
38 { PEN_IETF, PA_SUBTYPE_IETF_VPN }
39 };
40
41 static imc_agent_t *imc_scanner;
42
43 /**
44 * see section 3.8.1 of TCG TNC IF-IMC Specification 1.3
45 */
46 TNC_Result TNC_IMC_Initialize(TNC_IMCID imc_id,
47 TNC_Version min_version,
48 TNC_Version max_version,
49 TNC_Version *actual_version)
50 {
51 if (imc_scanner)
52 {
53 DBG1(DBG_IMC, "IMC \"%s\" has already been initialized", imc_name);
54 return TNC_RESULT_ALREADY_INITIALIZED;
55 }
56 imc_scanner = imc_agent_create(imc_name, msg_types, countof(msg_types),
57 imc_id, actual_version);
58 if (!imc_scanner)
59 {
60 return TNC_RESULT_FATAL;
61 }
62 if (min_version > TNC_IFIMC_VERSION_1 || max_version < TNC_IFIMC_VERSION_1)
63 {
64 DBG1(DBG_IMC, "no common IF-IMC version");
65 return TNC_RESULT_NO_COMMON_VERSION;
66 }
67 return TNC_RESULT_SUCCESS;
68 }
69
70 /**
71 * see section 3.8.2 of TCG TNC IF-IMC Specification 1.3
72 */
73 TNC_Result TNC_IMC_NotifyConnectionChange(TNC_IMCID imc_id,
74 TNC_ConnectionID connection_id,
75 TNC_ConnectionState new_state)
76 {
77 imc_state_t *state;
78
79 if (!imc_scanner)
80 {
81 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
82 return TNC_RESULT_NOT_INITIALIZED;
83 }
84 switch (new_state)
85 {
86 case TNC_CONNECTION_STATE_CREATE:
87 state = imc_scanner_state_create(connection_id);
88 return imc_scanner->create_state(imc_scanner, state);
89 case TNC_CONNECTION_STATE_HANDSHAKE:
90 if (imc_scanner->change_state(imc_scanner, connection_id, new_state,
91 &state) != TNC_RESULT_SUCCESS)
92 {
93 return TNC_RESULT_FATAL;
94 }
95 state->set_result(state, imc_id,
96 TNC_IMV_EVALUATION_RESULT_DONT_KNOW);
97 return TNC_RESULT_SUCCESS;
98 case TNC_CONNECTION_STATE_DELETE:
99 return imc_scanner->delete_state(imc_scanner, connection_id);
100 default:
101 return imc_scanner->change_state(imc_scanner, connection_id,
102 new_state, NULL);
103 }
104 }
105
106 /**
107 * Determine all TCP and UDP server sockets listening on physical interfaces
108 */
109 static bool do_netstat(ietf_attr_port_filter_t *attr)
110 {
111 FILE *file;
112 char buf[BUF_LEN];
113 chunk_t line, token;
114 int n = 0;
115 bool success = FALSE;
116 const char loopback_v4[] = "127.0.0.1";
117 const char loopback_v6[] = "::1";
118
119 /* Open a pipe stream for reading the output of the netstat commmand */
120 file = popen("/bin/netstat -n -l -p -4 -6 --inet", "r");
121 if (!file)
122 {
123 DBG1(DBG_IMC, "failed to run netstat command");
124 return FALSE;
125 }
126
127 /* Read the output a line at a time */
128 while (fgets(buf, sizeof(buf), file))
129 {
130 u_char *pos;
131 u_int8_t new_protocol, protocol;
132 u_int16_t new_port, port;
133 int i;
134 enumerator_t *enumerator;
135 bool allowed, found = FALSE;
136
137 DBG2(DBG_IMC, "%.*s", (int)(strlen(buf)-1), buf);
138
139 if (n++ < 2)
140 {
141 /* skip the first two header lines */
142 continue;
143 }
144 line = chunk_create(buf, strlen(buf));
145
146 /* Extract the IP protocol type */
147 if (!extract_token(&token, ' ', &line))
148 {
149 DBG1(DBG_IMC, "protocol field in netstat output not found");
150 goto end;
151 }
152 if (match("tcp", &token) || match("tcp6", &token))
153 {
154 new_protocol = IPPROTO_TCP;
155 }
156 else if (match("udp", &token) || match("udp6", &token))
157 {
158 new_protocol = IPPROTO_UDP;
159 }
160 else
161 {
162 DBG1(DBG_IMC, "skipped unknown IP protocol in netstat output");
163 continue;
164 }
165
166 /* Skip the Recv-Q and Send-Q fields */
167 for (i = 0; i < 3; i++)
168 {
169 if (!eat_whitespace(&line) || !extract_token(&token, ' ', &line))
170 {
171 token = chunk_empty;
172 break;
173 }
174 }
175 if (token.len == 0)
176 {
177 DBG1(DBG_IMC, "local address field in netstat output not found");
178 goto end;
179 }
180
181 /* Find the local port appended to the local address */
182 pos = token.ptr + token.len;
183 while (*--pos != ':' && --token.len);
184 if (*pos != ':')
185 {
186 DBG1(DBG_IMC, "local port field in netstat output not found");
187 goto end;
188 }
189 token.len--;
190
191 /* ignore ports of IPv4 and IPv6 loopback interfaces */
192 if ((token.len == strlen(loopback_v4) &&
193 memeq(loopback_v4, token.ptr, token.len)) ||
194 (token.len == strlen(loopback_v6) &&
195 memeq(loopback_v6, token.ptr, token.len)))
196 {
197 continue;
198 }
199
200 /* convert the port string to an integer */
201 new_port = atoi(pos+1);
202
203 /* check if the there is already a port entry */
204 enumerator = attr->create_port_enumerator(attr);
205 while (enumerator->enumerate(enumerator, &allowed, &protocol, &port))
206 {
207 if (new_port == port && new_protocol == protocol)
208 {
209 found = TRUE;
210 }
211 }
212 enumerator->destroy(enumerator);
213
214 /* Skip the duplicate port entry */
215 if (found)
216 {
217 continue;
218 }
219
220 /* Add new port entry */
221 attr->add_port(attr, FALSE, new_protocol, new_port);
222 }
223
224 /* Successfully completed the parsing of the netstat output */
225 success = TRUE;
226
227 end:
228 /* Close the pipe stream */
229 pclose(file);
230 return success;
231 }
232
233 /**
234 * Add IETF Port Filter attribute to the send queue
235 */
236 static TNC_Result add_port_filter(imc_msg_t *msg)
237 {
238 pa_tnc_attr_t *attr;
239 ietf_attr_port_filter_t *attr_port_filter;
240
241 attr = ietf_attr_port_filter_create();
242 attr->set_noskip_flag(attr, TRUE);
243 attr_port_filter = (ietf_attr_port_filter_t*)attr;
244 if (!do_netstat(attr_port_filter))
245 {
246 attr->destroy(attr);
247 return TNC_RESULT_FATAL;
248 }
249 msg->add_attribute(msg, attr);
250
251 return TNC_RESULT_SUCCESS;
252 }
253
254 /**
255 * see section 3.8.3 of TCG TNC IF-IMC Specification 1.3
256 */
257 TNC_Result TNC_IMC_BeginHandshake(TNC_IMCID imc_id,
258 TNC_ConnectionID connection_id)
259 {
260 imc_state_t *state;
261 imc_msg_t *out_msg;
262 TNC_Result result = TNC_RESULT_SUCCESS;
263
264 if (!imc_scanner)
265 {
266 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
267 return TNC_RESULT_NOT_INITIALIZED;
268 }
269 if (!imc_scanner->get_state(imc_scanner, connection_id, &state))
270 {
271 return TNC_RESULT_FATAL;
272 }
273 if (lib->settings->get_bool(lib->settings,
274 "libimcv.plugins.imc-scanner.send_ports", TRUE))
275 {
276 out_msg = imc_msg_create(imc_scanner, state, connection_id, imc_id,
277 TNC_IMVID_ANY, msg_types[0]);
278 result = add_port_filter(out_msg);
279 if (result == TNC_RESULT_SUCCESS)
280 {
281 /* send PA-TNC message with the excl flag not set */
282 result = out_msg->send(out_msg, FALSE);
283 }
284 out_msg->destroy(out_msg);
285 }
286
287 return result;
288 }
289
290 static TNC_Result receive_message(imc_msg_t *in_msg)
291 {
292 imc_msg_t *out_msg;
293 enumerator_t *enumerator;
294 pa_tnc_attr_t *attr;
295 pen_type_t attr_type;
296 TNC_Result result = TNC_RESULT_SUCCESS;
297 bool fatal_error = FALSE;
298
299 /* parse received PA-TNC message and handle local and remote errors */
300 result = in_msg->receive(in_msg, &fatal_error);
301 if (result != TNC_RESULT_SUCCESS)
302 {
303 return result;
304 }
305 out_msg = imc_msg_create_as_reply(in_msg);
306
307 /* analyze PA-TNC attributes */
308 enumerator = in_msg->create_attribute_enumerator(in_msg);
309 while (enumerator->enumerate(enumerator, &attr))
310 {
311 attr_type = attr->get_type(attr);
312
313 if (attr_type.vendor_id != PEN_IETF)
314 {
315 continue;
316 }
317 if (attr_type.type == IETF_ATTR_ATTRIBUTE_REQUEST)
318 {
319 ietf_attr_attr_request_t *attr_cast;
320 pen_type_t *entry;
321 enumerator_t *e;
322
323 attr_cast = (ietf_attr_attr_request_t*)attr;
324
325 e = attr_cast->create_enumerator(attr_cast);
326 while (e->enumerate(e, &entry))
327 {
328 if (entry->vendor_id != PEN_IETF)
329 {
330 continue;
331 }
332 switch (entry->type)
333 {
334 case IETF_ATTR_PORT_FILTER:
335 result = add_port_filter(out_msg);
336 break;
337 default:
338 break;
339 }
340 }
341 e->destroy(e);
342 }
343 else if (attr_type.type == IETF_ATTR_REMEDIATION_INSTRUCTIONS)
344 {
345 ietf_attr_remediation_instr_t *attr_cast;
346 pen_type_t parameters_type;
347 chunk_t parameters, string, lang_code;
348
349 attr_cast = (ietf_attr_remediation_instr_t*)attr;
350 parameters_type = attr_cast->get_parameters_type(attr_cast);
351 parameters = attr_cast->get_parameters(attr_cast);
352
353 if (parameters_type.vendor_id == PEN_IETF)
354 {
355 switch (parameters_type.type)
356 {
357 case IETF_REMEDIATION_PARAMETERS_URI:
358 DBG1(DBG_IMC, "remediation uri: '%.*s'",
359 parameters.len, parameters.ptr);
360 break;
361 case IETF_REMEDIATION_PARAMETERS_STRING:
362 string = attr_cast->get_string(attr_cast, &lang_code);
363 DBG1(DBG_IMC, "remediation string: '%.*s' [%.*s]",
364 string.len, string.ptr,
365 lang_code.len, lang_code.ptr);
366 break;
367 default:
368 DBG1(DBG_IMC, "remediation parameters %B", &parameters);
369 }
370 }
371 else
372 {
373 DBG1(DBG_IMC, "remediation parameters %B", &parameters);
374 }
375 }
376 }
377 enumerator->destroy(enumerator);
378
379 if (fatal_error)
380 {
381 result = TNC_RESULT_FATAL;
382 }
383 else if (result == TNC_RESULT_SUCCESS)
384 {
385 result = out_msg->send(out_msg, TRUE);
386 }
387 out_msg->destroy(out_msg);
388
389 return result;
390 }
391
392 /**
393 * see section 3.8.4 of TCG TNC IF-IMC Specification 1.3
394
395 */
396 TNC_Result TNC_IMC_ReceiveMessage(TNC_IMCID imc_id,
397 TNC_ConnectionID connection_id,
398 TNC_BufferReference msg,
399 TNC_UInt32 msg_len,
400 TNC_MessageType msg_type)
401 {
402 imc_state_t *state;
403 imc_msg_t *in_msg;
404 TNC_Result result;
405
406 if (!imc_scanner)
407 {
408 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
409 return TNC_RESULT_NOT_INITIALIZED;
410 }
411 if (!imc_scanner->get_state(imc_scanner, connection_id, &state))
412 {
413 return TNC_RESULT_FATAL;
414 }
415
416 in_msg = imc_msg_create_from_data(imc_scanner, state, connection_id,
417 msg_type, chunk_create(msg, msg_len));
418 result = receive_message(in_msg);
419 in_msg->destroy(in_msg);
420
421 return result;
422 }
423
424 /**
425 * see section 3.8.6 of TCG TNC IF-IMV Specification 1.3
426 */
427 TNC_Result TNC_IMC_ReceiveMessageLong(TNC_IMCID imc_id,
428 TNC_ConnectionID connection_id,
429 TNC_UInt32 msg_flags,
430 TNC_BufferReference msg,
431 TNC_UInt32 msg_len,
432 TNC_VendorID msg_vid,
433 TNC_MessageSubtype msg_subtype,
434 TNC_UInt32 src_imv_id,
435 TNC_UInt32 dst_imc_id)
436 {
437 imc_state_t *state;
438 imc_msg_t *in_msg;
439 TNC_Result result;
440
441 if (!imc_scanner)
442 {
443 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
444 return TNC_RESULT_NOT_INITIALIZED;
445 }
446 if (!imc_scanner->get_state(imc_scanner, connection_id, &state))
447 {
448 return TNC_RESULT_FATAL;
449 }
450 in_msg = imc_msg_create_from_long_data(imc_scanner, state, connection_id,
451 src_imv_id, dst_imc_id, msg_vid, msg_subtype,
452 chunk_create(msg, msg_len));
453 result = receive_message(in_msg);
454 in_msg->destroy(in_msg);
455
456 return result;
457 }
458
459 /**
460 * see section 3.8.7 of TCG TNC IF-IMC Specification 1.3
461 */
462 TNC_Result TNC_IMC_BatchEnding(TNC_IMCID imc_id,
463 TNC_ConnectionID connection_id)
464 {
465 if (!imc_scanner)
466 {
467 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
468 return TNC_RESULT_NOT_INITIALIZED;
469 }
470 return TNC_RESULT_SUCCESS;
471 }
472
473 /**
474 * see section 3.8.8 of TCG TNC IF-IMC Specification 1.3
475 */
476 TNC_Result TNC_IMC_Terminate(TNC_IMCID imc_id)
477 {
478 if (!imc_scanner)
479 {
480 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
481 return TNC_RESULT_NOT_INITIALIZED;
482 }
483 imc_scanner->destroy(imc_scanner);
484 imc_scanner = NULL;
485
486 return TNC_RESULT_SUCCESS;
487 }
488
489 /**
490 * see section 4.2.8.1 of TCG TNC IF-IMC Specification 1.3
491 */
492 TNC_Result TNC_IMC_ProvideBindFunction(TNC_IMCID imc_id,
493 TNC_TNCC_BindFunctionPointer bind_function)
494 {
495 if (!imc_scanner)
496 {
497 DBG1(DBG_IMC, "IMC \"%s\" has not been initialized", imc_name);
498 return TNC_RESULT_NOT_INITIALIZED;
499 }
500 return imc_scanner->bind_functions(imc_scanner, bind_function);
501 }