algs.c revision 4064:66e3f3aebd89
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <ipsec_util.h>
31#include <stdlib.h>
32#include <strings.h>
33#include <netdb.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include <libintl.h>
37#include <errno.h>
38
39static char *preamble =
40"# /etc/inet/ipsecalgs output from ipsecalgs(1m)\n"
41"#\n"
42"# DO NOT EDIT OR PARSE THIS FILE!\n"
43"#\n"
44"# Use the ipsecalgs(1m) command to change the contents of this file.\n"
45"\n";
46
47#define	CFG_PERMS S_IRUSR | S_IRGRP | S_IROTH	/* Perms 0444. */
48#define	CFG_OWNER 0	/* root */
49#define	CFG_GROUP 1	/* "other" */
50
51/*
52 * write_new_algfile() helper macros to check for write errors.
53 */
54
55#define	FPRINTF_ERR(fcall) if ((fcall) < 0) {	\
56	rc = LIBIPSEC_ALGS_DIAG_ALGSFILEWRITE;	\
57	goto bail;				\
58}
59
60#define	FPUT_ERR(fcall) if ((fcall) == EOF) {	\
61	rc = LIBIPSEC_ALGS_DIAG_ALGSFILEWRITE;	\
62	goto bail;				\
63}
64
65/*
66 * Helper macros to start and finish a list of entries that were added
67 * as part of a package installation.
68 */
69
70#define	PKG_SEC_START(pkgname, doing_pkg, cur_pkg) {		\
71	(void) strcpy((cur_pkg), (pkgname));			\
72	FPRINTF_ERR(fprintf(f, "%s%s\n",			\
73	    LIBIPSEC_ALGS_LINE_PKGSTART, (cur_pkg)));		\
74	(doing_pkg) = B_TRUE;					\
75}
76
77#define	PKG_SEC_END(doing_pkg, cur_pkg) {			\
78	if (doing_pkg) {					\
79		FPRINTF_ERR(fprintf(f, "%s%s\n",		\
80		    LIBIPSEC_ALGS_LINE_PKGEND, (cur_pkg)));	\
81		(doing_pkg) = B_FALSE;				\
82	}							\
83}
84
85/*
86 * Take a zero-terminated int array and print int1,int2...,intN.
87 * If zero-only, then print a single '0'.
88 * Returns 0 on success, -1 if an error occurred while writing to
89 * the specified file.
90 */
91int
92list_ints(FILE *f, int *floater)
93{
94	boolean_t executed = B_FALSE;
95
96	while (*floater != 0) {
97		executed = B_TRUE;
98		if (fprintf(f, "%d", *floater) < 0)
99			return (-1);
100		if (*(++floater) != 0)
101			if (fputc(',', f) == EOF)
102				return (-1);
103	}
104
105	if (!executed)
106		if (fputc('0', f) == EOF)
107			return (-1);
108
109	return (0);
110}
111
112/*
113 * If the specified algorithm was defined within a package section, i.e.
114 * between the lines "# Start <pkgname>" and "# End <pkgname>", returns
115 * the value of <pkgname>.
116 */
117static char *
118alg_has_pkg(ipsec_proto_t *proto, struct ipsecalgent *alg)
119{
120	int i;
121
122	if (proto->proto_algs_pkgs == NULL)
123		return (NULL);
124
125	for (i = 0; i < proto->proto_algs_npkgs; i++)
126		if (proto->proto_algs_pkgs[i].alg_num == alg->a_alg_num)
127			return (proto->proto_algs_pkgs[i].pkg_name);
128
129	return (NULL);
130}
131
132/*
133 * Writes the package start/end delimiters according to the package
134 * name associated with the current protocol or algorithm, and
135 * the state of the packaging information already written to the file.
136 * Called by write_new_algfile(). Returns 0 on success, one of the
137 * LIBIPSEC_DIAG codes on failure.
138 */
139static int
140pkg_section(FILE *f, char *pkg_name, boolean_t *doing_pkg, char *cur_pkg)
141{
142	int rc = 0;
143
144	if (pkg_name != NULL) {
145		/* protocol or algorithm is associated with a package */
146		if (!*doing_pkg) {
147			/* start of a new package section */
148			PKG_SEC_START(pkg_name, *doing_pkg, cur_pkg);
149		} else {
150			/* already in a package section */
151			if (strcmp(pkg_name, cur_pkg) != 0) {
152				/* different package name */
153				PKG_SEC_END(*doing_pkg, cur_pkg);
154				PKG_SEC_START(pkg_name, *doing_pkg, cur_pkg);
155			}
156		}
157	} else if (*doing_pkg) {
158		/* in a package section when the entry isn't */
159		PKG_SEC_END(*doing_pkg, cur_pkg);
160	}
161bail:
162	return (rc);
163}
164
165/*
166 * Given a list of protocols and number, write them to a new algorithm file.
167 * This function takes num_protos + num_protos * dois-per-alg operations.
168 * Also free the protocol structure.
169 *
170 * Note that no locking spans the read/update/write phases that can be
171 * used by callers of this routine. This could cause this function to suffer
172 * from the "lost update" problem. Since updates to the IPsec protocols
173 * and algorithm tables are very infrequent, this should not be a issue in
174 * practice.
175 */
176static int
177write_new_algfile(ipsec_proto_t *protos, int num_protos)
178{
179	FILE *f;
180	int fd, i, j, k;
181	int rc = 0;
182	struct ipsecalgent *alg;
183	char cur_pkg[1024];
184	boolean_t doing_pkg = B_FALSE;
185	char *alg_pkg;
186	char tmp_name_template[] = INET_IPSECALGSPATH "ipsecalgsXXXXXX";
187	char *tmp_name;
188
189	/*
190	 * In order to avoid potentially corrupting the configuration
191	 * file on file system failure, write the new configuration info
192	 * to a temporary file which is then renamed to the configuration
193	 * file (INET_IPSECALGSFILE.)
194	 */
195	tmp_name = mktemp(tmp_name_template);
196
197	fd = open(tmp_name, O_WRONLY|O_CREAT|O_EXCL, CFG_PERMS);
198	if (fd == -1) {
199		rc = LIBIPSEC_ALGS_DIAG_ALGSFILEOPEN;
200		goto bail;
201	}
202
203	f = fdopen(fd, "w");
204	if (f == NULL) {
205		(void) close(fd);
206		rc = LIBIPSEC_ALGS_DIAG_ALGSFILEFDOPEN;
207		goto bail;
208	}
209
210	FPUT_ERR(fputs(preamble, f));
211
212	/* Write protocol entries. */
213	for (i = 0; i < num_protos; i++) {
214
215		/* add package section delimiters if needed */
216		rc = pkg_section(f, protos[i].proto_pkg, &doing_pkg, cur_pkg);
217		if (rc != 0)
218			goto bail;
219
220		FPRINTF_ERR(fprintf(f, "%s%d|%s|",
221		    LIBIPSEC_ALGS_LINE_PROTO,
222		    protos[i].proto_num, protos[i].proto_name));
223		switch (protos[i].proto_exec_mode) {
224		case LIBIPSEC_ALGS_EXEC_SYNC:
225			FPRINTF_ERR(fprintf(f, "sync\n"));
226			break;
227		case LIBIPSEC_ALGS_EXEC_ASYNC:
228			FPRINTF_ERR(fprintf(f, "async\n"));
229			break;
230		}
231	}
232
233	/* terminate the package section for the protocols if needed */
234	PKG_SEC_END(doing_pkg, cur_pkg);
235
236	FPUT_ERR(fputs("\n", f));
237
238	/* Write algorithm entries. */
239
240	for (i = 0; i < num_protos; i++) {
241		for (j = 0; j < protos[i].proto_numalgs; j++) {
242			alg = protos[i].proto_algs[j];
243
244			/* add package section delimiters if needed */
245			alg_pkg = alg_has_pkg(&protos[i], alg);
246			rc = pkg_section(f, alg_pkg, &doing_pkg, cur_pkg);
247			if (rc != 0)
248				goto bail;
249
250			/* protocol and algorithm numbers */
251			FPRINTF_ERR(fprintf(f, "%s%d|%d|",
252			    LIBIPSEC_ALGS_LINE_ALG,
253			    alg->a_proto_num, alg->a_alg_num));
254
255			/* algorithm names */
256			for (k = 0; alg->a_names[k] != NULL; k++) {
257				FPRINTF_ERR(fprintf(f, "%s", alg->a_names[k]));
258				if (alg->a_names[k+1] != NULL)
259					FPRINTF_ERR(fprintf(f, ","));
260			}
261
262			/* mechanism name */
263			FPRINTF_ERR(fprintf(f, "|%s|", alg->a_mech_name));
264
265			/* key sizes */
266			if (alg->a_key_increment == 0) {
267				/* key sizes defined by enumeration */
268				if (list_ints(f, alg->a_key_sizes) == -1) {
269					rc = LIBIPSEC_ALGS_DIAG_ALGSFILEWRITE;
270					goto bail;
271				}
272			} else {
273				/* key sizes defined by range */
274				FPRINTF_ERR(fprintf(f, "%d/%d-%d,%d",
275				    alg->a_key_sizes[0], alg->a_key_sizes[1],
276				    alg->a_key_sizes[2], alg->a_key_increment));
277			}
278			FPUT_ERR(fputc('|', f));
279
280			/* block sizes */
281			if (list_ints(f, alg->a_block_sizes) == -1) {
282				rc = LIBIPSEC_ALGS_DIAG_ALGSFILEWRITE;
283				goto bail;
284			}
285			FPUT_ERR(fputc('\n', f));
286		}
287	}
288
289	/* terminate the package section for the algorithms if needed */
290	PKG_SEC_END(doing_pkg, cur_pkg);
291
292	if (fchmod(fd, CFG_PERMS) == -1) {
293		rc = LIBIPSEC_ALGS_DIAG_ALGSFILECHMOD;
294		goto bail;
295	}
296	if (fchown(fd, CFG_OWNER, CFG_GROUP) == -1) {
297		rc = LIBIPSEC_ALGS_DIAG_ALGSFILECHOWN;
298		goto bail;
299	}
300	if (fclose(f) == EOF) {
301		rc = LIBIPSEC_ALGS_DIAG_ALGSFILECLOSE;
302		goto bail;
303	}
304
305	if (rename(tmp_name, INET_IPSECALGSFILE) == -1)
306		rc = LIBIPSEC_ALGS_DIAG_ALGSFILERENAME;
307
308bail:
309	_clean_trash(protos, num_protos);
310	return (rc);
311}
312
313/*
314 * Return a pointer to the protocol entry corresponding to the specified
315 * protocol num proto_num. Also builds the list of currently defined
316 * protocols.
317 */
318static ipsec_proto_t *
319proto_setup(ipsec_proto_t **protos, int *num_protos, int proto_num,
320    boolean_t cleanup)
321{
322	int i;
323	ipsec_proto_t *current_proto, *ret_proto = NULL;
324
325	_build_internal_algs(protos, num_protos);
326
327	if (*protos == NULL)
328		return (NULL);
329
330	for (i = 0; i < *num_protos; i++) {
331		current_proto = (*protos) + i;
332		if (current_proto->proto_num == proto_num) {
333			ret_proto = current_proto;
334			break;
335		}
336	}
337
338	if (ret_proto == NULL) {
339		if (cleanup)
340			_clean_trash(*protos, *num_protos);
341		/* else caller wants parsed /etc/inet/ipsecalgs anyway */
342	}
343
344	return (ret_proto);
345}
346
347/*
348 * Delete the first found algorithm of the specified protocol which
349 * has the same name as the one specified by alg_name. Deletion of
350 * the entry takes place only if the delete_it flag is set. If an
351 * entry was found, return B_TRUE, otherwise return B_FALSE.
352 */
353static boolean_t
354delipsecalgbyname_common(const char *name, ipsec_proto_t *proto,
355    boolean_t delete_it)
356{
357	int i;
358	char **name_check;
359	boolean_t found_match = B_FALSE;
360
361	for (i = 0; i < proto->proto_numalgs; i++) {
362		if (!found_match) {
363			for (name_check =
364			    proto->proto_algs[i]->a_names;
365			    *name_check != NULL; name_check++) {
366				/*
367				 * Can use strcmp because the algorithm names
368				 * are bound.
369				 */
370				if (strcmp(*name_check, name) == 0) {
371					found_match = B_TRUE;
372					if (!delete_it)
373						return (found_match);
374					freeipsecalgent(proto->proto_algs[i]);
375					break;
376				}
377			}
378		} else {
379			proto->proto_algs[i - 1] = proto->proto_algs[i];
380		}
381	}
382
383	if (found_match)
384		proto->proto_numalgs--;
385
386	return (found_match);
387}
388
389/*
390 * Returns B_TRUE if the specified 0-terminated lists of key or
391 * block sizes match, B_FALSE otherwise.
392 */
393static boolean_t
394sizes_match(int *a1, int *a2)
395{
396	int i;
397
398	for (i = 0; (a1[i] != 0) && (a2[i] != 0); i++) {
399		if (a1[i] != a2[i])
400			return (B_FALSE);
401	}
402	if ((a1[i] != 0) || (a2[i] != 0))
403		return (B_FALSE);
404
405	return (B_TRUE);
406}
407
408/*
409 * Returns B_TRUE if an _exact_ equivalent of the specified algorithm
410 * already exists, B_FALSE otherwise.
411 */
412static boolean_t
413ipsecalg_exists(struct ipsecalgent *newbie, ipsec_proto_t *proto)
414{
415	struct ipsecalgent *curalg;
416	char **curname, **newbiename;
417	int i;
418	boolean_t match;
419
420	for (i = 0; i < proto->proto_numalgs; i++) {
421		curalg = proto->proto_algs[i];
422
423		if (curalg->a_alg_num != newbie->a_alg_num)
424			continue;
425
426		if (curalg->a_key_increment != newbie->a_key_increment)
427			continue;
428
429		if (strcmp(curalg->a_mech_name, newbie->a_mech_name) != 0)
430			continue;
431
432		curname = curalg->a_names;
433		newbiename = newbie->a_names;
434		match = B_TRUE;
435		while ((*curname != NULL) && (*newbiename != NULL) && match) {
436			match = (strcmp(*curname, *newbiename) == 0);
437			curname++;
438			newbiename++;
439		}
440		if (!match || (*curname != NULL) || (*newbiename != NULL))
441			continue;
442
443		if (!sizes_match(curalg->a_block_sizes, newbie->a_block_sizes))
444			continue;
445
446		if (!sizes_match(curalg->a_key_sizes, newbie->a_key_sizes))
447			continue;
448
449		/* we found an exact match */
450		return (B_TRUE);
451	}
452
453	return (B_FALSE);
454}
455
456/*
457 * Add a new algorithm to the /etc/inet/ipsecalgs file.  Caller must free
458 * or otherwise address "newbie".
459 */
460int
461addipsecalg(struct ipsecalgent *newbie, uint_t flags)
462{
463	ipsec_proto_t *protos, *current_proto;
464	struct ipsecalgent *clone, **holder;
465	int num_protos, i;
466	char **name_check;
467	boolean_t forced_add = (flags & LIBIPSEC_ALGS_ADD_FORCE) != 0;
468	boolean_t found_match;
469
470	if ((current_proto = proto_setup(&protos, &num_protos,
471	    newbie->a_proto_num, B_TRUE)) == NULL)
472		return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO);
473
474	/*
475	 * If an algorithm that matches _exactly_ the new algorithm
476	 * already exists, we're done.
477	 */
478	if (ipsecalg_exists(newbie, current_proto))
479		return (0);
480
481	/*
482	 * We don't allow a new algorithm to be created if one of
483	 * its names is already defined for an existing algorithm,
484	 * unless the operation is forced, in which case existing
485	 * algorithm entries that conflict with the new one are
486	 * deleted.
487	 */
488	for (name_check = newbie->a_names; *name_check != NULL; name_check++) {
489		found_match = delipsecalgbyname_common(*name_check,
490		    current_proto, forced_add);
491		if (found_match && !forced_add) {
492			/*
493			 * Duplicate entry found, but the addition was
494			 * not forced.
495			 */
496			_clean_trash(protos, num_protos);
497			return (LIBIPSEC_ALGS_DIAG_ALG_EXISTS);
498		}
499	}
500
501	for (i = 0; i < current_proto->proto_numalgs; i++) {
502		if (current_proto->proto_algs[i]->a_alg_num ==
503		    newbie->a_alg_num) {
504			/*
505			 * An algorithm with the same protocol number
506			 * and algorithm number already exists. Fail
507			 * addition unless the operation is forced.
508			 */
509			if (flags & LIBIPSEC_ALGS_ADD_FORCE) {
510				clone = _duplicate_alg(newbie);
511				if (clone != NULL) {
512					freeipsecalgent(
513					    current_proto->proto_algs[i]);
514					current_proto->proto_algs[i] = clone;
515					return (write_new_algfile(protos,
516						    num_protos));
517				} else {
518					_clean_trash(protos, num_protos);
519					return (LIBIPSEC_ALGS_DIAG_NOMEM);
520				}
521			} else {
522				_clean_trash(protos, num_protos);
523				return (LIBIPSEC_ALGS_DIAG_ALG_EXISTS);
524			}
525		}
526	}
527
528	/* append the new algorithm */
529	holder = realloc(current_proto->proto_algs,
530	    sizeof (struct ipsecalgent *) * (i + 1));
531	if (holder == NULL) {
532		_clean_trash(protos, num_protos);
533		return (LIBIPSEC_ALGS_DIAG_NOMEM);
534	}
535	clone = _duplicate_alg(newbie);
536	if (clone == NULL) {
537		free(holder);
538		_clean_trash(protos, num_protos);
539		return (LIBIPSEC_ALGS_DIAG_NOMEM);
540	}
541	current_proto->proto_numalgs++;
542	current_proto->proto_algs = holder;
543	current_proto->proto_algs[i] = clone;
544	return (write_new_algfile(protos, num_protos));
545}
546
547/*
548 * Delete an algorithm by name & protocol number from /etc/inet/ipsecalgs.
549 * Only deletes the first encountered instance.
550 */
551int
552delipsecalgbyname(const char *name, int proto_num)
553{
554	ipsec_proto_t *protos, *current_proto;
555	int num_protos;
556
557	if ((current_proto = proto_setup(&protos, &num_protos, proto_num,
558	    B_TRUE)) == NULL)
559		return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO);
560
561	if (delipsecalgbyname_common(name, current_proto, B_TRUE))
562		return (write_new_algfile(protos, num_protos));
563
564	_clean_trash(protos, num_protos);
565	return (LIBIPSEC_ALGS_DIAG_UNKN_ALG);
566}
567
568/*
569 * Delete an algorithm by num + protocol num from /etc/inet/ipsecalgs.
570 */
571int
572delipsecalgbynum(int alg_num, int proto_num)
573{
574	ipsec_proto_t *protos, *current_proto;
575	int i, num_protos;
576	boolean_t found_match = B_FALSE;
577
578	if ((current_proto = proto_setup(&protos, &num_protos, proto_num,
579	    B_TRUE)) == NULL)
580		return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO);
581
582	for (i = 0; i < current_proto->proto_numalgs; i++) {
583		if (!found_match) {
584			if (current_proto->proto_algs[i]->a_alg_num ==
585			    alg_num) {
586				found_match = B_TRUE;
587				freeipsecalgent(current_proto->proto_algs[i]);
588			}
589		} else {
590			current_proto->proto_algs[i - 1] =
591			    current_proto->proto_algs[i];
592		}
593	}
594
595	if (found_match) {
596		current_proto->proto_numalgs--;
597		return (write_new_algfile(protos, num_protos));
598	}
599
600	_clean_trash(protos, num_protos);
601	return (LIBIPSEC_ALGS_DIAG_UNKN_ALG);
602}
603
604/*
605 * Remove the specified protocol entry from the list of protocols.
606 */
607static void
608delipsecproto_common(ipsec_proto_t *protos, int num_protos,
609    ipsec_proto_t *proto)
610{
611	int i;
612
613	/* free protocol storage */
614	free(proto->proto_name);
615	for (i = 0; i < proto->proto_numalgs; i++)
616		freeipsecalgent(proto->proto_algs[i]);
617
618	/* remove from list of prototocols */
619	for (i = (proto - protos + 1); i < num_protos; i++)
620		protos[i - 1] = protos[i];
621}
622
623/*
624 * Add an IPsec protocol to /etc/inet/ipsecalgs.
625 */
626int
627addipsecproto(const char *proto_name, int proto_num,
628    ipsecalgs_exec_mode_t proto_exec_mode, uint_t flags)
629{
630	ipsec_proto_t *protos, *current_proto, *new_proto;
631	int i, num_protos;
632
633	/*
634	 * NOTE:If build_internal_algs returns NULL for any
635	 *	reason, we will end up clobbering /etc/inet/ipsecalgs!
636	 */
637
638	current_proto = proto_setup(&protos, &num_protos, proto_num, B_FALSE);
639
640	/* check for protocol with duplicate id */
641	if (current_proto != NULL) {
642		if ((strcmp(proto_name, current_proto->proto_name) == 0) &&
643		    (proto_exec_mode == current_proto->proto_exec_mode)) {
644			/*
645			 * The current protocol being added matches
646			 * exactly an existing protocol, we're done.
647			 */
648			return (0);
649		}
650		if (!(flags & LIBIPSEC_ALGS_ADD_FORCE))
651			return (LIBIPSEC_ALGS_DIAG_PROTO_EXISTS);
652		delipsecproto_common(protos, num_protos--, current_proto);
653	}
654
655	/* check for protocol with duplicate name */
656	for (i = 0; i < num_protos; i++) {
657		if (strcmp(protos[i].proto_name, proto_name) == 0) {
658			if (!(flags & LIBIPSEC_ALGS_ADD_FORCE))
659				return (LIBIPSEC_ALGS_DIAG_PROTO_EXISTS);
660			delipsecproto_common(protos, num_protos--, &protos[i]);
661			break;
662		}
663	}
664
665	/* add new protocol */
666	num_protos++;
667	new_proto = realloc(protos, num_protos *
668	    sizeof (ipsec_proto_t));
669	if (new_proto == NULL) {
670		_clean_trash(protos, num_protos - 1);
671		return (LIBIPSEC_ALGS_DIAG_NOMEM);
672	}
673	protos = new_proto;
674	new_proto += (num_protos - 1);
675
676	/* initialize protocol entry */
677	new_proto->proto_num = proto_num;
678	new_proto->proto_numalgs = 0;
679	new_proto->proto_algs = NULL;
680	new_proto->proto_name = strdup(proto_name);
681	if (new_proto->proto_name == NULL) {
682		_clean_trash(protos, num_protos);
683		return (LIBIPSEC_ALGS_DIAG_NOMEM);
684	}
685	new_proto->proto_pkg = NULL;
686	new_proto->proto_algs_pkgs = NULL;
687	new_proto->proto_algs_npkgs = 0;
688	new_proto->proto_exec_mode = proto_exec_mode;
689
690	return (write_new_algfile(protos, num_protos));
691}
692
693/*
694 * Delete an IPsec protocol entry from /etc/inet/ipsecalgs.  This also
695 * nukes the associated algorithms.
696 */
697int
698delipsecprotobynum(int proto_num)
699{
700	ipsec_proto_t *protos, *current_proto;
701	int num_protos;
702
703	if ((current_proto = proto_setup(&protos, &num_protos, proto_num,
704	    B_TRUE)) == NULL)
705		return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO);
706
707	delipsecproto_common(protos, num_protos--, current_proto);
708
709	return (write_new_algfile(protos, num_protos));
710}
711
712int
713delipsecprotobyname(const char *proto_name)
714{
715	int proto_num;
716
717	proto_num = getipsecprotobyname(proto_name);
718	if (proto_num == -1)
719		return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO);
720
721	return (delipsecprotobynum(proto_num));
722}
723
724/*
725 * Implement these in libnsl since these are read-only operations.
726 */
727int *
728getipsecprotos(int *nentries)
729{
730	return (_real_getipsecprotos(nentries));
731}
732
733int *
734getipsecalgs(int *nentries, int proto_num)
735{
736	return (_real_getipsecalgs(nentries, proto_num));
737}
738
739const char *
740ipsecalgs_diag(int diag)
741{
742	switch (diag) {
743	case LIBIPSEC_ALGS_DIAG_ALG_EXISTS:
744		return (dgettext(TEXT_DOMAIN, "Algorithm already exists"));
745	case LIBIPSEC_ALGS_DIAG_PROTO_EXISTS:
746		return (dgettext(TEXT_DOMAIN, "Protocol already exists"));
747	case LIBIPSEC_ALGS_DIAG_UNKN_PROTO:
748		return (dgettext(TEXT_DOMAIN, "Unknown protocol"));
749	case LIBIPSEC_ALGS_DIAG_UNKN_ALG:
750		return (dgettext(TEXT_DOMAIN, "Unknown algorithm"));
751	case LIBIPSEC_ALGS_DIAG_NOMEM:
752		return (dgettext(TEXT_DOMAIN, "Out of memory"));
753	case LIBIPSEC_ALGS_DIAG_ALGSFILEOPEN:
754		return (dgettext(TEXT_DOMAIN, "open() failed"));
755	case LIBIPSEC_ALGS_DIAG_ALGSFILEFDOPEN:
756		return (dgettext(TEXT_DOMAIN, "fdopen() failed"));
757	case LIBIPSEC_ALGS_DIAG_ALGSFILELOCK:
758		return (dgettext(TEXT_DOMAIN, "lockf() failed"));
759	case LIBIPSEC_ALGS_DIAG_ALGSFILERENAME:
760		return (dgettext(TEXT_DOMAIN, "rename() failed"));
761	case LIBIPSEC_ALGS_DIAG_ALGSFILEWRITE:
762		return (dgettext(TEXT_DOMAIN, "write to file failed"));
763	case LIBIPSEC_ALGS_DIAG_ALGSFILECHMOD:
764		return (dgettext(TEXT_DOMAIN, "chmod() failed"));
765	case LIBIPSEC_ALGS_DIAG_ALGSFILECHOWN:
766		return (dgettext(TEXT_DOMAIN, "chown() failed"));
767	case LIBIPSEC_ALGS_DIAG_ALGSFILECLOSE:
768		return (dgettext(TEXT_DOMAIN, "close() failed"));
769	default:
770		return (dgettext(TEXT_DOMAIN, "failed"));
771	}
772}
773
774/*
775 * Get the execution mode corresponding to the specified protocol.
776 * Returns 0 on success, one of the LIBIPSEC_ALGS_DIAG_* values on
777 * failure.
778 */
779int
780ipsecproto_get_exec_mode(int proto_num, ipsecalgs_exec_mode_t *exec_mode)
781{
782	ipsec_proto_t *protos, *current_proto;
783	int num_protos;
784
785	if ((current_proto = proto_setup(&protos, &num_protos, proto_num,
786	    B_TRUE)) == NULL)
787		return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO);
788
789	*exec_mode = current_proto->proto_exec_mode;
790
791	_clean_trash(protos, num_protos);
792	return (0);
793}
794
795/*
796 * Set the execution mode of the specified protocol. Returns 0 on success,
797 * or one of the LIBIPSEC_ALGS_DIAG_* values on failure.
798 */
799int
800ipsecproto_set_exec_mode(int proto_num, ipsecalgs_exec_mode_t exec_mode)
801{
802	ipsec_proto_t *protos, *current_proto;
803	int num_protos;
804
805	if ((current_proto = proto_setup(&protos, &num_protos, proto_num,
806	    B_TRUE)) == NULL)
807		return (LIBIPSEC_ALGS_DIAG_UNKN_PROTO);
808
809	current_proto->proto_exec_mode = exec_mode;
810
811	return (write_new_algfile(protos, num_protos));
812}
813