xref: /illumos-gate/usr/src/lib/libipsecutil/common/algs.c (revision 2bbdd445a21f9d61f4a0ca0faf05d5ceb2bd91f3)
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 
37 static 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  */
98 int
99 list_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  */
124 static char *
125 alg_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  */
146 static int
147 pkg_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 	}
168 bail:
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  */
183 static int
184 write_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 
331 bail:
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  */
341 static ipsec_proto_t *
342 proto_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  */
376 static boolean_t
377 delipsecalgbyname_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  */
416 static boolean_t
417 sizes_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  */
435 static boolean_t
436 ipsecalg_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  */
483 int
484 addipsecalg(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  */
574 int
575 delipsecalgbyname(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  */
594 int
595 delipsecalgbynum(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  */
630 static void
631 delipsecproto_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  */
649 int
650 addipsecproto(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  */
720 int
721 delipsecprotobynum(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 
735 int
736 delipsecprotobyname(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  */
750 int *
751 getipsecprotos(int *nentries)
752 {
753 	return (_real_getipsecprotos(nentries));
754 }
755 
756 int *
757 getipsecalgs(int *nentries, int proto_num)
758 {
759 	return (_real_getipsecalgs(nentries, proto_num));
760 }
761 
762 const char *
763 ipsecalgs_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  */
802 int
803 ipsecproto_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  */
822 int
823 ipsecproto_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