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