summaryrefslogtreecommitdiffstats
path: root/ipsec-tools/src/racoon/racoonctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipsec-tools/src/racoon/racoonctl.c')
-rw-r--r--ipsec-tools/src/racoon/racoonctl.c1529
1 files changed, 1529 insertions, 0 deletions
diff --git a/ipsec-tools/src/racoon/racoonctl.c b/ipsec-tools/src/racoon/racoonctl.c
new file mode 100644
index 00000000..da28ecd7
--- /dev/null
+++ b/ipsec-tools/src/racoon/racoonctl.c
@@ -0,0 +1,1529 @@
+/* $NetBSD: racoonctl.c,v 1.18 2010/11/12 09:08:26 tteras Exp $ */
+
+/* Id: racoonctl.c,v 1.11 2006/04/06 17:06:25 manubsd Exp */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * Copyright (C) 2008 Timo Teras.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/pfkeyv2.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+#include <netdb.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <err.h>
+#include <sys/ioctl.h>
+#include <resolv.h>
+
+#include "var.h"
+#include "vmbuf.h"
+#include "misc.h"
+#include "gcmalloc.h"
+
+#include "racoonctl.h"
+#include "admin.h"
+#include "schedule.h"
+#include "handler.h"
+#include "sockmisc.h"
+#include "vmbuf.h"
+#include "plog.h"
+#include "isakmp_var.h"
+#include "isakmp.h"
+#include "isakmp_xauth.h"
+#include "isakmp_cfg.h"
+#include "isakmp_unity.h"
+#include "ipsec_doi.h"
+#include "evt.h"
+
+char *adminsock_path = ADMINSOCK_PATH;
+
+static void usage __P((void));
+static vchar_t *get_combuf __P((int, char **));
+static int handle_recv __P((vchar_t *));
+static vchar_t *f_reload __P((int, char **));
+static vchar_t *f_getsched __P((int, char **));
+static vchar_t *f_getsa __P((int, char **));
+static vchar_t *f_getsacert __P((int, char **));
+static vchar_t *f_flushsa __P((int, char **));
+static vchar_t *f_deletesa __P((int, char **));
+static vchar_t *f_exchangesa __P((int, char **));
+static vchar_t *f_vpnc __P((int, char **));
+static vchar_t *f_vpnd __P((int, char **));
+static vchar_t *f_getevt __P((int, char **));
+#ifdef ENABLE_HYBRID
+static vchar_t *f_logoutusr __P((int, char **));
+#endif
+
+struct cmd_tag {
+ vchar_t *(*func) __P((int, char **));
+ char *str;
+} cmdtab[] = {
+ { f_reload, "reload-config" },
+ { f_reload, "rc" },
+ { f_getsched, "show-schedule" },
+ { f_getsched, "sc" },
+ { f_getsa, "show-sa" },
+ { f_getsa, "ss" },
+ { f_getsacert, "get-cert" },
+ { f_getsacert, "gc" },
+ { f_flushsa, "flush-sa" },
+ { f_flushsa, "fs" },
+ { f_deletesa, "delete-sa" },
+ { f_deletesa, "ds" },
+ { f_exchangesa, "establish-sa" },
+ { f_exchangesa, "es" },
+ { f_vpnc, "vpn-connect" },
+ { f_vpnc, "vc" },
+ { f_vpnd, "vpn-disconnect" },
+ { f_vpnd, "vd" },
+ { f_getevt, "show-event" },
+ { f_getevt, "se" },
+#ifdef ENABLE_HYBRID
+ { f_logoutusr, "logout-user" },
+ { f_logoutusr, "lu" },
+#endif
+ { NULL, NULL },
+};
+
+struct evtmsg {
+ int type;
+ char *msg;
+} evtmsg[] = {
+ { EVT_RACOON_QUIT, "Racoon terminated" },
+
+ { EVT_PHASE1_UP, "Phase 1 established" },
+ { EVT_PHASE1_DOWN, "Phase 1 deleted" },
+ { EVT_PHASE1_NO_RESPONSE, "Phase 1 error: peer not responding" },
+ { EVT_PHASE1_NO_PROPOSAL, "Phase 1 error: no proposal chosen" },
+ { EVT_PHASE1_AUTH_FAILED,
+ "Phase 1 error: authentication failed (bad certificate?)" },
+ { EVT_PHASE1_DPD_TIMEOUT, "Phase 1 error: dead peer detected" },
+ { EVT_PHASE1_MODE_CFG, "Phase 1 mode configuration done" },
+ { EVT_PHASE1_XAUTH_SUCCESS, "Phase 1 Xauth succeeded" },
+ { EVT_PHASE1_XAUTH_FAILED, "Phase 1 Xauth failed" },
+
+ { EVT_PHASE2_NO_PHASE1, "Phase 2 error: no suitable phase 1" },
+ { EVT_PHASE2_UP, "Phase 2 established" },
+ { EVT_PHASE2_DOWN, "Phase 2 deleted" },
+ { EVT_PHASE2_NO_RESPONSE, "Phase 2 error: no response" },
+};
+
+static vchar_t *get_proto_and_index __P((int, char **, u_int16_t *));
+static int get_proto __P((char *));
+static vchar_t *get_index __P((int, char **));
+static int get_family __P((char *));
+static vchar_t *get_comindexes __P((int, int, char **));
+static int get_comindex __P((char *, char **, char **, char **));
+static int get_ulproto __P((char *));
+
+struct proto_tag {
+ int proto;
+ char *str;
+} prototab[] = {
+ { ADMIN_PROTO_ISAKMP, "isakmp" },
+ { ADMIN_PROTO_IPSEC, "ipsec" },
+ { ADMIN_PROTO_AH, "ah" },
+ { ADMIN_PROTO_ESP, "esp" },
+ { ADMIN_PROTO_INTERNAL, "internal" },
+ { 0, NULL },
+};
+
+struct ulproto_tag {
+ int ul_proto;
+ char *str;
+} ulprototab[] = {
+ { 0, "any" },
+ { IPPROTO_ICMP, "icmp" },
+ { IPPROTO_TCP, "tcp" },
+ { IPPROTO_UDP, "udp" },
+ { IPPROTO_GRE, "gre" },
+ { 0, NULL },
+};
+
+int so;
+
+static char _addr1_[NI_MAXHOST], _addr2_[NI_MAXHOST];
+
+char *pname;
+int long_format = 0;
+int evt_quit_event = 0;
+
+void dump_isakmp_sa __P((char *, int));
+void dump_internal __P((char *, int));
+char *pindex_isakmp __P((isakmp_index *));
+void print_schedule __P((caddr_t, int));
+void print_evt __P((struct evt_async *));
+char * fixed_addr __P((char *, char *, int));
+
+static void
+usage()
+{
+ printf(
+"Usage:\n"
+" %s [opts] reload-config\n"
+" %s [opts] show-schedule\n"
+" %s [opts] show-sa [protocol]\n"
+" %s [opts] flush-sa [protocol]\n"
+" %s [opts] delete-sa <saopts>\n"
+" %s [opts] establish-sa [-u identity] [-n remoteconf] [-w] <saopts>\n"
+" %s [opts] vpn-connect [-u identity] vpn_gateway\n"
+" %s [opts] vpn-disconnect vpn_gateway\n"
+" %s [opts] show-event\n"
+" %s [opts] logout-user login\n"
+"\n"
+"General options:\n"
+" -d Debug: hexdump admin messages before sending\n"
+" -l Increase output verbosity (mainly for show-sa)\n"
+" -s <socket> Specify adminport socket to use (default: %s)\n"
+"\n"
+"Parameter specifications:\n"
+" <protocol>: \"isakmp\", \"esp\" or \"ah\".\n"
+" In the case of \"show-sa\" or \"flush-sa\", you can use \"ipsec\".\n"
+"\n"
+" <saopts>: \"isakmp\" <family> <src> <dst>\n"
+" : {\"esp\",\"ah\"} <family> <src/prefixlen/port> <dst/prefixlen/port>\n"
+" <ul_proto>\n"
+" <family>: \"inet\" or \"inet6\"\n"
+" <ul_proto>: \"icmp\", \"tcp\", \"udp\", \"gre\" or \"any\"\n"
+"\n",
+ pname, pname, pname, pname, pname, pname, pname, pname, pname, pname,
+ ADMINSOCK_PATH);
+}
+
+/*
+ * Check for proper racoonctl interface
+ */
+#if ((RACOONCTL_INTERFACE_MAJOR != 1) || (RACOONCTL_INTERFACE < 20041230))
+#error "Incompatible racoonctl interface"
+#endif
+
+int
+main(ac, av)
+ int ac;
+ char **av;
+{
+ vchar_t *combuf;
+ int c;
+
+ pname = *av;
+
+ /*
+ * Check for proper racoonctl interface
+ */
+ if ((racoonctl_interface_major != RACOONCTL_INTERFACE_MAJOR) ||
+ (racoonctl_interface < RACOONCTL_INTERFACE))
+ errx(1, "Incompatible racoonctl interface");
+
+#ifdef __linux__
+ /*
+ * Disable GNU extensions that will prevent racoonct vc -u login
+ * from working (GNU getopt(3) does not like options after vc)
+ */
+ setenv("POSIXLY_CORRECT", "1", 0);
+#endif
+ while ((c = getopt(ac, av, "lds:")) != -1) {
+ switch(c) {
+ case 'l':
+ long_format++;
+ break;
+
+ case 'd':
+ loglevel++;
+ break;
+
+ case 's':
+ adminsock_path = optarg;
+ break;
+
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ ac -= optind;
+ av += optind;
+
+ combuf = get_combuf(ac, av);
+ if (!combuf)
+ err(1, "kmpstat");
+
+ if (loglevel)
+ racoon_hexdump(combuf, ((struct admin_com *)combuf)->ac_len);
+
+ com_init();
+
+ if (com_send(combuf) != 0)
+ goto bad;
+
+ vfree(combuf);
+
+ do {
+ if (com_recv(&combuf) != 0)
+ goto bad;
+ if (handle_recv(combuf) != 0)
+ goto bad;
+ vfree(combuf);
+ } while (evt_quit_event != 0);
+
+ close(so);
+ exit(0);
+
+bad:
+ close(so);
+ if (errno == EEXIST)
+ exit(0);
+ exit(1);
+}
+
+/* %%% */
+/*
+ * return command buffer.
+ */
+static vchar_t *
+get_combuf(ac, av)
+ int ac;
+ char **av;
+{
+ struct cmd_tag *cp;
+
+ if (ac == 0) {
+ usage();
+ exit(0);
+ }
+
+ /* checking the string of command. */
+ for (cp = &cmdtab[0]; cp->str; cp++) {
+ if (strcmp(*av, cp->str) == 0) {
+ break;
+ }
+ }
+ if (!cp->str) {
+ printf("Invalid command [%s]\n", *av);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ ac--;
+ av++;
+ return (cp->func)(ac, av);
+}
+
+static vchar_t *
+make_request(u_int16_t cmd, u_int16_t proto, size_t len)
+{
+ vchar_t *buf;
+ struct admin_com *head;
+
+ buf = vmalloc(sizeof(struct admin_com) + len);
+ if (buf == NULL)
+ errx(1, "not enough core");
+
+ head = (struct admin_com *) buf->v;
+ head->ac_len = buf->l;
+ head->ac_cmd = ADMIN_FLAG_VERSION | cmd;
+ head->ac_version = 1;
+ head->ac_proto = proto;
+
+ return buf;
+}
+
+static vchar_t *
+f_reload(ac, av)
+ int ac;
+ char **av;
+{
+ return make_request(ADMIN_RELOAD_CONF, 0, 0);
+}
+
+static vchar_t *
+f_getevt(ac, av)
+ int ac;
+ char **av;
+{
+ evt_quit_event = -1;
+ if (ac >= 1)
+ errx(1, "too many arguments");
+
+ return make_request(ADMIN_SHOW_EVT, 0, 0);
+}
+
+static vchar_t *
+f_getsched(ac, av)
+ int ac;
+ char **av;
+{
+ return make_request(ADMIN_SHOW_SCHED, 0, 0);
+}
+
+static vchar_t *
+f_getsa(ac, av)
+ int ac;
+ char **av;
+{
+ int proto;
+
+ /* need protocol */
+ if (ac != 1)
+ errx(1, "insufficient arguments");
+ proto = get_proto(*av);
+ if (proto == -1)
+ errx(1, "unknown protocol %s", *av);
+
+ return make_request(ADMIN_SHOW_SA, proto, 0);
+}
+
+static vchar_t *
+f_getsacert(ac, av)
+ int ac;
+ char **av;
+{
+ vchar_t *buf, *index;
+ struct admin_com_indexes *com;
+
+ index = get_index(ac, av);
+ if (index == NULL)
+ return NULL;
+
+ com = (struct admin_com_indexes *) index->v;
+ buf = make_request(ADMIN_GET_SA_CERT, ADMIN_PROTO_ISAKMP, index->l);
+ if (buf == NULL)
+ errx(1, "Cannot allocate buffer");
+
+ memcpy(buf->v+sizeof(struct admin_com), index->v, index->l);
+
+ vfree(index);
+
+ return buf;
+}
+
+static vchar_t *
+f_flushsa(ac, av)
+ int ac;
+ char **av;
+{
+ vchar_t *buf;
+ struct admin_com *head;
+ int proto;
+
+ /* need protocol */
+ if (ac != 1)
+ errx(1, "insufficient arguments");
+ proto = get_proto(*av);
+ if (proto == -1)
+ errx(1, "unknown protocol %s", *av);
+
+ return make_request(ADMIN_FLUSH_SA, proto, 0);
+}
+
+static vchar_t *
+f_deletesa(ac, av)
+ int ac;
+ char **av;
+{
+ vchar_t *buf, *index;
+ int proto;
+
+ /* need protocol */
+ if (ac < 1)
+ errx(1, "insufficient arguments");
+ proto = get_proto(*av);
+ if (proto == -1)
+ errx(1, "unknown protocol %s", *av);
+
+ /* get index(es) */
+ av++;
+ ac--;
+ switch (proto) {
+ case ADMIN_PROTO_ISAKMP:
+ index = get_index(ac, av);
+ if (index == NULL)
+ return NULL;
+ break;
+ case ADMIN_PROTO_AH:
+ case ADMIN_PROTO_ESP:
+ index = get_index(ac, av);
+ if (index == NULL)
+ return NULL;
+ break;
+ default:
+ errno = EPROTONOSUPPORT;
+ return NULL;
+ }
+
+ buf = make_request(ADMIN_DELETE_SA, proto, index->l);
+ if (buf == NULL)
+ goto out;
+
+ memcpy(buf->v + sizeof(struct admin_com), index->v, index->l);
+
+out:
+ if (index != NULL)
+ vfree(index);
+
+ return buf;
+}
+
+static vchar_t *
+f_deleteallsadst(ac, av)
+ int ac;
+ char **av;
+{
+ vchar_t *buf, *index;
+ u_int16_t proto;
+
+ index = get_proto_and_index(ac, av, &proto);
+ if (index == NULL)
+ return NULL;
+
+ buf = make_request(ADMIN_DELETE_ALL_SA_DST, proto, index->l);
+ if (buf == NULL)
+ goto out;
+
+ memcpy(buf->v+sizeof(struct admin_com), index->v, index->l);
+
+out:
+ if (index != NULL)
+ vfree(index);
+
+ return buf;
+}
+
+static vchar_t *
+f_exchangesa(ac, av)
+ int ac;
+ char **av;
+{
+ vchar_t *buf, *index;
+ u_int16_t proto;
+ int cmd = ADMIN_ESTABLISH_SA;
+ size_t com_len = 0;
+ char *id = NULL;
+ char *key = NULL;
+ char *remoteconf = NULL;
+ struct admin_com_psk *acp;
+ int wait = 0;
+
+ if (ac < 1)
+ errx(1, "insufficient arguments");
+
+ /* Optional -u identity */
+ if (strcmp(av[0], "-u") == 0) {
+ if (ac < 2)
+ errx(1, "-u require an argument");
+
+ id = av[1];
+ if ((key = getpass("Password: ")) == NULL)
+ errx(1, "getpass() failed: %s", strerror(errno));
+
+ com_len += sizeof(*acp) + strlen(id) + 1 + strlen(key) + 1;
+ cmd = ADMIN_ESTABLISH_SA_PSK;
+
+ av += 2;
+ ac -= 2;
+ }
+
+ if (ac >= 2 && strcmp(av[0], "-n") == 0) {
+ /* Remoteconf name */
+ remoteconf = av[1];
+ av += 2;
+ ac -= 2;
+ }
+
+ if (ac >= 1 && strcmp(av[0], "-w") == 0) {
+ wait = 1;
+ av++;
+ ac--;
+ }
+
+ index = get_proto_and_index(ac, av, &proto);
+ if (index == NULL)
+ return NULL;
+
+ if (proto == ADMIN_PROTO_ISAKMP && cmd == ADMIN_ESTABLISH_SA &&
+ remoteconf != NULL)
+ com_len += strlen(remoteconf) + 1;
+
+ if (wait) {
+ switch (proto) {
+ case ADMIN_PROTO_ISAKMP:
+ evt_quit_event = EVT_PHASE1_MODE_CFG;
+ break;
+ case ADMIN_PROTO_AH:
+ case ADMIN_PROTO_ESP:
+ evt_quit_event = EVT_PHASE2_UP;
+ break;
+ default:
+ errno = EPROTONOSUPPORT;
+ return NULL;
+ }
+ }
+
+ com_len += index->l;
+ buf = make_request(cmd, proto, com_len);
+ if (buf == NULL)
+ errx(1, "Cannot allocate buffer");
+
+ memcpy(buf->v+sizeof(struct admin_com), index->v, index->l);
+
+ if (proto == ADMIN_PROTO_ISAKMP && cmd == ADMIN_ESTABLISH_SA &&
+ remoteconf != NULL) {
+ strcpy(buf->v + sizeof(struct admin_com) + index->l,
+ remoteconf);
+ } else if (id && key) {
+ char *data;
+ acp = (struct admin_com_psk *)
+ (buf->v + sizeof(struct admin_com) + index->l);
+
+ acp->id_type = IDTYPE_USERFQDN;
+ acp->id_len = strlen(id) + 1;
+ acp->key_len = strlen(key) + 1;
+
+ data = (char *)(acp + 1);
+ strcpy(data, id);
+
+ data = (char *)(data + acp->id_len);
+ strcpy(data, key);
+ }
+
+ vfree(index);
+
+ return buf;
+}
+
+static vchar_t *
+f_vpnc(ac, av)
+ int ac;
+ char **av;
+{
+ char *nav[] = {NULL, NULL, NULL, NULL, NULL, NULL};
+ int nac = 0;
+ char *isakmp = "isakmp";
+ char *inet = "inet";
+ char *srcaddr;
+ struct addrinfo hints, *res;
+ struct sockaddr *src;
+ char *idx;
+
+ if (ac < 1)
+ errx(1, "insufficient arguments");
+
+ evt_quit_event = EVT_PHASE1_MODE_CFG;
+
+ /* Optional -u identity */
+ if (strcmp(av[0], "-u") == 0) {
+ if (ac < 2)
+ errx(1, "-u require an argument");
+
+ nav[nac++] = av[0];
+ nav[nac++] = av[1];
+
+ ac -= 2;
+ av += 2;
+ }
+
+ if (ac < 1)
+ errx(1, "VPN gateway required");
+ if (ac > 1)
+ warnx("Extra arguments");
+
+ /*
+ * Find the source address
+ */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ if (getaddrinfo(av[0], "4500", &hints, &res) != 0)
+ errx(1, "Cannot resolve destination address");
+
+ if ((src = getlocaladdr(res->ai_addr)) == NULL)
+ errx(1, "cannot find source address");
+
+ if ((srcaddr = saddr2str(src)) == NULL)
+ errx(1, "cannot read source address");
+
+ /* We get "ip[port]" strip the port */
+ if ((idx = index(srcaddr, '[')) == NULL)
+ errx(1, "unexpected source address format");
+ *idx = '\0';
+
+ nav[nac++] = isakmp;
+ nav[nac++] = inet;
+ nav[nac++] = srcaddr;
+ nav[nac++] = av[0];
+
+ return f_exchangesa(nac, nav);
+}
+
+static vchar_t *
+f_vpnd(ac, av)
+ int ac;
+ char **av;
+{
+ char *nav[] = {NULL, NULL, NULL, NULL};
+ int nac = 0;
+ char *isakmp = "isakmp";
+ char *inet = "inet";
+ char *anyaddr = "0.0.0.0";
+ char *idx;
+
+ if (ac < 1)
+ errx(1, "VPN gateway required");
+ if (ac > 1)
+ warnx("Extra arguments");
+
+ evt_quit_event = EVT_PHASE1_DOWN;
+
+ nav[nac++] = isakmp;
+ nav[nac++] = inet;
+ nav[nac++] = anyaddr;
+ nav[nac++] = av[0];
+
+ return f_deleteallsadst(nac, nav);
+}
+
+#ifdef ENABLE_HYBRID
+static vchar_t *
+f_logoutusr(ac, av)
+ int ac;
+ char **av;
+{
+ vchar_t *buf;
+ char *user;
+ size_t userlen;
+
+ /* need username */
+ if (ac < 1)
+ errx(1, "insufficient arguments");
+ user = av[0];
+ userlen = strlen(user);
+ if ((user == NULL) || (userlen > LOGINLEN))
+ errx(1, "bad login (too long?)");
+
+ buf = make_request(ADMIN_LOGOUT_USER, 0, userlen);
+ if (buf == NULL)
+ return NULL;
+
+ strncpy(buf->v + sizeof(struct admin_com), user, userlen);
+
+ return buf;
+}
+#endif /* ENABLE_HYBRID */
+
+static vchar_t *
+get_proto_and_index(ac, av, proto)
+ int ac;
+ char **av;
+ u_int16_t *proto;
+{
+ vchar_t *index = NULL;
+
+ /* need protocol */
+ if (ac < 1)
+ errx(1, "insufficient arguments");
+ *proto = get_proto(*av);
+ if (*proto == (u_int16_t) -1)
+ errx(1, "unknown protocol %s", *av);
+
+ /* get index(es) */
+ av++;
+ ac--;
+ switch (*proto) {
+ case ADMIN_PROTO_ISAKMP:
+ case ADMIN_PROTO_AH:
+ case ADMIN_PROTO_ESP:
+ index = get_index(ac, av);
+ break;
+ default:
+ errno = EPROTONOSUPPORT;
+ break;
+ }
+ return index;
+}
+
+static int
+get_proto(str)
+ char *str;
+{
+ struct proto_tag *cp;
+
+ if (str == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* checking the string of command. */
+ for (cp = &prototab[0]; cp->str; cp++) {
+ if (strcmp(str, cp->str) == 0)
+ return cp->proto;
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+static vchar_t *
+get_index(ac, av)
+ int ac;
+ char **av;
+{
+ int family;
+
+ if (ac != 3 && ac != 4) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* checking the string of family */
+ family = get_family(*av);
+ if (family == -1)
+ return NULL;
+ av++;
+ ac--;
+
+ return get_comindexes(family, ac, av);
+}
+
+static int
+get_family(str)
+ char *str;
+{
+ if (strcmp("inet", str) == 0)
+ return AF_INET;
+#ifdef INET6
+ else if (strcmp("inet6", str) == 0)
+ return AF_INET6;
+#endif
+ errno = EAFNOSUPPORT;
+ return -1;
+}
+
+static vchar_t *
+get_comindexes(family, ac, av)
+ int family;
+ int ac;
+ char **av;
+{
+ vchar_t *buf;
+ struct admin_com_indexes *ci;
+ char *p_name = NULL, *p_port = NULL;
+ char *p_prefs = NULL, *p_prefd = NULL;
+ struct sockaddr *src = NULL, *dst = NULL;
+ int ulproto;
+
+ if (ac != 2 && ac != 3) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (get_comindex(*av, &p_name, &p_port, &p_prefs) == -1)
+ goto bad;
+ src = get_sockaddr(family, p_name, p_port);
+ if (p_name) {
+ racoon_free(p_name);
+ p_name = NULL;
+ }
+ if (p_port) {
+ racoon_free(p_port);
+ p_port = NULL;
+ }
+ if (src == NULL)
+ goto bad;
+ av++;
+ ac--;
+ if (get_comindex(*av, &p_name, &p_port, &p_prefd) == -1)
+ goto bad;
+ dst = get_sockaddr(family, p_name, p_port);
+ if (p_name) {
+ racoon_free(p_name);
+ p_name = NULL;
+ }
+ if (p_port) {
+ racoon_free(p_port);
+ p_port = NULL;
+ }
+ if (dst == NULL)
+ goto bad;
+
+ buf = vmalloc(sizeof(*ci));
+ if (buf == NULL)
+ goto bad;
+
+ av++;
+ ac--;
+ if(ac){
+ ulproto = get_ulproto(*av);
+ if (ulproto == -1)
+ goto bad;
+ }else
+ ulproto=0;
+
+ ci = (struct admin_com_indexes *)buf->v;
+ if(p_prefs)
+ ci->prefs = (u_int8_t)atoi(p_prefs); /* XXX should be handled error. */
+ else
+ ci->prefs = 32;
+ if(p_prefd)
+ ci->prefd = (u_int8_t)atoi(p_prefd); /* XXX should be handled error. */
+ else
+ ci->prefd = 32;
+ ci->ul_proto = ulproto;
+ memcpy(&ci->src, src, sysdep_sa_len(src));
+ memcpy(&ci->dst, dst, sysdep_sa_len(dst));
+
+ if (p_name)
+ racoon_free(p_name);
+
+ return buf;
+
+ bad:
+ if (p_name)
+ racoon_free(p_name);
+ if (p_port)
+ racoon_free(p_port);
+ if (p_prefs)
+ racoon_free(p_prefs);
+ if (p_prefd)
+ racoon_free(p_prefd);
+ return NULL;
+}
+
+static int
+get_comindex(str, name, port, pref)
+ char *str, **name, **port, **pref;
+{
+ char *p;
+
+ *name = *port = *pref = NULL;
+
+ *name = racoon_strdup(str);
+ STRDUP_FATAL(*name);
+ p = strpbrk(*name, "/[");
+ if (p != NULL) {
+ if (*(p + 1) == '\0')
+ goto bad;
+ if (*p == '/') {
+ *p = '\0';
+ *pref = racoon_strdup(p + 1);
+ STRDUP_FATAL(*pref);
+ p = strchr(*pref, '[');
+ if (p != NULL) {
+ if (*(p + 1) == '\0')
+ goto bad;
+ *p = '\0';
+ *port = racoon_strdup(p + 1);
+ STRDUP_FATAL(*port);
+ p = strchr(*pref, ']');
+ if (p == NULL)
+ goto bad;
+ *p = '\0';
+ }
+ } else if (*p == '[') {
+ if (*pref == NULL)
+ goto bad;
+ *p = '\0';
+ *port = racoon_strdup(p + 1);
+ STRDUP_FATAL(*port);
+ p = strchr(*pref, ']');
+ if (p == NULL)
+ goto bad;
+ *p = '\0';
+ } else {
+ /* XXX */
+ }
+ }
+
+ return 0;
+
+ bad:
+
+ if (*name)
+ racoon_free(*name);
+ if (*port)
+ racoon_free(*port);
+ if (*pref)
+ racoon_free(*pref);
+ *name = *port = *pref = NULL;
+ return -1;
+}
+
+static int
+get_ulproto(str)
+ char *str;
+{
+ struct ulproto_tag *cp;
+
+ if(str == NULL){
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* checking the string of upper layer protocol. */
+ for (cp = &ulprototab[0]; cp->str; cp++) {
+ if (strcmp(str, cp->str) == 0)
+ return cp->ul_proto;
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+/* %%% */
+void
+dump_isakmp_sa(buf, len)
+ char *buf;
+ int len;
+{
+ struct ph1dump *pd;
+ struct tm *tm;
+ char tbuf[56];
+ caddr_t p = NULL;
+
+/* isakmp status header */
+/* short header;
+ 1234567890123456789012 0000000000000000:0000000000000000 000000000000
+*/
+char *header1 =
+"Destination Cookies Created";
+
+/* semi long header;
+ 1234567890123456789012 0000000000000000:0000000000000000 00 X 00 X 0000-00-00 00:00:00 000000
+*/
+char *header2 =
+"Destination Cookies ST S V E Created Phase2";
+
+/* long header;
+ 0000:0000:0000:0000:0000:0000:0000:0000.00000 0000:0000:0000:0000:0000:0000:0000:0000.00000 0000000000000000:0000000000000000 00 X 00 X 0000-00-00 00:00:00 000000
+*/
+char *header3 =
+"Source Destination Cookies ST S V E Created Phase2";
+
+/* phase status header */
+/* short format;
+ side stats source address destination address
+ xxx xxxxx 1234567890123456789012 1234567890123456789012
+*/
+
+ static char *estr[] = { "", "B", "M", "U", "A", "I", };
+
+ switch (long_format) {
+ case 0:
+ printf("%s\n", header1);
+ break;
+ case 1:
+ printf("%s\n", header2);
+ break;
+ case 2:
+ default:
+ printf("%s\n", header3);
+ break;
+ }
+
+ if (len % sizeof(*pd))
+ printf("invalid length %d\n", len);
+ len /= sizeof(*pd);
+
+ pd = (struct ph1dump *)buf;
+
+ while (len-- > 0) {
+ /* source address */
+ if (long_format >= 2) {
+ GETNAMEINFO((struct sockaddr *)&pd->local, _addr1_, _addr2_);
+ switch (long_format) {
+ case 0:
+ break;
+ case 1:
+ p = fixed_addr(_addr1_, _addr2_, 22);
+ break;
+ case 2:
+ default:
+ p = fixed_addr(_addr1_, _addr2_, 45);
+ break;
+ }
+ printf("%s ", p);
+ }
+
+ /* destination address */
+ GETNAMEINFO((struct sockaddr *)&pd->remote, _addr1_, _addr2_);
+ switch (long_format) {
+ case 0:
+ case 1:
+ p = fixed_addr(_addr1_, _addr2_, 22);
+ break;
+ case 2:
+ default:
+ p = fixed_addr(_addr1_, _addr2_, 45);
+ break;
+ }
+ printf("%s ", p);
+
+ printf("%s ", pindex_isakmp(&pd->index));
+
+ /* statuc, side and version */
+ if (long_format >= 1) {
+ printf("%2d %c %2x ",
+ pd->status,
+ pd->side == INITIATOR ? 'I' : 'R',
+ pd->version);
+ if (ARRAYLEN(estr) > pd->etype)
+ printf("%s ", estr[pd->etype]);
+ }
+
+ /* created date */
+ if (pd->created) {
+ tm = localtime(&pd->created);
+ strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %T", tm);
+ } else
+ snprintf(tbuf, sizeof(tbuf), " ");
+ printf("%s ", tbuf);
+
+ /* counter of phase 2 */
+ if (long_format >= 1)
+ printf("%6d ", pd->ph2cnt);
+
+ printf("\n");
+
+ pd++;
+ }
+
+ return;
+}
+
+/* %%% */
+void
+dump_internal(buf, tlen)
+ char *buf;
+ int tlen;
+{
+ struct ph2handle *iph2;
+ struct sockaddr *addr;
+
+/*
+short header;
+ source address destination address
+ 1234567890123456789012 1234567890123456789012
+*/
+char *short_h1 =
+"Source Destination ";
+
+/*
+long header;
+ source address destination address
+ 123456789012345678901234567890123456789012345 123456789012345678901234567890123456789012345
+ 0000:0000:0000:0000:0000:0000:0000:0000.00000 0000:0000:0000:0000:0000:0000:0000:0000.00000 0000:0000:0000:0000:0000:0000:0000:0000.00000
+*/
+char *long_h1 =
+"Source Destination ";
+
+ printf("%s\n", long_format ? long_h1 : short_h1);
+
+ while (tlen > 0) {
+ iph2 = (struct ph2handle *)buf;
+ addr = (struct sockaddr *)(++iph2);
+
+ GETNAMEINFO(addr, _addr1_, _addr2_);
+ printf("%s ", long_format ?
+ fixed_addr(_addr1_, _addr2_, 45)
+ : fixed_addr(_addr1_, _addr2_, 22));
+ addr++;
+ tlen -= sysdep_sa_len(addr);
+
+ GETNAMEINFO(addr, _addr1_, _addr2_);
+ printf("%s ", long_format ?
+ fixed_addr(_addr1_, _addr2_, 45)
+ : fixed_addr(_addr1_, _addr2_, 22));
+ addr++;
+ tlen -= sysdep_sa_len(addr);
+
+ printf("\n");
+ }
+
+ return;
+}
+
+/* %%% */
+char *
+pindex_isakmp(index)
+ isakmp_index *index;
+{
+ static char buf[64];
+ u_char *p;
+ int i, j;
+
+ memset(buf, 0, sizeof(buf));
+
+ /* copy index */
+ p = (u_char *)index;
+ for (j = 0, i = 0; i < sizeof(isakmp_index); i++) {
+ snprintf((char *)&buf[j], sizeof(buf) - j, "%02x", p[i]);
+ j += 2;
+ switch (i) {
+ case 7:
+#if 0
+ case 15:
+#endif
+ buf[j++] = ':';
+ }
+ }
+
+ return buf;
+}
+
+/* print schedule */
+char *str_sched_stat[] = {
+"off",
+"on",
+"dead",
+};
+
+char *str_sched_id[] = {
+"PH1resend",
+"PH1lifetime",
+"PH2resend",
+"PSTacquire",
+"PSTlifetime",
+};
+
+void
+print_schedule(buf, len)
+ caddr_t buf;
+ int len;
+{
+ struct scheddump *sc = (struct scheddump *)buf;
+ struct tm *tm;
+ char tbuf[56];
+
+ if (len % sizeof(*sc))
+ printf("invalid length %d\n", len);
+ len /= sizeof(*sc);
+
+ /* 00000000 00000000 00000000 xxx........*/
+ printf("index tick xtime created\n");
+
+ while (len-- > 0) {
+ tm = localtime(&sc->created);
+ strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %T", tm);
+
+ printf("%-8ld %-8ld %-8ld %s\n",
+ sc->id,
+ (long)sc->tick,
+ (long)sc->xtime,
+ tbuf);
+ sc++;
+ }
+
+ return;
+}
+
+
+void
+print_evt(evtdump)
+ struct evt_async *evtdump;
+{
+ int i;
+ char *srcstr;
+ char *dststr;
+
+ for (i = 0; i < sizeof(evtmsg) / sizeof(evtmsg[0]); i++)
+ if (evtmsg[i].type == evtdump->ec_type)
+ break;
+
+ if (evtmsg[i].msg == NULL)
+ printf("Event %d: ", evtdump->ec_type);
+ else
+ printf("%s : ", evtmsg[i].msg);
+
+ if ((srcstr = saddr2str((struct sockaddr *)&evtdump->ec_ph1src)) == NULL)
+ printf("unknown");
+ else
+ printf("%s", srcstr);
+ printf(" -> ");
+ if ((dststr = saddr2str((struct sockaddr *)&evtdump->ec_ph1dst)) == NULL)
+ printf("unknown");
+ else
+ printf("%s", dststr);
+ printf("\n");
+}
+
+/*
+ * Print ISAKMP mode config info (IP and banner)
+ */
+void
+print_cfg(buf, len)
+ caddr_t buf;
+ int len;
+{
+ struct evt_async *evtdump = (struct evt_async *)buf;
+ struct isakmp_data *attr;
+ char *banner = NULL;
+ struct in_addr addr4;
+
+ memset(&addr4, 0, sizeof(addr4));
+
+ if (evtdump->ec_type != EVT_PHASE1_MODE_CFG)
+ return;
+
+ len -= sizeof(*evtdump);
+ attr = (struct isakmp_data *)(evtdump + 1);
+
+ while (len > 0) {
+ if (len < sizeof(*attr)) {
+ printf("short attribute too short\n");
+ break;
+ }
+
+ if ((ntohs(attr->type) & ISAKMP_GEN_MASK) == ISAKMP_GEN_TV) {
+ /* Short attribute, skip */
+ len -= sizeof(*attr);
+ attr++;
+ } else { /* Long attribute */
+ char *n;
+
+ if (len < (sizeof(*attr) + ntohs(attr->lorv))) {
+ printf("long attribute too long\n");
+ break;
+ }
+
+ switch (ntohs(attr->type) & ~ISAKMP_GEN_MASK) {
+ case INTERNAL_IP4_ADDRESS:
+ if (ntohs(attr->lorv) < sizeof(addr4)) {
+ printf("addr4 attribute too short\n");
+ break;
+ }
+ memcpy(&addr4, attr + 1, sizeof(addr4));
+ break;
+
+ case UNITY_BANNER:
+ banner = racoon_malloc(ntohs(attr->lorv) + 1);
+ if (banner == NULL) {
+ printf("malloc failed\n");
+ break;
+ }
+ memcpy(banner, attr + 1, ntohs(attr->lorv));
+ banner[ntohs(attr->lorv)] = '\0';
+ break;
+
+ default:
+ break;
+ }
+
+ len -= (sizeof(*attr) + ntohs(attr->lorv));
+ n = (char *)attr;
+ attr = (struct isakmp_data *)
+ (n + sizeof(*attr) + ntohs(attr->lorv));
+ }
+ }
+
+ if (len > 0)
+ printf("Bound to address %s\n", inet_ntoa(addr4));
+ else
+ printf("VPN connexion established\n");
+
+ if (banner) {
+ struct winsize win;
+ int col = 0;
+ int i;
+
+ if (ioctl(1, TIOCGWINSZ, &win) != 1)
+ col = win.ws_col;
+
+ for (i = 0; i < col; i++)
+ printf("%c", '=');
+ printf("\n%s\n", banner);
+ for (i = 0; i < col; i++)
+ printf("%c", '=');
+ printf("\n");
+ racoon_free(banner);
+ }
+}
+
+
+char *
+fixed_addr(addr, port, len)
+ char *addr, *port;
+ int len;
+{
+ static char _addr_buf_[BUFSIZ];
+ char *p;
+ int plen, i;
+
+ /* initialize */
+ memset(_addr_buf_, ' ', sizeof(_addr_buf_));
+
+ plen = strlen(port);
+ if (len < plen + 1)
+ return NULL;
+
+ p = _addr_buf_;
+ for (i = 0; i < len - plen - 1 && addr[i] != '\0'; /*noting*/)
+ *p++ = addr[i++];
+ *p++ = '.';
+
+ for (i = 0; i < plen && port[i] != '\0'; /*noting*/)
+ *p++ = port[i++];
+
+ _addr_buf_[len] = '\0';
+
+ return _addr_buf_;
+}
+
+static int
+handle_recv(combuf)
+ vchar_t *combuf;
+{
+ struct admin_com *com;
+ caddr_t buf;
+ int len;
+
+ com = (struct admin_com *)combuf->v;
+ if (com->ac_cmd & ADMIN_FLAG_LONG_REPLY)
+ len = ((u_int32_t)com->ac_len) + (((u_int32_t)com->ac_len_high) << 16);
+ else
+ len = com->ac_len;
+ len -= sizeof(*com);
+ buf = combuf->v + sizeof(*com);
+
+ switch (com->ac_cmd & ~ADMIN_FLAG_LONG_REPLY) {
+ case ADMIN_SHOW_SCHED:
+ print_schedule(buf, len);
+ break;
+
+ case ADMIN_SHOW_EVT: {
+ struct evt_async *ec;
+
+ /* We got no event? */
+ if (len == 0)
+ break;
+
+ if (len < sizeof(struct evt_async))
+ errx(1, "Short buffer\n");
+
+ ec = (struct evt_async *) buf;
+ if (evt_quit_event <= 0)
+ print_evt(ec);
+ else if (evt_quit_event == ec->ec_type) {
+ switch (ec->ec_type) {
+ case EVT_PHASE1_MODE_CFG:
+ print_cfg(ec, len);
+ break;
+ default:
+ print_evt(ec);
+ break;
+ }
+ evt_quit_event = 0;
+ }
+ break;
+ }
+
+ case ADMIN_GET_SA_CERT:
+ fwrite(buf, len, 1, stdout);
+ break;
+
+ case ADMIN_SHOW_SA:
+ {
+ switch (com->ac_proto) {
+ case ADMIN_PROTO_ISAKMP:
+ dump_isakmp_sa(buf, len);
+ break;
+ case ADMIN_PROTO_IPSEC:
+ case ADMIN_PROTO_AH:
+ case ADMIN_PROTO_ESP:
+ {
+ struct sadb_msg *msg = (struct sadb_msg *)buf;
+
+ switch (msg->sadb_msg_errno) {
+ case ENOENT:
+ switch (msg->sadb_msg_type) {
+ case SADB_DELETE:
+ case SADB_GET:
+ printf("No entry.\n");
+ break;
+ case SADB_DUMP:
+ printf("No SAD entries.\n");
+ break;
+ }
+ break;
+ case 0:
+ while (1) {
+ pfkey_sadump(msg);
+ if (msg->sadb_msg_seq == 0)
+ break;
+ msg = (struct sadb_msg *)((caddr_t)msg +
+ PFKEY_UNUNIT64(msg->sadb_msg_len));
+ }
+ break;
+ default:
+ printf("%s.\n", strerror(msg->sadb_msg_errno));
+ }
+ }
+ break;
+ case ADMIN_PROTO_INTERNAL:
+ dump_internal(buf, len);
+ break;
+ default:
+ printf("Invalid proto [%d]\n", com->ac_proto);
+ }
+
+ }
+ break;
+
+ default:
+ /* IGNORE */
+ break;
+ }
+
+ return 0;
+
+bad:
+ return -1;
+}