freebsd-dev/usr.sbin/keyadmin/keyadmin.c

1255 lines
32 KiB
C
Raw Normal View History

/*----------------------------------------------------------------------
key.c: Main routines for the key(8) tool for manually managing
cryptographic keys and security associations inside the
Key Engine of the operating system.
Future Enhancements should support:
multiple sensitivity levels
OSPFv2 keys
RIPv2 keys
Triple DES for ESP
DES+MD5 for ESP
Copyright 1995 by Bao Phan, Randall Atkinson, & Dan McDonald,
All Rights Reserved. All Rights have been assigned to the
US Naval Research Laboratory. The NRL Copyright Notice and
License govern distribution and use of this software.
----------------------------------------------------------------------*/
/*----------------------------------------------------------------------
# @(#)COPYRIGHT 1.1a (NRL) 17 August 1995
COPYRIGHT NOTICE
All of the documentation and software included in this software
distribution from the US Naval Research Laboratory (NRL) are
copyrighted by their respective developers.
This software and documentation were developed at NRL by various
people. Those developers have each copyrighted the portions that they
developed at NRL and have assigned All Rights for those portions to
NRL. Outside the USA, NRL also has copyright on the software
developed at NRL. The affected files all contain specific copyright
notices and those notices must be retained in any derived work.
NRL LICENSE
NRL grants permission for redistribution and use in source and binary
forms, with or without modification, of the software and documentation
created at NRL 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. All advertising materials mentioning features or use of this software
must display the following acknowledgement:
This product includes software developed at the Information
Technology Division, US Naval Research Laboratory.
4. Neither the name of the NRL nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL 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 NRL 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.
The views and conclusions contained in the software and documentation
are those of the authors and should not be interpreted as representing
official policies, either expressed or implied, of the US Naval
Research Laboratory (NRL).
----------------------------------------------------------------------*/
/*
* $ANA: keyadmin.c,v 1.2 1996/06/13 19:42:40 wollman Exp $
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#ifdef INET6
#include <netinet6/in6.h>
#else /* INET6 */
#if 0
#include <netinet6/in6_types.h>
#endif
#endif /* INET6 */
#ifdef IPSEC
#include <netsec/ipsec.h>
#endif
#include <netkey/key.h>
#ifdef INET6
#include <netinet6/support.h>
#endif
#ifndef INET6 /* XXX */
#define hostname2addr(a, b, c) gethostbyname(a)
#define addr2hostname(a, b, c, d) gethostbyaddr((a), (b), (c))
#endif
#include <arpa/inet.h>
int parse7 __P((int, char **));
int parse4 __P((int, char **));
int docmd __P((int, char **));
#define KEYCMD_ARG_MAX 10
#define KEYCMD_LOAD 1
#define KEYCMD_SAVE 2
#define KEYCMD_ADD 3
#define KEYCMD_DEL 4
#define KEYCMD_FLUSH 5
#define KEYCMD_GET 6
#define KEYCMD_DUMP 7
#define KEYCMD_HELP 8
#define KEYCMD_EXIT 9
#define KEYCMD_SHELL 10
struct nametonum {
char *name;
int num;
int flags;
};
char parse7usage[] = "<type> <spi> <src> <dst> <transform> <key> [iv]";
char parse4usage[] = "<type> <spi> <src> <dst>";
struct keycmd {
char *name;
int num;
int (*parse) __P((int, char **));
char *usage;
char *help;
} keycmds[] = {
{ "add", KEYCMD_ADD, parse7, parse7usage,
"Adds a specific key entry to the kernel key table." },
{ "del", KEYCMD_DEL, parse4, parse4usage,
"Removes a specific key entry from the kernel key table." },
{ "get", KEYCMD_GET, parse4, parse4usage,
"Retrieves a key entry from the kernel key table." },
{ "dump", KEYCMD_DUMP, NULL, " ",
"Retrieves all key entries from the kernel key table." },
{ "load", KEYCMD_LOAD, NULL, "{ <filename> | - }",
"Loads keys from a file or stdin into the kernel key table." },
{ "save", KEYCMD_SAVE, NULL, "{ <filename> | - }",
"Saves keys from the kernel key table to a file or stdout." },
{ "help", KEYCMD_HELP, NULL, "[command]",
"Provides limited on-line help. Read the man page for more." },
{ "flush", KEYCMD_FLUSH, NULL, NULL,
"Clears the kernel key table." },
{ "!", KEYCMD_SHELL, NULL, "[command]",
"Executes a subshell." },
{ "exit", KEYCMD_EXIT, NULL, NULL,
"Exits the program." },
{ "quit", KEYCMD_EXIT, NULL, NULL,
"Exits the program." },
{ NULL, 0, NULL, NULL, NULL }
};
/* flags: index into algorithmtabs */
struct nametonum keytypes[] = {
#ifdef IPSEC
{ "ah", KEY_TYPE_AH, 0 },
{ "esp", KEY_TYPE_ESP, 1 },
#endif
{ "rsvp", KEY_TYPE_RSVP, 2 },
{ "ospf", KEY_TYPE_OSPF, 3 },
{ "rip", KEY_TYPE_RIPV2, 4 },
{ NULL, 0, 0 }
};
#ifndef IPSEC_ALGTYPE_AUTH_MD5 /* XXX */
#define IPSEC_ALGTYPE_AUTH_MD5 1
#endif
/* flags: true = iv req. */
struct nametonum authalgorithms[] = {
{ "md5", IPSEC_ALGTYPE_AUTH_MD5, 0 },
#ifdef DEBUG
/* These provide no security but are useful for debugging the
kernel's ESP and Key Engine and PF_KEY routines */
{ "dummy", IPSEC_ALGTYPE_AUTH_DUMMY, 0 },
{ "cksum", IPSEC_ALGTYPE_AUTH_CKSUM, 0 },
#endif
{ NULL, 0, 0 }
};
#ifndef IPSEC_ALGTYPE_ESP_DES_CBC /* XXX */
#define IPSEC_ALGTYPE_ESP_DES_CBC 1
#endif
/* flags: true = iv req. */
struct nametonum encralgorithms[] = {
{ "des-cbc", IPSEC_ALGTYPE_ESP_DES_CBC, 1 },
#ifdef DEBUG
/* These provide no security but are useful for debugging the
kernel's ESP and Key Engine and PF_KEY routines */
{ "dummy", IPSEC_ALGTYPE_ESP_DUMMY, 0 },
#endif
{ NULL, 0, 0 }
};
/*
* These numbers should be defined in a header file somewhere
* and shared with the consuming programs, once someone has
* actually written the support in those programs (rspvd,
* gated, and routed). Probably <protocols/...>...?
*/
#define RSVP_AUTHTYPE_MD5 1 /* XXX */
struct nametonum rsvpalgorithms[] = {
{ "md5", RSVP_AUTHTYPE_MD5, 0 },
{ NULL, 0, 0 }
};
#define OSPF_AUTHTYPE_MD5 1 /* XXX */
struct nametonum ospfalgorithms[] = {
{ "md5", OSPF_AUTHTYPE_MD5, 0 },
{ NULL, 0, 0 }
};
#define RIPV2_AUTHTYPE_MD5 1 /* XXX */
struct nametonum ripalgorithms[] = {
{ "md5", RIPV2_AUTHTYPE_MD5, 0 },
{ NULL, 0, 0 }
};
/* NB: It is the ordering here that determines the values for the
flags specified above that are used to index into algorithmtabs[] */
struct nametonum *algorithmtabs[] = {
authalgorithms,
encralgorithms,
rsvpalgorithms,
ospfalgorithms,
ripalgorithms,
NULL
};
char buffer[1024] = "\0";
#define MAXRCVBUFSIZE 8 * 1024
char key_message[sizeof(struct key_msghdr) + MAX_SOCKADDR_SZ * 3
+ MAX_KEY_SZ + MAX_IV_SZ];
int key_messageptr;
int keysock = -1;
int keygetseqno = 1;
int keydumpseqno = 1;
pid_t mypid;
/*----------------------------------------------------------------------
help: Print appropriate help message on stdout.
----------------------------------------------------------------------*/
int help(cmdname)
char *cmdname;
{
int i;
if (cmdname) {
for (i = 0; keycmds[i].name; i++)
if (!strcasecmp(keycmds[i].name, cmdname))
break;
if (!keycmds[i].name) {
warnx("unknown command: %s", cmdname);
return 0;
}
printf("%s%s%s\n", keycmds[i].name, keycmds[i].usage ? " " : "",
keycmds[i].usage ? keycmds[i].usage : "");
if (keycmds[i].help)
puts(keycmds[i].help);
} else {
for (i = 0; keycmds[i].name; i++)
printf("\t%s%s%s\n", keycmds[i].name, keycmds[i].usage ? " " : "",
keycmds[i].usage ? keycmds[i].usage : "");
}
return 0;
}
/*----------------------------------------------------------------------
usage: print suitable usage message on stdout.
----------------------------------------------------------------------*/
static void
usage()
{
fprintf(stderr, "usage: keyadmin <command> <args>\n");
}
/*----------------------------------------------------------------------
parsekey: parse argument into a binary key and also record
the length of the resulting key.
----------------------------------------------------------------------*/
int parsekey(key, keylen, arg)
u_int8_t *key;
u_int8_t *keylen;
char *arg;
{
int i, j, k, l;
u_int8_t thisbyte;
i = strlen(arg);
if ((i == 1) && (arg[0] == '0')) {
*keylen = 0;
return 0;
}
if ((i % 2)) {
printf("Invalid number \"%s\"\n", arg);
return -1;
}
j = 1;
k = l = thisbyte = 0;
while(arg[k]) {
if ((arg[k] >= '0') && (arg[k] <= '9'))
thisbyte |= arg[k] - '0';
else
if ((arg[k] >= 'a') && (arg[k] <= 'f'))
thisbyte |= arg[k] - 'a' + 10;
else
if ((arg[k] >= 'A') && (arg[k] <= 'F'))
thisbyte |= arg[k] - 'A' + 10;
else {
printf("Invalid hex number \"%s\"\n", arg);
return 1;
}
if (!(j % 2))
key[l++] = thisbyte;
thisbyte = (thisbyte << 4);
j++;
k++;
}
*keylen = l;
return 0;
}
/*----------------------------------------------------------------------
parsenametonum: converts command-line name into index number.
----------------------------------------------------------------------*/
int parsenametonum(tab, arg)
struct nametonum *tab;
char *arg;
{
int i;
for (i = 0; tab[i].name; i++)
if (!strcasecmp(tab[i].name, arg))
return i;
if (!tab[i].name)
return -1;
}
/*----------------------------------------------------------------------
parsesockaddr: Convert hostname arg into an appropriate sockaddr.
----------------------------------------------------------------------*/
int parsesockaddr(sockaddr, arg)
struct sockaddr *sockaddr;
char *arg;
{
struct hostent *hostent;
struct in_addr in_addr, *in_addrp;
#ifdef INET6
struct in_addr6 in_addr6, *in_addr6p;
#endif /* INET6 */
if ((hostent = hostname2addr(arg, AF_INET, 0)))
if ((hostent->h_addrtype == AF_INET) &&
(hostent->h_length == sizeof(struct in_addr))) {
in_addrp = ((struct in_addr *)hostent->h_addr_list[0]);
goto fillin4;
}
if (ascii2addr(AF_INET, arg, (char *)&in_addr) ==
sizeof(struct in_addr)) {
in_addrp = &in_addr;
goto fillin4;
}
#ifdef INET6
if (hostent = hostname2addr(arg, AF_INET6))
if ((hostent->h_addrtype == AF_INET6) &&
(hostent->h_length == sizeof(struct in_addr6))) {
in_addr6p = ((struct in_addr6 *)hostent->h_addr_list[0]);
goto fillin6;
}
if (ascii2addr(AF_INET6, arg, (char *)&in_addr6)
== sizeof(struct in_addr6)) {
in_addr6p = &in_addr6;
goto fillin6;
}
#endif /* INET6 */
warnx("unknown host \"%s\"", arg);
return 1;
fillin4:
bzero(sockaddr, sizeof(struct sockaddr_in));
sockaddr->sa_len = sizeof(struct sockaddr_in);
sockaddr->sa_family = AF_INET;
((struct sockaddr_in *)sockaddr)->sin_addr = *in_addrp;
return 0;
#ifdef INET6
fillin6:
bzero(sockaddr, sizeof(struct sockaddr_in6));
sockaddr->sa_len = sizeof(struct sockaddr_in6);
sockaddr->sa_family = AF_INET6;
((struct sockaddr_in6 *)sockaddr)->sin6_addr = *in_addr6p;
return 0;
#endif /* INET6 */
}
/*----------------------------------------------------------------------
dummyfromaddr: Creates a zeroed sockaddr of family af.
----------------------------------------------------------------------*/
void dummyfromaddr(sa, af)
struct sockaddr *sa;
int af;
{
int size;
#ifdef INET6
size = (af == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
#else /* INET6 */
size = sizeof(struct sockaddr_in);
#endif /* INET6 */
bzero((char *)sa, size);
sa->sa_family = af;
sa->sa_len = size;
}
/*
* Macros to ensure word alignment.
*/
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
#define ADVANCE(x, n) \
{ x += ROUNDUP(n); }
/*----------------------------------------------------------------------
parse4: parse keytype, spi, src addr, and dest addr from argv (command line)
and stick in structure pointed to by key_messageptr.
----------------------------------------------------------------------*/
int parse4(argc, argv)
int argc;
char *argv[];
{
int i;
if (argc < 4)
return 1;
if ((i = parsenametonum(keytypes, argv[0])) < 0)
return i;
((struct key_msghdr *)key_message)->type = keytypes[i].num;
/* Should we zero check? */
((struct key_msghdr *)key_message)->spi = atoi(argv[1]);
if (parsesockaddr(key_message + key_messageptr, argv[2]) != 0)
return 1;
ADVANCE(key_messageptr, ((struct sockaddr *)(key_message +
key_messageptr))->sa_len);
if (parsesockaddr(key_message + key_messageptr, argv[3]) != 0)
return 1;
ADVANCE(key_messageptr, ((struct sockaddr *)(key_message +
key_messageptr))->sa_len);
/*
* We need to put a dummy from address since the kernel expects
* this to be in the message.
*/
#ifdef INET6
dummyfromaddr(key_message + key_messageptr, AF_INET6);
#else /* INET6 */
dummyfromaddr(key_message + key_messageptr, AF_INET);
#endif /* INET6 */
ADVANCE(key_messageptr, ((struct sockaddr *)(key_message +
key_messageptr))->sa_len);
return 0;
}
/*----------------------------------------------------------------------
parse7: parse keytype, spi, src addr, dest addr, alg type, key, and iv
from argv (command line)
and stick in structure pointed to by key_messageptr.
----------------------------------------------------------------------*/
int parse7(argc, argv)
int argc;
char *argv[];
{
int i, j;
if (argc < 6)
return 1;
if ((i = parsenametonum(keytypes, argv[0])) < 0)
return i;
((struct key_msghdr *)key_message)->type = keytypes[i].num;
/* Should we zero check? */
((struct key_msghdr *)key_message)->spi = atoi(argv[1]);
if (parsesockaddr(key_message + key_messageptr, argv[2]) != 0)
return 1;
ADVANCE(key_messageptr, ((struct sockaddr *)(key_message +
key_messageptr))->sa_len);
if (parsesockaddr(key_message + key_messageptr, argv[3]) != 0)
return 1;
ADVANCE(key_messageptr, ((struct sockaddr *)(key_message +
key_messageptr))->sa_len);
/*
* We need to put a dummy from address since the kernel expects
* this to be in the message.
*/
#ifdef INET6
dummyfromaddr(key_message + key_messageptr, AF_INET6);
#else /* INET6 */
dummyfromaddr(key_message + key_messageptr, AF_INET);
#endif /* INET6 */
ADVANCE(key_messageptr, ((struct sockaddr *)(key_message +
key_messageptr))->sa_len);
if ((j = parsenametonum(algorithmtabs[keytypes[i].flags], argv[4])) < 0)
return j;
((struct key_msghdr *)key_message)->algorithm =
algorithmtabs[keytypes[i].flags][j].num;
if ((argc < 7) && algorithmtabs[keytypes[i].flags][j].flags)
return 1;
if (parsekey(key_message + key_messageptr,
&(((struct key_msghdr *)key_message)->keylen), argv[5]) != 0)
return 1;
ADVANCE(key_messageptr, ((struct key_msghdr *)key_message)->keylen);
if (argc >= 7) {
if (parsekey(key_message + key_messageptr,
&(((struct key_msghdr *)key_message)->ivlen), argv[6]) != 0)
return 1;
ADVANCE(key_messageptr, ((struct key_msghdr *)key_message)->ivlen);
}
return 0;
}
/*----------------------------------------------------------------------
parsecmdline:
----------------------------------------------------------------------*/
int parsecmdline(buffer, argv, argc)
char *buffer;
char **argv;
int *argc;
{
int i = 0, iargc = 0;
char *head;
head = buffer;
while(buffer[i] && (iargc < KEYCMD_ARG_MAX)) {
if ((buffer[i] == '\n') || (buffer[i] == '#')) {
buffer[i] = 0;
if (*head)
argv[iargc++] = head;
break;
}
if ((buffer[i] == ' ') || (buffer[i] == '\t')) {
buffer[i] = 0;
if (*head)
argv[iargc++] = head;
head = &(buffer[++i]);
} else
i++;
};
argv[iargc] = NULL;
*argc = iargc;
return iargc ? 0 : 1;
}
/*----------------------------------------------------------------------
load: load keys from file filename into Key Engine.
----------------------------------------------------------------------*/
int load(filename)
char *filename;
{
FILE *fh;
char buffer[1024], *buf, *largv[KEYCMD_ARG_MAX], *c;
int i, largc, left, line = 0;
if (strcmp(filename, "-")) {
if (!(fh = fopen(filename, "r")))
return -1;
} else
fh = stdin;
largv[0] = "add";
buf = buffer;
left = sizeof(buffer);
while(fgets(buf, left, fh)) {
line++;
if ((c = strchr(buffer, '\\'))) {
left = (sizeof(buffer) - 1) - (--c - buffer);
buf = c;
} else {
buffer[sizeof(buffer)-1] = 0;
if ((i = parsecmdline(buffer, &(largv[1]), &largc)) < 0)
return i;
if (!i) {
if ((i = docmd(++largc, largv))) {
if (i > 0) {
warnx("parse error on line %d of %s", line, filename);
return 0;
}
return i;
}
}
buf = buffer;
left = sizeof(buffer);
}
};
return 0;
}
/*----------------------------------------------------------------------
parsedata:
----------------------------------------------------------------------*/
int
parsedata(km, kip)
struct key_msghdr *km;
struct key_msgdata *kip;
{
char *cp, *cpmax;
if (!km)
return (-1);
if (!(km->key_msglen))
return (-1);
cp = (caddr_t)(km + 1);
cpmax = (caddr_t)km + km->key_msglen;
#define NEXTDATA(x, n) \
{ x += ROUNDUP(n); if (cp >= cpmax) { \
warnx("kernel returned a truncated message!"); return(-1); } }
/* Grab src addr */
kip->src = (struct sockaddr *)cp;
if (!kip->src->sa_len)
return(-1);
NEXTDATA(cp, kip->src->sa_len);
/* Grab dest addr */
kip->dst = (struct sockaddr *)cp;
if (!kip->dst->sa_len)
return(-1);
NEXTDATA(cp, kip->dst->sa_len);
/* Grab from addr */
kip->from = (struct sockaddr *)cp;
if (!kip->from->sa_len)
return(-1);
NEXTDATA(cp, kip->from->sa_len);
/* Grab key */
if (kip->keylen = km->keylen) {
kip->key = cp;
NEXTDATA(cp, km->keylen);
}
/* Grab iv */
if (kip->ivlen = km->ivlen)
kip->iv = cp;
cp += kip->ivlen;
printf("key: parsedata: difference=%d\n", cp - cpmax);
return (0);
}
/*----------------------------------------------------------------------
printkeyiv:
----------------------------------------------------------------------*/
void printkeyiv(fp, cp, len)
FILE *fp;
caddr_t cp;
int len;
{
int i;
for (i=0; i<len; i++)
fprintf(fp, "%02x", (u_int8_t)*(cp+i));
}
/*----------------------------------------------------------------------
printsockaddr:
----------------------------------------------------------------------*/
void printsockaddr(fp, sa)
FILE *fp;
struct sockaddr *sa;
{
struct hostent *hp;
char *addrp;
int len;
#ifdef INET6
if (sa->sa_family == AF_INET6) {
len = sizeof(struct in_addr6);
addrp = (char *)&(((struct sockaddr_in6 *)sa)->sin6_addr);
} else {
#endif /* INET6 */
len = sizeof(struct in_addr);
addrp = (char *)&(((struct sockaddr_in *)sa)->sin_addr);
#ifdef INET6
}
#endif /* INET6 */
if((hp = addr2hostname(addrp, len, sa->sa_family, 0)) != NULL)
fprintf(fp, "%s", hp->h_name);
else
fprintf(fp, "%s", addr2ascii(sa->sa_family, addrp, len, NULL));
}
/*----------------------------------------------------------------------
parsenumtoname:
----------------------------------------------------------------------*/
char *
parsenumtoname(tab, num)
struct nametonum *tab;
int num;
{
int i;
for (i = 0; tab[i].name; i++) {
if (num == tab[i].num)
return(tab[i].name);
}
return(0);
}
/*----------------------------------------------------------------------
parsenumtoflag:
----------------------------------------------------------------------*/
int
parsenumtoflag(tab, num)
struct nametonum *tab;
int num;
{
int i;
for (i = 0; tab[i].name; i++) {
if (num == tab[i].num)
return(tab[i].flags);
}
return(-1);
}
/*----------------------------------------------------------------------
printkeymsg:
----------------------------------------------------------------------*/
void printkeymsg(kmp, kdp)
struct key_msghdr *kmp;
struct key_msgdata *kdp;
{
printf("type=%d(%s) ",kmp->type, parsenumtoname(keytypes, kmp->type));
printf("spi=%u ", kmp->spi);
printf("algorithm=%u(%s) ", kmp->algorithm,
parsenumtoname(algorithmtabs[parsenumtoflag(keytypes, kmp->type)],
kmp->algorithm));
printf("state=0x%x ",kmp->state);
if (kmp->state != 0) {
if (kmp->state & K_USED)
printf("USED ");
if (kmp->state & K_UNIQUE)
printf("UNIQUE ");
if (kmp->state & K_LARVAL)
printf("LARVAL ");
if (kmp->state & K_ZOMBIE)
printf("ZOMBIE ");
if (kmp->state & K_DEAD)
printf("DEAD ");
if (kmp->state & K_INBOUND)
printf("INBOUND ");
if (kmp->state & K_OUTBOUND)
printf("OUTBOUND");
}
printf("\n");
printf("sensitivity_label=%d ",kmp->label);
printf("lifetype=%d ",kmp->lifetype);
printf("lifetime1=%d ",kmp->lifetime1);
printf("lifetime2=%d\n",kmp->lifetime2);
printf("key (len=%d):\t",kdp->keylen);
printkeyiv(stdout, kdp->key, kdp->keylen);
printf("\n");
printf("iv (len=%d):\t", kdp->ivlen);
printkeyiv(stdout, kdp->iv, kdp->ivlen);
printf("\n");
printf("src:\t");
printsockaddr(stdout, (struct sockaddr *)kdp->src);
printf("\n");
printf("dst:\t");
printsockaddr(stdout, (struct sockaddr *)kdp->dst);
printf("\n");
/* printf("from:\t");
printsockaddr(stdout, (struct sockaddr *)kdp->from); */
printf("\n");
}
/*----------------------------------------------------------------------
docmd:
----------------------------------------------------------------------*/
int docmd(argc, argv)
int argc;
char **argv;
{
int i, j, seqno;
int fd;
FILE *fp;
if (!argv[0] || !argc)
return -1;
if (!argv[0][0])
return -1;
bzero(&key_message, sizeof(key_message));
key_messageptr = sizeof(struct key_msghdr);
for (i = 0; keycmds[i].name; i++)
if (!strcasecmp(keycmds[i].name, argv[0]))
break;
if (!keycmds[i].name)
return -1;
if (keycmds[i].parse)
if ((j = keycmds[i].parse(argc - 1, &(argv[1]))))
return j;
((struct key_msghdr *)key_message)->key_msglen = key_messageptr;
((struct key_msghdr *)key_message)->key_msgvers = 1;
((struct key_msghdr *)key_message)->key_seq = 1;
switch(keycmds[i].num) {
case KEYCMD_ADD:
#ifndef NOSANITYCHK
/*
* For now, we do sanity check of security association
* information here until we find a better place.
*/
{
struct key_msghdr *kmp = (struct key_msghdr *)key_message;
if ((kmp->type == KEY_TYPE_AH ||
kmp->type == KEY_TYPE_ESP) && (kmp->spi < 256)) {
warnx("add: spi must be greater than 255");
return(0);
}
if (kmp->type == KEY_TYPE_ESP &&
(kmp->algorithm == IPSEC_ALGTYPE_ESP_DES_CBC
#ifdef IPSEC_ALGTYPE_ESP_3DES
|| kmp->algorithm == IPSEC_ALGTYPE_ESP_3DES
#endif
)) {
if (kmp->keylen != 8) {
warnx("add: key must be 8 bytes");
return (0);
}
if (kmp->ivlen != 4 && kmp->ivlen != 8) {
warnx("add: iv must be 4 or 8 bytes");
return (0);
}
}
if (kmp->type == KEY_TYPE_AH &&
kmp->algorithm == IPSEC_ALGTYPE_AUTH_MD5 && kmp->keylen == 0) {
warnx("add: no key specified");
return (0);
}
}
#endif
((struct key_msghdr *)key_message)->key_msgtype = KEY_ADD;
if (write(keysock, key_message,
((struct key_msghdr *)key_message)->key_msglen) !=
((struct key_msghdr *)key_message)->key_msglen) {
if (errno == EEXIST)
warnx("add: security association already exists");
else
warn("add");
return -1;
}
read(keysock, key_message, sizeof(key_message));
return (0);
case KEYCMD_DEL:
((struct key_msghdr *)key_message)->key_msgtype = KEY_DELETE;
if (write(keysock, key_message,
((struct key_msghdr *)key_message)->key_msglen) !=
((struct key_msghdr *)key_message)->key_msglen) {
if (errno == ESRCH) {
warnx("delete: security association not found");
return 0;
} else {
warn("delete");
return -1;
}
}
read(keysock, key_message, sizeof(key_message));
return (0);
case KEYCMD_GET:
((struct key_msghdr *)key_message)->key_msgtype = KEY_GET;
((struct key_msghdr *)key_message)->key_seq = seqno = keygetseqno++;
if (write(keysock, key_message,
((struct key_msghdr *)key_message)->key_msglen) !=
((struct key_msghdr *)key_message)->key_msglen) {
if (errno == ESRCH) {
warnx("get: security association not found");
return 0;
} else {
warn("get");
return (-1);
} /* endif ESRCH */
} /* endif write() */
{
int len;
struct key_msgdata keymsgdata;
len = sizeof(struct key_msghdr) + MAX_SOCKADDR_SZ * 3
+ MAX_KEY_SZ + MAX_IV_SZ;
readmesg:
if (read(keysock, key_message, len) < 0) {
warn("read");
return -1;
}
if (!((((struct key_msghdr *)&key_message)->key_pid==mypid)
&& (((struct key_msghdr *)&key_message)->key_msgtype==KEY_GET)
&& (((struct key_msghdr *)&key_message)->key_seq==seqno))) {
fprintf(stderr, ".");
goto readmesg;
}
if (((struct key_msghdr *)&key_message)->key_errno != 0) {
printf("Error: kernel reporting errno=%d\n",
((struct key_msghdr *)&key_message)->key_errno);
return 0;
}
if (parsedata((struct key_msghdr *)&key_message, &keymsgdata) < 0) {
printf("get: can't parse reply\n");
return -1;
}
printf("\n");
printkeymsg((struct key_msghdr *)&key_message, &keymsgdata);
}
return (0);
case KEYCMD_FLUSH:
((struct key_msghdr *)key_message)->key_msgtype = KEY_FLUSH;
if (write(keysock, key_message,
((struct key_msghdr *)key_message)->key_msglen) !=
((struct key_msghdr *)key_message)->key_msglen) {
warn("write");
return -1;
}
read(keysock, key_message, sizeof(key_message));
return (0);
case KEYCMD_HELP:
return help((argc > 1) ? argv[1] : NULL);
case KEYCMD_SHELL:
if (argc > 1) {
char buffer[1024], *ap, *ep, *c;
int i;
ep = buffer + sizeof(buffer) - 1;
for (i = 1, ap = buffer; (i < argc) && (ap < ep); i++) {
c = argv[i];
while ((*(ap++) = *(c++)) && (ap < ep));
*(ap - 1) = ' ';
}
*(ap - 1) = 0;
system(buffer);
} else {
char *c = getenv("SHELL");
if (!c)
c = "/bin/sh";
system(c);
}
return 0;
case KEYCMD_EXIT:
exit(0);
case KEYCMD_LOAD:
if (argc != 2)
return 1;
return load(argv[1]);
case KEYCMD_SAVE:
if (argc != 2)
return 1;
if (!strcmp(argv[1], "-"))
fp = stdout;
else if ((fd = open(argv[1], O_CREAT | O_RDWR | O_EXCL,
S_IRUSR | S_IWUSR)) < 0) {
warn("open");
return 1;
} else if (!(fp = fdopen(fd, "w"))) {
warn("fdopen");
return 1;
}
case KEYCMD_DUMP:
((struct key_msghdr *)key_message)->key_msgtype = KEY_DUMP;
if (write(keysock, key_message,
((struct key_msghdr *)key_message)->key_msglen) !=
((struct key_msghdr *)key_message)->key_msglen) {
warn("write");
return -1;
}
{
struct key_msgdata keymsgdata;
readmesg2:
if (read(keysock, key_message, sizeof(key_message)) < 0) {
warn("read");
return -1;
}
if (!((((struct key_msghdr *)&key_message)->key_pid==mypid)
&& (((struct key_msghdr *)&key_message)->key_msgtype==KEY_DUMP)))
goto readmesg2;
/*
* Kernel is done sending secassoc if key_seq == 0
*/
if (((struct key_msghdr *)&key_message)->key_seq == 0) {
if ((keycmds[i].num == KEYCMD_SAVE) && (fp != stdout))
fclose(fp);
return 0;
}
if (parsedata((struct key_msghdr *)&key_message, &keymsgdata) < 0) {
printf("get: can't parse reply\n");
goto readmesg2;
}
if (keycmds[i].num == KEYCMD_SAVE) {
char *keytype, *algorithm;
keytype = parsenumtoname(keytypes,
((struct key_msghdr *)&key_message)->type);
algorithm = parsenumtoname(algorithmtabs[parsenumtoflag(keytypes,
((struct key_msghdr *)&key_message)->type)],
((struct key_msghdr *)&key_message)->algorithm);
fprintf(fp, "%s %u ", keytype,
((struct key_msghdr *)&key_message)->spi);
printsockaddr(fp, (struct sockaddr *)(keymsgdata.src));
fprintf(fp, " ");
printsockaddr(fp, (struct sockaddr *)(keymsgdata.dst));
fprintf(fp, " ");
fprintf(fp, "%s ", algorithm);
printkeyiv(fp, keymsgdata.key, keymsgdata.keylen);
fprintf(fp, " ");
printkeyiv(fp, keymsgdata.iv, keymsgdata.ivlen);
fprintf(fp, "\n");
} else
printkeymsg((struct key_msghdr *)&key_message, &keymsgdata);
goto readmesg2;
}
return (0);
}
return (-1);
}
/*----------------------------------------------------------------------
main:
----------------------------------------------------------------------*/
int main(argc, argv)
int argc;
char *argv[];
{
int i, j;
u_long rcvsize;
if (getuid())
errx(1, "this program is intended for the superuser only");
if (!(keysock = socket(PF_KEY, SOCK_RAW, 0))) {
warn("socket");
return -1;
}
for (rcvsize = MAXRCVBUFSIZE; rcvsize; rcvsize -= 1024) {
if (setsockopt(keysock, SOL_SOCKET, SO_RCVBUF, &rcvsize,
sizeof(rcvsize)) <= 0)
break;
}
mypid = getpid();
if (mypid < 0) {
warn("getpid");
return -1;
}
if (argc > 1) {
/*
* Attempt to do a single command, based on command line arguments.
*/
if (strcasecmp(argv[1], "add") == 0)
errx(1, "cannot add keys from the command line. RTFM for why");
if ((i = docmd(argc - 1, &(argv[1])))) {
if (i > 0) {
for (j = 0; keycmds[j].name; j++)
if (!strcasecmp(keycmds[j].name, argv[1]))
break;
if (keycmds[j].name) {
fprintf(stderr, "usage: %s%s%s\n", keycmds[j].name,
keycmds[j].usage ? " " : "",
keycmds[j].usage ? keycmds[j].usage : "");
exit(1);
}
}
usage();
}
return 0;
}
{
char buffer[1024];
char *iargv[KEYCMD_ARG_MAX];
int iargc;
while(1) {
printf("key> ");
if (!(fgets(buffer, sizeof(buffer), stdin)))
return -1;
buffer[sizeof(buffer)-1] = 0;
/*
* get command line, and parse into an argc/argv form.
*/
if ((i = parsecmdline(buffer, iargv, &iargc)) < 0)
exit(1);
if (i > 0)
continue;
errno = 0;
/*
* given argc/argv, process argument as if it came from the command
* line.
*/
if ((i = docmd(iargc, iargv))) {
if (i > 0) {
for (j = 0; keycmds[j].name; j++)
if (!strcasecmp(keycmds[j].name, iargv[0]))
break;
if (keycmds[j].name) {
fprintf(stderr, "usage: %s%s%s\n", keycmds[j].name,
keycmds[j].usage ? " " : "",
keycmds[j].usage ? keycmds[j].usage : "");
} else
i = -1;
}
if (i < 0) {
if (errno)
warn("system error");
else
warnx("unrecognized command");
warnx("type 'help' if you need help");
};
};
};
};
}
/* EOF */