/* $NetBSD: racoonctl.c,v 1.7.6.2 2009/04/20 13:32:57 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. * 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_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 **)); int cmd; char *str; } cmdtab[] = { { f_reload, ADMIN_RELOAD_CONF, "reload-config" }, { f_reload, ADMIN_RELOAD_CONF, "rc" }, { f_getsched, ADMIN_SHOW_SCHED, "show-schedule" }, { f_getsched, ADMIN_SHOW_SCHED, "sc" }, { f_getsa, ADMIN_SHOW_SA, "show-sa" }, { f_getsa, ADMIN_SHOW_SA, "ss" }, { f_flushsa, ADMIN_FLUSH_SA, "flush-sa" }, { f_flushsa, ADMIN_FLUSH_SA, "fs" }, { f_deletesa, ADMIN_DELETE_SA, "delete-sa" }, { f_deletesa, ADMIN_DELETE_SA, "ds" }, { f_exchangesa, ADMIN_ESTABLISH_SA, "establish-sa" }, { f_exchangesa, ADMIN_ESTABLISH_SA, "es" }, { f_vpnc, ADMIN_ESTABLISH_SA, "vpn-connect" }, { f_vpnc, ADMIN_ESTABLISH_SA, "vc" }, { f_vpnd, ADMIN_DELETE_ALL_SA_DST,"vpn-disconnect" }, { f_vpnd, ADMIN_DELETE_ALL_SA_DST,"vd" }, { f_getevt, ADMIN_SHOW_EVT, "show-event" }, { f_getevt, ADMIN_SHOW_EVT, "se" }, #ifdef ENABLE_HYBRID { f_logoutusr, ADMIN_LOGOUT_USER, "logout-user" }, { f_logoutusr, ADMIN_LOGOUT_USER, "lu" }, #endif { NULL, 0, NULL }, }; struct evtmsg { int type; char *msg; enum { UNSPEC, ERROR, INFO } level; } evtmsg[] = { { EVTT_PHASE1_UP, "Phase 1 established", INFO }, { EVTT_PHASE1_DOWN, "Phase 1 deleted", INFO }, { EVTT_XAUTH_SUCCESS, "Xauth exchange passed", INFO }, { EVTT_ISAKMP_CFG_DONE, "ISAKMP mode config done", INFO }, { EVTT_PHASE2_UP, "Phase 2 established", INFO }, { EVTT_PHASE2_DOWN, "Phase 2 deleted", INFO }, { EVTT_DPD_TIMEOUT, "Peer not reachable anymore", ERROR }, { EVTT_PEER_NO_RESPONSE, "Peer not responding", ERROR }, { EVTT_PEER_DELETE, "Peer terminated security association", ERROR }, { EVTT_RACOON_QUIT, "Raccon terminated", ERROR }, { EVTT_OVERFLOW, "Event queue overflow", ERROR }, { EVTT_XAUTH_FAILED, "Xauth exchange failed", ERROR }, { EVTT_PEERPH1AUTH_FAILED, "Peer failed phase 1 authentication " "(certificate problem?)", ERROR }, { EVTT_PEERPH1_NOPROP, "Peer failed phase 1 initiation " "(proposal problem?)", ERROR }, { 0, NULL, UNSPEC }, { EVTT_NO_ISAKMP_CFG, "No need for ISAKMP mode config ", INFO }, }; 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" }, { 0, NULL }, }; int so; static char _addr1_[NI_MAXHOST], _addr2_[NI_MAXHOST]; char *pname; int long_format = 0; #define EVTF_NONE 0x0000 /* Ignore any events */ #define EVTF_LOOP 0x0001 /* Loop awaiting for new events */ #define EVTF_CFG_STOP 0x0002 /* Stop after ISAKMP mode config */ #define EVTF_CFG 0x0004 /* Print ISAKMP mode config info */ #define EVTF_ALL 0x0008 /* Print any events */ #define EVTF_PURGE 0x0010 /* Print all available events */ #define EVTF_PH1DOWN_STOP 0x0020 /* Stop when phase 1 SA gets down */ #define EVTF_PH1DOWN 0x0040 /* Print that phase 1 SA got down */ #define EVTF_ERR 0x0080 /* Print any error */ #define EVTF_ERR_STOP 0x0100 /* Stop on any error */ int evt_filter = EVTF_NONE; time_t evt_start; 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((caddr_t, int)); void print_cfg __P((caddr_t, int)); void print_err __P((caddr_t, int)); void print_ph1down __P((caddr_t, int)); void print_ph1up __P((caddr_t, int)); int evt_poll __P((void)); char * fixed_addr __P((char *, char *, int)); static void usage() { printf( "Usage:\n" " %s reload-config\n" " %s [-l [-l]] show-sa [protocol]\n" " %s flush-sa [protocol]\n" " %s delete-sa <saopts>\n" " %s establish-sa [-u identity] <saopts>\n" " %s vpn-connect [-u identity] vpn_gateway\n" " %s vpn-disconnect vpn_gateway\n" "\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\" or \"any\"\n", pname, pname, pname, pname, pname, pname, pname); } /* * 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); if (com_recv(&combuf) != 0) goto bad; if (handle_recv(combuf) != 0) goto bad; vfree(combuf); if (evt_filter != EVTF_NONE) if (evt_poll() != 0) goto bad; exit(0); bad: exit(1); } int evt_poll(void) { struct timeval tv; vchar_t *recvbuf; vchar_t *sendbuf; if ((sendbuf = f_getevt(0, NULL)) == NULL) errx(1, "Cannot make combuf"); while (evt_filter & (EVTF_LOOP|EVTF_PURGE)) { /* handle_recv closes the socket time, so open it each time */ com_init(); if (com_send(sendbuf) != 0) errx(1, "Cannot send combuf"); if (com_recv(&recvbuf) == 0) { handle_recv(recvbuf); vfree(recvbuf); } tv.tv_sec = 0; tv.tv_usec = 10; (void)select(0, NULL, NULL, NULL, &tv); } vfree(sendbuf); return 0; } /* %%% */ /* * 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 * f_reload(ac, av) int ac; char **av; { vchar_t *buf; struct admin_com *head; buf = vmalloc(sizeof(*head)); if (buf == NULL) errx(1, "not enough core"); head = (struct admin_com *)buf->v; head->ac_len = buf->l; head->ac_cmd = ADMIN_RELOAD_CONF; head->ac_errno = 0; head->ac_proto = 0; return buf; } static vchar_t * f_getevt(ac, av) int ac; char **av; { vchar_t *buf; struct admin_com *head; /* * There are 3 ways of getting here * 1) racoonctl vc => evt_filter = (EVTF_LOOP|EVTF_CFG| ... ) * 2) racoonctl es => evt_filter = EVTF_NONE * 3) racoonctl es -l => evt_filter = EVTF_LOOP * Catch the second case: show-event is here to purge all */ if (evt_filter == EVTF_NONE) evt_filter = (EVTF_ALL|EVTF_PURGE); if ((ac >= 1) && (strcmp(av[0], "-l") == 0)) evt_filter |= EVTF_LOOP; if (ac >= 2) errx(1, "too many arguments"); buf = vmalloc(sizeof(*head)); if (buf == NULL) errx(1, "not enough core"); head = (struct admin_com *)buf->v; head->ac_len = buf->l; head->ac_cmd = ADMIN_SHOW_EVT; head->ac_errno = 0; head->ac_proto = 0; return buf; } static vchar_t * f_getsched(ac, av) int ac; char **av; { vchar_t *buf; struct admin_com *head; buf = vmalloc(sizeof(*head)); if (buf == NULL) errx(1, "not enough core"); head = (struct admin_com *)buf->v; head->ac_len = buf->l; head->ac_cmd = ADMIN_SHOW_SCHED; head->ac_errno = 0; head->ac_proto = 0; return buf; } static vchar_t * f_getsa(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); buf = vmalloc(sizeof(*head)); if (buf == NULL) errx(1, "not enough core"); head = (struct admin_com *)buf->v; head->ac_len = buf->l; head->ac_cmd = ADMIN_SHOW_SA; head->ac_errno = 0; head->ac_proto = proto; 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); buf = vmalloc(sizeof(*head)); if (buf == NULL) errx(1, "not enough core"); head = (struct admin_com *)buf->v; head->ac_len = buf->l; head->ac_cmd = ADMIN_FLUSH_SA; head->ac_errno = 0; head->ac_proto = proto; return buf; } static vchar_t * f_deletesa(ac, av) int ac; char **av; { vchar_t *buf, *index; 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); /* 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 = vmalloc(sizeof(*head) + index->l); if (buf == NULL) goto out; head = (struct admin_com *)buf->v; head->ac_len = buf->l + index->l; head->ac_cmd = ADMIN_DELETE_SA; head->ac_errno = 0; head->ac_proto = proto; memcpy(buf->v+sizeof(*head), 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; 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); /* 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 = vmalloc(sizeof(*head) + index->l); if (buf == NULL) goto out; head = (struct admin_com *)buf->v; head->ac_len = buf->l + index->l; head->ac_cmd = ADMIN_DELETE_ALL_SA_DST; head->ac_errno = 0; head->ac_proto = proto; memcpy(buf->v+sizeof(*head), 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; struct admin_com *head; int proto; int cmd = ADMIN_ESTABLISH_SA; size_t com_len = 0; char *id = NULL; char *key = NULL; struct admin_com_psk *acp; 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; } /* need protocol */ if (ac < 1) errx(1, "insufficient arguments"); if ((proto = get_proto(*av)) == -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; } com_len += sizeof(*head) + index->l; if ((buf = vmalloc(com_len)) == NULL) errx(1, "Cannot allocate buffer"); head = (struct admin_com *)buf->v; head->ac_len = buf->l; head->ac_cmd = cmd; head->ac_errno = 0; head->ac_proto = proto; memcpy(buf->v+sizeof(*head), index->v, index->l); if (id && key) { char *data; acp = (struct admin_com_psk *) (buf->v + sizeof(*head) + 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_filter = (EVTF_LOOP|EVTF_CFG|EVTF_CFG_STOP|EVTF_ERR|EVTF_ERR_STOP); time(&evt_start); /* 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_filter = (EVTF_PH1DOWN|EVTF_PH1DOWN_STOP|EVTF_LOOP|EVTF_ERR|EVTF_ERR_STOP); 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; struct admin_com *head; 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 = vmalloc(sizeof(*head) + userlen); if (buf == NULL) return NULL; head = (struct admin_com *)buf->v; head->ac_len = buf->l; head->ac_cmd = ADMIN_LOGOUT_USER; head->ac_errno = 0; head->ac_proto = 0; strncpy((char *)(head + 1), user, userlen); return buf; } #endif /* ENABLE_HYBRID */ 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(buf, len) caddr_t buf; int len; { struct evtdump *evtdump = (struct evtdump *)buf; int i; char *srcstr; char *dststr; for (i = 0; evtmsg[i].msg; i++) if (evtmsg[i].type == evtdump->type) break; if (evtmsg[i].msg == NULL) printf("Event %d: ", evtdump->type); else printf("%s : ", evtmsg[i].msg); if ((srcstr = saddr2str((struct sockaddr *)&evtdump->src)) == NULL) printf("unknown"); else printf("%s", srcstr); printf(" -> "); if ((dststr = saddr2str((struct sockaddr *)&evtdump->dst)) == NULL) printf("unknown"); else printf("%s", dststr); printf("\n"); return; } void print_err(buf, len) caddr_t buf; int len; { struct evtdump *evtdump = (struct evtdump *)buf; int i; for (i = 0; evtmsg[i].msg; i++) if (evtmsg[i].type == evtdump->type) break; if (evtmsg[i].level != ERROR) return; if (evtmsg[i].msg == NULL) printf("Error: Event %d\n", evtdump->type); else printf("Error: %s\n", evtmsg[i].msg); if (evt_filter & EVTF_ERR_STOP) evt_filter &= ~EVTF_LOOP; return; } /* * Print a message when phase 1 SA goes down */ void print_ph1down(buf, len) caddr_t buf; int len; { struct evtdump *evtdump = (struct evtdump *)buf; if (evtdump->type != EVTT_PHASE1_DOWN) return; printf("VPN connexion terminated\n"); if (evt_filter & EVTF_PH1DOWN_STOP) evt_filter &= ~EVTF_LOOP; return; } /* * Print ISAKMP mode config info (IP and banner) */ void print_cfg(buf, len) caddr_t buf; int len; { struct evtdump *evtdump = (struct evtdump *)buf; struct isakmp_data *attr; char *banner = NULL; struct in_addr addr4; memset(&addr4, 0, sizeof(addr4)); if (evtdump->type != EVTT_ISAKMP_CFG_DONE && evtdump->type != EVTT_NO_ISAKMP_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 (evtdump->type == EVTT_ISAKMP_CFG_DONE) 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); } if (evt_filter & EVTF_CFG_STOP) evt_filter &= ~EVTF_LOOP; return; } 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 h, *com; caddr_t buf; int len; com = (struct admin_com *)combuf->v; len = com->ac_len - sizeof(*com); buf = combuf->v + sizeof(*com); switch (com->ac_cmd) { case ADMIN_SHOW_SCHED: print_schedule(buf, len); break; case ADMIN_SHOW_EVT: { struct evtdump *evtdump; /* We got no event */ if (len == 0) { /* If we were purging the queue, it is now done */ if (evt_filter & EVTF_PURGE) evt_filter &= ~EVTF_PURGE; break; } if (len < sizeof(struct evtdump)) errx(1, "Short buffer\n"); /* Toss outdated events */ evtdump = (struct evtdump *)buf; if (evtdump->timestamp < evt_start) break; if (evt_filter & EVTF_ALL) print_evt(buf, len); if (evt_filter & EVTF_ERR) print_err(buf, len); if (evt_filter & EVTF_CFG) print_cfg(buf, len); if (evt_filter & EVTF_PH1DOWN) print_ph1down(buf, len); 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; } close(so); return 0; bad: close(so); return -1; }