| /* $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; |
| } |