d9b20df2858d86d87e282659378ed7562a5d7538
[strongswan.git] / src / libcharon / plugins / tnc_ifmap / tnc_ifmap_soap.c
1 /*
2 * Copyright (C) 2011 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 "tnc_ifmap_soap.h"
17
18 #include <debug.h>
19
20 #include <axis2_util.h>
21 #include <axis2_client.h>
22 #include <axiom_soap.h>
23
24 #define IFMAP_NS "http://www.trustedcomputinggroup.org/2010/IFMAP/2"
25 #define IFMAP_META_NS "http://www.trustedcomputinggroup.org/2010/IFMAP-METADATA/2"
26 #define IFMAP_LOGFILE "strongswan_ifmap.log"
27 #define IFMAP_SERVER "https://localhost:8443/"
28
29 typedef struct private_tnc_ifmap_soap_t private_tnc_ifmap_soap_t;
30
31 /**
32 * Private data of an tnc_ifmap_soap_t object.
33 */
34 struct private_tnc_ifmap_soap_t {
35
36 /**
37 * Public tnc_ifmap_soap_t interface.
38 */
39 tnc_ifmap_soap_t public;
40
41 /**
42 * Axis2/C environment
43 */
44 axutil_env_t *env;
45
46 /**
47 * Axis2 service client
48 */
49 axis2_svc_client_t* svc_client;
50
51 /**
52 * SOAP Session ID
53 */
54 char *session_id;
55
56 /**
57 * IF-MAP Publisher ID
58 */
59 char *ifmap_publisher_id;
60
61 /**
62 * PEP and PDP device name
63 */
64 char *device_name;
65
66 };
67
68 /**
69 * Send request and receive result via SOAP
70 */
71 static bool send_receive(private_tnc_ifmap_soap_t *this,
72 char *request_qname, axiom_node_t *request,
73 char *receipt_qname, axiom_node_t **result)
74
75 {
76 axiom_node_t *parent, *node;
77 axiom_element_t *el;
78 axutil_qname_t *qname;
79 bool success = FALSE;
80
81 /* send request and receive result */
82 DBG2(DBG_TNC, "sending ifmap %s", request_qname);
83
84 parent = axis2_svc_client_send_receive(this->svc_client, this->env, request);
85 if (!parent)
86 {
87 DBG1(DBG_TNC, "no ifmap %s received from MAP server", receipt_qname);
88 return FALSE;
89 }
90
91 /* pre-process result */
92 node = axiom_node_get_first_child(parent, this->env);
93 if (node && axiom_node_get_node_type(node, this->env) == AXIOM_ELEMENT)
94 {
95 el = (axiom_element_t *)axiom_node_get_data_element(node, this->env);
96
97 qname = axiom_element_get_qname(el, this->env, node);
98 success = streq(receipt_qname, axutil_qname_to_string(qname, this->env));
99 if (success)
100 {
101 DBG2(DBG_TNC, "received ifmap %s", receipt_qname);
102 if (result)
103 {
104 *result = parent;
105 }
106 else
107 {
108 /* no further processing requested */
109 axiom_node_free_tree(parent, this->env);
110 }
111 return TRUE;
112 }
113 /* TODO proper error handling */
114 DBG1(DBG_TNC, "%s", axiom_element_to_string(el, this->env, node));
115 }
116
117 /* free parent in the error case */
118 axiom_node_free_tree(parent, this->env);
119
120 return FALSE;
121 }
122
123 METHOD(tnc_ifmap_soap_t, newSession, bool,
124 private_tnc_ifmap_soap_t *this)
125 {
126 axiom_node_t *request, *result, *node;
127 axiom_element_t *el;
128 axiom_namespace_t *ns;
129 axis2_char_t *value;
130
131
132 /* build newSession request */
133 ns = axiom_namespace_create(this->env, IFMAP_NS, "ifmap");
134 el = axiom_element_create(this->env, NULL, "newSession", ns, &request);
135
136 /* send newSession request and receive newSessionResult */
137 if (!send_receive(this, "newSession", request, "newSessionResult", &result))
138 {
139 return FALSE;
140 }
141
142 /* process newSessionResult */
143 node = axiom_node_get_first_child(result, this->env);
144 el = (axiom_element_t *)axiom_node_get_data_element(node, this->env);
145
146 /* get session-id */
147 value = axiom_element_get_attribute_value_by_name(el, this->env,
148 "session-id");
149 this->session_id = strdup(value);
150
151 /* get ifmap-publisher-id */
152 value = axiom_element_get_attribute_value_by_name(el, this->env,
153 "ifmap-publisher-id");
154 this->ifmap_publisher_id = strdup(value);
155
156 DBG1(DBG_TNC, "session-id: %s, ifmap-publisher-id: %s",
157 this->session_id, this->ifmap_publisher_id);
158
159 /* set PEP and PDP device name (defaults to IF-MAP Publisher ID) */
160 this->device_name = lib->settings->get_str(lib->settings,
161 "charon.plugins.tnc-ifmap.device_name",
162 this->ifmap_publisher_id);
163 this->device_name = strdup(this->device_name);
164
165 /* free result */
166 axiom_node_free_tree(result, this->env);
167
168 return this->session_id && this->ifmap_publisher_id;
169 }
170
171 METHOD(tnc_ifmap_soap_t, purgePublisher, bool,
172 private_tnc_ifmap_soap_t *this)
173 {
174 axiom_node_t *request;
175 axiom_element_t *el;
176 axiom_namespace_t *ns;
177 axiom_attribute_t *attr;
178
179 /* build purgePublisher request */
180 ns = axiom_namespace_create(this->env, IFMAP_NS, "ifmap");
181 el = axiom_element_create(this->env, NULL, "purgePublisher", ns, &request);
182 attr = axiom_attribute_create(this->env, "session-id",
183 this->session_id, NULL);
184 axiom_element_add_attribute(el, this->env, attr, request);
185 attr = axiom_attribute_create(this->env, "ifmap-publisher-id",
186 this->ifmap_publisher_id, NULL);
187 axiom_element_add_attribute(el, this->env, attr, request);
188
189 /* send purgePublisher request and receive purgePublisherReceived */
190 return send_receive(this, "purgePublisher", request,
191 "purgePublisherReceived", NULL);
192 }
193
194 /**
195 * Create an access-request based on device_name and ike_sa_id
196 */
197 static axiom_node_t* create_access_request(private_tnc_ifmap_soap_t *this,
198 u_int32_t id)
199 {
200 axiom_element_t *el;
201 axiom_node_t *node;
202 axiom_attribute_t *attr;
203 char buf[BUF_LEN];
204
205 el = axiom_element_create(this->env, NULL, "access-request", NULL, &node);
206
207 snprintf(buf, BUF_LEN, "%s:%d", this->device_name, id);
208 attr = axiom_attribute_create(this->env, "name", buf, NULL);
209 axiom_element_add_attribute(el, this->env, attr, node);
210
211 return node;
212 }
213
214 /**
215 * Create an identity
216 */
217 static axiom_node_t* create_identity(private_tnc_ifmap_soap_t *this,
218 identification_t *id, bool is_user)
219 {
220 axiom_element_t *el;
221 axiom_node_t *node;
222 axiom_attribute_t *attr;
223 char buf[BUF_LEN], *id_type;
224
225 el = axiom_element_create(this->env, NULL, "identity", NULL, &node);
226
227 snprintf(buf, BUF_LEN, "%Y", id);
228 attr = axiom_attribute_create(this->env, "name", buf, NULL);
229 axiom_element_add_attribute(el, this->env, attr, node);
230
231 switch (id->get_type(id))
232 {
233 case ID_FQDN:
234 id_type = is_user ? "username" : "dns-name";
235 break;
236 case ID_RFC822_ADDR:
237 id_type = "email-address";
238 break;
239 case ID_DER_ASN1_DN:
240 id_type = "distinguished-name";
241 break;
242 default:
243 id_type = "other";
244 }
245 attr = axiom_attribute_create(this->env, "type", id_type, NULL);
246 axiom_element_add_attribute(el, this->env, attr, node);
247
248 return node;
249 }
250
251 /**
252 * Create an ip-address
253 */
254 static axiom_node_t* create_ip_address(private_tnc_ifmap_soap_t *this,
255 host_t *host)
256 {
257 axiom_element_t *el;
258 axiom_node_t *node;
259 axiom_attribute_t *attr;
260 char buf[BUF_LEN];
261
262 el = axiom_element_create(this->env, NULL, "ip-address", NULL, &node);
263
264 if (host->get_family(host) == AF_INET6)
265 {
266 chunk_t address;
267 int len, written, i;
268 char *pos;
269 bool first = TRUE;
270
271 /* output IPv6 address in canonical IF-MAP 2.0 format */
272 address = host->get_address(host);
273 pos = buf;
274 len = sizeof(buf);
275
276 for (i = 0; i < address.len; i = i + 2)
277 {
278 written = snprintf(pos, len, "%s%x", first ? "" : ":",
279 256*address.ptr[i] + address.ptr[i+1]);
280 if (written < 0 || written > len)
281 {
282 break;
283 }
284 pos += written;
285 len -= written;
286 first = FALSE;
287 }
288 }
289 else
290 {
291 snprintf(buf, BUF_LEN, "%H", host);
292 }
293 attr = axiom_attribute_create(this->env, "value", buf, NULL);
294 axiom_element_add_attribute(el, this->env, attr, node);
295
296 attr = axiom_attribute_create(this->env, "type",
297 host->get_family(host) == AF_INET ? "IPv4" : "IPv6", NULL);
298 axiom_element_add_attribute(el, this->env, attr, node);
299
300 return node;
301 }
302
303 /**
304 * Create a device
305 */
306 static axiom_node_t* create_device(private_tnc_ifmap_soap_t *this)
307 {
308 axiom_element_t *el;
309 axiom_node_t *node, *node2, *node3;
310 axiom_text_t *text;
311
312 el = axiom_element_create(this->env, NULL, "device", NULL, &node);
313 el = axiom_element_create(this->env, NULL, "name", NULL, &node2);
314 axiom_node_add_child(node, this->env, node2);
315 text = axiom_text_create(this->env, node2, this->device_name, &node3);
316
317 return node;
318 }
319
320 /**
321 * Create metadata
322 */
323 static axiom_node_t* create_metadata(private_tnc_ifmap_soap_t *this,
324 char *metadata)
325 {
326 axiom_element_t *el;
327 axiom_node_t *node, *node2;
328 axiom_attribute_t *attr;
329 axiom_namespace_t *ns_meta;
330
331 el = axiom_element_create(this->env, NULL, "metadata", NULL, &node);
332 ns_meta = axiom_namespace_create(this->env, IFMAP_META_NS, "meta");
333
334 el = axiom_element_create(this->env, NULL, metadata, ns_meta, &node2);
335 axiom_node_add_child(node, this->env, node2);
336 attr = axiom_attribute_create(this->env, "ifmap-cardinality", "singleValue",
337 NULL);
338 axiom_element_add_attribute(el, this->env, attr, node2);
339
340 return node;
341 }
342
343 /**
344 * Create delete filter
345 */
346 static axiom_node_t* create_delete_filter(private_tnc_ifmap_soap_t *this,
347 char *metadata)
348 {
349 axiom_element_t *el;
350 axiom_node_t *node;
351 axiom_attribute_t *attr;
352 char buf[BUF_LEN];
353
354 el = axiom_element_create(this->env, NULL, "delete", NULL, &node);
355
356 snprintf(buf, BUF_LEN, "meta:%s[@ifmap-publisher-id='%s']",
357 metadata, this->ifmap_publisher_id);
358 attr = axiom_attribute_create(this->env, "filter", buf, NULL);
359 axiom_element_add_attribute(el, this->env, attr, node);
360
361 return node;
362 }
363
364 METHOD(tnc_ifmap_soap_t, publish_ike_sa, bool,
365 private_tnc_ifmap_soap_t *this, u_int32_t ike_sa_id, identification_t *id,
366 bool is_user, host_t *host, bool up)
367 {
368 axiom_node_t *request, *node;
369 axiom_element_t *el;
370 axiom_namespace_t *ns, *ns_meta;
371 axiom_attribute_t *attr;
372
373 /* build publish request */
374 ns = axiom_namespace_create(this->env, IFMAP_NS, "ifmap");
375 el = axiom_element_create(this->env, NULL, "publish", ns, &request);
376 ns_meta = axiom_namespace_create(this->env, IFMAP_META_NS, "meta");
377 axiom_element_declare_namespace(el, this->env, request, ns_meta);
378 attr = axiom_attribute_create(this->env, "session-id", this->session_id,
379 NULL);
380 axiom_element_add_attribute(el, this->env, attr, request);
381
382 /**
383 * update or delete authenticated-as metadata
384 */
385 if (up)
386 {
387 el = axiom_element_create(this->env, NULL, "update", NULL, &node);
388 }
389 else
390 {
391 node = create_delete_filter(this, "authenticated-as");
392 }
393 axiom_node_add_child(request, this->env, node);
394
395 /* add access-request, identity and [if up] metadata */
396 axiom_node_add_child(node, this->env,
397 create_access_request(this, ike_sa_id));
398 axiom_node_add_child(node, this->env,
399 create_identity(this, id, is_user));
400 if (up)
401 {
402 axiom_node_add_child(node, this->env,
403 create_metadata(this, "authenticated-as"));
404 }
405
406 /**
407 * update or delete access-request-ip metadata
408 */
409 if (up)
410 {
411 el = axiom_element_create(this->env, NULL, "update", NULL, &node);
412 }
413 else
414 {
415 node = create_delete_filter(this, "access-request-ip");
416 }
417 axiom_node_add_child(request, this->env, node);
418
419 /* add access-request, ip-address and [if up] metadata */
420 axiom_node_add_child(node, this->env,
421 create_access_request(this, ike_sa_id));
422 axiom_node_add_child(node, this->env,
423 create_ip_address(this, host));
424 if (up)
425 {
426 axiom_node_add_child(node, this->env,
427 create_metadata(this, "access-request-ip"));
428 }
429
430 /**
431 * update or delete authenticated-by metadata
432 */
433 if (up)
434 {
435 el = axiom_element_create(this->env, NULL, "update", NULL, &node);
436 }
437 else
438 {
439 node = create_delete_filter(this, "authenticated-by");
440 }
441 axiom_node_add_child(request, this->env, node);
442
443 /* add access-request, device and [if up] metadata */
444 axiom_node_add_child(node, this->env,
445 create_access_request(this, ike_sa_id));
446 axiom_node_add_child(node, this->env,
447 create_device(this));
448 if (up)
449 {
450 axiom_node_add_child(node, this->env,
451 create_metadata(this, "authenticated-by"));
452 }
453
454 /* send publish request and receive publishReceived */
455 return send_receive(this, "publish", request, "publishReceived", NULL);
456 }
457
458 METHOD(tnc_ifmap_soap_t, publish_device_ip, bool,
459 private_tnc_ifmap_soap_t *this, host_t *host)
460 {
461 axiom_node_t *request, *node;
462 axiom_element_t *el;
463 axiom_namespace_t *ns, *ns_meta;
464 axiom_attribute_t *attr;
465
466 /* build publish request */
467 ns = axiom_namespace_create(this->env, IFMAP_NS, "ifmap");
468 el = axiom_element_create(this->env, NULL, "publish", ns, &request);
469 ns_meta = axiom_namespace_create(this->env, IFMAP_META_NS, "meta");
470 axiom_element_declare_namespace(el, this->env, request, ns_meta);
471 attr = axiom_attribute_create(this->env, "session-id", this->session_id,
472 NULL);
473 axiom_element_add_attribute(el, this->env, attr, request);
474 el = axiom_element_create(this->env, NULL, "update", NULL, &node);
475 axiom_node_add_child(request, this->env, node);
476
477 /* add device, ip-address and metadata */
478 axiom_node_add_child(node, this->env,
479 create_device(this));
480 axiom_node_add_child(node, this->env,
481 create_ip_address(this, host));
482 axiom_node_add_child(node, this->env,
483 create_metadata(this, "device-ip"));
484
485 /* send publish request and receive publishReceived */
486 return send_receive(this, "publish", request, "publishReceived", NULL);
487 }
488
489 METHOD(tnc_ifmap_soap_t, endSession, bool,
490 private_tnc_ifmap_soap_t *this)
491 {
492 axiom_node_t *request;
493 axiom_element_t *el;
494 axiom_namespace_t *ns;
495 axiom_attribute_t *attr;
496
497 /* build endSession request */
498 ns = axiom_namespace_create(this->env, IFMAP_NS, "ifmap");
499 el = axiom_element_create(this->env, NULL, "endSession", ns, &request);
500 attr = axiom_attribute_create(this->env, "session-id", this->session_id, NULL);
501 axiom_element_add_attribute(el, this->env, attr, request);
502
503 /* send endSession request and receive end SessionResult */
504 return send_receive(this, "endSession", request, "endSessionResult", NULL);
505 }
506
507 METHOD(tnc_ifmap_soap_t, destroy, void,
508 private_tnc_ifmap_soap_t *this)
509 {
510 if (this->session_id)
511 {
512 endSession(this);
513 free(this->session_id);
514 free(this->ifmap_publisher_id);
515 free(this->device_name);
516 }
517 if (this->svc_client)
518 {
519 axis2_svc_client_free(this->svc_client, this->env);
520 }
521 if (this->env)
522 {
523 axutil_env_free(this->env);
524 }
525 free(this);
526 }
527
528 /**
529 * See header
530 */
531 tnc_ifmap_soap_t *tnc_ifmap_soap_create()
532 {
533 private_tnc_ifmap_soap_t *this;
534 axis2_char_t *server, *client_home, *username, *password, *auth_type;
535 axis2_endpoint_ref_t* endpoint_ref = NULL;
536 axis2_options_t *options = NULL;
537
538 client_home = lib->settings->get_str(lib->settings,
539 "charon.plugins.tnc-ifmap.client_home",
540 AXIS2_GETENV("AXIS2C_HOME"));
541 server = lib->settings->get_str(lib->settings,
542 "charon.plugins.tnc-ifmap.server", IFMAP_SERVER);
543 auth_type = lib->settings->get_str(lib->settings,
544 "charon.plugins.tnc-ifmap.auth_type", "Basic");
545 username = lib->settings->get_str(lib->settings,
546 "charon.plugins.tnc-ifmap.username", NULL);
547 password = lib->settings->get_str(lib->settings,
548 "charon.plugins.tnc-ifmap.password", NULL);
549
550 if (!username || !password)
551 {
552 DBG1(DBG_TNC, "MAP client %s%s%s not defined",
553 (!username) ? "username" : "",
554 (!username && ! password) ? " and " : "",
555 (!password) ? "password" : "");
556 return NULL;
557 }
558
559 INIT(this,
560 .public = {
561 .newSession = _newSession,
562 .purgePublisher = _purgePublisher,
563 .publish_ike_sa = _publish_ike_sa,
564 .publish_device_ip = _publish_device_ip,
565 .endSession = _endSession,
566 .destroy = _destroy,
567 },
568 );
569
570 /* Create Axis2/C environment and options */
571 this->env = axutil_env_create_all(IFMAP_LOGFILE, AXIS2_LOG_LEVEL_TRACE);
572 options = axis2_options_create(this->env);
573
574 /* Define the IF-MAP server as the to endpoint reference */
575 endpoint_ref = axis2_endpoint_ref_create(this->env, server);
576 axis2_options_set_to(options, this->env, endpoint_ref);
577
578 /* Create the axis2 service client */
579 this->svc_client = axis2_svc_client_create(this->env, client_home);
580 if (!this->svc_client)
581 {
582 DBG1(DBG_TNC, "could not create axis2 service client");
583 AXIS2_LOG_ERROR(this->env->log, AXIS2_LOG_SI,
584 "Stub invoke FAILED: Error code: %d :: %s",
585 this->env->error->error_number,
586 AXIS2_ERROR_GET_MESSAGE(this->env->error));
587 destroy(this);
588 return NULL;
589 }
590
591 axis2_svc_client_set_options(this->svc_client, this->env, options);
592 axis2_options_set_http_auth_info(options, this->env, username, password,
593 auth_type);
594 DBG1(DBG_TNC, "connecting as MAP client '%s' to MAP server at '%s'",
595 username, server);
596
597 return &this->public;
598 }
599