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