ipsecalgs.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* or http://www.opensolaris.org/os/licensing.
* 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 2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#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 <sys/crypto/common.h>
#define SPDSOCK_DIAG_BUF_LEN 128
typedef enum cmd_s {
CMD_NONE = 0,
CMD_ADD,
CMD_ADD_PROTO,
CMD_DEL,
CMD_DEL_PROTO,
CMD_EXEC_MODE,
CMD_LIST_KERNEL
} cmd_t;
static const char *comma = ",";
static int adddel_flags, increment = 0, default_keylen;
static boolean_t synch_kernel;
static cmd_t cmd = CMD_NONE;
static int proto_number = -1, alg_number = -1;
static char *proto_name, *alg_names_string, *block_sizes_string;
static char *key_sizes_string, *mech_name, *exec_mode_string;
static ipsecalgs_exec_mode_t proto_exec_mode = LIBIPSEC_ALGS_EXEC_SYNC;
/*
* Used by the algorithm walker callback to populate a SPD_UPDATEALGS
* request.
*/
#define SYNC_REQ_SIZE 2048
static uint64_t sync_req_buf[SYNC_REQ_SIZE];
static struct spd_attribute *sync_req_attr;
static uint_t sync_req_alg_count, sync_req_proto_count;
#define EMIT(ap, tag, value) { \
(ap)->spd_attr_tag = (tag); \
(ap)->spd_attr_value = (value); \
(ap)++; \
if ((char *)(ap) + sizeof (*ap) - \
(char *)sync_req_buf > SYNC_REQ_SIZE) \
bail_nomem(); \
}
static void dump_alg(struct ipsecalgent *);
static void algs_walker(void (*)(struct ipsecalgent *), void (*)(uint_t));
static void
usage(void)
{
errx(EXIT_FAILURE, gettext("Usage:\tipsecalgs\n"
"\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 [-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)
{
errx(EXIT_FAILURE, gettext("Out of memory."));
}
/*
* Return the number of key or block sizes in the specified array.
*/
static uint_t
num_sizes(int *sizes)
{
uint_t nsizes = 0;
while (sizes[nsizes] != 0)
nsizes++;
return (nsizes);
}
/*
* Algorithms walker callback. Adds an algorithm to the current SPD_UPDATEALGS
* request.
*/
static void
synch_emit_alg(struct ipsecalgent *alg)
{
uint_t nkey_sizes, nblock_sizes, i;
EMIT(sync_req_attr, SPD_ATTR_ALG_ID, alg->a_alg_num);
EMIT(sync_req_attr, SPD_ATTR_ALG_PROTO, alg->a_proto_num);
EMIT(sync_req_attr, SPD_ATTR_ALG_INCRBITS, alg->a_key_increment);
nkey_sizes = num_sizes(alg->a_key_sizes);
EMIT(sync_req_attr, SPD_ATTR_ALG_NKEYSIZES, nkey_sizes);
for (i = 0; i < nkey_sizes; i++)
EMIT(sync_req_attr, SPD_ATTR_ALG_KEYSIZE, alg->a_key_sizes[i]);
nblock_sizes = num_sizes(alg->a_block_sizes);
EMIT(sync_req_attr, SPD_ATTR_ALG_NBLOCKSIZES, nblock_sizes);
for (i = 0; i < nblock_sizes; i++) {
EMIT(sync_req_attr, SPD_ATTR_ALG_BLOCKSIZE,
alg->a_block_sizes[i]);
}
EMIT(sync_req_attr, SPD_ATTR_ALG_MECHNAME, CRYPTO_MAX_MECH_NAME);
(void) strncpy((char *)sync_req_attr, alg->a_mech_name,
CRYPTO_MAX_MECH_NAME);
sync_req_attr = (struct spd_attribute *)((uint64_t *)sync_req_attr +
SPD_8TO64(CRYPTO_MAX_MECH_NAME));
EMIT(sync_req_attr, SPD_ATTR_NEXT, 0);
sync_req_alg_count++;
}
/*
* Protocol walker callback. Add protocol related info to the current
* SPD_UPDATEALGS request.
*/
static void
synch_emit_proto(uint_t proto_num)
{
ipsecalgs_exec_mode_t exec_mode;
uint32_t exec_mode_spdval;
EMIT(sync_req_attr, SPD_ATTR_PROTO_ID, proto_num);
/* execution mode */
if (ipsecproto_get_exec_mode(proto_num, &exec_mode) != 0) {
errx(EXIT_FAILURE, gettext("cannot get execution mode for "
"proto %d"), proto_num);
}
switch (exec_mode) {
case LIBIPSEC_ALGS_EXEC_SYNC:
exec_mode_spdval = SPD_ALG_EXEC_MODE_SYNC;
break;
case LIBIPSEC_ALGS_EXEC_ASYNC:
exec_mode_spdval = SPD_ALG_EXEC_MODE_ASYNC;
break;
}
EMIT(sync_req_attr, SPD_ATTR_PROTO_EXEC_MODE, exec_mode_spdval);
EMIT(sync_req_attr, SPD_ATTR_NEXT, 0);
sync_req_proto_count++;
}
/*
* Causes the kernel to be re-synched with the contents of /etc/inet/algs
*/
static void
kernel_synch(void)
{
int sfd = socket(PF_POLICY, SOCK_RAW, PF_POLICY_V1);
int cnt, req_len;
struct spd_msg *msg;
struct spd_ext_actions *act;
struct spd_attribute *attr;
if (sfd < 0) {
err(EXIT_FAILURE, gettext("Unable to open policy socket"));
}
/*
* Initialize the SPD message header and action. Some fields
* are set after having walked through the algorithms (number
* of algorithms, sizes, etc.)
*/
msg = (struct spd_msg *)sync_req_buf;
(void) memset(msg, 0, sizeof (*msg));
msg->spd_msg_version = PF_POLICY_V1;
msg->spd_msg_type = SPD_UPDATEALGS;
act = (struct spd_ext_actions *)(msg + 1);
act->spd_actions_exttype = SPD_EXT_ACTION;
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;
sync_req_attr = (struct spd_attribute *)(act + 1);
algs_walker(synch_emit_alg, synch_emit_proto);
act->spd_actions_count = sync_req_alg_count + sync_req_proto_count;
/*
* Replace the last SPD_ATTR_NEXT attribute by a SPD_ATTR_END.
*/
attr = sync_req_attr - 1;
attr->spd_attr_tag = SPD_ATTR_END;
/*
* Now that the message is built, compute its total length and
* update the length fields that depend on this value.
*/
req_len = (char *)sync_req_attr - (char *)sync_req_buf;
msg->spd_msg_len = SPD_8TO64(req_len);
act->spd_actions_len = SPD_8TO64(req_len - sizeof (*msg));
/* ship the update request to spdsock */
cnt = write(sfd, sync_req_buf, req_len);
if (cnt != req_len) {
if (cnt < 0) {
err(EXIT_FAILURE, gettext("algs update write failed"));
} else {
errx(EXIT_FAILURE, gettext("algs update short write"));
}
/* err/errx call exit(). */
}
cnt = read(sfd, sync_req_buf, req_len);
if (cnt == -1) {
err(EXIT_FAILURE, gettext("algs update read failed"));
}
if (cnt < sizeof (struct spd_msg)) {
errx(EXIT_FAILURE, gettext(
"algs update failed while reading reply (short read)"));
}
msg = (struct spd_msg *)sync_req_buf;
if (msg->spd_msg_errno != 0) {
errno = msg->spd_msg_errno;
warn(gettext("algs update failed"));
if (msg->spd_msg_diagnostic != 0) {
warnx("%s", spdsock_diag(msg->spd_msg_diagnostic));
}
exit(EXIT_FAILURE);
}
(void) close(sfd);
}
static void
list_kernel_algs(void)
{
int sfd = socket(PF_POLICY, SOCK_RAW, PF_POLICY_V1);
int cnt, retval;
uint64_t reply_buf[2048];
spd_ext_t *exts[SPD_EXT_MAX+1];
struct spd_msg msg;
struct spd_ext_actions *actp;
struct spd_attribute *attr, *endattr;
uint64_t *start, *end;
struct ipsecalgent alg;
uint_t cur_key, cur_block;
uint_t nkey_sizes, nblock_sizes;
char diag_buf[SPDSOCK_DIAG_BUF_LEN];
if (sfd < 0) {
err(EXIT_FAILURE, gettext("Unable to open policy socket"));
}
(void) memset(&msg, 0, sizeof (msg));
msg.spd_msg_version = PF_POLICY_V1;
msg.spd_msg_type = SPD_DUMPALGS;
msg.spd_msg_len = SPD_8TO64(sizeof (msg));
cnt = write(sfd, &msg, sizeof (msg));
if (cnt != sizeof (msg)) {
if (cnt < 0) {
err(EXIT_FAILURE, gettext("dump algs write failed"));
} else {
errx(EXIT_FAILURE, gettext("dump algs short write"));
}
/* err/errx call exit(). */
}
cnt = read(sfd, reply_buf, sizeof (reply_buf));
if (cnt == -1) {
err(EXIT_FAILURE, gettext("dump algs read failed"));
}
if (cnt < sizeof (struct spd_msg)) {
errx(EXIT_FAILURE, gettext(
"dump algs failed while reading reply (short read)"));
}
(void) close(sfd);
retval = spdsock_get_ext(exts, (spd_msg_t *)reply_buf, SPD_8TO64(cnt),
diag_buf, SPDSOCK_DIAG_BUF_LEN);
if (retval == KGE_LEN && exts[0]->spd_ext_len == 0) {
/*
* 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) {
if (strlen(diag_buf) != 0)
warnx("%s", diag_buf);
errx(EXIT_FAILURE, gettext("invalid extension "
"in dump algs reply (%d)"), retval);
}
if (exts[SPD_EXT_ACTION] == NULL) {
errx(EXIT_FAILURE,
gettext("action missing in dump algs reply"));
}
actp = (struct spd_ext_actions *)exts[SPD_EXT_ACTION];
start = (uint64_t *)actp;
end = (start + actp->spd_actions_len);
endattr = (struct spd_attribute *)end;
attr = (struct spd_attribute *)&actp[1];
bzero(&alg, sizeof (alg));
nkey_sizes = nblock_sizes = 0;
(void) printf("Kernel list of algorithms:\n\n");
while (attr < endattr) {
switch (attr->spd_attr_tag) {
case SPD_ATTR_NOP:
case SPD_ATTR_EMPTY:
break;
case SPD_ATTR_END:
attr = endattr;
/* 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.
*/
dump_alg(&alg);
free(alg.a_key_sizes);
free(alg.a_block_sizes);
free(alg.a_mech_name);
bzero(&alg, sizeof (alg));
nkey_sizes = nblock_sizes = 0;
break;
case SPD_ATTR_ALG_ID:
alg.a_alg_num = attr->spd_attr_value;
break;
case SPD_ATTR_ALG_PROTO:
alg.a_proto_num = attr->spd_attr_value;
break;
case SPD_ATTR_ALG_INCRBITS:
alg.a_key_increment = attr->spd_attr_value;
break;
case SPD_ATTR_ALG_NKEYSIZES:
nkey_sizes = attr->spd_attr_value;
if (alg.a_key_sizes != NULL) {
errx(EXIT_FAILURE, gettext("duplicate number "
"of keys in dump algs reply"));
}
alg.a_key_sizes = calloc(nkey_sizes + 1, sizeof (int));
if (alg.a_key_sizes == NULL)
bail_nomem();
cur_key = 0;
break;
case SPD_ATTR_ALG_KEYSIZE:
if (cur_key >= nkey_sizes) {
errx(EXIT_FAILURE, gettext("too many key sizes"
" in dump algs reply"));
}
alg.a_key_sizes[cur_key++] = attr->spd_attr_value;
break;
case SPD_ATTR_ALG_NBLOCKSIZES:
nblock_sizes = attr->spd_attr_value;
if (alg.a_block_sizes != NULL) {
errx(EXIT_FAILURE, gettext("duplicate number "
"of blocks in dump algs reply"));
}
alg.a_block_sizes = calloc(nblock_sizes + 1,
sizeof (int));
if (alg.a_block_sizes == NULL)
bail_nomem();
cur_block = 0;
break;
case SPD_ATTR_ALG_BLOCKSIZE:
if (cur_block >= nblock_sizes) {
errx(EXIT_FAILURE, gettext("too many block "
"sizes in dump algs reply"));
}
alg.a_block_sizes[cur_block++] = attr->spd_attr_value;
break;
case SPD_ATTR_ALG_MECHNAME: {
char *mech_name;
if (alg.a_mech_name != NULL) {
errx(EXIT_FAILURE, gettext(
"duplicate mech name in dump algs reply"));
}
alg.a_mech_name = malloc(attr->spd_attr_value);
if (alg.a_mech_name == NULL)
bail_nomem();
mech_name = (char *)(attr + 1);
bcopy(mech_name, alg.a_mech_name, attr->spd_attr_value);
attr = (struct spd_attribute *)((uint64_t *)attr +
SPD_8TO64(attr->spd_attr_value));
break;
}
}
attr++;
}
}
static int *
parse_intlist(char *args, int *num_args)
{
int *rc = NULL;
char *holder = NULL;
while ((holder = strtok((holder == NULL) ? args : NULL, comma)) !=
NULL) {
(*num_args)++;
rc = realloc(rc, ((*num_args) + 1) * sizeof (int));
if (rc == NULL)
bail_nomem();
rc[(*num_args) - 1] = atoi(holder);
if (rc[(*num_args) - 1] == 0)
usage(); /* Malformed integer list! */
rc[*num_args] = 0;
}
return (rc);
}
static void
new_alg(void)
{
struct ipsecalgent newbie;
int num_names = 0, num_block_sizes = 0, num_key_sizes = 0;
int i, rc;
char *holder = NULL;
/* Parameter reality check... */
if (proto_number == -1) {
if (proto_name == NULL) {
warnx(gettext("Missing protocol number."));
usage();
}
proto_number = getipsecprotobyname(proto_name);
if (proto_number == -1) {
warnx(gettext("Unknown protocol."));
usage();
}
}
if (alg_number == -1) {
warnx(gettext("Missing algorithm number."));
usage();
}
if (key_sizes_string == NULL) {
warnx(gettext("Missing key size(s)."));
usage();
}
if (alg_names_string == NULL) {
warnx(gettext("Missing algorithm name(s)."));
usage();
}
if (block_sizes_string == NULL) {
warnx(gettext("Missing block/MAC lengths"));
usage();
}
if (mech_name == NULL) {
warnx(gettext("Missing mechanism name."));
usage();
}
newbie.a_proto_num = proto_number;
newbie.a_alg_num = alg_number;
newbie.a_key_increment = increment;
newbie.a_mech_name = mech_name;
newbie.a_names = NULL;
while ((holder = strtok((holder == NULL) ? alg_names_string : NULL,
comma)) != NULL) {
newbie.a_names = realloc(newbie.a_names,
sizeof (char *) * ((++num_names) + 1));
if (newbie.a_names == NULL)
bail_nomem();
newbie.a_names[num_names - 1] = holder;
newbie.a_names[num_names] = NULL;
}
/* Extract block sizes. */
newbie.a_block_sizes = parse_intlist(block_sizes_string,
&num_block_sizes);
/* Extract key sizes. */
if ((holder = strchr(key_sizes_string, '-')) != NULL) {
/* key sizes by range, key size increment required */
if (newbie.a_key_increment == 0) {
warnx(gettext("Missing key increment"));
usage();
}
newbie.a_key_sizes = calloc(sizeof (int),
LIBIPSEC_ALGS_KEY_NUM_VAL);
if (newbie.a_key_sizes == NULL)
bail_nomem();
*holder = '\0';
holder++;
/*
* At this point, holder points to high, key_sizes_string
* points to low.
*/
newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX] =
atoi(key_sizes_string);
if (newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX] == 0) {
warnx(gettext("Invalid lower key size range"));
usage();
}
newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX] = atoi(holder);
if (newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX] == 0) {
warnx(gettext("Invalid higher key size range"));
usage();
}
/* sanity check key range consistency */
if (newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX] >=
newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX]) {
warnx(gettext("Invalid key size range (min >= max)"));
usage();
}
/* check key increment vs key range */
if (((newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX] -
newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX]) %
newbie.a_key_increment) != 0) {
warnx(gettext("Key size increment"
" not consistent with key size range"));
usage();
}
/* default key size */
if (default_keylen != 0) {
/* check specified default key size */
if (default_keylen <
newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX] ||
default_keylen >
newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX] ||
((default_keylen -
newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX]) %
newbie.a_key_increment) != 0) {
warnx(gettext("Default key size not consistent"
" with key size range"));
usage();
}
newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_DEF_IDX] =
default_keylen;
} else {
/* min key size in range if not specified */
newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_DEF_IDX] =
newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX];
}
} else {
/* key sizes by enumeration */
if (newbie.a_key_increment != 0) {
warnx(gettext("Key increment must "
"not be specified with key sizes enumeration"));
usage();
}
newbie.a_key_sizes = parse_intlist(key_sizes_string,
&num_key_sizes);
/* 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++) {
if (newbie.a_key_sizes[i] == default_keylen)
break;
if (i >= num_key_sizes) {
warnx(gettext("Default key size not "
"in list of key sizes"));
usage();
}
newbie.a_key_sizes[i] = newbie.a_key_sizes[0];
newbie.a_key_sizes[0] = default_keylen;
}
}
}
/* Call things! */
if ((rc = addipsecalg(&newbie, adddel_flags)) != 0) {
errx(EXIT_FAILURE, gettext("addipsecalg() call failed: "
"%s"), ipsecalgs_diag(rc));
}
free(newbie.a_names);
free(newbie.a_block_sizes);
free(newbie.a_key_sizes);
}
static void
new_proto(void)
{
int rc;
if ((rc = addipsecproto(proto_name, proto_number, proto_exec_mode,
adddel_flags))
!= 0) {
errx(EXIT_FAILURE, gettext(
"Cannot add protocol %1$d \"%2$s\": %3$s"), proto_number,
proto_name, ipsecalgs_diag(rc));
}
}
static void
remove_alg(void)
{
int rc;
if (proto_number == -1) {
if (proto_name == NULL) {
warnx(gettext("Missing protocol number."));
usage();
}
proto_number = getipsecprotobyname(proto_name);
if (proto_number == -1) {
errx(EXIT_FAILURE, gettext(
"Unknown protocol \"%s\"."), proto_name);
}
}
if (alg_number == -1) {
if (alg_names_string == NULL) {
errx(EXIT_FAILURE, gettext("Missing algorithm ID."));
}
if (strchr(alg_names_string, ',') != NULL) {
errx(EXIT_FAILURE, gettext(
"Specify a single algorithm name for removal, "
"not a list."));
}
if ((rc = delipsecalgbyname(alg_names_string, proto_number))
!= 0) {
errx(EXIT_FAILURE, gettext(
"Could not remove algorithm %1$s: %2$s"),
alg_names_string, ipsecalgs_diag(rc));
}
} else {
if ((rc = delipsecalgbynum(alg_number, proto_number)) != 0) {
errx(EXIT_FAILURE, gettext(
"Could not remove algorithm %1$d: %2$s"),
alg_number, ipsecalgs_diag(rc));
}
}
}
static void
remove_proto(void)
{
int rc;
if (proto_number == -1) {
if (proto_name == NULL) {
warnx(gettext("Please specify protocol to remove."));
usage();
}
if ((rc = delipsecprotobyname(proto_name)) != 0) {
errx(EXIT_FAILURE, gettext(
"Could not remove protocol %1$s: %2$s"),
proto_name, ipsecalgs_diag(rc));
}
} else {
if ((rc = delipsecprotobynum(proto_number)) != 0) {
errx(EXIT_FAILURE, gettext(
"Could not remove protocol %1$d: %2$s"),
proto_number, ipsecalgs_diag(rc));
}
}
}
static void
set_exec_mode(void)
{
int rc;
if (proto_number == -1) {
if (proto_name == NULL) {
warnx(gettext(
"Please specify protocol name or number."));
usage();
}
proto_number = getipsecprotobyname(proto_name);
if (proto_number == -1) {
errx(EXIT_FAILURE, gettext("Unknown protocol %s"),
proto_name);
}
}
if ((rc = ipsecproto_set_exec_mode(proto_number, proto_exec_mode))
!= 0) {
errx(EXIT_FAILURE, gettext("Cannot set execution mode: %s"),
ipsecalgs_diag(rc));
}
}
/*
* Print a description of an algorithm to standard output.
*/
static void
dump_alg(struct ipsecalgent *alg)
{
int *ifloater;
char **floater;
/* protocol number */
(void) printf(gettext("\tProtocol number: %d\n"), alg->a_proto_num);
/* algorithm number */
(void) printf(gettext("\tAlgorithm number: %d\n"), alg->a_alg_num);
/* algorithm name(s) */
if (alg->a_names != NULL) {
(void) printf(gettext("\tAlgorithm names: "));
floater = alg->a_names;
assert(floater != NULL && *floater != NULL);
do {
/* Assume at least one string. */
(void) printf("%s", *floater);
if (*(++floater) != NULL)
(void) putchar(',');
} while (*floater != NULL);
(void) putchar('\n');
}
/* mechanism name */
(void) printf(gettext("\tMechanism Name: %s\n"), alg->a_mech_name);
/* block/MAC sizes */
(void) printf(gettext("\tBlock sizes or MAC sizes: "));
ifloater = alg->a_block_sizes;
(void) list_ints(stdout, ifloater);
(void) putchar('\n');
/* key sizes */
(void) printf(gettext("\tKey sizes: "));
if (alg->a_key_increment != 0)
/* key specified by range */
(void) printf(gettext(
"%1$d-%2$d, increment %3$d, default %4$d"),
alg->a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX],
alg->a_key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX],
alg->a_key_increment,
alg->a_key_sizes[LIBIPSEC_ALGS_KEY_DEF_IDX]);
else
/* key specified by enumeration */
(void) list_ints(stdout, alg->a_key_sizes);
(void) putchar('\n');
(void) putchar('\n');
}
/*
* Print the description of a protocol.
*/
static void
dump_proto(uint_t proto_id)
{
char *proto_name;
ipsecalgs_exec_mode_t exec_mode;
/* protocol name and number */
proto_name = getipsecprotobynum(proto_id);
(void) printf(gettext("Protocol %1$d/%2$s "),
proto_id, proto_name != NULL ? proto_name : gettext("<unknown>"));
/* execution mode */
(void) printf("(%s", gettext("execution mode: "));
if (ipsecproto_get_exec_mode(proto_id, &exec_mode) != 0) {
(void) printf(gettext("<unknown>"));
} 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");
free(proto_name);
}
/*
* Algorithm walker table. Call proto_action() for each protocol,
* and alg_action() for each algorithm.
*/
static void
algs_walker(void (*alg_action)(struct ipsecalgent *),
void (*proto_action)(uint_t))
{
int *proto_nums, proto_count, i;
int *alg_nums, alg_count, j;
struct ipsecalgent *alg;
proto_nums = getipsecprotos(&proto_count);
if (proto_nums == NULL) {
errx(EXIT_FAILURE, gettext("getipsecprotos() failed."));
}
for (i = 0; i < proto_count; i++) {
if (proto_action != NULL)
proto_action(proto_nums[i]);
alg_nums = getipsecalgs(&alg_count, proto_nums[i]);
if (alg_nums == NULL) {
free(proto_nums);
errx(EXIT_FAILURE, gettext("getipsecalgs() failed."));
}
for (j = 0; j < alg_count; j++) {
alg = getipsecalgbynum(alg_nums[j], proto_nums[i],
NULL);
if (alg == NULL)
continue;
if (alg_action != NULL)
alg_action(alg);
freeipsecalgent(alg);
}
free(alg_nums);
}
free(proto_nums);
}
/*
* 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. */
(void) puts(gettext(
"List of algorithms, grouped by IPsec protocol:\n"));
algs_walker(dump_alg, dump_proto);
}
static int
try_int(char *optarg, const char *what)
{
int rc = atoi(optarg);
if (rc <= 0) {
warnx(gettext("Invalid %s value"), what);
usage();
}
return (rc);
}
static void
try_cmd(cmd_t newcmd)
{
if (cmd != CMD_NONE)
usage();
cmd = newcmd;
}
int
main(int argc, char *argv[])
{
int c;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
if (argc == 1) {
show_algs();
return (EXIT_SUCCESS);
}
while ((c = getopt(argc, argv,
"aflrRsb:p:P:i:k:K:m:n:N:e:")) != EOF) {
switch (c) {
case 'a':
try_cmd(CMD_ADD);
break;
case 'f':
/* multiple occurences of -f are harmless */
adddel_flags = LIBIPSEC_ALGS_ADD_FORCE;
break;
case 'l':
try_cmd(CMD_LIST_KERNEL);
break;
case 'r':
try_cmd(CMD_DEL);
break;
case 'R':
try_cmd(CMD_DEL_PROTO);
break;
case 's':
/* multiple occurences of -s are harmless */
synch_kernel = B_TRUE;
break;
case 'n':
if (alg_names_string != NULL)
usage();
alg_names_string = optarg;
break;
case 'b':
if (block_sizes_string != NULL)
usage();
block_sizes_string = optarg;
break;
case 'p':
if (proto_name != NULL)
usage();
proto_name = optarg;
break;
case 'P':
if (proto_number != -1)
usage();
proto_number = try_int(optarg,
gettext("protocol number"));
break;
case 'e':
if (exec_mode_string != NULL)
usage();
exec_mode_string = optarg;
if (_str_to_ipsec_exec_mode(exec_mode_string,
&proto_exec_mode) == -1) {
warnx(gettext("Invalid execution mode \"%s\""),
exec_mode_string);
usage();
}
break;
case 'i':
if (increment != 0)
usage();
increment = try_int(optarg,
gettext("key size increment"));
break;
case 'k':
if (key_sizes_string != NULL)
usage();
key_sizes_string = optarg;
break;
case 'K':
if (default_keylen != 0)
usage();
default_keylen = try_int(optarg,
gettext("default key size"));
break;
case 'm':
if (mech_name != NULL)
usage();
mech_name = optarg;
break;
case 'N':
if (alg_number != -1)
usage();
alg_number = try_int(optarg,
gettext("algorithm number"));
break;
}
}
/*
* When both protocol name (-p) and protocol number (-P) are
* specified, a new protocol is being defined.
*/
if (proto_number != -1 && proto_name != NULL)
try_cmd(CMD_ADD_PROTO);
else if (exec_mode_string != NULL)
try_cmd(CMD_EXEC_MODE);
/*
* 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:
set_exec_mode();
break;
case CMD_LIST_KERNEL:
if (synch_kernel)
usage();
list_kernel_algs();
break;
default:
if (!synch_kernel)
usage();
}
if (synch_kernel)
kernel_synch();
return (EXIT_SUCCESS);
}