diff options
Diffstat (limited to 'src/swanctl/commands')
-rw-r--r-- | src/swanctl/commands/initiate.c | 132 | ||||
-rw-r--r-- | src/swanctl/commands/install.c | 125 | ||||
-rw-r--r-- | src/swanctl/commands/list_certs.c | 670 | ||||
-rw-r--r-- | src/swanctl/commands/list_conns.c | 242 | ||||
-rw-r--r-- | src/swanctl/commands/list_pols.c | 210 | ||||
-rw-r--r-- | src/swanctl/commands/list_pools.c | 101 | ||||
-rw-r--r-- | src/swanctl/commands/list_sas.c | 366 | ||||
-rw-r--r-- | src/swanctl/commands/load_conns.c | 419 | ||||
-rw-r--r-- | src/swanctl/commands/load_creds.c | 574 | ||||
-rw-r--r-- | src/swanctl/commands/load_pools.c | 292 | ||||
-rw-r--r-- | src/swanctl/commands/log.c | 101 | ||||
-rw-r--r-- | src/swanctl/commands/stats.c | 118 | ||||
-rw-r--r-- | src/swanctl/commands/terminate.c | 157 | ||||
-rw-r--r-- | src/swanctl/commands/version.c | 96 |
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, ¬Before, ¬After); + printf("validity: not before %T, ", ¬Before, FALSE); + if (now < notBefore) + { + printf("not valid yet (valid in %V)\n", &now, ¬Before); + } + else + { + printf("ok\n"); + } + printf(" not after %T, ", ¬After, FALSE); + if (now > notAfter) + { + printf("expired (%V ago)\n", &now, ¬After); + } + else + { + printf("ok (expires in %V)\n", &now, ¬After); + } + + 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, ¤t)) + { + 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, §ion)) + { + 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**)§ion) == 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, §ion)) + { + 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, §ion)) + { + 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, ¤t)) + { + 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, §ion)) + { + 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**)§ion) == 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"}, + } + }); +} |