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 2006 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 
39 static 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  */
91 int
92 list_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  */
117 static char *
118 alg_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  */
139 static int
140 pkg_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 	}
161 bail:
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  */
176 static int
177 write_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 
308 bail:
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  */
318 static ipsec_proto_t *
319 proto_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  */
353 static boolean_t
354 delipsecalgbyname_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  */
393 static boolean_t
394 sizes_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  */
412 static boolean_t
413 ipsecalg_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  */
460 int
461 addipsecalg(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  */
551 int
552 delipsecalgbyname(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  */
571 int
572 delipsecalgbynum(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  */
607 static void
608 delipsecproto_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  */
626 int
627 addipsecproto(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  */
697 int
698 delipsecprotobynum(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 
712 int
713 delipsecprotobyname(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  */
727 int *
728 getipsecprotos(int *nentries)
729 {
730 	return (_real_getipsecprotos(nentries));
731 }
732 
733 int *
734 getipsecalgs(int *nentries, int proto_num)
735 {
736 	return (_real_getipsecalgs(nentries, proto_num));
737 }
738 
739 const char *
740 ipsecalgs_diag(int diag)
741 {
742 	switch (diag) {
743 	case LIBIPSEC_ALGS_DIAG_ALG_EXISTS:
744 		return (gettext("Algorithm already exists"));
745 	case LIBIPSEC_ALGS_DIAG_PROTO_EXISTS:
746 		return (gettext("Protocol already exists"));
747 	case LIBIPSEC_ALGS_DIAG_UNKN_PROTO:
748 		return (gettext("Unknown protocol"));
749 	case LIBIPSEC_ALGS_DIAG_UNKN_ALG:
750 		return (gettext("Unknown algorithm"));
751 	case LIBIPSEC_ALGS_DIAG_NOMEM:
752 		return (gettext("Out of memory"));
753 	case LIBIPSEC_ALGS_DIAG_ALGSFILEOPEN:
754 		return (gettext("open() failed"));
755 	case LIBIPSEC_ALGS_DIAG_ALGSFILEFDOPEN:
756 		return (gettext("fdopen() failed"));
757 	case LIBIPSEC_ALGS_DIAG_ALGSFILELOCK:
758 		return (gettext("lockf() failed"));
759 	case LIBIPSEC_ALGS_DIAG_ALGSFILERENAME:
760 		return (gettext("rename() failed"));
761 	case LIBIPSEC_ALGS_DIAG_ALGSFILEWRITE:
762 		return (gettext("write to file failed"));
763 	case LIBIPSEC_ALGS_DIAG_ALGSFILECHMOD:
764 		return (gettext("chmod() failed"));
765 	case LIBIPSEC_ALGS_DIAG_ALGSFILECHOWN:
766 		return (gettext("chown() failed"));
767 	case LIBIPSEC_ALGS_DIAG_ALGSFILECLOSE:
768 		return (gettext("close() failed"));
769 	default:
770 		return (gettext("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  */
779 int
780 ipsecproto_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  */
799 int
800 ipsecproto_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