summaryrefslogtreecommitdiff
path: root/usr/src/lib/libipsecutil/common/algs.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libipsecutil/common/algs.c
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libipsecutil/common/algs.c')
-rw-r--r--usr/src/lib/libipsecutil/common/algs.c813
1 files changed, 813 insertions, 0 deletions
diff --git a/usr/src/lib/libipsecutil/common/algs.c b/usr/src/lib/libipsecutil/common/algs.c
new file mode 100644
index 0000000000..9027fc23d4
--- /dev/null
+++ b/usr/src/lib/libipsecutil/common/algs.c
@@ -0,0 +1,813 @@
+/*
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ipsec_util.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <errno.h>
+
+static char *preamble =
+"# /etc/inet/ipsecalgs output from ipsecalgs(1m)\n"
+"#\n"
+"# DO NOT EDIT OR PARSE THIS FILE!\n"
+"#\n"
+"# Use the ipsecalgs(1m) command to change the contents of this file.\n"
+"\n";
+
+#define CFG_PERMS S_IRUSR | S_IRGRP | S_IROTH /* Perms 0444. */
+#define CFG_OWNER 0 /* root */
+#define CFG_GROUP 1 /* "other" */
+
+/*
+ * write_new_algfile() helper macros to check for write errors.
+ */
+
+#define FPRINTF_ERR(fcall) if ((fcall) < 0) { \
+ rc = LIBIPSEC_ALGS_DIAG_ALGSFILEWRITE; \
+ goto bail; \
+}
+
+#define FPUT_ERR(fcall) if ((fcall) == EOF) { \
+ rc = LIBIPSEC_ALGS_DIAG_ALGSFILEWRITE; \
+ goto bail; \
+}
+
+/*
+ * Helper macros to start and finish a list of entries that were added
+ * as part of a package installation.
+ */
+
+#define PKG_SEC_START(pkgname, doing_pkg, cur_pkg) { \
+ (void) strcpy((cur_pkg), (pkgname)); \
+ FPRINTF_ERR(fprintf(f, "%s%s\n", \
+ LIBIPSEC_ALGS_LINE_PKGSTART, (cur_pkg))); \
+ (doing_pkg) = B_TRUE; \
+}
+
+#define PKG_SEC_END(doing_pkg, cur_pkg) { \
+ if (doing_pkg) { \
+ FPRINTF_ERR(fprintf(f, "%s%s\n", \
+ LIBIPSEC_ALGS_LINE_PKGEND, (cur_pkg))); \
+ (doing_pkg) = B_FALSE; \
+ } \
+}
+
+/*
+ * Take a zero-terminated int array and print int1,int2...,intN.
+ * If zero-only, then print a single '0'.
+ * Returns 0 on success, -1 if an error occurred while writing to
+ * the specified file.
+ */
+int
+list_ints(FILE *f, int *floater)
+{
+ boolean_t executed = B_FALSE;
+
+ while (*floater != 0) {
+ executed = B_TRUE;
+ if (fprintf(f, "%d", *floater) < 0)
+ return (-1);
+ if (*(++floater) != 0)
+ if (fputc(',', f) == EOF)
+ return (-1);
+ }
+
+ if (!executed)
+ if (fputc('0', f) == EOF)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * If the specified algorithm was defined within a package section, i.e.
+ * between the lines "# Start <pkgname>" and "# End <pkgname>", returns
+ * the value of <pkgname>.
+ */
+static char *
+alg_has_pkg(ipsec_proto_t *proto, struct ipsecalgent *alg)
+{
+ int i;
+
+ if (proto->proto_algs_pkgs == NULL)
+ return (NULL);
+
+ for (i = 0; i < proto->proto_algs_npkgs; i++)
+ if (proto->proto_algs_pkgs[i].alg_num == alg->a_alg_num)
+ return (proto->proto_algs_pkgs[i].pkg_name);
+
+ return (NULL);
+}
+
+/*
+ * Writes the package start/end delimiters according to the package
+ * name associated with the current protocol or algorithm, and
+ * the state of the packaging information already written to the file.
+ * Called by write_new_algfile(). Returns 0 on success, one of the
+ * LIBIPSEC_DIAG codes on failure.
+ */
+static int
+pkg_section(FILE *f, char *pkg_name, boolean_t *doing_pkg, char *cur_pkg)
+{
+ int rc = 0;
+
+ if (pkg_name != NULL) {
+ /* protocol or algorithm is associated with a package */
+ if (!*doing_pkg) {
+ /* start of a new package section */
+ PKG_SEC_START(pkg_name, *doing_pkg, cur_pkg);
+ } else {
+ /* already in a package section */
+ if (strcmp(pkg_name, cur_pkg) != 0) {
+ /* different package name */
+ PKG_SEC_END(*doing_pkg, cur_pkg);
+ PKG_SEC_START(pkg_name, *doing_pkg, cur_pkg);
+ }
+ }
+ } else if (*doing_pkg) {
+ /* in a package section when the entry isn't */
+ PKG_SEC_END(*doing_pkg, cur_pkg);
+ }
+bail:
+ return (rc);
+}
+
+/*
+ * Given a list of protocols and number, write them to a new algorithm file.
+ * This function takes num_protos + num_protos * dois-per-alg operations.
+ * Also free the protocol structure.
+ *
+ * Note that no locking spans the read/update/write phases that can be
+ * used by callers of this routine. This could cause this function to suffer
+ * from the "lost update" problem. Since updates to the IPsec protocols
+ * and algorithm tables are very infrequent, this should not be a issue in
+ * practice.
+ */
+static int
+write_new_algfile(ipsec_proto_t *protos, int num_protos)
+{
+ FILE *f;
+ int fd, i, j, k;
+ int rc = 0;
+ struct ipsecalgent *alg;
+ char cur_pkg[1024];
+ boolean_t doing_pkg = B_FALSE;
+ char *alg_pkg;
+ char *tmp_name_template = INET_IPSECALGSPATH "ipsecalgsXXXXXX";
+ char *tmp_name;
+
+ /*
+ * In order to avoid potentially corrupting the configuration
+ * file on file system failure, write the new configuration info
+ * to a temporary file which is then renamed to the configuration
+ * file (INET_IPSECALGSFILE.)
+ */
+ tmp_name = mktemp(tmp_name_template);
+
+ fd = open(tmp_name, O_WRONLY|O_CREAT|O_EXCL, CFG_PERMS);
+ if (fd == -1) {
+ rc = LIBIPSEC_ALGS_DIAG_ALGSFILEOPEN;
+ goto bail;
+ }
+
+ f = fdopen(fd, "w");
+ if (f == NULL) {
+ (void) close(fd);
+ rc = LIBIPSEC_ALGS_DIAG_ALGSFILEFDOPEN;
+ goto bail;
+ }
+
+ FPUT_ERR(fputs(preamble, f));
+
+ /* Write protocol entries. */
+ for (i = 0; i < num_protos; i++) {
+
+ /* add package section delimiters if needed */
+ rc = pkg_section(f, protos[i].proto_pkg, &doing_pkg, cur_pkg);
+ if (rc != 0)
+ goto bail;
+
+ FPRINTF_ERR(fprintf(f, "%s%d|%s|",
+ LIBIPSEC_ALGS_LINE_PROTO,
+ protos[i].proto_num, protos[i].proto_name));
+ switch (protos[i].proto_exec_mode) {
+ case LIBIPSEC_ALGS_EXEC_SYNC:
+ FPRINTF_ERR(fprintf(f, "sync\n"));
+ break;
+ case LIBIPSEC_ALGS_EXEC_ASYNC:
+ FPRINTF_ERR(fprintf(f, "async\n"));
+ break;
+ }
+ }
+
+ /* terminate the package section for the protocols if needed */
+ PKG_SEC_END(doing_pkg, cur_pkg);
+
+ FPUT_ERR(fputs("\n", f));
+
+ /* Write algorithm entries. */
+
+ for (i = 0; i < num_protos; i++) {
+ for (j = 0; j < protos[i].proto_numalgs; j++) {
+ alg = protos[i].proto_algs[j];
+
+ /* add package section delimiters if needed */
+ alg_pkg = alg_has_pkg(&protos[i], alg);
+ rc = pkg_section(f, alg_pkg, &doing_pkg, cur_pkg);
+ if (rc != 0)
+ goto bail;
+
+ /* protocol and algorithm numbers */
+ FPRINTF_ERR(fprintf(f, "%s%d|%d|",
+ LIBIPSEC_ALGS_LINE_ALG,
+ alg->a_proto_num, alg->a_alg_num));
+
+ /* algorithm names */
+ for (k = 0; alg->a_names[k] != NULL; k++) {
+ FPRINTF_ERR(fprintf(f, "%s", alg->a_names[k]));
+ if (alg->a_names[k+1] != NULL)
+ FPRINTF_ERR(fprintf(f, ","));
+ }
+
+ /* mechanism name */
+ FPRINTF_ERR(fprintf(f, "|%s|", alg->a_mech_name));
+
+ /* key sizes */
+ if (alg->a_key_increment == 0) {
+ /* key sizes defined by enumeration */
+ if (list_ints(f, alg->a_key_sizes) == -1) {
+ rc = LIBIPSEC_ALGS_DIAG_ALGSFILEWRITE;
+ goto bail;
+ }
+ } else {
+ /* key sizes defined by range */
+ FPRINTF_ERR(fprintf(f, "%d/%d-%d,%d",
+ alg->a_key_sizes[0], alg->a_key_sizes[1],
+ alg->a_key_sizes[2], alg->a_key_increment));
+ }
+ FPUT_ERR(fputc('|', f));
+
+ /* block sizes */
+ if (list_ints(f, alg->a_block_sizes) == -1) {
+ rc = LIBIPSEC_ALGS_DIAG_ALGSFILEWRITE;
+ goto bail;
+ }
+ FPUT_ERR(fputc('\n', f));
+ }
+ }
+
+ /* terminate the package section for the algorithms if needed */
+ PKG_SEC_END(doing_pkg, cur_pkg);
+
+ if (fchmod(fd, CFG_PERMS) == -1) {
+ rc = LIBIPSEC_ALGS_DIAG_ALGSFILECHMOD;
+ goto bail;
+ }
+ if (fchown(fd, CFG_OWNER, CFG_GROUP) == -1) {
+ rc = LIBIPSEC_ALGS_DIAG_ALGSFILECHOWN;
+ goto bail;
+ }
+ if (fclose(f) == EOF) {
+ rc = LIBIPSEC_ALGS_DIAG_ALGSFILECLOSE;
+ goto bail;
+ }
+
+ if (rename(tmp_name, INET_IPSECALGSFILE) == -1)
+ rc = LIBIPSEC_ALGS_DIAG_ALGSFILERENAME;
+
+bail:
+ _clean_trash(protos, num_protos);
+ return (rc);
+}
+
+/*
+ * Return a pointer to the protocol entry corresponding to the specified
+ * protocol num proto_num. Also builds the list of currently defined
+ * protocols.
+ */
+static ipsec_proto_t *
+proto_setup(ipsec_proto_t **protos, int *num_protos, int proto_num,
+ boolean_t cleanup)
+{
+ int i;
+ ipsec_proto_t *current_proto, *ret_proto = NULL;
+
+ _build_internal_algs(protos, num_protos);
+
+ if (*protos == NULL)
+ return (NULL);
+
+ for (i = 0; i < *num_protos; i++) {
+ current_proto = (*protos) + i;
+ if (current_proto->proto_num == proto_num) {
+ ret_proto = current_proto;
+ break;
+ }
+ }
+
+ if (ret_proto == NULL) {
+ if (cleanup)
+ _clean_trash(*protos, *num_protos);
+ /* else caller wants parsed /etc/inet/ipsecalgs anyway */
+ }
+
+ return (ret_proto);
+}
+
+/*
+ * Delete the first found algorithm of the specified protocol which
+ * has the same name as the one specified by alg_name. Deletion of
+ * the entry takes place only if the delete_it flag is set. If an
+ * entry was found, return B_TRUE, otherwise return B_FALSE.
+ */
+static boolean_t
+delipsecalgbyname_common(const char *name, ipsec_proto_t *proto,
+ boolean_t delete_it)
+{
+ int i;
+ char **name_check;
+ boolean_t found_match = B_FALSE;
+
+ for (i = 0; i < proto->proto_numalgs; i++) {
+ if (!found_match) {
+ for (name_check =
+ proto->proto_algs[i]->a_names;
+ *name_check != NULL; name_check++) {
+ /*
+ * Can use strcmp because the algorithm names
+ * are bound.
+ */
+ if (strcmp(*name_check, name) == 0) {
+ found_match = B_TRUE;
+ if (!delete_it)
+ return (found_match);
+ freeipsecalgent(proto->proto_algs[i]);
+ break;
+ }
+ }
+ } else {
+ proto->proto_algs[i - 1] = proto->proto_algs[i];
+ }
+ }
+
+ if (found_match)
+ proto->proto_numalgs--;
+
+ return (found_match);
+}
+
+/*
+ * Returns B_TRUE if the specified 0-terminated lists of key or
+ * block sizes match, B_FALSE otherwise.
+ */
+static boolean_t
+sizes_match(int *a1, int *a2)
+{
+ int i;
+
+ for (i = 0; (a1[i] != 0) && (a2[i] != 0); i++) {
+ if (a1[i] != a2[i])
+ return (B_FALSE);
+ }
+ if ((a1[i] != 0) || (a2[i] != 0))
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+/*
+ * Returns B_TRUE if an _exact_ equivalent of the specified algorithm
+ * already exists, B_FALSE otherwise.
+ */
+static boolean_t
+ipsecalg_exists(struct ipsecalgent *newbie, ipsec_proto_t *proto)
+{
+ struct ipsecalgent *curalg;
+ char **curname, **newbiename;
+ int i;
+ boolean_t match;
+
+ for (i = 0; i < proto->proto_numalgs; i++) {
+ curalg = proto->proto_algs[i];
+
+ if (curalg->a_alg_num != newbie->a_alg_num)
+ continue;
+
+ if (curalg->a_key_increment != newbie->a_key_increment)
+ continue;
+
+ if (strcmp(curalg->a_mech_name, newbie->a_mech_name) != 0)
+ continue;
+
+ curname = curalg->a_names;
+ newbiename = newbie->a_names;
+ match = B_TRUE;
+ while ((*curname != NULL) && (*newbiename != NULL) && match) {
+ match = (strcmp(*curname, *newbiename) == 0);
+ curname++;
+ newbiename++;
+ }
+ if (!match || (*curname != NULL) || (*newbiename != NULL))
+ continue;
+
+ if (!sizes_match(curalg->a_block_sizes, newbie->a_block_sizes))
+ continue;
+
+ if (!sizes_match(curalg->a_key_sizes, newbie->a_key_sizes))
+ continue;
+
+ /* we found an exact match */
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Add a new algorithm to the /etc/inet/ipsecalgs file. Caller must free
+ * or otherwise address "newbie".
+ */
+int
+addipsecalg(struct ipsecalgent *newbie, uint_t flags)
+{
+ ipsec_proto_t *protos, *current_proto;
+ struct ipsecalgent *clone, **holder;
+ int num_protos, i;
+ char **name_check;
+ boolean_t forced_add = (flags & LIBIPSEC_ALGS_ADD_FORCE) != 0;
+ boolean_t found_match;
+
+ if ((current_proto = proto_setup(&protos, &num_protos,
+ newbie->a_proto_num, B_TRUE)) == NULL)
+ return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO);
+
+ /*
+ * If an algorithm that matches _exactly_ the new algorithm
+ * already exists, we're done.
+ */
+ if (ipsecalg_exists(newbie, current_proto))
+ return (0);
+
+ /*
+ * We don't allow a new algorithm to be created if one of
+ * its names is already defined for an existing algorithm,
+ * unless the operation is forced, in which case existing
+ * algorithm entries that conflict with the new one are
+ * deleted.
+ */
+ for (name_check = newbie->a_names; *name_check != NULL; name_check++) {
+ found_match = delipsecalgbyname_common(*name_check,
+ current_proto, forced_add);
+ if (found_match && !forced_add) {
+ /*
+ * Duplicate entry found, but the addition was
+ * not forced.
+ */
+ _clean_trash(protos, num_protos);
+ return (LIBIPSEC_ALGS_DIAG_ALG_EXISTS);
+ }
+ }
+
+ for (i = 0; i < current_proto->proto_numalgs; i++) {
+ if (current_proto->proto_algs[i]->a_alg_num ==
+ newbie->a_alg_num) {
+ /*
+ * An algorithm with the same protocol number
+ * and algorithm number already exists. Fail
+ * addition unless the operation is forced.
+ */
+ if (flags & LIBIPSEC_ALGS_ADD_FORCE) {
+ clone = _duplicate_alg(newbie);
+ if (clone != NULL) {
+ freeipsecalgent(
+ current_proto->proto_algs[i]);
+ current_proto->proto_algs[i] = clone;
+ return (write_new_algfile(protos,
+ num_protos));
+ } else {
+ _clean_trash(protos, num_protos);
+ return (LIBIPSEC_ALGS_DIAG_NOMEM);
+ }
+ } else {
+ _clean_trash(protos, num_protos);
+ return (LIBIPSEC_ALGS_DIAG_ALG_EXISTS);
+ }
+ }
+ }
+
+ /* append the new algorithm */
+ holder = realloc(current_proto->proto_algs,
+ sizeof (struct ipsecalgent *) * (i + 1));
+ if (holder == NULL) {
+ _clean_trash(protos, num_protos);
+ return (LIBIPSEC_ALGS_DIAG_NOMEM);
+ }
+ clone = _duplicate_alg(newbie);
+ if (clone == NULL) {
+ free(holder);
+ _clean_trash(protos, num_protos);
+ return (LIBIPSEC_ALGS_DIAG_NOMEM);
+ }
+ current_proto->proto_numalgs++;
+ current_proto->proto_algs = holder;
+ current_proto->proto_algs[i] = clone;
+ return (write_new_algfile(protos, num_protos));
+}
+
+/*
+ * Delete an algorithm by name & protocol number from /etc/inet/ipsecalgs.
+ * Only deletes the first encountered instance.
+ */
+int
+delipsecalgbyname(const char *name, int proto_num)
+{
+ ipsec_proto_t *protos, *current_proto;
+ int num_protos;
+
+ if ((current_proto = proto_setup(&protos, &num_protos, proto_num,
+ B_TRUE)) == NULL)
+ return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO);
+
+ if (delipsecalgbyname_common(name, current_proto, B_TRUE))
+ return (write_new_algfile(protos, num_protos));
+
+ _clean_trash(protos, num_protos);
+ return (LIBIPSEC_ALGS_DIAG_UNKN_ALG);
+}
+
+/*
+ * Delete an algorithm by num + protocol num from /etc/inet/ipsecalgs.
+ */
+int
+delipsecalgbynum(int alg_num, int proto_num)
+{
+ ipsec_proto_t *protos, *current_proto;
+ int i, num_protos;
+ boolean_t found_match = B_FALSE;
+
+ if ((current_proto = proto_setup(&protos, &num_protos, proto_num,
+ B_TRUE)) == NULL)
+ return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO);
+
+ for (i = 0; i < current_proto->proto_numalgs; i++) {
+ if (!found_match) {
+ if (current_proto->proto_algs[i]->a_alg_num ==
+ alg_num) {
+ found_match = B_TRUE;
+ freeipsecalgent(current_proto->proto_algs[i]);
+ }
+ } else {
+ current_proto->proto_algs[i - 1] =
+ current_proto->proto_algs[i];
+ }
+ }
+
+ if (found_match) {
+ current_proto->proto_numalgs--;
+ return (write_new_algfile(protos, num_protos));
+ }
+
+ _clean_trash(protos, num_protos);
+ return (LIBIPSEC_ALGS_DIAG_UNKN_ALG);
+}
+
+/*
+ * Remove the specified protocol entry from the list of protocols.
+ */
+static void
+delipsecproto_common(ipsec_proto_t *protos, int num_protos,
+ ipsec_proto_t *proto)
+{
+ int i;
+
+ /* free protocol storage */
+ free(proto->proto_name);
+ for (i = 0; i < proto->proto_numalgs; i++)
+ freeipsecalgent(proto->proto_algs[i]);
+
+ /* remove from list of prototocols */
+ for (i = (proto - protos + 1); i < num_protos; i++)
+ protos[i - 1] = protos[i];
+}
+
+/*
+ * Add an IPsec protocol to /etc/inet/ipsecalgs.
+ */
+int
+addipsecproto(const char *proto_name, int proto_num,
+ ipsecalgs_exec_mode_t proto_exec_mode, uint_t flags)
+{
+ ipsec_proto_t *protos, *current_proto, *new_proto;
+ int i, num_protos;
+
+ /*
+ * NOTE:If build_internal_algs returns NULL for any
+ * reason, we will end up clobbering /etc/inet/ipsecalgs!
+ */
+
+ current_proto = proto_setup(&protos, &num_protos, proto_num, B_FALSE);
+
+ /* check for protocol with duplicate id */
+ if (current_proto != NULL) {
+ if ((strcmp(proto_name, current_proto->proto_name) == 0) &&
+ (proto_exec_mode == current_proto->proto_exec_mode)) {
+ /*
+ * The current protocol being added matches
+ * exactly an existing protocol, we're done.
+ */
+ return (0);
+ }
+ if (!(flags & LIBIPSEC_ALGS_ADD_FORCE))
+ return (LIBIPSEC_ALGS_DIAG_PROTO_EXISTS);
+ delipsecproto_common(protos, num_protos--, current_proto);
+ }
+
+ /* check for protocol with duplicate name */
+ for (i = 0; i < num_protos; i++) {
+ if (strcmp(protos[i].proto_name, proto_name) == 0) {
+ if (!(flags & LIBIPSEC_ALGS_ADD_FORCE))
+ return (LIBIPSEC_ALGS_DIAG_PROTO_EXISTS);
+ delipsecproto_common(protos, num_protos--, &protos[i]);
+ break;
+ }
+ }
+
+ /* add new protocol */
+ num_protos++;
+ new_proto = realloc(protos, num_protos *
+ sizeof (ipsec_proto_t));
+ if (new_proto == NULL) {
+ _clean_trash(protos, num_protos - 1);
+ return (LIBIPSEC_ALGS_DIAG_NOMEM);
+ }
+ protos = new_proto;
+ new_proto += (num_protos - 1);
+
+ /* initialize protocol entry */
+ new_proto->proto_num = proto_num;
+ new_proto->proto_numalgs = 0;
+ new_proto->proto_algs = NULL;
+ new_proto->proto_name = strdup(proto_name);
+ if (new_proto->proto_name == NULL) {
+ _clean_trash(protos, num_protos);
+ return (LIBIPSEC_ALGS_DIAG_NOMEM);
+ }
+ new_proto->proto_pkg = NULL;
+ new_proto->proto_algs_pkgs = NULL;
+ new_proto->proto_algs_npkgs = 0;
+ new_proto->proto_exec_mode = proto_exec_mode;
+
+ return (write_new_algfile(protos, num_protos));
+}
+
+/*
+ * Delete an IPsec protocol entry from /etc/inet/ipsecalgs. This also
+ * nukes the associated algorithms.
+ */
+int
+delipsecprotobynum(int proto_num)
+{
+ ipsec_proto_t *protos, *current_proto;
+ int num_protos;
+
+ if ((current_proto = proto_setup(&protos, &num_protos, proto_num,
+ B_TRUE)) == NULL)
+ return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO);
+
+ delipsecproto_common(protos, num_protos--, current_proto);
+
+ return (write_new_algfile(protos, num_protos));
+}
+
+int
+delipsecprotobyname(const char *proto_name)
+{
+ int proto_num;
+
+ proto_num = getipsecprotobyname(proto_name);
+ if (proto_num == -1)
+ return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO);
+
+ return (delipsecprotobynum(proto_num));
+}
+
+/*
+ * Implement these in libnsl since these are read-only operations.
+ */
+int *
+getipsecprotos(int *nentries)
+{
+ return (_real_getipsecprotos(nentries));
+}
+
+int *
+getipsecalgs(int *nentries, int proto_num)
+{
+ return (_real_getipsecalgs(nentries, proto_num));
+}
+
+const char *
+ipsecalgs_diag(int diag)
+{
+ switch (diag) {
+ case LIBIPSEC_ALGS_DIAG_ALG_EXISTS:
+ return (gettext("Algorithm already exists"));
+ case LIBIPSEC_ALGS_DIAG_PROTO_EXISTS:
+ return (gettext("Protocol already exists"));
+ case LIBIPSEC_ALGS_DIAG_UNKN_PROTO:
+ return (gettext("Unknown protocol"));
+ case LIBIPSEC_ALGS_DIAG_UNKN_ALG:
+ return (gettext("Unknown algorithm"));
+ case LIBIPSEC_ALGS_DIAG_NOMEM:
+ return (gettext("Out of memory"));
+ case LIBIPSEC_ALGS_DIAG_ALGSFILEOPEN:
+ return (gettext("open() failed"));
+ case LIBIPSEC_ALGS_DIAG_ALGSFILEFDOPEN:
+ return (gettext("fdopen() failed"));
+ case LIBIPSEC_ALGS_DIAG_ALGSFILELOCK:
+ return (gettext("lockf() failed"));
+ case LIBIPSEC_ALGS_DIAG_ALGSFILERENAME:
+ return (gettext("rename() failed"));
+ case LIBIPSEC_ALGS_DIAG_ALGSFILEWRITE:
+ return (gettext("write to file failed"));
+ case LIBIPSEC_ALGS_DIAG_ALGSFILECHMOD:
+ return (gettext("chmod() failed"));
+ case LIBIPSEC_ALGS_DIAG_ALGSFILECHOWN:
+ return (gettext("chown() failed"));
+ case LIBIPSEC_ALGS_DIAG_ALGSFILECLOSE:
+ return (gettext("close() failed"));
+ default:
+ return (gettext("failed"));
+ }
+}
+
+/*
+ * Get the execution mode corresponding to the specified protocol.
+ * Returns 0 on success, one of the LIBIPSEC_ALGS_DIAG_* values on
+ * failure.
+ */
+int
+ipsecproto_get_exec_mode(int proto_num, ipsecalgs_exec_mode_t *exec_mode)
+{
+ ipsec_proto_t *protos, *current_proto;
+ int num_protos;
+
+ if ((current_proto = proto_setup(&protos, &num_protos, proto_num,
+ B_TRUE)) == NULL)
+ return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO);
+
+ *exec_mode = current_proto->proto_exec_mode;
+
+ _clean_trash(protos, num_protos);
+ return (0);
+}
+
+/*
+ * Set the execution mode of the specified protocol. Returns 0 on success,
+ * or one of the LIBIPSEC_ALGS_DIAG_* values on failure.
+ */
+int
+ipsecproto_set_exec_mode(int proto_num, ipsecalgs_exec_mode_t exec_mode)
+{
+ ipsec_proto_t *protos, *current_proto;
+ int num_protos;
+
+ if ((current_proto = proto_setup(&protos, &num_protos, proto_num,
+ B_TRUE)) == NULL)
+ return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO);
+
+ current_proto->proto_exec_mode = exec_mode;
+
+ return (write_new_algfile(protos, num_protos));
+}