ipsecalgs.c revision 628b0c67908adce18522d53bb2bf8d6c3b321579
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <ipsec_util.h>
#include <netdb.h>
#include <locale.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <net/pfpolicy.h>
#include <strings.h>
#include <errno.h>
#include <zone.h>
#define SPDSOCK_DIAG_BUF_LEN 128
typedef enum cmd_s {
CMD_NONE = 0,
} cmd_t;
static const char *comma = ",";
static boolean_t synch_kernel;
static char *flag_string;
static int mech_params[max_param];
/*
* Used by the algorithm walker callback to populate a SPD_UPDATEALGS
* request.
*/
#define SYNC_REQ_SIZE 4096
static struct spd_attribute *sync_req_attr;
(ap)++; \
(char *)sync_req_buf > SYNC_REQ_SIZE) \
bail_nomem(); \
}
static void dump_alg(struct ipsecalgent *);
static int
{
static struct flagtable {
char *label;
int token;
} table[] = {
{"VALID", ALG_FLAG_VALID},
{"COUNTER", ALG_FLAG_COUNTERMODE},
{"COMBINED", ALG_FLAG_COMBINED},
{"CCM", ALG_FLAG_CCM},
{"GCM", ALG_FLAG_GCM},
{NULL, 0}
};
/* Print out flag labels for each flag set. */
(void) printf("INVALID ");
}
ft++;
}
return (0);
}
/* Or, lookup flag for supplied label. */
ft++;
}
static void
usage(void)
{
"\tipsecalgs -l\n"
"\tipsecalgs -s\n"
"\tipsecalgs -a [-P protocol-number | -p protocol-name]\n"
"\t\t-k keylen-list [-i inc]\n"
"\t\t[-K default-keylen] -b blocklen-list\n"
"\t\t-n alg-names -N alg-number -m mech-name\n"
"\t\t[-M MAC length] [-S salt length] [-I IV length]\n"
"\t\t[-F COMBINED,COUNTER,CCM|GCM ] [-f] [-s]\n"
"\tipsecalgs -P protocol-number -p protocol-name\n"
"\t\t[-e exec-mode] [-f] [-s]\n"
"\tipsecalgs -r -p protocol-name -n alg-name [-s]\n"
"\tipsecalgs -r -p protocol-name -N alg-number [-s]\n"
"\tipsecalgs -R -P protocol-number [-s]\n"
"\tipsecalgs -R -p protocol-name [-s]\n"
"\tipsecalgs -e exec-mode -P protocol-number [-s]\n"
"\tipsecalgs -e exec-mode -p protocol-number [-s]"));
}
static void
bail_nomem(void)
{
}
/*
* Return the number of key or block sizes in the specified array.
*/
static uint_t
{
nsizes++;
return (nsizes);
}
/*
* Algorithms walker callback. Adds an algorithm to the current SPD_UPDATEALGS
* request.
*/
static void
{
for (i = 0; i < nkey_sizes; i++)
for (i = 0; i < nblock_sizes; i++) {
alg->a_block_sizes[i]);
}
for (i = 0; i < nparams; i++) {
alg->a_mech_params[i]);
}
}
/*
* Protocol walker callback. Add protocol related info to the current
* SPD_UPDATEALGS request.
*/
static void
{
/* execution mode */
"proto %d"), proto_num);
}
switch (exec_mode) {
case LIBIPSEC_ALGS_EXEC_SYNC:
break;
case LIBIPSEC_ALGS_EXEC_ASYNC:
break;
}
}
/*
*/
static void
kernel_synch(void)
{
struct spd_ext_actions *act;
struct spd_attribute *attr;
if (sfd < 0) {
}
/*
* Initialize the SPD message header and action. Some fields
* are set after having walked through the algorithms (number
* of algorithms, sizes, etc.)
*/
act->spd_actions_reserved = 0;
/*
* Walk through the algorithms defined and populate the
* request buffer.
*/
sync_req_alg_count = 0;
sync_req_proto_count = 0;
/*
* Replace the last SPD_ATTR_NEXT attribute by a SPD_ATTR_END.
*/
/*
* Now that the message is built, compute its total length and
* update the length fields that depend on this value.
*/
/* ship the update request to spdsock */
if (cnt < 0) {
} else {
}
}
if (cnt == -1) {
}
"algs update failed while reading reply (short read)"));
}
if (msg->spd_msg_errno != 0) {
if (msg->spd_msg_diagnostic != 0) {
}
}
}
static void
list_kernel_algs(void)
{
struct spd_ext_actions *actp;
struct ipsecalgent alg;
char diag_buf[SPDSOCK_DIAG_BUF_LEN];
if (sfd < 0) {
}
if (cnt < 0) {
} else {
}
}
if (cnt == -1) {
}
"dump algs failed while reading reply (short read)"));
}
/*
* No algorithms are defined in the kernel, which caused
* the extension length to be zero, and spdsock_get_ext()
* to fail with a KGE_LEN error. This is not an error
* condition, so we return nicely.
*/
return;
} else if (retval != 0) {
"in dump algs reply (%d)"), retval);
}
gettext("action missing in dump algs reply"));
}
nkey_sizes = nblock_sizes = 0;
(void) printf("Kernel list of algorithms:\n\n");
switch (attr->spd_attr_tag) {
case SPD_ATTR_NOP:
case SPD_ATTR_EMPTY:
break;
case SPD_ATTR_END:
/* FALLTHRU */
case SPD_ATTR_NEXT:
/*
* Note that if the message received from the spdsock
* has a premature SPD_ATTR_END or SPD_ATTR_NEXT, this
* could cause the current algorithm to be only
* partially initialized.
*/
nkey_sizes = nblock_sizes = 0;
break;
case SPD_ATTR_ALG_ID:
break;
case SPD_ATTR_ALG_PROTO:
break;
case SPD_ATTR_ALG_INCRBITS:
break;
case SPD_ATTR_ALG_NKEYSIZES:
"of keys in dump algs reply"));
}
bail_nomem();
cur_key = 0;
break;
case SPD_ATTR_ALG_KEYSIZE:
if (cur_key >= nkey_sizes) {
" in dump algs reply"));
}
break;
case SPD_ATTR_ALG_NBLOCKSIZES:
"of blocks in dump algs reply"));
}
sizeof (int));
bail_nomem();
cur_block = 0;
break;
case SPD_ATTR_ALG_BLOCKSIZE:
if (cur_block >= nblock_sizes) {
"sizes in dump algs reply"));
}
break;
case SPD_ATTR_ALG_NPARAMS:
"of params in dump algs reply"));
}
sizeof (int));
bail_nomem();
cur_block = 0;
break;
case SPD_ATTR_ALG_PARAMS:
"in dump algs reply"));
}
break;
case SPD_ATTR_ALG_FLAGS:
break;
case SPD_ATTR_ALG_MECHNAME: {
char *mech_name;
"duplicate mech name in dump algs reply"));
}
bail_nomem();
break;
}
}
attr++;
}
}
static int *
{
NULL) {
(*num_args)++;
bail_nomem();
usage(); /* Malformed integer list! */
}
return (rc);
}
static void
new_alg(void)
{
struct ipsecalgent newbie;
int i, rc;
/* Parameter reality check... */
if (proto_number == -1) {
if (proto_name == NULL) {
usage();
}
if (proto_number == -1) {
usage();
}
}
if (alg_number == -1) {
usage();
}
if (key_sizes_string == NULL) {
usage();
}
if (alg_names_string == NULL) {
usage();
}
if (block_sizes_string == NULL) {
usage();
}
usage();
}
/*
* The ALG_FLAG_VALID is somewhat irrelevant as an input from the
* user, the kernel will decide if the algorithm description is
* valid or not and set the ALG_FLAG_VALID when the user dumps
* the kernel tables. To avoid confusion when the user dumps the
* contents off the ipsecalgs file, we set the ALG_FLAG_VALID here.
*/
if (!alg_flags) {
usage();
}
}
sizeof (char *) * ((++num_names) + 1));
bail_nomem();
}
/* Extract block sizes. */
/* Extract key sizes. */
/* key sizes by range, key size increment required */
if (newbie.a_key_increment == 0) {
usage();
}
bail_nomem();
*holder = '\0';
holder++;
/*
* At this point, holder points to high, key_sizes_string
* points to low.
*/
usage();
}
usage();
}
/* sanity check key range consistency */
usage();
}
/* check key increment vs key range */
newbie.a_key_increment) != 0) {
" not consistent with key size range"));
usage();
}
/* default key size */
if (default_keylen != 0) {
/* check specified default key size */
if (default_keylen <
((default_keylen -
newbie.a_key_increment) != 0) {
" with key size range"));
usage();
}
} else {
/* min key size in range if not specified */
}
} else {
/* key sizes by enumeration */
if (newbie.a_key_increment != 0) {
"not be specified with key sizes enumeration"));
usage();
}
/* default key size */
if (default_keylen != 0 && default_keylen !=
newbie.a_key_sizes[0]) {
/*
* The default key size is not at the front of the
* list. Swap it with the first element of the list.
*/
for (i = 1; i < num_key_sizes; i++) {
break;
if (i >= num_key_sizes) {
"in list of key sizes"));
usage();
}
}
}
}
/* Call things! */
}
}
static void
new_proto(void)
{
int rc;
!= 0) {
"Cannot add protocol %1$d \"%2$s\": %3$s"), proto_number,
}
}
static void
remove_alg(void)
{
int rc;
if (proto_number == -1) {
if (proto_name == NULL) {
usage();
}
if (proto_number == -1) {
"Unknown protocol \"%s\"."), proto_name);
}
}
if (alg_number == -1) {
if (alg_names_string == NULL) {
}
"Specify a single algorithm name for removal, "
"not a list."));
}
!= 0) {
"Could not remove algorithm %1$s: %2$s"),
}
} else {
"Could not remove algorithm %1$d: %2$s"),
}
}
}
static void
remove_proto(void)
{
int rc;
if (proto_number == -1) {
if (proto_name == NULL) {
usage();
}
"Could not remove protocol %1$s: %2$s"),
}
} else {
"Could not remove protocol %1$d: %2$s"),
}
}
}
static void
set_exec_mode(void)
{
int rc;
if (proto_number == -1) {
if (proto_name == NULL) {
"Please specify protocol name or number."));
usage();
}
if (proto_number == -1) {
}
}
!= 0) {
ipsecalgs_diag(rc));
}
}
/*
* Print a description of an algorithm to standard output.
*/
static void
{
int *ifloater;
char **floater;
/* protocol number */
/* algorithm number */
/* algorithm name(s) */
do {
/* Assume at least one string. */
(void) putchar(',');
(void) putchar('\n');
}
/* mechanism name */
(void) putchar('\n');
/* key sizes */
if (alg->a_key_increment != 0)
/* key specified by range */
"%1$d-%2$d, increment %3$d, default %4$d"),
else
/* key specified by enumeration */
(void) putchar('\n');
/* Alg parameters */
(void) putchar('\n');
/* Alg flags */
(void) putchar('\n');
(void) putchar('\n');
}
/*
* Print the description of a protocol.
*/
static void
{
char *proto_name;
/* protocol name and number */
/* execution mode */
} else {
switch (exec_mode) {
case LIBIPSEC_ALGS_EXEC_SYNC:
(void) printf("sync");
break;
case LIBIPSEC_ALGS_EXEC_ASYNC:
(void) printf("async");
break;
}
}
(void) printf(")\n\n");
}
/*
* Algorithm walker table. Call proto_action() for each protocol,
* and alg_action() for each algorithm.
*/
static void
void (*proto_action)(uint_t))
{
int *proto_nums, proto_count, i;
struct ipsecalgent *alg;
if (proto_nums == NULL) {
}
for (i = 0; i < proto_count; i++) {
if (proto_action != NULL)
proto_action(proto_nums[i]);
}
for (j = 0; j < alg_count; j++) {
NULL);
continue;
if (alg_action != NULL)
}
}
}
/*
* Use just the libnsl/libipsecutil APIs to dump out all of the algorithms.
*/
static void
show_algs(void)
{
/* Yes, I'm aware that this'll produce TWO newlines. */
"List of algorithms, grouped by IPsec protocol:\n"));
}
static int
{
if (rc <= 0) {
usage();
}
return (rc);
}
static void
{
usage();
}
int
{
int c;
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
if (argc == 1) {
show_algs();
return (EXIT_SUCCESS);
}
"aflrRsb:p:P:i:k:K:m:n:N:e:S:M:I:F:")) != EOF) {
switch (c) {
case 'a':
break;
case 'f':
/* multiple occurences of -f are harmless */
break;
case 'l':
break;
case 'r':
break;
case 'R':
break;
case 's':
/* multiple occurences of -s are harmless */
break;
case 'n':
if (alg_names_string != NULL)
usage();
break;
case 'b':
if (block_sizes_string != NULL)
usage();
break;
case 'p':
if (proto_name != NULL)
usage();
proto_name = optarg;
break;
case 'P':
if (proto_number != -1)
usage();
gettext("protocol number"));
break;
case 'e':
if (exec_mode_string != NULL)
usage();
&proto_exec_mode) == -1) {
usage();
}
break;
case 'i':
if (increment != 0)
usage();
gettext("key size increment"));
break;
case 'k':
if (key_sizes_string != NULL)
usage();
break;
case 'K':
if (default_keylen != 0)
usage();
gettext("default key size"));
break;
case 'm':
usage();
break;
case 'N':
if (alg_number != -1)
usage();
gettext("algorithm number"));
break;
case 'I':
if (mech_params[iv_len] != 0)
usage();
gettext("Initialization Vector length"));
break;
case 'M':
if (mech_params[mac_len] != 0)
usage();
gettext("Integrity Check Vector length"));
break;
case 'S':
if (mech_params[salt_bytes] != 0)
usage();
gettext("Salt length"));
break;
case 'F':
/*
* Multiple flags can be specified, the results
* are OR'd together. Flags can be specified as
* number or a comma separated string
*/
if (flags) {
flag_string = NULL;
} else {
}
break;
default:
usage();
}
}
/*
* When both protocol name (-p) and protocol number (-P) are
* specified, a new protocol is being defined.
*/
else if (exec_mode_string != NULL)
/*
* Process specified command.
*/
switch (cmd) {
case CMD_ADD:
new_alg();
break;
case CMD_ADD_PROTO:
new_proto();
break;
case CMD_DEL:
remove_alg();
break;
case CMD_DEL_PROTO:
remove_proto();
break;
case CMD_EXEC_MODE:
break;
case CMD_LIST_KERNEL:
if (synch_kernel)
usage();
break;
default:
if (!synch_kernel)
usage();
}
if (synch_kernel) {
/*
* This will only work in the global zone or
* a zone with an exclusive IP stack.
*/
kernel_synch();
} else {
sizeof (flags)) < 0) {
"Unable to determine zone IP type"));
}
if (flags & ZF_NET_EXCL) {
kernel_synch();
} else {
"kernel not appropriate in this zone.\n"));
}
}
}
return (EXIT_SUCCESS);
}