diff options
Diffstat (limited to 'src/charon/control')
-rw-r--r-- | src/charon/control/interface_manager.c | 15 | ||||
-rw-r--r-- | src/charon/control/interface_manager.h | 14 | ||||
-rw-r--r-- | src/charon/control/interfaces/dbus_interface.c | 55 | ||||
-rwxr-xr-x | src/charon/control/interfaces/stroke_interface.c | 204 | ||||
-rw-r--r-- | src/charon/control/interfaces/xml_interface.c | 396 |
5 files changed, 492 insertions, 192 deletions
diff --git a/src/charon/control/interface_manager.c b/src/charon/control/interface_manager.c index 700174c5b..c71036567 100644 --- a/src/charon/control/interface_manager.c +++ b/src/charon/control/interface_manager.c @@ -290,6 +290,13 @@ static status_t initiate(private_interface_manager_t *this, } charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + if (callback == NULL) + { + /* don't wait for a result if no callback is specified */ + charon->bus->set_listen_state(charon->bus, FALSE); + return NEED_MORE; + } + /* wait until we get a result */ while (TRUE) { @@ -669,6 +676,14 @@ static void load_interfaces(private_interface_manager_t *this) closedir(dir); } +/** + * See header + */ +bool interface_manager_cb_empty(void *param, signal_t signal, level_t level, + ike_sa_t *ike_sa, char *format, va_list args) +{ + return TRUE; +} /** * Implementation of stroke_t.destroy. diff --git a/src/charon/control/interface_manager.h b/src/charon/control/interface_manager.h index 06a5fe6c4..3ee1f0e39 100644 --- a/src/charon/control/interface_manager.h +++ b/src/charon/control/interface_manager.h @@ -40,6 +40,15 @@ typedef bool(*interface_manager_cb_t)(void* param, signal_t signal, level_t level, ike_sa_t* ike_sa, char* format, va_list args); +/** + * @brief Empty callback function for interface_manager_t functions. + * + * If you wan't to do a syncrhonous call, but don't need a callback, pass + * this function to the interface_managers methods. + */ +bool interface_manager_cb_empty(void *param, signal_t signal, level_t level, + ike_sa_t *ike_sa, char *format, va_list args); + typedef struct interface_manager_t interface_manager_t; /** @@ -62,6 +71,11 @@ typedef struct interface_manager_t interface_manager_t; * use the manager to fullfill their tasks (initiating, terminating, ...). * The interface_manager starts actions by creating jobs. It then tries to * evaluate the result of the operation by listening on the bus. + * + * Passing NULL as callback to the managers function calls them asynchronously. + * If a callback is specified, they are called synchronoulsy. There is a default + * callback "interface_manager_cb_empty" if you wan't to call a function + * synchronously, but don't need a callback. * * @b Constructors: * - interface_manager_create() diff --git a/src/charon/control/interfaces/dbus_interface.c b/src/charon/control/interfaces/dbus_interface.c index d93a5d048..39226aaef 100644 --- a/src/charon/control/interfaces/dbus_interface.c +++ b/src/charon/control/interfaces/dbus_interface.c @@ -118,55 +118,6 @@ static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name) return found; } -/** - * get a peer configuration by its name, or a name of its children - */ -static peer_cfg_t *get_peer_cfg_by_name(char *name) -{ - iterator_t *i1, *i2; - peer_cfg_t *current, *found = NULL; - child_cfg_t *child; - - i1 = charon->backends->create_iterator(charon->backends); - while (i1->iterate(i1, (void**)¤t)) - { - /* compare peer_cfgs name first */ - if (streq(current->get_name(current), name)) - { - found = current; - found->get_ref(found); - break; - } - /* compare all child_cfg names otherwise */ - i2 = current->create_child_cfg_iterator(current); - while (i2->iterate(i2, (void**)&child)) - { - if (streq(child->get_name(child), name)) - { - found = current; - found->get_ref(found); - break; - } - } - i2->destroy(i2); - if (found) - { - break; - } - } - i1->destroy(i1); - return found; -} - -/** - * logging dummy - */ -static bool dbus_log(void *param, signal_t signal, level_t level, - ike_sa_t *ike_sa, char *format, va_list args) -{ - return TRUE; -} - /** * process NetworkManagers startConnection method call @@ -197,7 +148,7 @@ static bool start_connection(private_dbus_interface_t *this, DBusMessage* msg) } set_state(this, NM_VPN_STATE_STARTING); - peer_cfg = get_peer_cfg_by_name(name); + peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, name); if (peer_cfg) { free(this->name); @@ -205,8 +156,8 @@ static bool start_connection(private_dbus_interface_t *this, DBusMessage* msg) child_cfg = get_child_from_peer(peer_cfg, name); if (child_cfg) { - status = charon->interfaces->initiate(charon->interfaces, peer_cfg, - child_cfg, dbus_log, NULL); + status = charon->interfaces->initiate(charon->interfaces, + peer_cfg, child_cfg, interface_manager_cb_empty, NULL); } else { diff --git a/src/charon/control/interfaces/stroke_interface.c b/src/charon/control/interfaces/stroke_interface.c index 7885fc2e6..66ed423ae 100755 --- a/src/charon/control/interfaces/stroke_interface.c +++ b/src/charon/control/interfaces/stroke_interface.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2007 Tobias Brunner * Copyright (C) 2006-2007 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -38,6 +39,8 @@ #include <stroke.h> #include <daemon.h> #include <crypto/x509.h> +#include <crypto/ietf_attr_list.h> +#include <crypto/ac.h> #include <crypto/ca.h> #include <crypto/crl.h> #include <control/interface_manager.h> @@ -49,9 +52,6 @@ #define PATH_BUF 256 #define STROKE_THREADS 3 -struct sockaddr_un socket_addr = { AF_UNIX, STROKE_SOCKET}; - - typedef struct private_stroke_interface_t private_stroke_interface_t; /** @@ -229,14 +229,18 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out) { ike_cfg_t *ike_cfg; peer_cfg_t *peer_cfg; + peer_cfg_t *mediated_by_cfg = NULL; child_cfg_t *child_cfg; identification_t *my_id, *other_id; identification_t *my_ca = NULL; identification_t *other_ca = NULL; + identification_t *peer_id = NULL; bool my_ca_same = FALSE; bool other_ca_same =FALSE; host_t *my_host, *other_host, *my_subnet, *other_subnet; host_t *my_vip = NULL, *other_vip = NULL; + linked_list_t *my_groups = linked_list_create(); + linked_list_t *other_groups = linked_list_create(); proposal_t *proposal; traffic_selector_t *my_ts, *other_ts; char *interface; @@ -252,7 +256,12 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out) pop_string(msg, &msg->add_conn.algorithms.esp); DBG2(DBG_CFG, " ike=%s", msg->add_conn.algorithms.ike); DBG2(DBG_CFG, " esp=%s", msg->add_conn.algorithms.esp); - + pop_string(msg, &msg->add_conn.p2p.mediated_by); + pop_string(msg, &msg->add_conn.p2p.peerid); + DBG2(DBG_CFG, " p2p_mediation=%s", msg->add_conn.p2p.mediation ? "yes" : "no"); + DBG2(DBG_CFG, " p2p_mediated_by=%s", msg->add_conn.p2p.mediated_by); + DBG2(DBG_CFG, " p2p_peerid=%s", msg->add_conn.p2p.peerid); + my_host = msg->add_conn.me.address? host_create_from_string(msg->add_conn.me.address, IKE_PORT) : NULL; if (my_host == NULL) @@ -319,6 +328,49 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out) goto destroy_hosts; } +#ifdef P2P + if (msg->add_conn.p2p.mediation && msg->add_conn.p2p.mediated_by) + { + DBG1(DBG_CFG, "a mediation connection cannot be a" + " mediated connection at the same time, aborting"); + goto destroy_ids; + } + + if (msg->add_conn.p2p.mediated_by) + { + mediated_by_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, msg->add_conn.p2p.mediated_by); + if (!mediated_by_cfg) + { + DBG1(DBG_CFG, "mediation connection '%s' not found, aborting", + msg->add_conn.p2p.mediated_by); + goto destroy_ids; + } + + if (!mediated_by_cfg->is_mediation(mediated_by_cfg)) + { + DBG1(DBG_CFG, "connection '%s' as referred to by '%s' is" + "no mediation connection, aborting", + msg->add_conn.p2p.mediated_by, msg->add_conn.name); + goto destroy_ids; + } + } + + if (msg->add_conn.p2p.peerid) + { + peer_id = identification_create_from_string(msg->add_conn.p2p.peerid); + if (!peer_id) + { + DBG1(DBG_CFG, "invalid peer ID: %s\n", msg->add_conn.p2p.peerid); + goto destroy_ids; + } + } + else +#endif /* P2P */ + { + // no peer ID supplied, assume right ID + peer_id = other_id->clone(other_id); + } + my_subnet = host_create_from_string(msg->add_conn.me.subnet ? msg->add_conn.me.subnet : msg->add_conn.me.address, IKE_PORT); if (my_subnet == NULL) @@ -336,11 +388,11 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out) goto destroy_ids; } - if (msg->add_conn.me.virtual_ip) + if (msg->add_conn.me.virtual_ip && msg->add_conn.me.sourceip) { my_vip = host_create_from_string(msg->add_conn.me.sourceip, 0); } - if (msg->add_conn.other.virtual_ip) + if (msg->add_conn.other.virtual_ip && msg->add_conn.other.sourceip) { other_vip = host_create_from_string(msg->add_conn.other.sourceip, 0); } @@ -474,6 +526,11 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out) DBG2(DBG_CFG, " my ca: '%D'", my_ca); DBG2(DBG_CFG, " other ca:'%D'", other_ca); + if (msg->add_conn.other.groups) + { + ietfAttr_list_create_from_string(msg->add_conn.other.groups, other_groups); + } + /* have a look for an (almost) identical peer config to reuse */ iterator = charon->backends->create_iterator(charon->backends); while (iterator->iterate(iterator, (void**)&peer_cfg)) @@ -484,6 +541,7 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out) && my_host->equals(my_host, ike_cfg->get_my_host(ike_cfg)) && other_host->equals(other_host, ike_cfg->get_other_host(ike_cfg)) && other_ca->equals(other_ca, peer_cfg->get_other_ca(peer_cfg)) + && ietfAttr_list_equals(other_groups, peer_cfg->get_groups(peer_cfg)) && peer_cfg->get_ike_version(peer_cfg) == (msg->add_conn.ikev2 ? 2 : 1) && peer_cfg->get_auth_method(peer_cfg) == msg->add_conn.auth_method && peer_cfg->get_eap_type(peer_cfg) == msg->add_conn.eap_type) @@ -506,11 +564,15 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out) other_host->destroy(other_host); other_id->destroy(other_id); other_ca->destroy(other_ca); + peer_id->destroy(peer_id); + DESTROY_IF(mediated_by_cfg); + ietfAttr_list_destroy(my_groups); + ietfAttr_list_destroy(other_groups); } else { ike_cfg = ike_cfg_create(msg->add_conn.other.sendcert != CERT_NEVER_SEND, - my_host, other_host); + msg->add_conn.force_encap, my_host, other_host); if (msg->add_conn.algorithms.ike) { @@ -553,13 +615,15 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out) peer_cfg = peer_cfg_create(msg->add_conn.name, msg->add_conn.ikev2 ? 2 : 1, - ike_cfg, my_id, other_id, my_ca, other_ca, msg->add_conn.me.sendcert, + ike_cfg, my_id, other_id, my_ca, other_ca, other_groups, + msg->add_conn.me.sendcert, msg->add_conn.auth_method, msg->add_conn.eap_type, msg->add_conn.rekey.tries, msg->add_conn.rekey.ike_lifetime, msg->add_conn.rekey.ike_lifetime - msg->add_conn.rekey.margin, msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100, - msg->add_conn.rekey.reauth, msg->add_conn.dpd.delay, - msg->add_conn.dpd.action,my_vip, other_vip); + msg->add_conn.rekey.reauth, msg->add_conn.mobike, + msg->add_conn.dpd.delay, msg->add_conn.dpd.action, my_vip, other_vip, + msg->add_conn.p2p.mediation, mediated_by_cfg, peer_id); } child_cfg = child_cfg_create( @@ -621,6 +685,8 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out) destroy_ids: my_id->destroy(my_id); other_id->destroy(other_id); + DESTROY_IF(mediated_by_cfg); + DESTROY_IF(peer_id); destroy_hosts: my_host->destroy(my_host); @@ -633,7 +699,8 @@ destroy_hosts: static void stroke_del_conn(stroke_msg_t *msg, FILE *out) { iterator_t *peer_iter, *child_iter; - peer_cfg_t *peer, *child; + peer_cfg_t *peer; + child_cfg_t *child; pop_string(msg, &(msg->del_conn.name)); DBG1(DBG_CFG, "received stroke: delete connection '%s'", msg->del_conn.name); @@ -706,46 +773,6 @@ static bool stroke_log(stroke_log_info_t *info, signal_t signal, level_t level, } /** - * get a peer configuration by its name, or a name of its children - */ -static peer_cfg_t *get_peer_cfg_by_name(char *name) -{ - iterator_t *i1, *i2; - peer_cfg_t *current, *found = NULL; - child_cfg_t *child; - - i1 = charon->backends->create_iterator(charon->backends); - while (i1->iterate(i1, (void**)¤t)) - { - /* compare peer_cfgs name first */ - if (streq(current->get_name(current), name)) - { - found = current; - found->get_ref(found); - break; - } - /* compare all child_cfg names otherwise */ - i2 = current->create_child_cfg_iterator(current); - while (i2->iterate(i2, (void**)&child)) - { - if (streq(child->get_name(child), name)) - { - found = current; - found->get_ref(found); - break; - } - } - i2->destroy(i2); - if (found) - { - break; - } - } - i1->destroy(i1); - return found; -} - -/** * initiate a connection by name */ static void stroke_initiate(stroke_msg_t *msg, FILE *out) @@ -757,7 +784,8 @@ static void stroke_initiate(stroke_msg_t *msg, FILE *out) pop_string(msg, &(msg->initiate.name)); DBG1(DBG_CFG, "received stroke: initiate '%s'", msg->initiate.name); - peer_cfg = get_peer_cfg_by_name(msg->initiate.name); + peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, + msg->initiate.name); if (peer_cfg == NULL) { fprintf(out, "no config named '%s'\n", msg->initiate.name); @@ -779,10 +807,18 @@ static void stroke_initiate(stroke_msg_t *msg, FILE *out) return; } - info.out = out; - info.level = msg->output_verbosity; - charon->interfaces->initiate(charon->interfaces, peer_cfg, child_cfg, - (interface_manager_cb_t)stroke_log, &info); + if (msg->output_verbosity < 0) + { + charon->interfaces->initiate(charon->interfaces, peer_cfg, child_cfg, + NULL, NULL); + } + else + { + info.out = out; + info.level = msg->output_verbosity; + charon->interfaces->initiate(charon->interfaces, peer_cfg, child_cfg, + (interface_manager_cb_t)stroke_log, &info); + } } /** @@ -797,7 +833,8 @@ static void stroke_route(stroke_msg_t *msg, FILE *out) pop_string(msg, &(msg->route.name)); DBG1(DBG_CFG, "received stroke: route '%s'", msg->route.name); - peer_cfg = get_peer_cfg_by_name(msg->route.name); + peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, + msg->route.name); if (peer_cfg == NULL) { fprintf(out, "no config named '%s'\n", msg->route.name); @@ -1079,10 +1116,10 @@ static void log_ike_sa(FILE *out, ike_sa_t *ike_sa, bool all) if (all) { - fprintf(out, "%12s[%d]: IKE SPIs: 0x%0llx_i%s 0x%0llx_r%s, ", + fprintf(out, "%12s[%d]: IKE SPIs: %.16llx_i%s %.16llx_r%s, ", ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa), id->get_initiator_spi(id), id->is_initiator(id) ? "*" : "", - id->get_responder_spi(id), id->is_initiator(id) ? "" : ""); + id->get_responder_spi(id), id->is_initiator(id) ? "" : "*"); ike_sa->get_stats(ike_sa, &next); if (next) @@ -1120,7 +1157,7 @@ static void log_child_sa(FILE *out, child_sa_t *child_sa, bool all) if (child_sa->get_state(child_sa) == CHILD_INSTALLED) { - fprintf(out, ", %N SPIs: 0x%0x_i 0x%0x_o", + fprintf(out, ", %N SPIs: %.8x_i %.8x_o", protocol_id_names, child_sa->get_protocol(child_sa), htonl(child_sa->get_spi(child_sa, TRUE)), htonl(child_sa->get_spi(child_sa, FALSE))); @@ -1242,6 +1279,7 @@ static void stroke_status(stroke_msg_t *msg, FILE *out, bool all) { identification_t *my_ca = peer_cfg->get_my_ca(peer_cfg); identification_t *other_ca = peer_cfg->get_other_ca(peer_cfg); + linked_list_t *groups = peer_cfg->get_groups(peer_cfg); if (my_ca->get_type(my_ca) != ID_ANY || other_ca->get_type(other_ca) != ID_ANY) @@ -1249,6 +1287,13 @@ static void stroke_status(stroke_msg_t *msg, FILE *out, bool all) fprintf(out, "%12s: CAs: '%D'...'%D'\n", peer_cfg->get_name(peer_cfg), my_ca, other_ca); } + if (groups->get_count(groups) > 0) + { + fprintf(out, "%12s: groups: ", peer_cfg->get_name(peer_cfg)); + ietfAttr_list_list(groups, out); + fprintf(out, "\n"); + } + } children = peer_cfg->create_child_cfg_iterator(peer_cfg); while (children->iterate(children, (void**)&child_cfg)) @@ -1372,6 +1417,23 @@ static void stroke_list(stroke_msg_t *msg, FILE *out) { list_auth_certificates(AUTH_AA, "AA", msg->list.utc, out); } + if (msg->list.flags & LIST_ACERTS) + { + x509ac_t *cert; + + iterator = charon->credentials->create_acert_iterator(charon->credentials); + if (iterator->get_count(iterator)) + { + fprintf(out, "\n"); + fprintf(out, "List of X.509 Attribute Certificates:\n"); + fprintf(out, "\n"); + } + while (iterator->iterate(iterator, (void**)&cert)) + { + cert->list(cert, out, msg->list.utc); + } + iterator->destroy(iterator); + } if (msg->list.flags & LIST_CAINFOS) { ca_info_t *ca_info; @@ -1445,6 +1507,10 @@ static void stroke_list(stroke_msg_t *msg, FILE *out) */ static void stroke_reread(stroke_msg_t *msg, FILE *out) { + if (msg->reread.flags & REREAD_SECRETS) + { + charon->credentials->load_secrets(charon->credentials, TRUE); + } if (msg->reread.flags & REREAD_CACERTS) { charon->credentials->load_ca_certificates(charon->credentials); @@ -1453,6 +1519,14 @@ static void stroke_reread(stroke_msg_t *msg, FILE *out) { charon->credentials->load_ocsp_certificates(charon->credentials); } + if (msg->reread.flags & REREAD_AACERTS) + { + charon->credentials->load_aa_certificates(charon->credentials); + } + if (msg->reread.flags & REREAD_ACERTS) + { + charon->credentials->load_attr_certificates(charon->credentials); + } if (msg->reread.flags & REREAD_CRLS) { charon->credentials->load_crls(charon->credentials); @@ -1655,7 +1729,6 @@ static void destroy(private_stroke_interface_t *this) { this->job->cancel(this->job); free(this); - unlink(socket_addr.sun_path); } /* @@ -1663,6 +1736,7 @@ static void destroy(private_stroke_interface_t *this) */ interface_t *interface_create() { + struct sockaddr_un socket_addr = { AF_UNIX, STROKE_SOCKET}; private_stroke_interface_t *this = malloc_thing(private_stroke_interface_t); mode_t old; @@ -1678,7 +1752,8 @@ interface_t *interface_create() return NULL; } - old = umask(~S_IRWXU); + unlink(socket_addr.sun_path); + old = umask(~(S_IRWXU | S_IRWXG)); if (bind(this->socket, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0) { DBG1(DBG_CFG, "could not bind stroke socket: %s", strerror(errno)); @@ -1687,6 +1762,11 @@ interface_t *interface_create() return NULL; } umask(old); + if (chown(socket_addr.sun_path, IPSEC_UID, IPSEC_GID) != 0) + { + DBG1(DBG_CFG, "changing stroke socket permissions failed: %s", + strerror(errno)); + } if (listen(this->socket, 0) < 0) { diff --git a/src/charon/control/interfaces/xml_interface.c b/src/charon/control/interfaces/xml_interface.c index 992377436..02da1064d 100644 --- a/src/charon/control/interfaces/xml_interface.c +++ b/src/charon/control/interfaces/xml_interface.c @@ -39,8 +39,6 @@ #include <daemon.h> #include <processing/jobs/callback_job.h> -static struct sockaddr_un socket_addr = { AF_UNIX, "/var/run/charon.xml"}; - typedef struct private_xml_interface_t private_xml_interface_t; @@ -65,27 +63,293 @@ struct private_xml_interface_t { callback_job_t *job; }; +ENUM(ike_sa_state_lower_names, IKE_CREATED, IKE_DELETING, + "created", + "connecting", + "established", + "rekeying", + "deleting", +); + +/** + * write a bool into element + */ +static void write_bool(xmlTextWriterPtr writer, char *element, bool val) +{ + xmlTextWriterWriteElement(writer, element, val ? "true" : "false"); +} + +/** + * write a identification_t into element + */ +static void write_id(xmlTextWriterPtr writer, char *element, identification_t *id) +{ + xmlTextWriterStartElement(writer, element); + switch (id->get_type(id)) + { + { + char *type = ""; + while (TRUE) + { + case ID_IPV4_ADDR: + type = "ipv4"; + break; + case ID_IPV6_ADDR: + type = "ipv6"; + break; + case ID_FQDN: + type = "fqdn"; + break; + case ID_RFC822_ADDR: + type = "email"; + break; + case ID_DER_ASN1_DN: + type = "asn1dn"; + break; + case ID_DER_ASN1_GN: + type = "asn1gn"; + break; + } + xmlTextWriterWriteAttribute(writer, "type", type); + xmlTextWriterWriteFormatString(writer, "%D", id); + break; + } + case ID_ANY: + xmlTextWriterWriteAttribute(writer, "type", "any"); + break; + default: + /* TODO: base64 keyid */ + xmlTextWriterWriteAttribute(writer, "type", "keyid"); + break; + } + xmlTextWriterEndElement(writer); +} + +/** + * write a host_t address into an element + */ +static void write_address(xmlTextWriterPtr writer, char *element, host_t *host) +{ + xmlTextWriterStartElement(writer, element); + xmlTextWriterWriteAttribute(writer, "type", + host->get_family(host) == AF_INET ? "ipv4" : "ipv6"); + if (host->is_anyaddr(host)) + { /* do not use %any for XML */ + xmlTextWriterWriteFormatString(writer, "%s", + host->get_family(host) == AF_INET ? "0.0.0.0" : "::"); + } + else + { + xmlTextWriterWriteFormatString(writer, "%H", host); + } + xmlTextWriterEndElement(writer); +} + /** - * process a getRequest message + * write a childEnd */ -static void process_get(xmlTextReaderPtr reader, xmlTextWriterPtr writer) +static void write_childend(xmlTextWriterPtr writer, child_sa_t *child, bool local) { - if (/* <GetResponse> */ - xmlTextWriterStartElement(writer, "GetResponse") < 0 || - /* <Status Code="200"><Message/></Status> */ - xmlTextWriterStartElement(writer, "Status") < 0 || - xmlTextWriterWriteAttribute(writer, "Code", "200") < 0 || - xmlTextWriterStartElement(writer, "Message") < 0 || - xmlTextWriterEndElement(writer) < 0 || - xmlTextWriterEndElement(writer) < 0 || - /* <ConnectionList/> */ - xmlTextWriterStartElement(writer, "ConnectionList") < 0 || - xmlTextWriterEndElement(writer) < 0 || - /* </GetResponse> */ - xmlTextWriterEndElement(writer) < 0) + iterator_t *iterator; + linked_list_t *list; + traffic_selector_t *ts; + xmlTextWriterWriteFormatElement(writer, "spi", "%lx", + htonl(child->get_spi(child, local))); + xmlTextWriterStartElement(writer, "networks"); + list = child->get_traffic_selectors(child, local); + iterator = list->create_iterator(list, TRUE); + while (iterator->iterate(iterator, (void**)&ts)) { - DBG1(DBG_CFG, "error writing XML document (GetResponse)"); + xmlTextWriterStartElement(writer, "network"); + xmlTextWriterWriteAttribute(writer, "type", + ts->get_type(ts) == TS_IPV4_ADDR_RANGE ? "ipv4" : "ipv6"); + xmlTextWriterWriteFormatString(writer, "%R", ts); + xmlTextWriterEndElement(writer); } + iterator->destroy(iterator); + xmlTextWriterEndElement(writer); +} + +/** + * write a child_sa_t + */ +static void write_child(xmlTextWriterPtr writer, child_sa_t *child) +{ + mode_t mode; + encryption_algorithm_t encr; + integrity_algorithm_t int_algo; + size_t encr_len, int_len; + u_int32_t rekey, use_in, use_out, use_fwd; + child_cfg_t *config; + + config = child->get_config(child); + child->get_stats(child, &mode, &encr, &encr_len, &int_algo, &int_len, + &rekey, &use_in, &use_out, &use_fwd); + + xmlTextWriterStartElement(writer, "childsa"); + xmlTextWriterWriteFormatElement(writer, "reqid", "%d", child->get_reqid(child)); + xmlTextWriterWriteFormatElement(writer, "childconfig", "%s", + config->get_name(config)); + xmlTextWriterStartElement(writer, "local"); + write_childend(writer, child, TRUE); + xmlTextWriterEndElement(writer); + xmlTextWriterStartElement(writer, "remote"); + write_childend(writer, child, FALSE); + xmlTextWriterEndElement(writer); + xmlTextWriterEndElement(writer); +} + +/** + * process a ikesalist query request message + */ +static void request_query_ikesa(xmlTextReaderPtr reader, xmlTextWriterPtr writer) +{ + iterator_t *iterator; + ike_sa_t *ike_sa; + + /* <ikesalist> */ + xmlTextWriterStartElement(writer, "ikesalist"); + + iterator = charon->ike_sa_manager->create_iterator(charon->ike_sa_manager); + while (iterator->iterate(iterator, (void**)&ike_sa)) + { + ike_sa_id_t *id; + host_t *local, *remote; + iterator_t *children; + child_sa_t *child_sa; + + id = ike_sa->get_id(ike_sa); + + xmlTextWriterStartElement(writer, "ikesa"); + xmlTextWriterWriteFormatElement(writer, "id", "%d", + ike_sa->get_unique_id(ike_sa)); + xmlTextWriterWriteFormatElement(writer, "status", "%N", + ike_sa_state_lower_names, ike_sa->get_state(ike_sa)); + xmlTextWriterWriteElement(writer, "role", + id->is_initiator(id) ? "initiator" : "responder"); + xmlTextWriterWriteElement(writer, "peerconfig", ike_sa->get_name(ike_sa)); + + /* <local> */ + local = ike_sa->get_my_host(ike_sa); + xmlTextWriterStartElement(writer, "local"); + xmlTextWriterWriteFormatElement(writer, "spi", "%.16llx", + id->is_initiator(id) ? id->get_initiator_spi(id) + : id->get_responder_spi(id)); + write_id(writer, "identification", ike_sa->get_my_id(ike_sa)); + write_address(writer, "address", local); + xmlTextWriterWriteFormatElement(writer, "port", "%d", + local->get_port(local)); + if (ike_sa->supports_extension(ike_sa, EXT_NATT)) + { + write_bool(writer, "nat", ike_sa->has_condition(ike_sa, COND_NAT_HERE)); + } + xmlTextWriterEndElement(writer); + /* </local> */ + + /* <remote> */ + remote = ike_sa->get_other_host(ike_sa); + xmlTextWriterStartElement(writer, "remote"); + xmlTextWriterWriteFormatElement(writer, "spi", "%.16llx", + id->is_initiator(id) ? id->get_responder_spi(id) + : id->get_initiator_spi(id)); + write_id(writer, "identification", ike_sa->get_other_id(ike_sa)); + write_address(writer, "address", remote); + xmlTextWriterWriteFormatElement(writer, "port", "%d", + remote->get_port(remote)); + if (ike_sa->supports_extension(ike_sa, EXT_NATT)) + { + write_bool(writer, "nat", ike_sa->has_condition(ike_sa, COND_NAT_THERE)); + } + xmlTextWriterEndElement(writer); + /* </remote> */ + + /* <childsalist> */ + xmlTextWriterStartElement(writer, "childsalist"); + children = ike_sa->create_child_sa_iterator(ike_sa); + while (children->iterate(children, (void**)&child_sa)) + { + write_child(writer, child_sa); + } + children->destroy(children); + /* </childsalist> */ + xmlTextWriterEndElement(writer); + + /* </ikesa> */ + xmlTextWriterEndElement(writer); + } + iterator->destroy(iterator); + + /* </ikesalist> */ + xmlTextWriterEndElement(writer); +} + +/** + * process a query request + */ +static void request_query(xmlTextReaderPtr reader, xmlTextWriterPtr writer) +{ + /* <query> */ + xmlTextWriterStartElement(writer, "query"); + while (xmlTextReaderRead(reader)) + { + if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) + { + if (streq(xmlTextReaderConstName(reader), "ikesalist")) + { + request_query_ikesa(reader, writer); + break; + } + } + } + /* </query> */ + xmlTextWriterEndElement(writer); +} + +/** + * process a request message + */ +static void request(xmlTextReaderPtr reader, char *id, int fd) +{ + xmlTextWriterPtr writer; + + writer = xmlNewTextWriter(xmlOutputBufferCreateFd(fd, NULL)); + if (writer == NULL) + { + DBG1(DBG_CFG, "opening SMP XML writer failed"); + return; + } + + xmlTextWriterStartDocument(writer, NULL, NULL, NULL); + /* <message xmlns="http://www.strongswan.org/smp/1.0" + id="id" type="response"> */ + xmlTextWriterStartElement(writer, "message"); + xmlTextWriterWriteAttribute(writer, "xmlns", + "http://www.strongswan.org/smp/1.0"); + xmlTextWriterWriteAttribute(writer, "id", id); + xmlTextWriterWriteAttribute(writer, "type", "response"); + + while (xmlTextReaderRead(reader)) + { + if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) + { + if (streq(xmlTextReaderConstName(reader), "query")) + { + request_query(reader, writer); + break; + } + } + } + /* </message> and close document */ + xmlTextWriterEndDocument(writer); + xmlFreeTextWriter(writer); +} + +/** + * cleanup helper function for open file descriptors + */ +static void closefdp(int *fd) +{ + close(*fd); } /** @@ -97,17 +361,20 @@ static job_requeue_t process(int *fdp) char buffer[4096]; size_t len; xmlTextReaderPtr reader; - xmlTextWriterPtr writer; + char *id = NULL, *type = NULL; + pthread_cleanup_push((void*)closefdp, (void*)&fd); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); len = read(fd, buffer, sizeof(buffer)); pthread_setcancelstate(oldstate, NULL); + pthread_cleanup_pop(0); if (len <= 0) { close(fd); DBG2(DBG_CFG, "SMP XML connection closed"); return JOB_REQUEUE_NONE; } + DBG3(DBG_CFG, "got XML request: %b", buffer, len); reader = xmlReaderForMemory(buffer, len, NULL, NULL, 0); if (reader == NULL) @@ -116,65 +383,32 @@ static job_requeue_t process(int *fdp) return JOB_REQUEUE_FAIR;; } - writer = xmlNewTextWriter(xmlOutputBufferCreateFd(fd, NULL)); - if (writer == NULL) - { - xmlFreeTextReader(reader); - DBG1(DBG_CFG, "opening SMP XML writer failed"); - return JOB_REQUEUE_FAIR;; - } - - /* create the standard message parts */ - if (xmlTextWriterStartDocument(writer, NULL, NULL, NULL) < 0 || - /* <SMPMessage xmlns="http://www.strongswan.org/smp/1.0"> */ - xmlTextWriterStartElement(writer, "SMPMessage") < 0 || - xmlTextWriterWriteAttribute(writer, "xmlns", - "http://www.strongswan.org/smp/1.0") < 0 || - /* <Body> */ - xmlTextWriterStartElement(writer, "Body") < 0) - { - xmlFreeTextReader(reader); - xmlFreeTextWriter(writer); - DBG1(DBG_CFG, "creating SMP XML message failed"); - return JOB_REQUEUE_FAIR;; - } - - while (TRUE) + /* read message type and id */ + while (xmlTextReaderRead(reader)) { - switch (xmlTextReaderRead(reader)) - { - case 1: - { - if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) - { - if (streq(xmlTextReaderConstName(reader), "GetRequest")) - { - process_get(reader, writer); - break; - } - } - continue; - } - case 0: - /* end of XML */ - break; - default: - DBG1(DBG_CFG, "parsing SMP XML message failed"); - break; + if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT && + streq(xmlTextReaderConstName(reader), "message")) + { + id = xmlTextReaderGetAttribute(reader, "id"); + type = xmlTextReaderGetAttribute(reader, "type"); + break; } - xmlFreeTextReader(reader); - break; } - /* write </Body></SMPMessage> and close document */ - if (xmlTextWriterEndDocument(writer) < 0) - { - DBG1(DBG_CFG, "completing SMP XML message failed"); - } - xmlFreeTextWriter(writer); - /* write a newline to indicate end of xml */ - write(fd, "\n", 1); - return JOB_REQUEUE_FAIR;; + /* process message */ + if (id && type) + { + if (streq(type, "request")) + { + request(reader, id, fd); + } + else + { + /* response(reader, id) */ + } + } + xmlFreeTextReader(reader); + return JOB_REQUEUE_FAIR;; } /** @@ -212,7 +446,7 @@ static job_requeue_t dispatch(private_xml_interface_t *this) static void destroy(private_xml_interface_t *this) { this->job->cancel(this->job); - unlink(socket_addr.sun_path); + close(this->socket); free(this); } @@ -221,6 +455,7 @@ static void destroy(private_xml_interface_t *this) */ interface_t *interface_create() { + struct sockaddr_un unix_addr = { AF_UNIX, IPSEC_PIDDIR "/charon.xml"}; private_xml_interface_t *this = malloc_thing(private_xml_interface_t); mode_t old; @@ -235,8 +470,9 @@ interface_t *interface_create() return NULL; } - old = umask(~S_IRWXU); - if (bind(this->socket, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0) + unlink(unix_addr.sun_path); + old = umask(~(S_IRWXU | S_IRWXG)); + if (bind(this->socket, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) < 0) { DBG1(DBG_CFG, "could not bind XML socket: %s", strerror(errno)); close(this->socket); @@ -244,8 +480,12 @@ interface_t *interface_create() return NULL; } umask(old); + if (chown(unix_addr.sun_path, IPSEC_UID, IPSEC_GID) != 0) + { + DBG1(DBG_CFG, "changing XML socket permissions failed: %s", strerror(errno)); + } - if (listen(this->socket, 0) < 0) + if (listen(this->socket, 5) < 0) { DBG1(DBG_CFG, "could not listen on XML socket: %s", strerror(errno)); close(this->socket); |