/* * 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 * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #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, alg_flags = 0; static char *proto_name, *alg_names_string, *block_sizes_string; static char *key_sizes_string, *mech_name, *exec_mode_string; static char *flag_string; static ipsecalgs_exec_mode_t proto_exec_mode = LIBIPSEC_ALGS_EXEC_SYNC; enum param_values {iv_len, mac_len, salt_bytes, max_param}; static int mech_params[max_param]; /* * Used by the algorithm walker callback to populate a SPD_UPDATEALGS * request. */ #define SYNC_REQ_SIZE 4096 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 int parse_flag(char *flag_str, uint_t flag) { 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} }; struct flagtable *ft = table; if (flag_str == NULL) { /* Print out flag labels for each flag set. */ if ((ALG_FLAG_KERNELCHECKED & flag) && !(ALG_FLAG_VALID & flag)) (void) printf("INVALID "); while (ft->token != 0) { if (ft->token & flag) { (void) printf("%s ", ft->label); } ft++; } return (0); } /* Or, lookup flag for supplied label. */ while (ft->label != NULL && strcmp(ft->label, flag_str) != 0) ft++; return (ft->token); } 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\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) { 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; uint_t nparams; 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); nparams = num_sizes(alg->a_mech_params); 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_NPARAMS, nparams); for (i = 0; i < nparams; i++) { EMIT(sync_req_attr, SPD_ATTR_ALG_PARAMS, alg->a_mech_params[i]); } EMIT(sync_req_attr, SPD_ATTR_ALG_FLAGS, alg->a_alg_flags); 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, nparams; 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. */ alg.a_alg_flags |= ALG_FLAG_KERNELCHECKED; dump_alg(&alg); free(alg.a_key_sizes); free(alg.a_block_sizes); free(alg.a_mech_name); free(alg.a_mech_params); 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_NPARAMS: nparams = attr->spd_attr_value; if (alg.a_mech_params != NULL) { errx(EXIT_FAILURE, gettext("duplicate number " "of params in dump algs reply")); } alg.a_mech_params = calloc(nparams + 1, sizeof (int)); if (alg.a_mech_params == NULL) bail_nomem(); cur_block = 0; break; case SPD_ATTR_ALG_PARAMS: if (cur_block >= nparams) { errx(EXIT_FAILURE, gettext("too many params " "in dump algs reply")); } alg.a_mech_params[cur_block++] = attr->spd_attr_value; break; case SPD_ATTR_ALG_FLAGS: alg.a_alg_flags = 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_alg_flags = alg_flags; /* * 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. */ newbie.a_alg_flags |= ALG_FLAG_VALID; while ((holder = strtok((holder == NULL) ? flag_string : NULL, comma)) != NULL) { alg_flags = parse_flag(holder, 0); if (!alg_flags) { warnx(gettext("Invalid flag: %s\n"), holder); usage(); } newbie.a_alg_flags |= alg_flags; } 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); newbie.a_mech_params = &mech_params[0]; /* 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'); /* Alg parameters */ (void) printf(gettext("\tAlgorithm parameters: ")); ifloater = alg->a_mech_params; (void) list_ints(stdout, ifloater); (void) putchar('\n'); /* Alg flags */ (void) printf(gettext("\tAlgorithm flags: ")); (void) parse_flag(NULL, alg->a_alg_flags); (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("")); /* execution mode */ (void) printf("(%s", gettext("execution mode: ")); if (ipsecproto_get_exec_mode(proto_id, &exec_mode) != 0) { (void) printf(gettext("")); } 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; zoneid_t zoneid; ushort_t flags; (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:S:M:I:F:")) != 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; case 'I': if (mech_params[iv_len] != 0) usage(); mech_params[iv_len] = try_int(optarg, gettext("Initialization Vector length")); break; case 'M': if (mech_params[mac_len] != 0) usage(); mech_params[mac_len] = try_int(optarg, gettext("Integrity Check Vector length")); break; case 'S': if (mech_params[salt_bytes] != 0) usage(); mech_params[salt_bytes] = try_int(optarg, 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 */ flags = atoi(optarg); if (flags) { alg_flags |= flags; flag_string = NULL; } else { flag_string = optarg; } break; default: usage(); } } /* * 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) { /* * This will only work in the global zone or * a zone with an exclusive IP stack. */ if ((zoneid = getzoneid()) == 0) { kernel_synch(); } else { if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &flags, sizeof (flags)) < 0) { err(EXIT_FAILURE, gettext( "Unable to determine zone IP type")); } if (flags & ZF_NET_EXCL) { kernel_synch(); } else { (void) printf(gettext("Synchronization with " "kernel not appropriate in this zone.\n")); } } } return (EXIT_SUCCESS); }