9e1f4da809
hacked up by me to remove the IPv6 stuff (fow now). I renamed it `keyadmin' since `key' was already taken by the S/Key calculator. Its purpose is to act as a command-driven interface to the `PF_KEY' socket domain, analogously to thefunction of route(8) in the `PF_ROUTE' domain. This program is believed to be exportable, since it does no actual cryptography itself.
1259 lines
32 KiB
C
1259 lines
32 KiB
C
/*----------------------------------------------------------------------
|
||
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/types.h>
|
||
#include <sys/stat.h>
|
||
#include <sys/socket.h>
|
||
#include <sys/socketvar.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
|
||
|
||
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.
|
||
|
||
----------------------------------------------------------------------*/
|
||
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) {
|
||
fprintf(stderr, "Unknown command: %s\n", 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.
|
||
|
||
----------------------------------------------------------------------*/
|
||
usage(myname)
|
||
char *myname;
|
||
{
|
||
int i;
|
||
|
||
fprintf(stderr, "usage: %s <command> <args>\n", myname);
|
||
printf("where <command> is one of:\n");
|
||
help(NULL);
|
||
}
|
||
|
||
/*----------------------------------------------------------------------
|
||
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 */
|
||
|
||
fprintf(stderr,"Unknown host \"%s\"\n", 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.
|
||
|
||
----------------------------------------------------------------------*/
|
||
int 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) {
|
||
fprintf(stderr, "Parse error on line %d of %s.\n", 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) { fprintf(stderr, "key: kernel returned a truncated message!\n"); 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;
|
||
{
|
||
int i;
|
||
char *cp;
|
||
|
||
printf("type=%d(%s) ",kmp->type, parsenumtoname(keytypes, kmp->type));
|
||
printf("spi=%u ", kmp->spi);
|
||
printf("alogrithm=%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)) {
|
||
fprintf(stderr, "add: spi must be greater than 255\n");
|
||
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) {
|
||
fprintf(stderr, "add: key must be 8 bytes\n");
|
||
return (0);
|
||
}
|
||
if (kmp->ivlen != 4 && kmp->ivlen != 8) {
|
||
fprintf(stderr, "add: iv must be 4 or 8 bytes\n");
|
||
return (0);
|
||
}
|
||
}
|
||
|
||
if (kmp->type == KEY_TYPE_AH &&
|
||
kmp->algorithm == IPSEC_ALGTYPE_AUTH_MD5 && kmp->keylen == 0) {
|
||
fprintf(stderr, "add: no key specified\n");
|
||
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)
|
||
fprintf(stderr, "add: security association already exists\n");
|
||
else
|
||
perror("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) {
|
||
fprintf(stderr, "delete: Security association not found\n");
|
||
return 0;
|
||
} else {
|
||
perror("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) {
|
||
fprintf(stderr, "get: Security association not found\n");
|
||
return 0;
|
||
} else {
|
||
perror("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) {
|
||
perror("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) {
|
||
perror("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) {
|
||
perror("open");
|
||
return 1;
|
||
} else if (!(fp = fdopen(fd, "w"))) {
|
||
perror("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) {
|
||
perror("write");
|
||
return -1;
|
||
}
|
||
|
||
{
|
||
struct key_msgdata keymsgdata;
|
||
|
||
readmesg2:
|
||
if (read(keysock, key_message, sizeof(key_message)) < 0) {
|
||
perror("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()) {
|
||
fprintf(stderr, "This program is intended for the superuser only.\n");
|
||
exit(1);
|
||
}
|
||
|
||
if (!(keysock = socket(PF_KEY, SOCK_RAW, 0))) {
|
||
perror("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) {
|
||
perror("getpid");
|
||
return -1;
|
||
}
|
||
|
||
if (argc > 1) {
|
||
/*
|
||
* Attempt to do a single command, based on command line arguments.
|
||
*/
|
||
if (strcasecmp(argv[1], "add") == 0)
|
||
{
|
||
fprintf(stderr,
|
||
"Cannot add keys from the command line. RTFM for why.\n");
|
||
exit(1);
|
||
}
|
||
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(argv[0]);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
{
|
||
char buffer[1024];
|
||
char *iargv[KEYCMD_ARG_MAX], *head;
|
||
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)
|
||
perror("System error");
|
||
else
|
||
fprintf(stderr, "Unrecognized command; ");
|
||
fprintf(stderr, "Type 'help' if you need help\n");
|
||
};
|
||
};
|
||
};
|
||
};
|
||
}
|
||
|
||
/* EOF */
|