487 lines
10 KiB
C
487 lines
10 KiB
C
/* $KAME: admin.c,v 1.23 2001/06/01 10:12:55 sakane 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 <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/signal.h>
|
|
#include <sys/un.h>
|
|
|
|
#include <net/pfkeyv2.h>
|
|
#include <netkey/key_var.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <netdb.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "var.h"
|
|
#include "misc.h"
|
|
#include "vmbuf.h"
|
|
#include "plog.h"
|
|
#include "sockmisc.h"
|
|
#include "debug.h"
|
|
|
|
#include "schedule.h"
|
|
#include "localconf.h"
|
|
#include "remoteconf.h"
|
|
#include "grabmyaddr.h"
|
|
#include "isakmp_var.h"
|
|
#include "isakmp.h"
|
|
#include "oakley.h"
|
|
#include "handler.h"
|
|
#include "pfkey.h"
|
|
#include "admin.h"
|
|
#include "admin_var.h"
|
|
#include "session.h"
|
|
#include "gcmalloc.h"
|
|
|
|
static struct sockaddr_un sunaddr;
|
|
static int admin_process __P((int, char *));
|
|
static int admin_reply __P((int, struct admin_com *, vchar_t *));
|
|
|
|
int
|
|
admin_handler()
|
|
{
|
|
int so2;
|
|
struct sockaddr_storage from;
|
|
int fromlen = sizeof(from);
|
|
struct admin_com com;
|
|
char *combuf = NULL;
|
|
pid_t pid = -1;
|
|
int len, error = -1;
|
|
|
|
so2 = accept(lcconf->sock_admin, (struct sockaddr *)&from, &fromlen);
|
|
if (so2 < 0) {
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"failed to accept admin command: %s\n",
|
|
strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
/* get buffer length */
|
|
while ((len = recv(so2, (char *)&com, sizeof(com), MSG_PEEK)) < 0) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"failed to recv admin command: %s\n",
|
|
strerror(errno));
|
|
goto end;
|
|
}
|
|
|
|
/* sanity check */
|
|
if (len < sizeof(com)) {
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"invalid header length of admin command\n");
|
|
goto end;
|
|
}
|
|
|
|
/* get buffer to receive */
|
|
if ((combuf = racoon_malloc(com.ac_len)) == 0) {
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"failed to alloc buffer for admin command\n");
|
|
goto end;
|
|
}
|
|
|
|
/* get real data */
|
|
while ((len = recv(so2, combuf, com.ac_len, 0)) < 0) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"failed to recv admin command: %s\n",
|
|
strerror(errno));
|
|
goto end;
|
|
}
|
|
|
|
/* don't fork() because of reloading config. */
|
|
if (com.ac_cmd == ADMIN_RELOAD_CONF) {
|
|
/* reload does not work at all! */
|
|
signal_handler(SIGHUP);
|
|
goto end;
|
|
}
|
|
|
|
/* fork for processing */
|
|
if (!f_foreground) {
|
|
if ((pid = fork()) < 0) {
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"failed to fork for admin processing: %s\n",
|
|
strerror(errno));
|
|
goto end;
|
|
}
|
|
|
|
/* parant's process. */
|
|
if (pid != 0) {
|
|
error = 0;
|
|
goto end;
|
|
}
|
|
|
|
/* child's process */
|
|
admin_close();
|
|
}
|
|
|
|
/* exit in this function. */
|
|
error = admin_process(so2, combuf);
|
|
|
|
end:
|
|
(void)close(so2);
|
|
if (combuf)
|
|
racoon_free(combuf);
|
|
|
|
/* exit if child's process. */
|
|
if (pid == 0 && !f_foreground)
|
|
exit(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* main child's process.
|
|
*/
|
|
static int
|
|
admin_process(so2, combuf)
|
|
int so2;
|
|
char *combuf;
|
|
{
|
|
struct admin_com *com = (struct admin_com *)combuf;
|
|
vchar_t *buf = NULL;
|
|
int error = 0;
|
|
|
|
com->ac_errno = 0;
|
|
|
|
switch (com->ac_cmd) {
|
|
case ADMIN_RELOAD_CONF:
|
|
/* don't entered because of proccessing it in other place. */
|
|
plog(LLV_ERROR, LOCATION, NULL, "should never reach here\n");
|
|
goto bad;
|
|
|
|
case ADMIN_SHOW_SCHED:
|
|
{
|
|
caddr_t p;
|
|
int len;
|
|
if (sched_dump(&p, &len) == -1)
|
|
com->ac_errno = -1;
|
|
buf = vmalloc(len);
|
|
if (buf == NULL)
|
|
com->ac_errno = -1;
|
|
memcpy(buf->v, p, len);
|
|
}
|
|
break;
|
|
case ADMIN_SHOW_SA:
|
|
case ADMIN_FLUSH_SA:
|
|
{
|
|
switch (com->ac_proto) {
|
|
case ADMIN_PROTO_ISAKMP:
|
|
switch (com->ac_cmd) {
|
|
case ADMIN_SHOW_SA:
|
|
buf = dumpph1();
|
|
if (buf == NULL)
|
|
com->ac_errno = -1;
|
|
break;
|
|
case ADMIN_FLUSH_SA:
|
|
flushph1();
|
|
break;
|
|
}
|
|
break;
|
|
case ADMIN_PROTO_IPSEC:
|
|
case ADMIN_PROTO_AH:
|
|
case ADMIN_PROTO_ESP:
|
|
switch (com->ac_cmd) {
|
|
case ADMIN_SHOW_SA:
|
|
{
|
|
u_int p;
|
|
p = admin2pfkey_proto(com->ac_proto);
|
|
if (p == -1)
|
|
goto bad;
|
|
buf = pfkey_dump_sadb(p);
|
|
if (buf == NULL)
|
|
com->ac_errno = -1;
|
|
}
|
|
break;
|
|
case ADMIN_FLUSH_SA:
|
|
pfkey_flush_sadb(com->ac_proto);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case ADMIN_PROTO_INTERNAL:
|
|
switch (com->ac_cmd) {
|
|
case ADMIN_SHOW_SA:
|
|
buf = NULL; /*XXX dumpph2(&error);*/
|
|
if (buf == NULL)
|
|
com->ac_errno = error;
|
|
break;
|
|
case ADMIN_FLUSH_SA:
|
|
/*XXX flushph2();*/
|
|
com->ac_errno = 0;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* ignore */
|
|
com->ac_errno = -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ADMIN_DELETE_SA:
|
|
break;
|
|
|
|
case ADMIN_ESTABLISH_SA:
|
|
{
|
|
struct sockaddr *dst;
|
|
struct sockaddr *src;
|
|
src = (struct sockaddr *)
|
|
&((struct admin_com_indexes *)
|
|
((caddr_t)com + sizeof(*com)))->src;
|
|
dst = (struct sockaddr *)
|
|
&((struct admin_com_indexes *)
|
|
((caddr_t)com + sizeof(*com)))->dst;
|
|
|
|
switch (com->ac_proto) {
|
|
case ADMIN_PROTO_ISAKMP:
|
|
{
|
|
struct remoteconf *rmconf;
|
|
struct sockaddr *remote;
|
|
struct sockaddr *local;
|
|
|
|
/* search appropreate configuration */
|
|
rmconf = getrmconf(dst);
|
|
if (rmconf == NULL) {
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"no configuration found "
|
|
"for %s\n", saddrwop2str(dst));
|
|
com->ac_errno = -1;
|
|
break;
|
|
}
|
|
|
|
/* get remote IP address and port number. */
|
|
remote = dupsaddr(dst);
|
|
if (remote == NULL) {
|
|
com->ac_errno = -1;
|
|
break;
|
|
}
|
|
switch (remote->sa_family) {
|
|
case AF_INET:
|
|
((struct sockaddr_in *)remote)->sin_port =
|
|
((struct sockaddr_in *)rmconf->remote)->sin_port;
|
|
break;
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
((struct sockaddr_in6 *)remote)->sin6_port =
|
|
((struct sockaddr_in6 *)rmconf->remote)->sin6_port;
|
|
break;
|
|
#endif
|
|
default:
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"invalid family: %d\n",
|
|
remote->sa_family);
|
|
com->ac_errno = -1;
|
|
break;
|
|
}
|
|
|
|
/* get local address */
|
|
local = dupsaddr(src);
|
|
if (local == NULL) {
|
|
com->ac_errno = -1;
|
|
break;
|
|
}
|
|
switch (local->sa_family) {
|
|
case AF_INET:
|
|
((struct sockaddr_in *)local)->sin_port =
|
|
getmyaddrsport(local);
|
|
break;
|
|
#ifdef INET6
|
|
case AF_INET6:
|
|
((struct sockaddr_in6 *)local)->sin6_port =
|
|
getmyaddrsport(local);
|
|
break;
|
|
#endif
|
|
default:
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"invalid family: %d\n",
|
|
local->sa_family);
|
|
com->ac_errno = -1;
|
|
break;
|
|
}
|
|
|
|
|
|
plog(LLV_INFO, LOCATION, NULL,
|
|
"accept a request to establish IKE-SA: "
|
|
"%s\n", saddrwop2str(remote));
|
|
|
|
/* begin ident mode */
|
|
if (isakmp_ph1begin_i(rmconf, remote) < 0) {
|
|
com->ac_errno = -1;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case ADMIN_PROTO_AH:
|
|
case ADMIN_PROTO_ESP:
|
|
break;
|
|
default:
|
|
/* ignore */
|
|
com->ac_errno = -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"invalid command: %d\n", com->ac_cmd);
|
|
com->ac_errno = -1;
|
|
}
|
|
|
|
if (admin_reply(so2, com, buf) < 0)
|
|
goto bad;
|
|
|
|
if (buf != NULL)
|
|
vfree(buf);
|
|
|
|
return 0;
|
|
|
|
bad:
|
|
if (buf != NULL)
|
|
vfree(buf);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
admin_reply(so, combuf, buf)
|
|
int so;
|
|
struct admin_com *combuf;
|
|
vchar_t *buf;
|
|
{
|
|
int tlen;
|
|
char *retbuf = NULL;
|
|
|
|
if (buf != NULL)
|
|
tlen = sizeof(*combuf) + buf->l;
|
|
else
|
|
tlen = sizeof(*combuf);
|
|
|
|
retbuf = racoon_calloc(1, tlen);
|
|
if (retbuf == NULL) {
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"failed to allocate admin buffer\n");
|
|
return -1;
|
|
}
|
|
|
|
memcpy(retbuf, combuf, sizeof(*combuf));
|
|
((struct admin_com *)retbuf)->ac_len = tlen;
|
|
|
|
if (buf != NULL)
|
|
memcpy(retbuf + sizeof(*combuf), buf->v, buf->l);
|
|
|
|
tlen = send(so, retbuf, tlen, 0);
|
|
racoon_free(retbuf);
|
|
if (tlen < 0) {
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"failed to send admin command: %s\n",
|
|
strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ADMIN_PROTO -> SADB_SATYPE */
|
|
int
|
|
admin2pfkey_proto(proto)
|
|
u_int proto;
|
|
{
|
|
switch (proto) {
|
|
case ADMIN_PROTO_IPSEC:
|
|
return SADB_SATYPE_UNSPEC;
|
|
case ADMIN_PROTO_AH:
|
|
return SADB_SATYPE_AH;
|
|
case ADMIN_PROTO_ESP:
|
|
return SADB_SATYPE_ESP;
|
|
default:
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"unsupported proto for admin: %d\n", proto);
|
|
return -1;
|
|
}
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
int
|
|
admin_init()
|
|
{
|
|
memset(&sunaddr, 0, sizeof(sunaddr));
|
|
sunaddr.sun_family = AF_UNIX;
|
|
snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path),
|
|
"%s", PORT_ADMIN);
|
|
|
|
lcconf->sock_admin = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
if (lcconf->sock_admin < 0) {
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"socket: %s\n", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (bind(lcconf->sock_admin, (struct sockaddr *)&sunaddr,
|
|
sizeof(sunaddr)) < 0) {
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"bind(sockname:%s): %s\n",
|
|
sunaddr.sun_path, strerror(errno));
|
|
(void)close(lcconf->sock_admin);
|
|
return -1;
|
|
}
|
|
|
|
if (listen(lcconf->sock_admin, 5) < 0) {
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
|
"listen(sockname:%s): %s\n",
|
|
sunaddr.sun_path, strerror(errno));
|
|
(void)close(lcconf->sock_admin);
|
|
return -1;
|
|
}
|
|
plog(LLV_DEBUG, LOCATION, NULL,
|
|
"open %s as racoon management.\n", sunaddr.sun_path);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
admin_close()
|
|
{
|
|
close(lcconf->sock_admin);
|
|
unlink(sunaddr.sun_path);
|
|
return 0;
|
|
}
|