summaryrefslogtreecommitdiff
path: root/src/swanctl/commands
diff options
context:
space:
mode:
Diffstat (limited to 'src/swanctl/commands')
-rw-r--r--src/swanctl/commands/initiate.c132
-rw-r--r--src/swanctl/commands/install.c125
-rw-r--r--src/swanctl/commands/list_certs.c670
-rw-r--r--src/swanctl/commands/list_conns.c242
-rw-r--r--src/swanctl/commands/list_pols.c210
-rw-r--r--src/swanctl/commands/list_pools.c101
-rw-r--r--src/swanctl/commands/list_sas.c366
-rw-r--r--src/swanctl/commands/load_conns.c419
-rw-r--r--src/swanctl/commands/load_creds.c574
-rw-r--r--src/swanctl/commands/load_pools.c292
-rw-r--r--src/swanctl/commands/log.c101
-rw-r--r--src/swanctl/commands/stats.c118
-rw-r--r--src/swanctl/commands/terminate.c157
-rw-r--r--src/swanctl/commands/version.c96
14 files changed, 3603 insertions, 0 deletions
diff --git a/src/swanctl/commands/initiate.c b/src/swanctl/commands/initiate.c
new file mode 100644
index 000000000..080dc4131
--- /dev/null
+++ b/src/swanctl/commands/initiate.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "command.h"
+
+#include <errno.h>
+
+CALLBACK(log_cb, void,
+ command_format_options_t *format, char *name, vici_res_t *msg)
+{
+ if (*format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(msg, "log", *format & COMMAND_FORMAT_PRETTY, stdout);
+ }
+ else
+ {
+ printf("[%s] %s\n",
+ vici_find_str(msg, " ", "group"),
+ vici_find_str(msg, "", "msg"));
+ }
+}
+
+static int initiate(vici_conn_t *conn)
+{
+ vici_req_t *req;
+ vici_res_t *res;
+ command_format_options_t format = COMMAND_FORMAT_NONE;
+ char *arg, *child = NULL;
+ int ret = 0, timeout = 0, level = 1;
+
+ while (TRUE)
+ {
+ switch (command_getopt(&arg))
+ {
+ case 'h':
+ return command_usage(NULL);
+ case 'P':
+ format |= COMMAND_FORMAT_PRETTY;
+ /* fall through to raw */
+ case 'r':
+ format |= COMMAND_FORMAT_RAW;
+ continue;
+ case 'c':
+ child = arg;
+ continue;
+ case 't':
+ timeout = atoi(arg);
+ continue;
+ case 'l':
+ level = atoi(arg);
+ continue;
+ case EOF:
+ break;
+ default:
+ return command_usage("invalid --initiate option");
+ }
+ break;
+ }
+
+ if (vici_register(conn, "control-log", log_cb, &format) != 0)
+ {
+ fprintf(stderr, "registering for log failed: %s\n", strerror(errno));
+ return errno;
+ }
+ req = vici_begin("initiate");
+ if (child)
+ {
+ vici_add_key_valuef(req, "child", "%s", child);
+ }
+ if (timeout)
+ {
+ vici_add_key_valuef(req, "timeout", "%d", timeout * 1000);
+ }
+ vici_add_key_valuef(req, "loglevel", "%d", level);
+ res = vici_submit(req, conn);
+ if (!res)
+ {
+ fprintf(stderr, "initiate request failed: %s\n", strerror(errno));
+ return errno;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "initiate reply", format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ else
+ {
+ if (streq(vici_find_str(res, "no", "success"), "yes"))
+ {
+ printf("initiate completed successfully\n");
+ }
+ else
+ {
+ fprintf(stderr, "initiate failed: %s\n",
+ vici_find_str(res, "", "errmsg"));
+ ret = 1;
+ }
+ }
+ vici_free_res(res);
+ return ret;
+}
+
+/**
+ * Register the command.
+ */
+static void __attribute__ ((constructor))reg()
+{
+ command_register((command_t) {
+ initiate, 'i', "initiate", "initiate a connection",
+ {"--child <name> [--timeout <s>] [--raw|--pretty]"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"child", 'c', 1, "initate a CHILD_SA configuration"},
+ {"timeout", 't', 1, "timeout in seconds before detaching"},
+ {"raw", 'r', 0, "dump raw response message"},
+ {"pretty", 'P', 0, "dump raw response message in pretty print"},
+ {"loglevel", 'l', 1, "verbosity of redirected log"},
+ }
+ });
+}
diff --git a/src/swanctl/commands/install.c b/src/swanctl/commands/install.c
new file mode 100644
index 000000000..e8727d573
--- /dev/null
+++ b/src/swanctl/commands/install.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "command.h"
+
+#include <errno.h>
+
+static int manage_policy(vici_conn_t *conn, char *label)
+{
+ vici_req_t *req;
+ vici_res_t *res;
+ command_format_options_t format = COMMAND_FORMAT_NONE;
+ char *arg, *child = NULL;
+ int ret = 0;
+
+ while (TRUE)
+ {
+ switch (command_getopt(&arg))
+ {
+ case 'h':
+ return command_usage(NULL);
+ case 'P':
+ format |= COMMAND_FORMAT_RAW;
+ /* fall through to raw */
+ case 'r':
+ format |= COMMAND_FORMAT_PRETTY;
+ continue;
+ case 'c':
+ child = arg;
+ continue;
+ case EOF:
+ break;
+ default:
+ return command_usage("invalid --%s option", label);
+ }
+ break;
+ }
+ req = vici_begin(label);
+ if (child)
+ {
+ vici_add_key_valuef(req, "child", "%s", child);
+ }
+ res = vici_submit(req, conn);
+ if (!res)
+ {
+ fprintf(stderr, "%s request failed: %s\n", label, strerror(errno));
+ return errno;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ puts(label);
+ vici_dump(res, " reply", format & COMMAND_FORMAT_PRETTY, stdout);
+ }
+ else
+ {
+ if (streq(vici_find_str(res, "no", "success"), "yes"))
+ {
+ printf("%s completed successfully\n", label);
+ }
+ else
+ {
+ fprintf(stderr, "%s failed: %s\n",
+ label, vici_find_str(res, "", "errmsg"));
+ ret = 1;
+ }
+ }
+ vici_free_res(res);
+ return ret;
+}
+
+static int uninstall(vici_conn_t *conn)
+{
+ return manage_policy(conn, "uninstall");
+}
+
+static int install(vici_conn_t *conn)
+{
+ return manage_policy(conn, "install");
+}
+
+/**
+ * Register the uninstall command.
+ */
+static void __attribute__ ((constructor))reg_uninstall()
+{
+ command_register((command_t) {
+ uninstall, 'u', "uninstall", "uninstall a trap or shunt policy",
+ {"--child <name> [--raw|--pretty]"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"child", 'c', 1, "CHILD_SA configuration to uninstall"},
+ {"raw", 'r', 0, "dump raw response message"},
+ {"pretty", 'P', 0, "dump raw response message in pretty print"},
+ }
+ });
+}
+
+/**
+ * Register install the command.
+ */
+static void __attribute__ ((constructor))reg_install()
+{
+ command_register((command_t) {
+ install, 'p', "install", "install a trap or shunt policy",
+ {"--child <name> [--raw|--pretty]"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"child", 'c', 1, "CHILD_SA configuration to install"},
+ {"raw", 'r', 0, "dump raw response message"},
+ {"pretty", 'P', 0, "dump raw response message in pretty print"},
+ }
+ });
+}
diff --git a/src/swanctl/commands/list_certs.c b/src/swanctl/commands/list_certs.c
new file mode 100644
index 000000000..bee5fda27
--- /dev/null
+++ b/src/swanctl/commands/list_certs.c
@@ -0,0 +1,670 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <time.h>
+
+#include <asn1/asn1.h>
+#include <asn1/oid.h>
+#include <credentials/certificates/certificate.h>
+#include <credentials/certificates/x509.h>
+#include <credentials/certificates/crl.h>
+#include <credentials/certificates/ac.h>
+#include <selectors/traffic_selector.h>
+
+#include "command.h"
+
+/**
+ * Print PEM encoding of a certificate
+ */
+static void print_pem(certificate_t *cert)
+{
+ chunk_t encoding;
+
+ if (cert->get_encoding(cert, CERT_PEM, &encoding))
+ {
+ printf("%.*s", (int)encoding.len, encoding.ptr);
+ free(encoding.ptr);
+ }
+ else
+ {
+ fprintf(stderr, "PEM encoding certificate failed\n");
+ }
+}
+
+/**
+ * Print public key information
+ */
+static void print_pubkey(public_key_t *key, bool has_privkey)
+{
+ chunk_t chunk;
+
+ printf("pubkey: %N %d bits", key_type_names, key->get_type(key),
+ key->get_keysize(key));
+ if (has_privkey)
+ {
+ printf(", has private key");
+ }
+ printf("\n");
+ if (key->get_fingerprint(key, KEYID_PUBKEY_INFO_SHA1, &chunk))
+ {
+ printf("keyid: %#B\n", &chunk);
+ }
+ if (key->get_fingerprint(key, KEYID_PUBKEY_SHA1, &chunk))
+ {
+ printf("subjkey: %#B\n", &chunk);
+ }
+}
+
+/**
+ * Print X509 specific certificate information
+ */
+static void print_x509(x509_t *x509)
+{
+ enumerator_t *enumerator;
+ identification_t *id;
+ traffic_selector_t *block;
+ chunk_t chunk;
+ bool first;
+ char *uri;
+ int len, explicit, inhibit;
+ x509_flag_t flags;
+ x509_cdp_t *cdp;
+ x509_cert_policy_t *policy;
+ x509_policy_mapping_t *mapping;
+
+ chunk = chunk_skip_zero(x509->get_serial(x509));
+ printf("serial: %#B\n", &chunk);
+
+ first = TRUE;
+ enumerator = x509->create_subjectAltName_enumerator(x509);
+ while (enumerator->enumerate(enumerator, &id))
+ {
+ if (first)
+ {
+ printf("altNames: ");
+ first = FALSE;
+ }
+ else
+ {
+ printf(", ");
+ }
+ printf("%Y", id);
+ }
+ if (!first)
+ {
+ printf("\n");
+ }
+ enumerator->destroy(enumerator);
+
+ flags = x509->get_flags(x509);
+ printf("flags: ");
+ if (flags & X509_CA)
+ {
+ printf("CA ");
+ }
+ if (flags & X509_CRL_SIGN)
+ {
+ printf("CRLSign ");
+ }
+ if (flags & X509_AA)
+ {
+ printf("AA ");
+ }
+ if (flags & X509_OCSP_SIGNER)
+ {
+ printf("OCSP ");
+ }
+ if (flags & X509_AA)
+ {
+ printf("AA ");
+ }
+ if (flags & X509_SERVER_AUTH)
+ {
+ printf("serverAuth ");
+ }
+ if (flags & X509_CLIENT_AUTH)
+ {
+ printf("clientAuth ");
+ }
+ if (flags & X509_IKE_INTERMEDIATE)
+ {
+ printf("iKEIntermediate ");
+ }
+ if (flags & X509_SELF_SIGNED)
+ {
+ printf("self-signed ");
+ }
+ printf("\n");
+
+ first = TRUE;
+ enumerator = x509->create_crl_uri_enumerator(x509);
+ while (enumerator->enumerate(enumerator, &cdp))
+ {
+ if (first)
+ {
+ printf("CRL URIs: %s", cdp->uri);
+ first = FALSE;
+ }
+ else
+ {
+ printf(" %s", cdp->uri);
+ }
+ if (cdp->issuer)
+ {
+ printf(" (CRL issuer: %Y)", cdp->issuer);
+ }
+ printf("\n");
+ }
+ enumerator->destroy(enumerator);
+
+ first = TRUE;
+ enumerator = x509->create_ocsp_uri_enumerator(x509);
+ while (enumerator->enumerate(enumerator, &uri))
+ {
+ if (first)
+ {
+ printf("OCSP URIs: %s\n", uri);
+ first = FALSE;
+ }
+ else
+ {
+ printf(" %s\n", uri);
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ len = x509->get_constraint(x509, X509_PATH_LEN);
+ if (len != X509_NO_CONSTRAINT)
+ {
+ printf("pathlen: %d\n", len);
+ }
+
+ first = TRUE;
+ enumerator = x509->create_name_constraint_enumerator(x509, TRUE);
+ while (enumerator->enumerate(enumerator, &id))
+ {
+ if (first)
+ {
+ printf("Permitted NameConstraints:\n");
+ first = FALSE;
+ }
+ printf(" %Y\n", id);
+ }
+ enumerator->destroy(enumerator);
+ first = TRUE;
+ enumerator = x509->create_name_constraint_enumerator(x509, FALSE);
+ while (enumerator->enumerate(enumerator, &id))
+ {
+ if (first)
+ {
+ printf("Excluded NameConstraints:\n");
+ first = FALSE;
+ }
+ printf(" %Y\n", id);
+ }
+ enumerator->destroy(enumerator);
+
+ first = TRUE;
+ enumerator = x509->create_cert_policy_enumerator(x509);
+ while (enumerator->enumerate(enumerator, &policy))
+ {
+ char *oid;
+
+ if (first)
+ {
+ printf("CertificatePolicies:\n");
+ first = FALSE;
+ }
+ oid = asn1_oid_to_string(policy->oid);
+ if (oid)
+ {
+ printf(" %s\n", oid);
+ free(oid);
+ }
+ else
+ {
+ printf(" %#B\n", &policy->oid);
+ }
+ if (policy->cps_uri)
+ {
+ printf(" CPS: %s\n", policy->cps_uri);
+ }
+ if (policy->unotice_text)
+ {
+ printf(" Notice: %s\n", policy->unotice_text);
+
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ first = TRUE;
+ enumerator = x509->create_policy_mapping_enumerator(x509);
+ while (enumerator->enumerate(enumerator, &mapping))
+ {
+ char *issuer_oid, *subject_oid;
+
+ if (first)
+ {
+ printf("PolicyMappings:\n");
+ first = FALSE;
+ }
+ issuer_oid = asn1_oid_to_string(mapping->issuer);
+ subject_oid = asn1_oid_to_string(mapping->subject);
+ printf(" %s => %s\n", issuer_oid, subject_oid);
+ free(issuer_oid);
+ free(subject_oid);
+ }
+ enumerator->destroy(enumerator);
+
+ explicit = x509->get_constraint(x509, X509_REQUIRE_EXPLICIT_POLICY);
+ inhibit = x509->get_constraint(x509, X509_INHIBIT_POLICY_MAPPING);
+ len = x509->get_constraint(x509, X509_INHIBIT_ANY_POLICY);
+
+ if (explicit != X509_NO_CONSTRAINT || inhibit != X509_NO_CONSTRAINT ||
+ len != X509_NO_CONSTRAINT)
+ {
+ printf("PolicyConstraints:\n");
+ if (explicit != X509_NO_CONSTRAINT)
+ {
+ printf(" requireExplicitPolicy: %d\n", explicit);
+ }
+ if (inhibit != X509_NO_CONSTRAINT)
+ {
+ printf(" inhibitPolicyMapping: %d\n", inhibit);
+ }
+ if (len != X509_NO_CONSTRAINT)
+ {
+ printf(" inhibitAnyPolicy: %d\n", len);
+ }
+ }
+
+ chunk = x509->get_authKeyIdentifier(x509);
+ if (chunk.ptr)
+ {
+ printf("authkeyId: %#B\n", &chunk);
+ }
+
+ chunk = x509->get_subjectKeyIdentifier(x509);
+ if (chunk.ptr)
+ {
+ printf("subjkeyId: %#B\n", &chunk);
+ }
+ if (x509->get_flags(x509) & X509_IP_ADDR_BLOCKS)
+ {
+ first = TRUE;
+ printf("addresses: ");
+ enumerator = x509->create_ipAddrBlock_enumerator(x509);
+ while (enumerator->enumerate(enumerator, &block))
+ {
+ if (first)
+ {
+ first = FALSE;
+ }
+ else
+ {
+ printf(", ");
+ }
+ printf("%R", block);
+ }
+ enumerator->destroy(enumerator);
+ printf("\n");
+ }
+}
+
+/**
+ * Print CRL specific information
+ */
+static void print_crl(crl_t *crl)
+{
+ enumerator_t *enumerator;
+ time_t ts;
+ crl_reason_t reason;
+ chunk_t chunk;
+ int count = 0;
+ bool first;
+ char buf[64];
+ struct tm tm;
+ x509_cdp_t *cdp;
+
+ chunk = chunk_skip_zero(crl->get_serial(crl));
+ printf("serial: %#B\n", &chunk);
+
+ if (crl->is_delta_crl(crl, &chunk))
+ {
+ chunk = chunk_skip_zero(chunk);
+ printf("delta CRL: for serial %#B\n", &chunk);
+ }
+ chunk = crl->get_authKeyIdentifier(crl);
+ printf("authKeyId: %#B\n", &chunk);
+
+ first = TRUE;
+ enumerator = crl->create_delta_crl_uri_enumerator(crl);
+ while (enumerator->enumerate(enumerator, &cdp))
+ {
+ if (first)
+ {
+ printf("freshest: %s", cdp->uri);
+ first = FALSE;
+ }
+ else
+ {
+ printf(" %s", cdp->uri);
+ }
+ if (cdp->issuer)
+ {
+ printf(" (CRL issuer: %Y)", cdp->issuer);
+ }
+ printf("\n");
+ }
+ enumerator->destroy(enumerator);
+
+ enumerator = crl->create_enumerator(crl);
+ while (enumerator->enumerate(enumerator, &chunk, &ts, &reason))
+ {
+ count++;
+ }
+ enumerator->destroy(enumerator);
+
+ printf("%d revoked certificate%s%s\n", count,
+ count == 1 ? "" : "s", count ? ":" : "");
+ enumerator = crl->create_enumerator(crl);
+ while (enumerator->enumerate(enumerator, &chunk, &ts, &reason))
+ {
+ chunk = chunk_skip_zero(chunk);
+ localtime_r(&ts, &tm);
+ strftime(buf, sizeof(buf), "%F %T", &tm);
+ printf(" %#B %N %s\n", &chunk, crl_reason_names, reason, buf);
+ count++;
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * Print AC specific information
+ */
+static void print_ac(ac_t *ac)
+{
+ ac_group_type_t type;
+ identification_t *id;
+ enumerator_t *groups;
+ chunk_t chunk;
+ bool first = TRUE;
+
+ chunk = chunk_skip_zero(ac->get_serial(ac));
+ printf("serial: %#B\n", &chunk);
+
+ id = ac->get_holderIssuer(ac);
+ if (id)
+ {
+ printf("hissuer: \"%Y\"\n", id);
+ }
+ chunk = chunk_skip_zero(ac->get_holderSerial(ac));
+ if (chunk.ptr)
+ {
+ printf("hserial: %#B\n", &chunk);
+ }
+ groups = ac->create_group_enumerator(ac);
+ while (groups->enumerate(groups, &type, &chunk))
+ {
+ int oid;
+ char *str;
+
+ if (first)
+ {
+ printf("groups: ");
+ first = FALSE;
+ }
+ else
+ {
+ printf(" ");
+ }
+ switch (type)
+ {
+ case AC_GROUP_TYPE_STRING:
+ printf("%.*s", (int)chunk.len, chunk.ptr);
+ break;
+ case AC_GROUP_TYPE_OID:
+ oid = asn1_known_oid(chunk);
+ if (oid == OID_UNKNOWN)
+ {
+ str = asn1_oid_to_string(chunk);
+ if (str)
+ {
+ printf("%s", str);
+ free(str);
+ }
+ else
+ {
+ printf("OID:%#B", &chunk);
+ }
+ }
+ else
+ {
+ printf("%s", oid_names[oid].name);
+ }
+ break;
+ case AC_GROUP_TYPE_OCTETS:
+ printf("%#B", &chunk);
+ break;
+ }
+ printf("\n");
+ }
+ groups->destroy(groups);
+
+ chunk = ac->get_authKeyIdentifier(ac);
+ if (chunk.ptr)
+ {
+ printf("authkey: %#B\n", &chunk);
+ }
+}
+
+/**
+ * Print certificate information
+ */
+static void print_cert(certificate_t *cert, bool has_privkey)
+{
+ time_t now, notAfter, notBefore;
+ public_key_t *key;
+
+ now = time(NULL);
+
+ printf("cert: %N\n", certificate_type_names, cert->get_type(cert));
+ if (cert->get_type(cert) != CERT_X509_CRL)
+ {
+ printf("subject: \"%Y\"\n", cert->get_subject(cert));
+ }
+ printf("issuer: \"%Y\"\n", cert->get_issuer(cert));
+
+ cert->get_validity(cert, &now, &notBefore, &notAfter);
+ printf("validity: not before %T, ", &notBefore, FALSE);
+ if (now < notBefore)
+ {
+ printf("not valid yet (valid in %V)\n", &now, &notBefore);
+ }
+ else
+ {
+ printf("ok\n");
+ }
+ printf(" not after %T, ", &notAfter, FALSE);
+ if (now > notAfter)
+ {
+ printf("expired (%V ago)\n", &now, &notAfter);
+ }
+ else
+ {
+ printf("ok (expires in %V)\n", &now, &notAfter);
+ }
+
+ switch (cert->get_type(cert))
+ {
+ case CERT_X509:
+ print_x509((x509_t*)cert);
+ break;
+ case CERT_X509_CRL:
+ print_crl((crl_t*)cert);
+ break;
+ case CERT_X509_AC:
+ print_ac((ac_t*)cert);
+ break;
+ default:
+ fprintf(stderr, "parsing certificate subtype %N not implemented\n",
+ certificate_type_names, cert->get_type(cert));
+ break;
+ }
+ key = cert->get_public_key(cert);
+ if (key)
+ {
+ print_pubkey(key, has_privkey);
+ key->destroy(key);
+ }
+ printf("\n");
+}
+
+CALLBACK(list_cb, void,
+ command_format_options_t *format, char *name, vici_res_t *res)
+{
+ if (*format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "list-cert event", *format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ else
+ {
+ certificate_type_t type;
+ certificate_t *cert;
+ void *buf;
+ int len;
+ bool has_privkey;
+
+ buf = vici_find(res, &len, "data");
+ has_privkey = streq(vici_find_str(res, "no", "has_privkey"), "yes");
+ if (enum_from_name(certificate_type_names,
+ vici_find_str(res, "ANY", "type"), &type) &&
+ type != CERT_ANY && buf)
+ {
+ cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, type,
+ BUILD_BLOB_ASN1_DER, chunk_create(buf, len),
+ BUILD_END);
+ if (cert)
+ {
+ if (*format & COMMAND_FORMAT_PEM)
+ {
+ print_pem(cert);
+ }
+ else
+ {
+ print_cert(cert, has_privkey);
+ }
+ cert->destroy(cert);
+ }
+ else
+ {
+ fprintf(stderr, "parsing certificate failed\n");
+ }
+ }
+ else
+ {
+ fprintf(stderr, "received incomplete certificate data\n");
+ }
+ }
+}
+
+static int list_certs(vici_conn_t *conn)
+{
+ vici_req_t *req;
+ vici_res_t *res;
+ command_format_options_t format = COMMAND_FORMAT_NONE;
+ char *arg, *subject = NULL, *type = NULL;
+
+ while (TRUE)
+ {
+ switch (command_getopt(&arg))
+ {
+ case 'h':
+ return command_usage(NULL);
+ case 's':
+ subject = arg;
+ continue;
+ case 't':
+ type = arg;
+ continue;
+ case 'p':
+ format |= COMMAND_FORMAT_PEM;
+ continue;
+ case 'P':
+ format |= COMMAND_FORMAT_PRETTY;
+ /* fall through to raw */
+ case 'r':
+ format |= COMMAND_FORMAT_RAW;
+ continue;
+ case EOF:
+ break;
+ default:
+ return command_usage("invalid --list-certs option");
+ }
+ break;
+ }
+ if (vici_register(conn, "list-cert", list_cb, &format) != 0)
+ {
+ fprintf(stderr, "registering for certificates failed: %s\n",
+ strerror(errno));
+ return errno;
+ }
+ req = vici_begin("list-certs");
+ if (type)
+ {
+ vici_add_key_valuef(req, "type", "%s", type);
+ }
+ if (subject)
+ {
+ vici_add_key_valuef(req, "subject", "%s", subject);
+ }
+ res = vici_submit(req, conn);
+ if (!res)
+ {
+ fprintf(stderr, "list-certs request failed: %s\n", strerror(errno));
+ return errno;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "list-certs reply", format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ vici_free_res(res);
+ return 0;
+}
+
+/**
+ * Register the command.
+ */
+static void __attribute__ ((constructor))reg()
+{
+ command_register((command_t) {
+ list_certs, 'x', "list-certs", "list stored certificates",
+ {"[--subject <dn/san>] [--type X509|X509_AC|X509_CRL] [--pem] "
+ "[--raw|--pretty]"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"subject", 's', 1, "filter by certificate subject"},
+ {"type", 't', 1, "filter by certificate type"},
+ {"pem", 'p', 0, "print PEM encoding of certificate"},
+ {"raw", 'r', 0, "dump raw response message"},
+ {"pretty", 'P', 0, "dump raw response message in pretty print"},
+ }
+ });
+}
diff --git a/src/swanctl/commands/list_conns.c b/src/swanctl/commands/list_conns.c
new file mode 100644
index 000000000..ec5da4bef
--- /dev/null
+++ b/src/swanctl/commands/list_conns.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+
+#include "command.h"
+
+#include <collections/hashtable.h>
+
+/**
+ * Free hashtable with contained strings
+ */
+static void free_hashtable(hashtable_t *hashtable)
+{
+ enumerator_t *enumerator;
+ char *str;
+
+ enumerator = hashtable->create_enumerator(hashtable);
+ while (enumerator->enumerate(enumerator, NULL, &str))
+ {
+ free(str);
+ }
+ enumerator->destroy(enumerator);
+
+ hashtable->destroy(hashtable);
+}
+
+CALLBACK(values, int,
+ hashtable_t *sa, vici_res_t *res, char *name, void *value, int len)
+{
+ chunk_t chunk;
+ char *str;
+
+ chunk = chunk_create(value, len);
+ if (chunk_printable(chunk, NULL, ' '))
+ {
+ if (asprintf(&str, "%.*s", len, value) >= 0)
+ {
+ free(sa->put(sa, name, str));
+ }
+ }
+ return 0;
+}
+
+
+CALLBACK(list, int,
+ hashtable_t *sa, vici_res_t *res, char *name, void *value, int len)
+{
+ chunk_t chunk;
+ char *str;
+
+ chunk = chunk_create(value, len);
+ if (chunk_printable(chunk, NULL, ' '))
+ {
+ str = sa->get(sa, name);
+ if (asprintf(&str, "%s%s%.*s",
+ str ?: "", str ? " " : "", len, value) >= 0)
+ {
+ free(sa->put(sa, name, str));
+ }
+ }
+ return 0;
+}
+
+CALLBACK(children_sn, int,
+ hashtable_t *ike, vici_res_t *res, char *name)
+{
+ hashtable_t *child;
+ int ret;
+
+ child = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1);
+ ret = vici_parse_cb(res, NULL, values, list, child);
+ if (ret == 0)
+ {
+ printf(" %s: %s\n", name, child->get(child, "mode"));
+ printf(" local: %s\n", child->get(child, "local-ts"));
+ printf(" remote: %s\n", child->get(child, "remote-ts"));
+ }
+ free_hashtable(child);
+ return ret;
+}
+
+CALLBACK(conn_sn, int,
+ hashtable_t *ike, vici_res_t *res, char *name)
+{
+ int ret = 0;
+
+ if (streq(name, "children"))
+ {
+ return vici_parse_cb(res, children_sn, NULL, NULL, NULL);
+ }
+ if (streq(name, "local") || streq(name, "remote"))
+ {
+ hashtable_t *auth;
+
+ auth = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1);
+ ret = vici_parse_cb(res, NULL, values, list, auth);
+ if (ret == 0)
+ {
+ printf(" %s %s authentication:\n",
+ name, auth->get(auth, "class") ?: "unspecified");
+ if (auth->get(auth, "id"))
+ {
+ printf(" id: %s\n", auth->get(auth, "id"));
+ }
+ if (auth->get(auth, "groups"))
+ {
+ printf(" groups: %s\n", auth->get(auth, "groups"));
+ }
+ if (auth->get(auth, "certs"))
+ {
+ printf(" certs: %s\n", auth->get(auth, "certs"));
+ }
+ if (auth->get(auth, "cacerts"))
+ {
+ printf(" cacerts: %s\n", auth->get(auth, "cacerts"));
+ }
+ }
+ free_hashtable(auth);
+ }
+ return ret;
+}
+
+CALLBACK(conn_list, int,
+ hashtable_t *sa, vici_res_t *res, char *name, void *value, int len)
+{
+ if (chunk_printable(chunk_create(value, len), NULL, ' '))
+ {
+ if (streq(name, "local_addrs"))
+ {
+ printf(" local: %.*s\n", len, value);
+ }
+ if (streq(name, "remote_addrs"))
+ {
+ printf(" remote: %.*s\n", len, value);
+ }
+ }
+ return 0;
+}
+
+CALLBACK(conns, int,
+ void *null, vici_res_t *res, char *name)
+{
+ printf("%s: %s\n", name, vici_find_str(res, "", "%s.version", name));
+
+ return vici_parse_cb(res, conn_sn, NULL, conn_list, NULL);
+}
+
+CALLBACK(list_cb, void,
+ command_format_options_t *format, char *name, vici_res_t *res)
+{
+ if (*format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "list-conn event", *format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ else
+ {
+ if (vici_parse_cb(res, conns, NULL, NULL, NULL) != 0)
+ {
+ fprintf(stderr, "parsing conn event failed: %s\n", strerror(errno));
+ }
+ }
+}
+
+static int list_conns(vici_conn_t *conn)
+{
+ vici_req_t *req;
+ vici_res_t *res;
+ command_format_options_t format = COMMAND_FORMAT_NONE;
+ char *arg;
+
+ while (TRUE)
+ {
+ switch (command_getopt(&arg))
+ {
+ case 'h':
+ return command_usage(NULL);
+ case 'P':
+ format |= COMMAND_FORMAT_PRETTY;
+ /* fall through to raw */
+ case 'r':
+ format |= COMMAND_FORMAT_RAW;
+ continue;
+ case EOF:
+ break;
+ default:
+ return command_usage("invalid --list-conns option");
+ }
+ break;
+ }
+ if (vici_register(conn, "list-conn", list_cb, &format) != 0)
+ {
+ fprintf(stderr, "registering for connections failed: %s\n",
+ strerror(errno));
+ return errno;
+ }
+ req = vici_begin("list-conns");
+ res = vici_submit(req, conn);
+ if (!res)
+ {
+ fprintf(stderr, "list-conns request failed: %s\n", strerror(errno));
+ return errno;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "list-conns reply", format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ vici_free_res(res);
+ return 0;
+}
+
+/**
+ * Register the command.
+ */
+static void __attribute__ ((constructor))reg()
+{
+ command_register((command_t) {
+ list_conns, 'L', "list-conns", "list loaded configurations",
+ {"[--raw|--pretty]"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"raw", 'r', 0, "dump raw response message"},
+ {"pretty", 'P', 0, "dump raw response message in pretty print"},
+ }
+ });
+}
diff --git a/src/swanctl/commands/list_pols.c b/src/swanctl/commands/list_pols.c
new file mode 100644
index 000000000..2317b2542
--- /dev/null
+++ b/src/swanctl/commands/list_pols.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+
+#include "command.h"
+
+#include <collections/hashtable.h>
+
+/**
+ * Free hashtable with contained strings
+ */
+static void free_hashtable(hashtable_t *hashtable)
+{
+ enumerator_t *enumerator;
+ char *str;
+
+ enumerator = hashtable->create_enumerator(hashtable);
+ while (enumerator->enumerate(enumerator, NULL, &str))
+ {
+ free(str);
+ }
+ enumerator->destroy(enumerator);
+
+ hashtable->destroy(hashtable);
+}
+
+CALLBACK(policy_values, int,
+ hashtable_t *pol, vici_res_t *res, char *name, void *value, int len)
+{
+ chunk_t chunk;
+ char *str;
+
+ chunk = chunk_create(value, len);
+ if (chunk_printable(chunk, NULL, ' '))
+ {
+ if (asprintf(&str, "%.*s", len, value) >= 0)
+ {
+ free(pol->put(pol, name, str));
+ }
+ }
+ return 0;
+}
+
+CALLBACK(policy_list, int,
+ hashtable_t *pol, vici_res_t *res, char *name, void *value, int len)
+{
+ chunk_t chunk;
+ char *str;
+
+ chunk = chunk_create(value, len);
+ if (chunk_printable(chunk, NULL, ' '))
+ {
+ str = pol->get(pol, name);
+ if (asprintf(&str, "%s%s%.*s",
+ str ?: "", str ? " " : "", len, value) >= 0)
+ {
+ free(pol->put(pol, name, str));
+ }
+ }
+ return 0;
+}
+
+CALLBACK(policies, int,
+ void *null, vici_res_t *res, char *name)
+{
+ hashtable_t *pol;
+ int ret;
+
+ pol = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1);
+ ret = vici_parse_cb(res, NULL, policy_values, policy_list, pol);
+
+ printf("%s, %s\n", name, pol->get(pol, "mode"));
+ printf(" local: %s\n", pol->get(pol, "local-ts"));
+ printf(" remote: %s\n", pol->get(pol, "remote-ts"));
+
+ free_hashtable(pol);
+ return ret;
+}
+
+CALLBACK(list_cb, void,
+ command_format_options_t *format, char *name, vici_res_t *res)
+{
+ if (*format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "list-policy event", *format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ else
+ {
+ if (vici_parse_cb(res, policies, NULL, NULL, NULL) != 0)
+ {
+ fprintf(stderr, "parsing policy event failed: %s\n", strerror(errno));
+ }
+ }
+}
+
+static int list_pols(vici_conn_t *conn)
+{
+ vici_req_t *req;
+ vici_res_t *res;
+ bool trap = FALSE, drop = FALSE, pass = FALSE;
+ command_format_options_t format = COMMAND_FORMAT_NONE;
+ char *arg, *child = NULL;
+
+ while (TRUE)
+ {
+ switch (command_getopt(&arg))
+ {
+ case 'h':
+ return command_usage(NULL);
+ case 'c':
+ child = arg;
+ continue;
+ case 't':
+ trap = TRUE;
+ continue;
+ case 'd':
+ drop = TRUE;
+ continue;
+ case 'p':
+ pass = TRUE;
+ continue;
+ case 'P':
+ format |= COMMAND_FORMAT_PRETTY;
+ /* fall through to raw */
+ case 'r':
+ format |= COMMAND_FORMAT_RAW;
+ continue;
+ case EOF:
+ break;
+ default:
+ return command_usage("invalid --list-pols option");
+ }
+ break;
+ }
+ if (!trap && !drop && !pass)
+ {
+ trap = drop = pass = TRUE;
+ }
+ if (vici_register(conn, "list-policy", list_cb, &format) != 0)
+ {
+ fprintf(stderr, "registering for policies failed: %s\n",
+ strerror(errno));
+ return errno;
+ }
+ req = vici_begin("list-policies");
+ if (child)
+ {
+ vici_add_key_valuef(req, "child", "%s", child);
+ }
+ if (trap)
+ {
+ vici_add_key_valuef(req, "trap", "yes");
+ }
+ if (drop)
+ {
+ vici_add_key_valuef(req, "drop", "yes");
+ }
+ if (pass)
+ {
+ vici_add_key_valuef(req, "pass", "yes");
+ }
+ res = vici_submit(req, conn);
+ if (!res)
+ {
+ fprintf(stderr, "list-policies request failed: %s\n", strerror(errno));
+ return errno;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "list-policies reply", format & COMMAND_FORMAT_PRETTY, stdout);
+ }
+ vici_free_res(res);
+ return 0;
+}
+
+/**
+ * Register the command.
+ */
+static void __attribute__ ((constructor))reg()
+{
+ command_register((command_t) {
+ list_pols, 'P', "list-pols", "list currently installed policies",
+ {"[--child <name>] [--trap] [--drop] [--pass] [--raw|--pretty]"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"child", 'c', 1, "filter policies by CHILD_SA config name"},
+ {"trap", 't', 0, "list trap policies"},
+ {"drop", 'd', 0, "list drop policies"},
+ {"pass", 'p', 0, "list bypass policies"},
+ {"raw", 'r', 0, "dump raw response message"},
+ {"pretty", 'P', 0, "dump raw response message in pretty print"},
+ }
+ });
+}
diff --git a/src/swanctl/commands/list_pools.c b/src/swanctl/commands/list_pools.c
new file mode 100644
index 000000000..17ea539a9
--- /dev/null
+++ b/src/swanctl/commands/list_pools.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+
+#include "command.h"
+
+CALLBACK(list_pool, int,
+ linked_list_t *list, vici_res_t *res, char *name)
+{
+ char pool[64], leases[32];
+
+ snprintf(pool, sizeof(pool), "%s:", name);
+ snprintf(leases, sizeof(leases), "%s / %s / %s",
+ vici_find_str(res, "", "%s.online", name),
+ vici_find_str(res, "", "%s.offline", name),
+ vici_find_str(res, "", "%s.size", name));
+
+ printf("%-20s %-30s %16s\n",
+ name, vici_find_str(res, "", "%s.base", name), leases);
+
+ return 0;
+}
+
+static int list_pools(vici_conn_t *conn)
+{
+ vici_req_t *req;
+ vici_res_t *res;
+ command_format_options_t format = COMMAND_FORMAT_NONE;
+ char *arg;
+ int ret = 0;
+
+ while (TRUE)
+ {
+ switch (command_getopt(&arg))
+ {
+ case 'h':
+ return command_usage(NULL);
+ case 'P':
+ format |= COMMAND_FORMAT_PRETTY;
+ /* fall through to raw */
+ case 'r':
+ format |= COMMAND_FORMAT_RAW;
+ continue;
+ case EOF:
+ break;
+ default:
+ return command_usage("invalid --list-pools option");
+ }
+ break;
+ }
+
+ req = vici_begin("get-pools");
+ res = vici_submit(req, conn);
+ if (!res)
+ {
+ fprintf(stderr, "get-pools request failed: %s\n", strerror(errno));
+ return errno;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "get-pools reply", format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ else
+ {
+ ret = vici_parse_cb(res, list_pool, NULL, NULL, NULL);
+ }
+ vici_free_res(res);
+ return ret;
+}
+
+/**
+ * Register the command.
+ */
+static void __attribute__ ((constructor))reg()
+{
+ command_register((command_t) {
+ list_pools, 'A', "list-pools", "list loaded pool configurations",
+ {"[--raw|--pretty]"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"raw", 'r', 0, "dump raw response message"},
+ {"pretty", 'P', 0, "dump raw response message in pretty print"},
+ }
+ });
+}
diff --git a/src/swanctl/commands/list_sas.c b/src/swanctl/commands/list_sas.c
new file mode 100644
index 000000000..80c279ce8
--- /dev/null
+++ b/src/swanctl/commands/list_sas.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+
+#include "command.h"
+
+#include <collections/hashtable.h>
+
+/**
+ * Free hashtable with contained strings
+ */
+static void free_hashtable(hashtable_t *hashtable)
+{
+ enumerator_t *enumerator;
+ char *str;
+
+ enumerator = hashtable->create_enumerator(hashtable);
+ while (enumerator->enumerate(enumerator, NULL, &str))
+ {
+ free(str);
+ }
+ enumerator->destroy(enumerator);
+
+ hashtable->destroy(hashtable);
+}
+
+CALLBACK(sa_values, int,
+ hashtable_t *sa, vici_res_t *res, char *name, void *value, int len)
+{
+ chunk_t chunk;
+ char *str;
+
+ chunk = chunk_create(value, len);
+ if (chunk_printable(chunk, NULL, ' '))
+ {
+ if (asprintf(&str, "%.*s", len, value) >= 0)
+ {
+ free(sa->put(sa, name, str));
+ }
+ }
+ return 0;
+}
+
+
+CALLBACK(sa_list, int,
+ hashtable_t *sa, vici_res_t *res, char *name, void *value, int len)
+{
+ chunk_t chunk;
+ char *str;
+
+ chunk = chunk_create(value, len);
+ if (chunk_printable(chunk, NULL, ' '))
+ {
+ str = sa->get(sa, name);
+ if (asprintf(&str, "%s%s%.*s",
+ str ?: "", str ? " " : "", len, value) >= 0)
+ {
+ free(sa->put(sa, name, str));
+ }
+ }
+ return 0;
+}
+
+CALLBACK(child_sas, int,
+ hashtable_t *ike, vici_res_t *res, char *name)
+{
+ hashtable_t *child;
+ int ret;
+
+ child = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1);
+ ret = vici_parse_cb(res, NULL, sa_values, sa_list, child);
+ if (ret == 0)
+ {
+ printf(" %s: #%s, %s, %s%s, %s:",
+ name, child->get(child, "reqid"),
+ child->get(child, "state"), child->get(child, "mode"),
+ child->get(child, "encap") ? "-in-UDP" : "",
+ child->get(child, "protocol"));
+
+ if (child->get(child, "encr-alg"))
+ {
+ printf("%s", child->get(child, "encr-alg"));
+ if (child->get(child, "encr-keysize"))
+ {
+ printf("-%s", child->get(child, "encr-keysize"));
+ }
+ }
+ if (child->get(child, "integ-alg"))
+ {
+ if (child->get(child, "encr-alg"))
+ {
+ printf("/");
+ }
+ printf("%s", child->get(child, "integ-alg"));
+ if (child->get(child, "integ-keysize"))
+ {
+ printf("-%s", child->get(child, "integ-keysize"));
+ }
+ }
+ if (child->get(child, "prf-alg"))
+ {
+ printf("/%s", child->get(child, "prf-alg"));
+ }
+ if (child->get(child, "dh-group"))
+ {
+ printf("/%s", child->get(child, "dh-group"));
+ }
+ if (child->get(child, "esn"))
+ {
+ printf("/%s", child->get(child, "esn"));
+ }
+ printf("\n");
+
+ printf(" installed %s ago", child->get(child, "install-time"));
+ if (child->get(child, "rekey-time"))
+ {
+ printf(", rekeying in %ss", child->get(child, "rekey-time"));
+ }
+ if (child->get(child, "life-time"))
+ {
+ printf(", expires in %ss", child->get(child, "life-time"));
+ }
+ printf("\n");
+
+ printf(" in %s%s%s", child->get(child, "spi-in"),
+ child->get(child, "cpi-in") ? "/" : "",
+ child->get(child, "cpi-in") ?: "");
+ printf(", %6s bytes, %5s packets",
+ child->get(child, "bytes-in"), child->get(child, "packets-in"));
+ if (child->get(child, "use-in"))
+ {
+ printf(", %5ss ago", child->get(child, "use-in"));
+ }
+ printf("\n");
+
+ printf(" out %s%s%s", child->get(child, "spi-out"),
+ child->get(child, "cpi-out") ? "/" : "",
+ child->get(child, "cpi-out") ?: "");
+ printf(", %6s bytes, %5s packets",
+ child->get(child, "bytes-out"), child->get(child, "packets-out"));
+ if (child->get(child, "use-out"))
+ {
+ printf(", %5ss ago", child->get(child, "use-out"));
+ }
+ printf("\n");
+
+ printf(" local %s\n", child->get(child, "local-ts"));
+ printf(" remote %s\n", child->get(child, "remote-ts"));
+ }
+ free_hashtable(child);
+ return ret;
+}
+
+CALLBACK(ike_sa, int,
+ hashtable_t *ike, vici_res_t *res, char *name)
+{
+ if (streq(name, "child-sas"))
+ {
+ printf("%s: #%s, %s, IKEv%s, %s:%s\n",
+ ike->get(ike, "name"), ike->get(ike, "uniqueid"),
+ ike->get(ike, "state"), ike->get(ike, "version"),
+ ike->get(ike, "initiator-spi"), ike->get(ike, "responder-spi"));
+
+ printf(" local '%s' @ %s\n",
+ ike->get(ike, "local-id"), ike->get(ike, "local-host"));
+ printf(" remote '%s' @ %s",
+ ike->get(ike, "remote-id"), ike->get(ike, "remote-host"));
+ if (ike->get(ike, "remote-eap-id"))
+ {
+ printf(" EAP: '%s'", ike->get(ike, "remote-eap-id"));
+ }
+ if (ike->get(ike, "remote-xauth-id"))
+ {
+ printf(" XAuth: '%s'", ike->get(ike, "remote-xauth-id"));
+ }
+ printf("\n");
+
+ if (ike->get(ike, "encr-alg"))
+ {
+ printf(" %s", ike->get(ike, "encr-alg"));
+ if (ike->get(ike, "encr-keysize"))
+ {
+ printf("-%s", ike->get(ike, "encr-keysize"));
+ }
+ if (ike->get(ike, "integ-alg"))
+ {
+ printf("/%s", ike->get(ike, "integ-alg"));
+ }
+ if (ike->get(ike, "integ-keysize"))
+ {
+ printf("-%s", ike->get(ike, "integ-keysize"));
+ }
+ printf("/%s", ike->get(ike, "prf-alg"));
+ printf("/%s", ike->get(ike, "dh-group"));
+ printf("\n");
+ }
+
+ if (ike->get(ike, "established"))
+ {
+ printf(" established %ss ago", ike->get(ike, "established"));
+ if (ike->get(ike, "rekey-time"))
+ {
+ printf(", rekeying in %ss", ike->get(ike, "rekey-time"));
+ }
+ if (ike->get(ike, "reauth-time"))
+ {
+ printf(", reauth in %ss", ike->get(ike, "reauth-time"));
+ }
+ if (ike->get(ike, "life-time"))
+ {
+ printf(", expires in %ss", ike->get(ike, "life-time"));
+ }
+ printf("\n");
+ }
+
+ if (ike->get(ike, "tasks-queued"))
+ {
+ printf(" queued: %s\n", ike->get(ike, "tasks-queued"));
+ }
+ if (ike->get(ike, "tasks-active"))
+ {
+ printf(" active: %s\n", ike->get(ike, "tasks-active"));
+ }
+ if (ike->get(ike, "tasks-passive"))
+ {
+ printf(" passive: %s\n", ike->get(ike, "tasks-passive"));
+ }
+
+ return vici_parse_cb(res, child_sas, NULL, NULL, ike);
+ }
+ return 0;
+}
+
+CALLBACK(ike_sas, int,
+ void *null, vici_res_t *res, char *name)
+{
+ hashtable_t *ike;
+ int ret;
+
+ ike = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1);
+ ike->put(ike, "name", strdup(name));
+ ret = vici_parse_cb(res, ike_sa, sa_values, sa_list, ike);
+ free_hashtable(ike);
+ return ret;
+}
+
+CALLBACK(list_cb, void,
+ command_format_options_t *format, char *name, vici_res_t *res)
+{
+ if (*format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "list-sa event", *format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ else
+ {
+ if (vici_parse_cb(res, ike_sas, NULL, NULL, NULL) != 0)
+ {
+ fprintf(stderr, "parsing SA event failed: %s\n", strerror(errno));
+ }
+ }
+}
+
+static int list_sas(vici_conn_t *conn)
+{
+ vici_req_t *req;
+ vici_res_t *res;
+ bool noblock = FALSE;
+ command_format_options_t format = COMMAND_FORMAT_NONE;
+ char *arg, *ike = NULL;
+ int ike_id = 0;
+
+ while (TRUE)
+ {
+ switch (command_getopt(&arg))
+ {
+ case 'h':
+ return command_usage(NULL);
+ case 'i':
+ ike = arg;
+ continue;
+ case 'I':
+ ike_id = atoi(arg);
+ continue;
+ case 'n':
+ noblock = TRUE;
+ continue;
+ case 'P':
+ format |= COMMAND_FORMAT_PRETTY;
+ /* fall through to raw */
+ case 'r':
+ format |= COMMAND_FORMAT_RAW;
+ continue;
+ case EOF:
+ break;
+ default:
+ return command_usage("invalid --list-sas option");
+ }
+ break;
+ }
+ if (vici_register(conn, "list-sa", list_cb, &format) != 0)
+ {
+ fprintf(stderr, "registering for SAs failed: %s\n", strerror(errno));
+ return errno;
+ }
+ req = vici_begin("list-sas");
+ if (ike)
+ {
+ vici_add_key_valuef(req, "ike", "%s", ike);
+ }
+ if (ike_id)
+ {
+ vici_add_key_valuef(req, "ike-id", "%d", ike_id);
+ }
+ if (noblock)
+ {
+ vici_add_key_valuef(req, "noblock", "yes");
+ }
+ res = vici_submit(req, conn);
+ if (!res)
+ {
+ fprintf(stderr, "list-sas request failed: %s\n", strerror(errno));
+ return errno;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "list-sas reply", format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ vici_free_res(res);
+ return 0;
+}
+
+/**
+ * Register the command.
+ */
+static void __attribute__ ((constructor))reg()
+{
+ command_register((command_t) {
+ list_sas, 'l', "list-sas", "list currently active IKE_SAs",
+ {"[--raw|--pretty]"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"ike", 'i', 1, "filter IKE_SAs by name"},
+ {"ike-id", 'I', 1, "filter IKE_SAs by unique identifier"},
+ {"noblock", 'n', 0, "don't wait for IKE_SAs in use"},
+ {"raw", 'r', 0, "dump raw response message"},
+ {"pretty", 'P', 0, "dump raw response message in pretty print"},
+ }
+ });
+}
diff --git a/src/swanctl/commands/load_conns.c b/src/swanctl/commands/load_conns.c
new file mode 100644
index 000000000..7383f7a1e
--- /dev/null
+++ b/src/swanctl/commands/load_conns.c
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "command.h"
+#include "swanctl.h"
+
+/**
+ * Check if we should handle a key as a list of comma separated values
+ */
+static bool is_list_key(char *key)
+{
+ char *keys[] = {
+ "local_addrs",
+ "remote_addrs",
+ "proposals",
+ "esp_proposals",
+ "ah_proposals",
+ "local_ts",
+ "remote_ts",
+ "vips",
+ "pools",
+ "groups",
+ };
+ int i;
+
+ for (i = 0; i < countof(keys); i++)
+ {
+ if (strcaseeq(keys[i], key))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Check if we should handle a key as a list of comma separated files
+ */
+static bool is_file_list_key(char *key)
+{
+ char *keys[] = {
+ "certs",
+ "cacerts",
+ };
+ int i;
+
+ for (i = 0; i < countof(keys); i++)
+ {
+ if (strcaseeq(keys[i], key))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Add a vici list from a comma separated string value
+ */
+static void add_list_key(vici_req_t *req, char *key, char *value)
+{
+ enumerator_t *enumerator;
+ char *token;
+
+ vici_begin_list(req, key);
+ enumerator = enumerator_create_token(value, ",", " ");
+ while (enumerator->enumerate(enumerator, &token))
+ {
+ vici_add_list_itemf(req, "%s", token);
+ }
+ enumerator->destroy(enumerator);
+ vici_end_list(req);
+}
+
+/**
+ * Add a vici list of blobs from a comma separated file list
+ */
+static void add_file_list_key(vici_req_t *req, char *key, char *value)
+{
+ enumerator_t *enumerator;
+ chunk_t *map;
+ char *token, buf[PATH_MAX];
+
+ vici_begin_list(req, key);
+ enumerator = enumerator_create_token(value, ",", " ");
+ while (enumerator->enumerate(enumerator, &token))
+ {
+ if (!path_absolute(token))
+ {
+ if (streq(key, "certs"))
+ {
+ snprintf(buf, sizeof(buf), "%s%s%s",
+ SWANCTL_X509DIR, DIRECTORY_SEPARATOR, token);
+ token = buf;
+ }
+ if (streq(key, "cacerts"))
+ {
+ snprintf(buf, sizeof(buf), "%s%s%s",
+ SWANCTL_X509CADIR, DIRECTORY_SEPARATOR, token);
+ token = buf;
+ }
+ }
+
+ map = chunk_map(token, FALSE);
+ if (map)
+ {
+ vici_add_list_item(req, map->ptr, map->len);
+ chunk_unmap(map);
+ }
+ else
+ {
+ fprintf(stderr, "loading certificate '%s' failed: %s\n",
+ token, strerror(errno));
+ }
+ }
+ enumerator->destroy(enumerator);
+ vici_end_list(req);
+}
+
+/**
+ * Translate setting key/values from a section into vici key-values/lists
+ */
+static void add_key_values(vici_req_t *req, settings_t *cfg, char *section)
+{
+ enumerator_t *enumerator;
+ char *key, *value;
+
+ enumerator = cfg->create_key_value_enumerator(cfg, section);
+ while (enumerator->enumerate(enumerator, &key, &value))
+ {
+ if (is_list_key(key))
+ {
+ add_list_key(req, key, value);
+ }
+ else if (is_file_list_key(key))
+ {
+ add_file_list_key(req, key, value);
+ }
+ else
+ {
+ vici_add_key_valuef(req, key, "%s", value);
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * Translate a settings section to a vici section
+ */
+static void add_sections(vici_req_t *req, settings_t *cfg, char *section)
+{
+ enumerator_t *enumerator;
+ char *name, buf[256];
+
+ enumerator = cfg->create_section_enumerator(cfg, section);
+ while (enumerator->enumerate(enumerator, &name))
+ {
+ vici_begin_section(req, name);
+ snprintf(buf, sizeof(buf), "%s.%s", section, name);
+ add_key_values(req, cfg, buf);
+ add_sections(req, cfg, buf);
+ vici_end_section(req);
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * Load an IKE_SA config with CHILD_SA configs from a section
+ */
+static bool load_conn(vici_conn_t *conn, settings_t *cfg,
+ char *section, command_format_options_t format)
+{
+ vici_req_t *req;
+ vici_res_t *res;
+ bool ret = TRUE;
+ char buf[128];
+
+ snprintf(buf, sizeof(buf), "%s.%s", "connections", section);
+
+ req = vici_begin("load-conn");
+
+ vici_begin_section(req, section);
+ add_key_values(req, cfg, buf);
+ add_sections(req, cfg, buf);
+ vici_end_section(req);
+
+ res = vici_submit(req, conn);
+ if (!res)
+ {
+ fprintf(stderr, "load-conn request failed: %s\n", strerror(errno));
+ return FALSE;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "load-conn reply", format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ else if (!streq(vici_find_str(res, "no", "success"), "yes"))
+ {
+ fprintf(stderr, "loading connection '%s' failed: %s\n",
+ section, vici_find_str(res, "", "errmsg"));
+ ret = FALSE;
+ }
+ else
+ {
+ printf("loaded connection '%s'\n", section);
+ }
+ vici_free_res(res);
+ return ret;
+}
+
+CALLBACK(list_conn, int,
+ linked_list_t *list, vici_res_t *res, char *name, void *value, int len)
+{
+ if (streq(name, "conns"))
+ {
+ char *str;
+
+ if (asprintf(&str, "%.*s", len, value) != -1)
+ {
+ list->insert_last(list, str);
+ }
+ }
+ return 0;
+}
+
+/**
+ * Create a list of currently loaded connections
+ */
+static linked_list_t* list_conns(vici_conn_t *conn,
+ command_format_options_t format)
+{
+ linked_list_t *list;
+ vici_res_t *res;
+
+ list = linked_list_create();
+
+ res = vici_submit(vici_begin("get-conns"), conn);
+ if (res)
+ {
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "get-conns reply", format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ vici_parse_cb(res, NULL, NULL, list_conn, list);
+ vici_free_res(res);
+ }
+ return list;
+}
+
+/**
+ * Remove and free a string from a list
+ */
+static void remove_from_list(linked_list_t *list, char *str)
+{
+ enumerator_t *enumerator;
+ char *current;
+
+ enumerator = list->create_enumerator(list);
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ if (streq(current, str))
+ {
+ list->remove_at(list, enumerator);
+ free(current);
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * Unload a connection by name
+ */
+static bool unload_conn(vici_conn_t *conn, char *name,
+ command_format_options_t format)
+{
+ vici_req_t *req;
+ vici_res_t *res;
+ bool ret = TRUE;
+
+ req = vici_begin("unload-conn");
+ vici_add_key_valuef(req, "name", "%s", name);
+ res = vici_submit(req, conn);
+ if (!res)
+ {
+ fprintf(stderr, "unload-conn request failed: %s\n", strerror(errno));
+ return FALSE;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "unload-conn reply", format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ else if (!streq(vici_find_str(res, "no", "success"), "yes"))
+ {
+ fprintf(stderr, "unloading connection '%s' failed: %s\n",
+ name, vici_find_str(res, "", "errmsg"));
+ ret = FALSE;
+ }
+ vici_free_res(res);
+ return ret;
+}
+
+static int load_conns(vici_conn_t *conn)
+{
+ u_int found = 0, loaded = 0, unloaded = 0;
+ command_format_options_t format = COMMAND_FORMAT_NONE;
+ char *arg, *section;
+ enumerator_t *enumerator;
+ linked_list_t *conns;
+ settings_t *cfg;
+
+ while (TRUE)
+ {
+ switch (command_getopt(&arg))
+ {
+ case 'h':
+ return command_usage(NULL);
+ case 'P':
+ format |= COMMAND_FORMAT_PRETTY;
+ /* fall through to raw */
+ case 'r':
+ format |= COMMAND_FORMAT_RAW;
+ continue;
+ case EOF:
+ break;
+ default:
+ return command_usage("invalid --load-conns option");
+ }
+ break;
+ }
+
+ cfg = settings_create(SWANCTL_CONF);
+ if (!cfg)
+ {
+ fprintf(stderr, "parsing '%s' failed\n", SWANCTL_CONF);
+ return EINVAL;
+ }
+
+ conns = list_conns(conn, format);
+
+ enumerator = cfg->create_section_enumerator(cfg, "connections");
+ while (enumerator->enumerate(enumerator, &section))
+ {
+ remove_from_list(conns, section);
+ found++;
+ if (load_conn(conn, cfg, section, format))
+ {
+ loaded++;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ cfg->destroy(cfg);
+
+ /* unload all connection in daemon, but not in file */
+ while (conns->remove_first(conns, (void**)&section) == SUCCESS)
+ {
+ if (unload_conn(conn, section, format))
+ {
+ unloaded++;
+ }
+ free(section);
+ }
+ conns->destroy(conns);
+
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ return 0;
+ }
+ if (found == 0)
+ {
+ printf("no connections found, %u unloaded\n", unloaded);
+ return 0;
+ }
+ if (loaded == found)
+ {
+ printf("successfully loaded %u connections, %u unloaded\n",
+ loaded, unloaded);
+ return 0;
+ }
+ fprintf(stderr, "loaded %u of %u connections, %u failed to load, "
+ "%u unloaded\n", loaded, found, found - loaded, unloaded);
+ return EINVAL;
+}
+
+/**
+ * Register the command.
+ */
+static void __attribute__ ((constructor))reg()
+{
+ command_register((command_t) {
+ load_conns, 'c', "load-conns", "(re-)load connection configuration",
+ {"[--raw|--pretty]"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"raw", 'r', 0, "dump raw response message"},
+ {"pretty", 'P', 0, "dump raw response message in pretty print"},
+ }
+ });
+}
diff --git a/src/swanctl/commands/load_creds.c b/src/swanctl/commands/load_creds.c
new file mode 100644
index 000000000..f77084c60
--- /dev/null
+++ b/src/swanctl/commands/load_creds.c
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "command.h"
+#include "swanctl.h"
+
+#include <credentials/sets/mem_cred.h>
+#include <credentials/sets/callback_cred.h>
+
+/**
+ * Load a single certificate over vici
+ */
+static bool load_cert(vici_conn_t *conn, command_format_options_t format,
+ char *dir, char *type, chunk_t data)
+{
+ vici_req_t *req;
+ vici_res_t *res;
+ bool ret = TRUE;
+
+ req = vici_begin("load-cert");
+
+ vici_add_key_valuef(req, "type", "%s", type);
+ vici_add_key_value(req, "data", data.ptr, data.len);
+
+ res = vici_submit(req, conn);
+ if (!res)
+ {
+ fprintf(stderr, "load-cert request failed: %s\n", strerror(errno));
+ return FALSE;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "load-cert reply", format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ else if (!streq(vici_find_str(res, "no", "success"), "yes"))
+ {
+ fprintf(stderr, "loading '%s' failed: %s\n",
+ dir, vici_find_str(res, "", "errmsg"));
+ ret = FALSE;
+ }
+ else
+ {
+ printf("loaded %s certificate '%s'\n", type, dir);
+ }
+ vici_free_res(res);
+ return ret;
+}
+
+/**
+ * Load certficiates from a directory
+ */
+static void load_certs(vici_conn_t *conn, command_format_options_t format,
+ char *type, char *dir)
+{
+ enumerator_t *enumerator;
+ struct stat st;
+ chunk_t *map;
+ char *path;
+
+ enumerator = enumerator_create_directory(dir);
+ if (enumerator)
+ {
+ while (enumerator->enumerate(enumerator, NULL, &path, &st))
+ {
+ if (S_ISREG(st.st_mode))
+ {
+ map = chunk_map(path, FALSE);
+ if (map)
+ {
+ load_cert(conn, format, path, type, *map);
+ chunk_unmap(map);
+ }
+ else
+ {
+ fprintf(stderr, "mapping '%s' failed: %s, skipped\n",
+ path, strerror(errno));
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+}
+
+/**
+ * Load a single private key over vici
+ */
+static bool load_key(vici_conn_t *conn, command_format_options_t format,
+ char *dir, char *type, chunk_t data)
+{
+ vici_req_t *req;
+ vici_res_t *res;
+ bool ret = TRUE;
+
+ req = vici_begin("load-key");
+
+ vici_add_key_valuef(req, "type", "%s", type);
+ vici_add_key_value(req, "data", data.ptr, data.len);
+
+ res = vici_submit(req, conn);
+ if (!res)
+ {
+ fprintf(stderr, "load-key request failed: %s\n", strerror(errno));
+ return FALSE;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "load-key reply", format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ else if (!streq(vici_find_str(res, "no", "success"), "yes"))
+ {
+ fprintf(stderr, "loading '%s' failed: %s\n",
+ dir, vici_find_str(res, "", "errmsg"));
+ ret = FALSE;
+ }
+ else
+ {
+ printf("loaded %s key '%s'\n", type, dir);
+ }
+ vici_free_res(res);
+ return ret;
+}
+
+/**
+ * Callback function to prompt for private key passwords
+ */
+CALLBACK(password_cb, shared_key_t*,
+ char *prompt, shared_key_type_t type,
+ identification_t *me, identification_t *other,
+ id_match_t *match_me, id_match_t *match_other)
+{
+ char *pwd = NULL;
+
+ if (type != SHARED_PRIVATE_KEY_PASS)
+ {
+ return NULL;
+ }
+#ifdef HAVE_GETPASS
+ pwd = getpass(prompt);
+#endif
+ if (!pwd || strlen(pwd) == 0)
+ {
+ return NULL;
+ }
+ if (match_me)
+ {
+ *match_me = ID_MATCH_PERFECT;
+ }
+ if (match_other)
+ {
+ *match_other = ID_MATCH_PERFECT;
+ }
+ return shared_key_create(type, chunk_clone(chunk_from_str(pwd)));
+}
+
+/**
+ * Try to parse a potentially encrypted private key using password prompt
+ */
+static private_key_t* decrypt_key(char *name, char *type, chunk_t encoding)
+{
+ key_type_t kt = KEY_ANY;
+ private_key_t *private;
+ callback_cred_t *cb;
+ char buf[128];
+
+ if (streq(type, "rsa"))
+ {
+ kt = KEY_RSA;
+ }
+ else if (streq(type, "ecdsa"))
+ {
+ kt = KEY_ECDSA;
+ }
+
+ snprintf(buf, sizeof(buf), "Password for '%s': ", name);
+
+ cb = callback_cred_create_shared(password_cb, buf);
+ lib->credmgr->add_set(lib->credmgr, &cb->set);
+
+ private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, kt,
+ BUILD_BLOB_PEM, encoding, BUILD_END);
+
+ lib->credmgr->remove_set(lib->credmgr, &cb->set);
+ cb->destroy(cb);
+
+ return private;
+}
+
+/**
+ * Try to parse a potentially encrypted private key using configured secret
+ */
+static private_key_t* decrypt_key_with_config(settings_t *cfg, char *name,
+ char *type, chunk_t encoding)
+{ key_type_t kt = KEY_ANY;
+ enumerator_t *enumerator, *secrets;
+ char *section, *key, *value, *file, buf[128];
+ shared_key_t *shared;
+ private_key_t *private = NULL;
+ mem_cred_t *mem = NULL;
+
+ if (streq(type, "rsa"))
+ {
+ kt = KEY_RSA;
+ }
+ else if (streq(type, "ecdsa"))
+ {
+ kt = KEY_ECDSA;
+ }
+ else
+ {
+ type = "pkcs8";
+ }
+
+ /* load all secrets for this key type */
+ enumerator = cfg->create_section_enumerator(cfg, "secrets");
+ while (enumerator->enumerate(enumerator, &section))
+ {
+ if (strpfx(section, type))
+ {
+ file = cfg->get_str(cfg, "secrets.%s.file", NULL, section);
+ if (file && strcaseeq(file, name))
+ {
+ snprintf(buf, sizeof(buf), "secrets.%s", section);
+ secrets = cfg->create_key_value_enumerator(cfg, buf);
+ while (secrets->enumerate(secrets, &key, &value))
+ {
+ if (strpfx(key, "secret"))
+ {
+ if (!mem)
+ {
+ mem = mem_cred_create();
+ }
+ shared = shared_key_create(SHARED_PRIVATE_KEY_PASS,
+ chunk_clone(chunk_from_str(value)));
+ mem->add_shared(mem, shared, NULL);
+ }
+ }
+ secrets->destroy(secrets);
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (mem)
+ {
+ lib->credmgr->add_local_set(lib->credmgr, &mem->set, FALSE);
+
+ private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, kt,
+ BUILD_BLOB_PEM, encoding, BUILD_END);
+
+ lib->credmgr->remove_local_set(lib->credmgr, &mem->set);
+
+ if (!private)
+ {
+ fprintf(stderr, "configured decryption secret for '%s' invalid\n",
+ name);
+ }
+
+ mem->destroy(mem);
+ }
+
+ return private;
+}
+
+/**
+ * Try to decrypt and load a private key
+ */
+static bool load_encrypted_key(vici_conn_t *conn,
+ command_format_options_t format, settings_t *cfg,
+ char *rel, char *path, char *type, bool noprompt,
+ chunk_t data)
+{
+ private_key_t *private;
+ bool loaded = FALSE;
+ chunk_t encoding;
+
+ private = decrypt_key_with_config(cfg, rel, type, data);
+ if (!private && !noprompt)
+ {
+ private = decrypt_key(rel, type, data);
+ }
+ if (private)
+ {
+ if (private->get_encoding(private, PRIVKEY_ASN1_DER, &encoding))
+ {
+ switch (private->get_type(private))
+ {
+ case KEY_RSA:
+ loaded = load_key(conn, format, path, "rsa", encoding);
+ break;
+ case KEY_ECDSA:
+ loaded = load_key(conn, format, path, "ecdsa", encoding);
+ break;
+ default:
+ break;
+ }
+ chunk_clear(&encoding);
+ }
+ private->destroy(private);
+ }
+ return loaded;
+}
+
+/**
+ * Load private keys from a directory
+ */
+static void load_keys(vici_conn_t *conn, command_format_options_t format,
+ bool noprompt, settings_t *cfg, char *type, char *dir)
+{
+ enumerator_t *enumerator;
+ struct stat st;
+ chunk_t *map;
+ char *path, *rel;
+
+ enumerator = enumerator_create_directory(dir);
+ if (enumerator)
+ {
+ while (enumerator->enumerate(enumerator, &rel, &path, &st))
+ {
+ if (S_ISREG(st.st_mode))
+ {
+ map = chunk_map(path, FALSE);
+ if (map)
+ {
+ if (!load_encrypted_key(conn, format, cfg, rel, path, type,
+ noprompt, *map))
+ {
+ load_key(conn, format, path, type, *map);
+ }
+ chunk_unmap(map);
+ }
+ else
+ {
+ fprintf(stderr, "mapping '%s' failed: %s, skipped\n",
+ path, strerror(errno));
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+}
+
+/**
+ * Load a single secret over VICI
+ */
+static bool load_secret(vici_conn_t *conn, settings_t *cfg,
+ char *section, command_format_options_t format)
+{
+ enumerator_t *enumerator;
+ vici_req_t *req;
+ vici_res_t *res;
+ chunk_t data;
+ char *key, *value, buf[128], *type = NULL;
+ bool ret = TRUE;
+ int i;
+ char *types[] = {
+ "eap",
+ "xauth",
+ "ike",
+ "rsa",
+ "ecdsa",
+ "pkcs8",
+ };
+
+ for (i = 0; i < countof(types); i++)
+ {
+ if (strpfx(section, types[i]))
+ {
+ type = types[i];
+ break;
+ }
+ }
+ if (!type)
+ {
+ fprintf(stderr, "ignoring unsupported secret '%s'\n", section);
+ return FALSE;
+ }
+ if (!streq(type, "eap") && !streq(type, "xauth") && !streq(type, "ike"))
+ { /* skip non-shared secrets */
+ return TRUE;
+ }
+
+ value = cfg->get_str(cfg, "secrets.%s.secret", NULL, section);
+ if (!value)
+ {
+ fprintf(stderr, "missing secret in '%s', ignored\n", section);
+ return FALSE;
+ }
+ if (strcasepfx(value, "0x"))
+ {
+ data = chunk_from_hex(chunk_from_str(value + 2), NULL);
+ }
+ else if (strcasepfx(value, "0s"))
+ {
+ data = chunk_from_base64(chunk_from_str(value + 2), NULL);
+ }
+ else
+ {
+ data = chunk_clone(chunk_from_str(value));
+ }
+
+ req = vici_begin("load-shared");
+
+ vici_add_key_valuef(req, "type", "%s", type);
+ vici_add_key_value(req, "data", data.ptr, data.len);
+ chunk_clear(&data);
+
+ vici_begin_list(req, "owners");
+ snprintf(buf, sizeof(buf), "secrets.%s", section);
+ enumerator = cfg->create_key_value_enumerator(cfg, buf);
+ while (enumerator->enumerate(enumerator, &key, &value))
+ {
+ if (strpfx(key, "id"))
+ {
+ vici_add_list_itemf(req, "%s", value);
+ }
+ }
+ enumerator->destroy(enumerator);
+ vici_end_list(req);
+
+ res = vici_submit(req, conn);
+ if (!res)
+ {
+ fprintf(stderr, "load-shared request failed: %s\n", strerror(errno));
+ return FALSE;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "load-shared reply", format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ else if (!streq(vici_find_str(res, "no", "success"), "yes"))
+ {
+ fprintf(stderr, "loading shared secret failed: %s\n",
+ vici_find_str(res, "", "errmsg"));
+ ret = FALSE;
+ }
+ else
+ {
+ printf("loaded %s secret '%s'\n", type, section);
+ }
+ vici_free_res(res);
+ return ret;
+}
+
+/**
+ * Clear all currently loaded credentials
+ */
+static bool clear_creds(vici_conn_t *conn, command_format_options_t format)
+{
+ vici_res_t *res;
+
+ res = vici_submit(vici_begin("clear-creds"), conn);
+ if (!res)
+ {
+ fprintf(stderr, "clear-creds request failed: %s\n", strerror(errno));
+ return FALSE;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "clear-creds reply", format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ vici_free_res(res);
+ return TRUE;
+}
+
+static int load_creds(vici_conn_t *conn)
+{
+ bool clear = FALSE, noprompt = FALSE;
+ command_format_options_t format = COMMAND_FORMAT_NONE;
+ enumerator_t *enumerator;
+ settings_t *cfg;
+ char *arg, *section;
+
+ while (TRUE)
+ {
+ switch (command_getopt(&arg))
+ {
+ case 'h':
+ return command_usage(NULL);
+ case 'c':
+ clear = TRUE;
+ continue;
+ case 'n':
+ noprompt = TRUE;
+ continue;
+ case 'P':
+ format |= COMMAND_FORMAT_PRETTY;
+ /* fall through to raw */
+ case 'r':
+ format |= COMMAND_FORMAT_RAW;
+ continue;
+ case EOF:
+ break;
+ default:
+ return command_usage("invalid --load-creds option");
+ }
+ break;
+ }
+
+ if (clear)
+ {
+ if (!clear_creds(conn, format))
+ {
+ return ECONNREFUSED;
+ }
+ }
+
+ cfg = settings_create(SWANCTL_CONF);
+ if (!cfg)
+ {
+ fprintf(stderr, "parsing '%s' failed\n", SWANCTL_CONF);
+ return EINVAL;
+ }
+
+ load_certs(conn, format, "x509", SWANCTL_X509DIR);
+ load_certs(conn, format, "x509ca", SWANCTL_X509CADIR);
+ load_certs(conn, format, "x509aa", SWANCTL_X509AADIR);
+ load_certs(conn, format, "x509crl", SWANCTL_X509CRLDIR);
+ load_certs(conn, format, "x509ac", SWANCTL_X509ACDIR);
+
+ load_keys(conn, format, noprompt, cfg, "rsa", SWANCTL_RSADIR);
+ load_keys(conn, format, noprompt, cfg, "ecdsa", SWANCTL_ECDSADIR);
+ load_keys(conn, format, noprompt, cfg, "any", SWANCTL_PKCS8DIR);
+
+ enumerator = cfg->create_section_enumerator(cfg, "secrets");
+ while (enumerator->enumerate(enumerator, &section))
+ {
+ load_secret(conn, cfg, section, format);
+ }
+ enumerator->destroy(enumerator);
+
+ cfg->destroy(cfg);
+
+ return 0;
+}
+
+/**
+ * Register the command.
+ */
+static void __attribute__ ((constructor))reg()
+{
+ command_register((command_t) {
+ load_creds, 's', "load-creds", "(re-)load credentials",
+ {"[--raw|--pretty]"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"clear", 'c', 0, "clear previously loaded credentials"},
+ {"noprompt", 'n', 0, "do not prompt for passwords"},
+ {"raw", 'r', 0, "dump raw response message"},
+ {"pretty", 'P', 0, "dump raw response message in pretty print"},
+ }
+ });
+}
diff --git a/src/swanctl/commands/load_pools.c b/src/swanctl/commands/load_pools.c
new file mode 100644
index 000000000..0ec56cc43
--- /dev/null
+++ b/src/swanctl/commands/load_pools.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <limits.h>
+
+#include "command.h"
+#include "swanctl.h"
+
+/**
+ * Add a vici list from a comma separated string value
+ */
+static void add_list_key(vici_req_t *req, char *key, char *value)
+{
+ enumerator_t *enumerator;
+ char *token;
+
+ vici_begin_list(req, key);
+ enumerator = enumerator_create_token(value, ",", " ");
+ while (enumerator->enumerate(enumerator, &token))
+ {
+ vici_add_list_itemf(req, "%s", token);
+ }
+ enumerator->destroy(enumerator);
+ vici_end_list(req);
+}
+
+/**
+ * Translate setting key/values from a section into vici key-values/lists
+ */
+static void add_key_values(vici_req_t *req, settings_t *cfg, char *section)
+{
+ enumerator_t *enumerator;
+ char *key, *value;
+
+ enumerator = cfg->create_key_value_enumerator(cfg, section);
+ while (enumerator->enumerate(enumerator, &key, &value))
+ {
+ /* pool subnet is encoded as key/value, all other attributes as list */
+ if (streq(key, "addrs"))
+ {
+ vici_add_key_valuef(req, key, "%s", value);
+ }
+ else
+ {
+ add_list_key(req, key, value);
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * Load a pool configuration
+ */
+static bool load_pool(vici_conn_t *conn, settings_t *cfg,
+ char *section, command_format_options_t format)
+{
+ vici_req_t *req;
+ vici_res_t *res;
+ bool ret = TRUE;
+ char buf[128];
+
+ snprintf(buf, sizeof(buf), "%s.%s", "pools", section);
+
+ req = vici_begin("load-pool");
+
+ vici_begin_section(req, section);
+ add_key_values(req, cfg, buf);
+ vici_end_section(req);
+
+ res = vici_submit(req, conn);
+ if (!res)
+ {
+ fprintf(stderr, "load-pool request failed: %s\n", strerror(errno));
+ return FALSE;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "load-pool reply", format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ else if (!streq(vici_find_str(res, "no", "success"), "yes"))
+ {
+ fprintf(stderr, "loading pool '%s' failed: %s\n",
+ section, vici_find_str(res, "", "errmsg"));
+ ret = FALSE;
+ }
+ else
+ {
+ printf("loaded pool '%s'\n", section);
+ }
+ vici_free_res(res);
+ return ret;
+}
+
+CALLBACK(list_pool, int,
+ linked_list_t *list, vici_res_t *res, char *name)
+{
+ list->insert_last(list, strdup(name));
+ return 0;
+}
+
+/**
+ * Create a list of currently loaded pools
+ */
+static linked_list_t* list_pools(vici_conn_t *conn,
+ command_format_options_t format)
+{
+ linked_list_t *list;
+ vici_res_t *res;
+
+ list = linked_list_create();
+
+ res = vici_submit(vici_begin("get-pools"), conn);
+ if (res)
+ {
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "get-pools reply", format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ vici_parse_cb(res, list_pool, NULL, NULL, list);
+ vici_free_res(res);
+ }
+ return list;
+}
+
+/**
+ * Remove and free a string from a list
+ */
+static void remove_from_list(linked_list_t *list, char *str)
+{
+ enumerator_t *enumerator;
+ char *current;
+
+ enumerator = list->create_enumerator(list);
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ if (streq(current, str))
+ {
+ list->remove_at(list, enumerator);
+ free(current);
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * Unload a pool by name
+ */
+static bool unload_pool(vici_conn_t *conn, char *name,
+ command_format_options_t format)
+{
+ vici_req_t *req;
+ vici_res_t *res;
+ bool ret = TRUE;
+
+ req = vici_begin("unload-pool");
+ vici_add_key_valuef(req, "name", "%s", name);
+ res = vici_submit(req, conn);
+ if (!res)
+ {
+ fprintf(stderr, "unload-pool request failed: %s\n", strerror(errno));
+ return FALSE;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "unload-pool reply", format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ else if (!streq(vici_find_str(res, "no", "success"), "yes"))
+ {
+ fprintf(stderr, "unloading pool '%s' failed: %s\n",
+ name, vici_find_str(res, "", "errmsg"));
+ ret = FALSE;
+ }
+ vici_free_res(res);
+ return ret;
+}
+
+static int load_pools(vici_conn_t *conn)
+{
+ command_format_options_t format = COMMAND_FORMAT_NONE;
+ u_int found = 0, loaded = 0, unloaded = 0;
+ char *arg, *section;
+ enumerator_t *enumerator;
+ linked_list_t *pools;
+ settings_t *cfg;
+
+ while (TRUE)
+ {
+ switch (command_getopt(&arg))
+ {
+ case 'h':
+ return command_usage(NULL);
+ case 'P':
+ format |= COMMAND_FORMAT_PRETTY;
+ /* fall through to raw */
+ case 'r':
+ format |= COMMAND_FORMAT_RAW;
+ continue;
+ case EOF:
+ break;
+ default:
+ return command_usage("invalid --load-pools option");
+ }
+ break;
+ }
+
+ cfg = settings_create(SWANCTL_CONF);
+ if (!cfg)
+ {
+ fprintf(stderr, "parsing '%s' failed\n", SWANCTL_CONF);
+ return EINVAL;
+ }
+
+ pools = list_pools(conn, format);
+
+ enumerator = cfg->create_section_enumerator(cfg, "pools");
+ while (enumerator->enumerate(enumerator, &section))
+ {
+ remove_from_list(pools, section);
+ found++;
+ if (load_pool(conn, cfg, section, format))
+ {
+ loaded++;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ cfg->destroy(cfg);
+
+ /* unload all pools in daemon, but not in file */
+ while (pools->remove_first(pools, (void**)&section) == SUCCESS)
+ {
+ if (unload_pool(conn, section, format))
+ {
+ unloaded++;
+ }
+ free(section);
+ }
+ pools->destroy(pools);
+
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ return 0;
+ }
+ if (found == 0)
+ {
+ printf("no pools found, %u unloaded\n", unloaded);
+ return 0;
+ }
+ if (loaded == found)
+ {
+ printf("successfully loaded %u pools, %u unloaded\n",
+ loaded, unloaded);
+ return 0;
+ }
+ fprintf(stderr, "loaded %u of %u pools, %u failed to load, "
+ "%u unloaded\n", loaded, found, found - loaded, unloaded);
+ return EINVAL;
+}
+
+/**
+ * Register the command.
+ */
+static void __attribute__ ((constructor))reg()
+{
+ command_register((command_t) {
+ load_pools, 'a', "load-pools", "(re-)load pool configuration",
+ {"[--raw|--pretty"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"raw", 'r', 0, "dump raw response message"},
+ {"pretty", 'P', 0, "dump raw response message in pretty print"},
+ }
+ });
+}
diff --git a/src/swanctl/commands/log.c b/src/swanctl/commands/log.c
new file mode 100644
index 000000000..99ba328a7
--- /dev/null
+++ b/src/swanctl/commands/log.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "command.h"
+
+#include <errno.h>
+#include <unistd.h>
+
+CALLBACK(log_cb, void,
+ command_format_options_t *format, char *name, vici_res_t *msg)
+{
+ if (*format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(msg, "log", *format & COMMAND_FORMAT_PRETTY, stdout);
+ }
+ else
+ {
+ char *current, *next;
+
+ current = vici_find_str(msg, NULL, "msg");
+ while (current)
+ {
+ next = strchr(current, '\n');
+ printf("%.2d[%s] ", vici_find_int(msg, 0, "thread"),
+ vici_find_str(msg, " ", "group"));
+ if (next == NULL)
+ {
+ printf("%s\n", current);
+ break;
+ }
+ printf("%.*s\n", (int)(next - current), current);
+ current = next + 1;
+ }
+ }
+}
+
+static int logcmd(vici_conn_t *conn)
+{
+ command_format_options_t format = COMMAND_FORMAT_NONE;
+ char *arg;
+
+ while (TRUE)
+ {
+ switch (command_getopt(&arg))
+ {
+ case 'h':
+ return command_usage(NULL);
+ case 'P':
+ format |= COMMAND_FORMAT_PRETTY;
+ /* fall through to raw */
+ case 'r':
+ format |= COMMAND_FORMAT_RAW;
+ continue;
+ case EOF:
+ break;
+ default:
+ return command_usage("invalid --log option");
+ }
+ break;
+ }
+
+ if (vici_register(conn, "log", log_cb, &format) != 0)
+ {
+ fprintf(stderr, "registering for log failed: %s\n", strerror(errno));
+ return errno;
+ }
+
+ wait_sigint();
+
+ fprintf(stderr, "disconnecting...\n");
+
+ return 0;
+}
+
+/**
+ * Register the command.
+ */
+static void __attribute__ ((constructor))reg()
+{
+ command_register((command_t) {
+ logcmd, 'T', "log", "trace logging output",
+ {"[--raw|--pretty]"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"raw", 'r', 0, "dump raw response message"},
+ {"pretty", 'P', 0, "dump raw response message in pretty print"},
+ }
+ });
+}
diff --git a/src/swanctl/commands/stats.c b/src/swanctl/commands/stats.c
new file mode 100644
index 000000000..b5425f504
--- /dev/null
+++ b/src/swanctl/commands/stats.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "command.h"
+
+#include <errno.h>
+
+static int stats(vici_conn_t *conn)
+{
+ vici_req_t *req;
+ vici_res_t *res;
+ char *arg;
+ command_format_options_t format = COMMAND_FORMAT_NONE;
+
+ while (TRUE)
+ {
+ switch (command_getopt(&arg))
+ {
+ case 'h':
+ return command_usage(NULL);
+ case 'P':
+ format |= COMMAND_FORMAT_PRETTY;
+ /* fall through to raw */
+ case 'r':
+ format |= COMMAND_FORMAT_RAW;
+ continue;
+ case EOF:
+ break;
+ default:
+ return command_usage("invalid --stats option");
+ }
+ break;
+ }
+
+ req = vici_begin("stats");
+ res = vici_submit(req, conn);
+ if (!res)
+ {
+ fprintf(stderr, "stats request failed: %s\n", strerror(errno));
+ return errno;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "stats reply", format & COMMAND_FORMAT_PRETTY, stdout);
+ }
+ else
+ {
+ printf("uptime: %s, since %s\n",
+ vici_find_str(res, "", "uptime.running"),
+ vici_find_str(res, "", "uptime.since"));
+
+ printf("worker threads: %s total, %s idle, working: %s/%s/%s/%s\n",
+ vici_find_str(res, "", "workers.total"),
+ vici_find_str(res, "", "workers.idle"),
+ vici_find_str(res, "", "workers.active.critical"),
+ vici_find_str(res, "", "workers.active.high"),
+ vici_find_str(res, "", "workers.active.medium"),
+ vici_find_str(res, "", "workers.active.low"));
+
+ printf("job queues: %s/%s/%s/%s\n",
+ vici_find_str(res, "", "queues.critical"),
+ vici_find_str(res, "", "queues.high"),
+ vici_find_str(res, "", "queues.medium"),
+ vici_find_str(res, "", "queues.low"));
+
+ printf("jobs scheduled: %s\n",
+ vici_find_str(res, "", "scheduled"));
+
+ printf("IKE_SAs: %s total, %s half-open\n",
+ vici_find_str(res, "", "ikesas.total"),
+ vici_find_str(res, "", "ikesas.half-open"));
+
+ if (vici_find_str(res, NULL, "mem.total"))
+ {
+ printf("memory usage: %s bytes, %s allocations\n",
+ vici_find_str(res, "", "mem.total"),
+ vici_find_str(res, "", "mem.allocs"));
+ }
+ if (vici_find_str(res, NULL, "mallinfo.sbrk"))
+ {
+ printf("mallinfo: sbrk %s, mmap %s, used %s, free %s\n",
+ vici_find_str(res, "", "mallinfo.sbrk"),
+ vici_find_str(res, "", "mallinfo.mmap"),
+ vici_find_str(res, "", "mallinfo.used"),
+ vici_find_str(res, "", "mallinfo.free"));
+ }
+ }
+ vici_free_res(res);
+ return 0;
+}
+
+/**
+ * Register the command.
+ */
+static void __attribute__ ((constructor))reg()
+{
+ command_register((command_t) {
+ stats, 'S', "stats", "show daemon stats information",
+ {"[--raw|--pretty]"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"raw", 'r', 0, "dump raw response message"},
+ {"pretty", 'P', 0, "dump raw response message in pretty print"},
+ }
+ });
+}
diff --git a/src/swanctl/commands/terminate.c b/src/swanctl/commands/terminate.c
new file mode 100644
index 000000000..689ba4d50
--- /dev/null
+++ b/src/swanctl/commands/terminate.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "command.h"
+
+#include <errno.h>
+
+CALLBACK(log_cb, void,
+ command_format_options_t *format, char *name, vici_res_t *msg)
+{
+ if (*format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(msg, "log", *format & COMMAND_FORMAT_PRETTY, stdout);
+ }
+ else
+ {
+ printf("[%s] %s\n",
+ vici_find_str(msg, " ", "group"),
+ vici_find_str(msg, "", "msg"));
+ }
+}
+
+static int terminate(vici_conn_t *conn)
+{
+ vici_req_t *req;
+ vici_res_t *res;
+ command_format_options_t format = COMMAND_FORMAT_NONE;
+ char *arg, *child = NULL, *ike = NULL;
+ int ret = 0, timeout = 0, level = 1, child_id = 0, ike_id = 0;
+
+ while (TRUE)
+ {
+ switch (command_getopt(&arg))
+ {
+ case 'h':
+ return command_usage(NULL);
+ case 'P':
+ format |= COMMAND_FORMAT_PRETTY;
+ /* fall through to raw */
+ case 'r':
+ format |= COMMAND_FORMAT_RAW;
+ continue;
+ case 'c':
+ child = arg;
+ continue;
+ case 'i':
+ ike = arg;
+ continue;
+ case 'C':
+ child_id = atoi(arg);
+ continue;
+ case 'I':
+ ike_id = atoi(arg);
+ continue;
+ case 't':
+ timeout = atoi(arg);
+ continue;
+ case 'l':
+ level = atoi(arg);
+ continue;
+ case EOF:
+ break;
+ default:
+ return command_usage("invalid --terminate option");
+ }
+ break;
+ }
+
+ if (vici_register(conn, "control-log", log_cb, &format) != 0)
+ {
+ fprintf(stderr, "registering for log failed: %s\n", strerror(errno));
+ return errno;
+ }
+ req = vici_begin("terminate");
+ if (child)
+ {
+ vici_add_key_valuef(req, "child", "%s", child);
+ }
+ if (ike)
+ {
+ vici_add_key_valuef(req, "ike", "%s", ike);
+ }
+ if (child_id)
+ {
+ vici_add_key_valuef(req, "child-id", "%d", child_id);
+ }
+ if (ike_id)
+ {
+ vici_add_key_valuef(req, "ike-id", "%d", ike_id);
+ }
+ if (timeout)
+ {
+ vici_add_key_valuef(req, "timeout", "%d", timeout * 1000);
+ }
+ vici_add_key_valuef(req, "loglevel", "%d", level);
+ res = vici_submit(req, conn);
+ if (!res)
+ {
+ fprintf(stderr, "terminate request failed: %s\n", strerror(errno));
+ return errno;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "terminate reply", format & COMMAND_FORMAT_PRETTY,
+ stdout);
+ }
+ else
+ {
+ if (streq(vici_find_str(res, "no", "success"), "yes"))
+ {
+ printf("terminate completed successfully\n");
+ }
+ else
+ {
+ fprintf(stderr, "terminate failed: %s\n",
+ vici_find_str(res, "", "errmsg"));
+ ret = 1;
+ }
+ }
+ vici_free_res(res);
+ return ret;
+}
+
+/**
+ * Register the command.
+ */
+static void __attribute__ ((constructor))reg()
+{
+ command_register((command_t) {
+ terminate, 't', "terminate", "terminate a connection",
+ {"--child <name> | --ike <name | --child-id <id> | --ike-id <id>",
+ "[--timeout <s>] [--raw|--pretty]"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"child", 'c', 1, "terminate by CHILD_SA name"},
+ {"ike", 'i', 1, "terminate by IKE_SA name"},
+ {"child-id", 'C', 1, "terminate by CHILD_SA reqid"},
+ {"ike-id", 'I', 1, "terminate by IKE_SA unique identifier"},
+ {"timeout", 't', 1, "timeout in seconds before detaching"},
+ {"raw", 'r', 0, "dump raw response message"},
+ {"pretty", 'P', 0, "dump raw response message in pretty print"},
+ {"loglevel", 'l', 1, "verbosity of redirected log"},
+ }
+ });
+}
diff --git a/src/swanctl/commands/version.c b/src/swanctl/commands/version.c
new file mode 100644
index 000000000..4f24a0fc2
--- /dev/null
+++ b/src/swanctl/commands/version.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "command.h"
+
+#include <errno.h>
+
+static int version(vici_conn_t *conn)
+{
+ vici_req_t *req;
+ vici_res_t *res;
+ char *arg;
+ bool daemon = FALSE;
+ command_format_options_t format = COMMAND_FORMAT_NONE;
+
+ while (TRUE)
+ {
+ switch (command_getopt(&arg))
+ {
+ case 'h':
+ return command_usage(NULL);
+ case 'P':
+ format |= COMMAND_FORMAT_PRETTY;
+ /* fall through to raw */
+ case 'r':
+ format |= COMMAND_FORMAT_RAW;
+ continue;
+ case 'd':
+ daemon = TRUE;
+ continue;
+ case EOF:
+ break;
+ default:
+ return command_usage("invalid --terminate option");
+ }
+ break;
+ }
+
+ if (!daemon)
+ {
+ printf("strongSwan swanctl %s\n", VERSION);
+ return 0;
+ }
+
+ req = vici_begin("version");
+ res = vici_submit(req, conn);
+ if (!res)
+ {
+ fprintf(stderr, "version request failed: %s\n", strerror(errno));
+ return errno;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "version reply", format & COMMAND_FORMAT_PRETTY, stdout);
+ }
+ else
+ {
+ printf("strongSwan %s %s (%s, %s, %s)\n",
+ vici_find_str(res, "", "version"),
+ vici_find_str(res, "", "daemon"),
+ vici_find_str(res, "", "sysname"),
+ vici_find_str(res, "", "release"),
+ vici_find_str(res, "", "machine"));
+ }
+ vici_free_res(res);
+ return 0;
+}
+
+/**
+ * Register the command.
+ */
+static void __attribute__ ((constructor))reg()
+{
+ command_register((command_t) {
+ version, 'v', "version", "show version information",
+ {"[--raw|--pretty]"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"daemon", 'd', 0, "query daemon version"},
+ {"raw", 'r', 0, "dump raw response message"},
+ {"pretty", 'P', 0, "dump raw response message in pretty print"},
+ }
+ });
+}