xref: /illumos-gate/usr/src/lib/libnsl/ipsec/algs.c (revision 7c478bd9)
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 2003 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/errno.h>
31 #include <sys/stat.h>
32 #include <ipsec_util.h>
33 #include <netdb.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <synch.h>
37 #include <string.h>
38 #include <strings.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <syslog.h>
42 
43 /* Globals... */
44 static rwlock_t proto_rw = DEFAULTRWLOCK; /* Protects cached algorithm list. */
45 static time_t proto_last_update;
46 static ipsec_proto_t *protos;
47 static int num_protos;
48 
49 void
50 _clean_trash(ipsec_proto_t *proto, int num)
51 {
52 	int alg_offset;
53 
54 	if (proto == NULL)
55 		return;
56 
57 	while (num-- != 0) {
58 		free(proto[num].proto_name);
59 		free(proto[num].proto_pkg);
60 		for (alg_offset = 0; alg_offset < proto[num].proto_numalgs;
61 		    alg_offset++)
62 			freeipsecalgent(proto[num].proto_algs[alg_offset]);
63 		free(proto[num].proto_algs);
64 		for (alg_offset = 0; alg_offset < proto[num].proto_algs_npkgs;
65 		    alg_offset++)
66 			free(proto[num].proto_algs_pkgs[alg_offset].pkg_name);
67 		free(proto[num].proto_algs_pkgs);
68 	}
69 
70 	free(proto);
71 }
72 
73 static const char *pipechar = "|";
74 static const char *comma = ",";
75 static const char *dash = "-";
76 static const char *slash = "/";
77 
78 /*
79  * Returns >= 0 if success (and > 0 means "increment").
80  * Returns -1 if failure.
81  */
82 static int
83 build_keysizes(int **sizep, char *input_string)
84 {
85 	char *lasts, *token;
86 	int *key_sizes = NULL, num_sizes, key_low, key_high, key_default;
87 	int key_increment = 0;
88 
89 	/*
90 	 * Okay, let's check the format of the key string.  It'll be either:
91 	 *
92 	 * enumeration: size1,size2...,sizeN
93 	 * range: defaultSize/sizeLow-sizeHi,increment
94 	 *
95 	 * In the case of an enumeration, the default key size is the
96 	 * first one in the list.
97 	 */
98 
99 	if (strchr(input_string, '/') != NULL) {
100 		/* key sizes specified by range */
101 
102 		/* default */
103 		token = strtok_r(input_string, slash, &lasts);
104 		if (token == NULL || (key_default = atoi(token)) == 0)
105 			return (-1);
106 
107 		/* low */
108 		token = strtok_r(NULL, dash, &lasts);
109 		if (token == NULL || (key_low = atoi(token)) == 0)
110 			return (-1);
111 
112 		/* high */
113 		token = strtok_r(NULL, comma, &lasts);
114 		if (token == NULL || (key_high = atoi(token)) == 0 ||
115 		    key_high <= key_low)
116 			return (-1);
117 
118 		/* increment */
119 		token = strtok_r(NULL, "", &lasts);
120 		if (token == NULL || (key_increment = atoi(token)) == 0)
121 			return (-1);
122 
123 		key_sizes = (int *)malloc(LIBIPSEC_ALGS_KEY_NUM_VAL *
124 		    sizeof (int));
125 		if (key_sizes == NULL)
126 			return (-1);
127 
128 		key_sizes[LIBIPSEC_ALGS_KEY_DEF_IDX] = key_default;
129 		key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX] = key_low;
130 		key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX] = key_high;
131 		key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX + 1] = 0;
132 	} else {
133 		/* key sizes specified by enumeration */
134 
135 		key_sizes = (int *)malloc(sizeof (int));
136 		if (key_sizes == NULL)
137 			return (-1);
138 		num_sizes = 0;
139 
140 		token = strtok_r(input_string, comma, &lasts);
141 		if (token == NULL || key_sizes == NULL)
142 			return (-1);
143 		*key_sizes = 0;
144 		do {
145 			int *nks;
146 
147 			nks = (int *)realloc(key_sizes,
148 			    sizeof (int) * ((++num_sizes) + 1));
149 			if (nks == NULL) {
150 				free(key_sizes);
151 				return (-1);
152 			}
153 			key_sizes = nks;
154 			/* Can't check for atoi() == 0 here... */
155 			key_sizes[num_sizes - 1] = atoi(token);
156 			key_sizes[num_sizes] = 0;
157 		} while ((token = strtok_r(NULL, comma, &lasts)) != NULL);
158 	}
159 
160 	*sizep = key_sizes;
161 
162 	return (key_increment);
163 }
164 
165 /*
166  * Find the execution mode corresponding to the given string.
167  * Returns 0 on success, -1 on failure.
168  */
169 int
170 _str_to_ipsec_exec_mode(char *str, ipsecalgs_exec_mode_t *exec_mode)
171 {
172 	if (strcmp(str, "sync") == 0) {
173 		*exec_mode = LIBIPSEC_ALGS_EXEC_SYNC;
174 		return (0);
175 	} else if (strcmp(str, "async") == 0) {
176 		*exec_mode = LIBIPSEC_ALGS_EXEC_ASYNC;
177 		return (0);
178 	}
179 
180 	return (-1);
181 }
182 
183 /*
184  * Given a file pointer, read all the text from the file and convert it into
185  * a bunch of ipsec_proto_t's, each with an array of struct ipsecalgent
186  * pointers - one for each algorithm.
187  */
188 static ipsec_proto_t *
189 build_list(FILE *f, int *num)
190 {
191 	char line[1024];
192 	char *token, *lasts, *alg_names, *ef_name, *key_string, *block_string;
193 	char *proto_name;
194 	ipsec_proto_t *rc = NULL, *new_proto = NULL;
195 	int *block_sizes, *key_sizes;
196 	int rc_num = 0, key_increment;
197 	int new_num, alg_num, num_sizes;
198 	struct ipsecalgent *curalg, **newalglist;
199 	char cur_pkg[1024];
200 	boolean_t doing_pkg = B_FALSE;
201 	ipsecalgs_exec_mode_t exec_mode;
202 	char diag_buf[128];
203 
204 	diag_buf[0] = '\0';
205 
206 	while (fgets(line, sizeof (line), f) != NULL) {
207 		if (strncasecmp(line, LIBIPSEC_ALGS_LINE_PROTO,
208 		    sizeof (LIBIPSEC_ALGS_LINE_PROTO) - 1) != 0 &&
209 		    strncasecmp(line, LIBIPSEC_ALGS_LINE_ALG,
210 		    sizeof (LIBIPSEC_ALGS_LINE_ALG) - 1) != 0 &&
211 		    strncasecmp(line, LIBIPSEC_ALGS_LINE_PKGSTART,
212 		    sizeof (LIBIPSEC_ALGS_LINE_PKGSTART) - 1) != 0 &&
213 		    strncasecmp(line, LIBIPSEC_ALGS_LINE_PKGEND,
214 		    sizeof (LIBIPSEC_ALGS_LINE_PKGEND) - 1) != 0) {
215 			if ((token = strtok_r(line, " \t\n", &lasts)) == NULL ||
216 			    token[0] == '#') {
217 				continue;
218 			} else {
219 				snprintf(diag_buf, sizeof (diag_buf),
220 					"non-recognized start of line");
221 				goto bail;
222 			}
223 		}
224 
225 		if (strncasecmp(line, LIBIPSEC_ALGS_LINE_PROTO,
226 		    sizeof (LIBIPSEC_ALGS_LINE_PROTO) - 1) == 0) {
227 			/* current line defines a new protocol */
228 
229 			/* skip the protocol token */
230 			token = strtok_r(line, pipechar, &lasts);
231 
232 			/* protocol number */
233 			token = strtok_r(NULL, pipechar, &lasts);
234 			if (token == NULL || (new_num = atoi(token)) == 0) {
235 				snprintf(diag_buf, sizeof (diag_buf),
236 				    "invalid protocol number");
237 				goto bail;
238 			}
239 
240 			/* protocol name */
241 			token = strtok_r(NULL, pipechar, &lasts);
242 			if (token == NULL) {
243 				snprintf(diag_buf, sizeof (diag_buf),
244 				    "cannot read protocol name");
245 				goto bail;
246 			}
247 			proto_name = token;
248 
249 			/* execution mode */
250 			token = strtok_r(NULL, pipechar, &lasts);
251 			if (token == NULL) {
252 				snprintf(diag_buf, sizeof (diag_buf),
253 				    "cannot read execution mode");
254 				goto bail;
255 			}
256 			/* remove trailing '\n' */
257 			token[strlen(token) - 1] = '\0';
258 			if (_str_to_ipsec_exec_mode(token, &exec_mode) != 0) {
259 				snprintf(diag_buf, sizeof (diag_buf),
260 				    "invalid execution mode: \"%s\"", token);
261 				goto bail;
262 			}
263 
264 			/* initialize protocol structure */
265 			rc_num++;
266 			new_proto = (ipsec_proto_t *)realloc(rc,
267 			    sizeof (ipsec_proto_t) * rc_num);
268 			rc = new_proto;
269 			if (new_proto == NULL)
270 				goto bail;
271 			new_proto += (rc_num - 1);
272 			new_proto->proto_num = new_num;
273 			new_proto->proto_algs = NULL;
274 			new_proto->proto_numalgs = 0;
275 			new_proto->proto_name = strdup(proto_name);
276 			if (new_proto->proto_name == NULL)
277 				goto bail;
278 			new_proto->proto_exec_mode = exec_mode;
279 
280 			if (doing_pkg) {
281 				/* record proto as being part of current pkg */
282 				new_proto->proto_pkg = strdup(cur_pkg);
283 				if (new_proto->proto_pkg == NULL)
284 					goto bail;
285 			} else {
286 				new_proto->proto_pkg = NULL;
287 			}
288 
289 			new_proto->proto_algs_pkgs = NULL;
290 			new_proto->proto_algs_npkgs = 0;
291 
292 		} else if (strncasecmp(line, LIBIPSEC_ALGS_LINE_ALG,
293 		    sizeof (LIBIPSEC_ALGS_LINE_ALG) - 1) == 0) {
294 			/* current line defines a new algorithm */
295 
296 			/* skip the algorithm token */
297 			token = strtok_r(line, pipechar, &lasts);
298 
299 			/* protocol number */
300 			token = strtok_r(NULL, pipechar, &lasts);
301 			if (token == NULL || (new_num = atoi(token)) == 0) {
302 				snprintf(diag_buf, sizeof (diag_buf),
303 				    "invalid algorithm number");
304 				goto bail;
305 			}
306 
307 			/* We can be O(N) for now.  There aren't that many. */
308 			for (new_proto = rc; new_proto < (rc + new_num);
309 			    new_proto++)
310 				if (new_proto->proto_num == new_num)
311 					break;
312 			if (new_proto == (rc + new_num)) {
313 				snprintf(diag_buf, sizeof (diag_buf),
314 				    "invalid protocol number %d for algorithm",
315 				    new_num);
316 				goto bail;
317 			}
318 
319 			/* algorithm number */
320 			token = strtok_r(NULL, pipechar, &lasts);
321 			if (token == NULL) {
322 				snprintf(diag_buf, sizeof (diag_buf),
323 				    "cannot read algorithm number");
324 				goto bail;
325 			}
326 			/* Can't check for 0 here. */
327 			alg_num = atoi(token);
328 
329 			/* algorithm names */
330 			token = strtok_r(NULL, pipechar, &lasts);
331 			if (token == NULL) {
332 				snprintf(diag_buf, sizeof (diag_buf),
333 				    "cannot read algorithm number");
334 				goto bail;
335 			}
336 			alg_names = token;
337 
338 			/* mechanism name */
339 			token = strtok_r(NULL, pipechar, &lasts);
340 			if (token == NULL) {
341 				snprintf(diag_buf, sizeof (diag_buf),
342 				    "cannot read mechanism name for alg %d "
343 				    "(proto %d)", alg_num,
344 				    new_proto->proto_num);
345 				goto bail;
346 			}
347 			ef_name = token;
348 
349 			/* key sizes */
350 			token = strtok_r(NULL, pipechar, &lasts);
351 			if (token == NULL) {
352 				snprintf(diag_buf, sizeof (diag_buf),
353 				    "cannot read key sizes for alg %d "
354 				    "(proto %d)", alg_num,
355 				    new_proto->proto_num);
356 				goto bail;
357 			}
358 			key_string = token;
359 
360 			/* block sizes */
361 			token = strtok_r(NULL, pipechar, &lasts);
362 			if (token == NULL) {
363 				snprintf(diag_buf, sizeof (diag_buf),
364 				    "cannot read mechanism name for alg %d "
365 				    "(proto %d)", alg_num,
366 				    new_proto->proto_num);
367 				goto bail;
368 			}
369 			block_string = token;
370 
371 			/* extract key sizes */
372 			key_increment = build_keysizes(&key_sizes, key_string);
373 			if (key_increment == -1) {
374 				snprintf(diag_buf, sizeof (diag_buf),
375 				    "invalid key sizes for alg %d (proto %d)",
376 				    alg_num, new_proto->proto_num);
377 				goto bail;
378 			}
379 
380 			/* extract block sizes */
381 			block_sizes = (int *)malloc(sizeof (int));
382 			if (block_sizes == NULL) {
383 				free(key_sizes);
384 				goto bail;
385 			}
386 			num_sizes = 0;
387 			token = strtok_r(block_string, comma, &lasts);
388 			if (token == NULL) {
389 				snprintf(diag_buf, sizeof (diag_buf),
390 				    "invalid block sizes for alg %d (proto %d)",
391 				    alg_num, new_proto->proto_num);
392 				free(key_sizes);
393 				goto bail;
394 			}
395 			*block_sizes = 0;
396 			do {
397 				int *nbk;
398 
399 				nbk = (int *)realloc(block_sizes,
400 				    sizeof (int) * ((++num_sizes) + 1));
401 				if (nbk == NULL) {
402 					free(key_sizes);
403 					free(block_sizes);
404 					goto bail;
405 				}
406 				block_sizes = nbk;
407 				/* Can't check for 0 here... */
408 				block_sizes[num_sizes - 1] = atoi(token);
409 				block_sizes[num_sizes] = 0;
410 			} while ((token = strtok_r(NULL, comma, &lasts)) !=
411 			    NULL);
412 
413 			/* Allocate a new struct ipsecalgent. */
414 			curalg = (struct ipsecalgent *)calloc(
415 			    sizeof (struct ipsecalgent), 1);
416 			if (curalg == NULL) {
417 				free(key_sizes);
418 				free(block_sizes);
419 				goto bail;
420 			}
421 			curalg->a_proto_num = new_num;
422 			curalg->a_alg_num = alg_num;
423 			curalg->a_block_sizes = block_sizes;
424 			curalg->a_key_sizes = key_sizes;
425 			curalg->a_key_increment = key_increment;
426 			if ((curalg->a_mech_name = strdup(ef_name)) == NULL) {
427 				freeipsecalgent(curalg);
428 				goto bail;
429 			}
430 			/* Set names. */
431 			curalg->a_names = (char **)malloc(sizeof (char *));
432 			num_sizes = 0;	/* Recycle "sizes" */
433 			token = strtok_r(alg_names, comma, &lasts);
434 			if (curalg->a_names == NULL || token == NULL) {
435 				freeipsecalgent(curalg);
436 				goto bail;
437 			}
438 			do {
439 				char **nnames;
440 
441 				nnames = (char **)realloc(curalg->a_names,
442 				    sizeof (char *) * ((++num_sizes) + 1));
443 				if (nnames == NULL) {
444 					freeipsecalgent(curalg);
445 					goto bail;
446 				}
447 				curalg->a_names = nnames;
448 				curalg->a_names[num_sizes] = NULL;
449 				curalg->a_names[num_sizes - 1] =
450 				    strdup(token);
451 				if (curalg->a_names[num_sizes - 1] == NULL) {
452 					freeipsecalgent(curalg);
453 					goto bail;
454 				}
455 			} while ((token = strtok_r(NULL, comma, &lasts)) !=
456 			    NULL);
457 
458 			if (doing_pkg) {
459 				/* record alg as being part of current pkg */
460 				int npkgs = new_proto->proto_algs_npkgs;
461 
462 				new_proto->proto_algs_pkgs = realloc(
463 				    new_proto->proto_algs_pkgs,
464 				    (npkgs + 1) * sizeof (ipsecalgs_pkg_t));
465 				if (new_proto->proto_algs_pkgs == NULL)
466 					goto bail;
467 
468 				new_proto->proto_algs_pkgs[npkgs].alg_num =
469 					curalg->a_alg_num;
470 				new_proto->proto_algs_pkgs[npkgs].pkg_name =
471 					strdup(cur_pkg);
472 				if (new_proto->proto_algs_pkgs[npkgs].pkg_name
473 				    == NULL)
474 					goto bail;
475 
476 				new_proto->proto_algs_npkgs = npkgs + 1;
477 			}
478 
479 			/* add new alg to protocol */
480 			newalglist = realloc(new_proto->proto_algs,
481 			    (new_proto->proto_numalgs + 1) *
482 			    sizeof (struct ipsecalgent *));
483 			if (newalglist == NULL) {
484 				freeipsecalgent(curalg);
485 				goto bail;
486 			}
487 			newalglist[new_proto->proto_numalgs] = curalg;
488 			new_proto->proto_numalgs++;
489 			new_proto->proto_algs = newalglist;
490 
491 		} else if (strncasecmp(line, LIBIPSEC_ALGS_LINE_PKGSTART,
492 		    sizeof (LIBIPSEC_ALGS_LINE_PKGSTART) - 1) == 0) {
493 			/* start of package delimiter */
494 			if (doing_pkg) {
495 				snprintf(diag_buf, sizeof (diag_buf),
496 				    "duplicate package start delimiters");
497 				goto bail;
498 			}
499 			(void) strncpy(cur_pkg, line +
500 			    (sizeof (LIBIPSEC_ALGS_LINE_PKGSTART) - 1),
501 			    sizeof (cur_pkg));
502 			/* remove trailing '\n' */
503 			cur_pkg[strlen(cur_pkg) - 1] = '\0';
504 			doing_pkg = B_TRUE;
505 
506 		} else {
507 			/* end of package delimiter */
508 			char tmp_pkg[1024];
509 
510 			if (!doing_pkg) {
511 				snprintf(diag_buf, sizeof (diag_buf),
512 				    "end package delimiter without start");
513 				goto bail;
514 			}
515 			/*
516 			 * Get specified pkg name, fail if it doesn't match
517 			 * the package specified by the last # Begin.
518 			 */
519 			(void) strncpy(tmp_pkg, line +
520 			    (sizeof (LIBIPSEC_ALGS_LINE_PKGEND) - 1),
521 			    sizeof (tmp_pkg));
522 			/* remove trailing '\n' */
523 			tmp_pkg[strlen(tmp_pkg) - 1] = '\0';
524 			if (strncmp(cur_pkg, tmp_pkg, sizeof (cur_pkg)) != 0)
525 				goto bail;
526 			doing_pkg = B_FALSE;
527 		}
528 	}
529 
530 	*num = rc_num;
531 	return (rc);
532 
533 bail:
534 	if (strlen(diag_buf) > 0) {
535 		syslog(LOG_ERR, "possibly corrupt %s file: %s\n",
536 		    INET_IPSECALGSFILE, diag_buf);
537 	}
538 	_clean_trash(rc, rc_num);
539 	return (NULL);
540 }
541 
542 /*
543  * If alg_context is NULL, update the library's cached copy of
544  * INET_IPSECALGSFILE.  If alg_context is non-NULL, hang a
545  * library-internal representation of a cached copy.  The latter is useful
546  * for routines in libipsecutil that _write_ the contents out.
547  */
548 void
549 _build_internal_algs(ipsec_proto_t **alg_context, int *alg_nums)
550 {
551 	FILE *f = NULL;
552 	int fd, rc, trash_num;
553 	ipsec_proto_t *new_protos = NULL, *trash;
554 	time_t filetime;
555 	struct stat statbuf;
556 
557 	/*
558 	 * Construct new_protos from the file.
559 	 */
560 	if (alg_context == NULL) {
561 		/*
562 		 * Check the time w/o holding the lock.  This is just a
563 		 * cache reality check.  We'll do it again for real if this
564 		 * surface check fails.
565 		 */
566 		if (stat(INET_IPSECALGSFILE, &statbuf) == -1 ||
567 		    (statbuf.st_mtime < proto_last_update &&
568 			protos != NULL))
569 			return;
570 		rw_wrlock(&proto_rw);
571 	}
572 
573 	fd = open(INET_IPSECALGSFILE, O_RDONLY);
574 	if (fd != -1) {
575 		f = fdopen(fd, "r");
576 		if (f == NULL) {
577 			close(fd);
578 		} else {
579 			rc = fstat(fd, &statbuf);
580 			if (rc != -1) {
581 				/*
582 				 * Update if the file is newer than our
583 				 * last cached copy.
584 				 */
585 				filetime = statbuf.st_mtime;
586 				if (alg_context != NULL ||
587 				    filetime > proto_last_update)
588 					new_protos = build_list(f, &rc);
589 			}
590 		}
591 	}
592 
593 	if (alg_context == NULL) {
594 		/*
595 		 * If we have failed anywhere above, new_protoss will be NULL.
596 		 * This way, the previous cached protos will still be intact.
597 		 */
598 		if (new_protos != NULL) {
599 			proto_last_update = filetime;
600 			trash = protos;
601 			trash_num = num_protos;
602 			protos = new_protos;
603 			num_protos = rc;
604 		} else {
605 			/*
606 			 * Else the original protocols and algorithms lists
607 			 * remains the same.
608 			 */
609 			trash = NULL;
610 		}
611 		rw_unlock(&proto_rw);
612 		_clean_trash(trash, trash_num);
613 	} else {
614 		/*
615 		 * Assume caller has done the appropriate locking,
616 		 * cleanup, etc.  And if new_protos is NULL, it's the caller's
617 		 * problem.
618 		 */
619 		*alg_context = new_protos;
620 		*alg_nums = rc;
621 	}
622 
623 	/* Since f is read-only, can avoid all of the failures... */
624 	if (f != NULL)
625 		(void) fclose(f);
626 }
627 
628 /*
629  * Assume input is 0-terminated.
630  */
631 static int *
632 duplicate_intarr(int *orig)
633 {
634 	size_t allocsize = sizeof (int);
635 	int *iwalker = orig;
636 
637 	if (orig == NULL)
638 		return (NULL);
639 
640 	while (*iwalker != 0) {
641 		allocsize += sizeof (int);
642 		iwalker++;
643 	}
644 
645 	iwalker = malloc(allocsize);
646 	if (iwalker != NULL)
647 		memcpy(iwalker, orig, allocsize);
648 
649 	return (iwalker);
650 }
651 
652 /*
653  * Assume input is NULL terminated.
654  */
655 static char **
656 duplicate_strarr(char **orig)
657 {
658 	int i;
659 	char **swalker;
660 	char **newbie;
661 
662 	if (orig == NULL)
663 		return (NULL);
664 
665 	/* count number of elements in source array */
666 	for (swalker = orig; *swalker != NULL; swalker++);
667 
668 	/* use calloc() to get NULL-initialization */
669 	newbie = calloc(swalker - orig + 1, sizeof (char *));
670 
671 	if (newbie != NULL) {
672 		/* do the copy */
673 		for (i = 0; orig[i] != NULL; i++) {
674 			newbie[i] = strdup(orig[i]);
675 			if (newbie[i] == NULL) {
676 				for (swalker = newbie; *swalker != NULL;
677 				    swalker++)
678 					free(*swalker);
679 				free(newbie);
680 				return (NULL);
681 			}
682 		}
683 	}
684 
685 	return (newbie);
686 }
687 
688 struct ipsecalgent *
689 _duplicate_alg(struct ipsecalgent *orig)
690 {
691 	struct ipsecalgent *rc;
692 
693 	/* use calloc() to get NULL-initialization. */
694 	rc = calloc(1, sizeof (struct ipsecalgent));
695 	if (rc == NULL)
696 		return (NULL);
697 
698 	rc->a_proto_num = orig->a_proto_num;
699 	rc->a_alg_num = orig->a_alg_num;
700 	rc->a_key_increment = orig->a_key_increment;
701 	rc->a_mech_name = strdup(orig->a_mech_name);
702 	rc->a_block_sizes = duplicate_intarr(orig->a_block_sizes);
703 	rc->a_key_sizes = duplicate_intarr(orig->a_key_sizes);
704 	rc->a_names = duplicate_strarr(orig->a_names);
705 
706 	if (rc->a_mech_name == NULL || rc->a_block_sizes == NULL ||
707 	    rc->a_key_sizes == NULL || rc->a_names == NULL) {
708 		freeipsecalgent(rc);
709 		return (NULL);
710 	}
711 
712 	return (rc);
713 }
714 
715 /*
716  * Assume the rwlock is held for reading.
717  */
718 static ipsec_proto_t *
719 findprotobynum(int proto_num)
720 {
721 	int i;
722 
723 	for (i = 0; i < num_protos; i++) {
724 		if (protos[i].proto_num == proto_num)
725 			return (protos + i);
726 	}
727 
728 	return (NULL);
729 }
730 
731 static ipsec_proto_t *
732 findprotobyname(const char *name)
733 {
734 	int i;
735 
736 	if (name == NULL)
737 		return (NULL);
738 
739 	for (i = 0; i < num_protos; i++) {
740 		/* Can use strcasecmp because our proto_name is bounded. */
741 		if (strcasecmp(protos[i].proto_name, name) == 0)
742 			return (protos + i);
743 	}
744 
745 	return (NULL);
746 }
747 
748 int *
749 _real_getipsecprotos(int *nentries)
750 {
751 	int *rc, i;
752 
753 	if (nentries == NULL)
754 		return (NULL);
755 
756 	_build_internal_algs(NULL, NULL);
757 
758 	rw_rdlock(&proto_rw);
759 	*nentries = num_protos;
760 	/*
761 	 * Allocate 1 byte if there are no protocols so a non-NULL return
762 	 * happens.
763 	 */
764 	rc = malloc((num_protos == 0) ? 1 : num_protos * sizeof (int));
765 	if (rc != NULL) {
766 		for (i = 0; i < num_protos; i++)
767 			rc[i] = protos[i].proto_num;
768 	}
769 	rw_unlock(&proto_rw);
770 	return (rc);
771 }
772 
773 int *
774 _real_getipsecalgs(int *nentries, int proto_num)
775 {
776 	int *rc = NULL, i;
777 	ipsec_proto_t *proto;
778 
779 	if (nentries == NULL)
780 		return (NULL);
781 
782 	_build_internal_algs(NULL, NULL);
783 
784 	rw_rdlock(&proto_rw);
785 	proto = findprotobynum(proto_num);
786 	if (proto != NULL) {
787 		*nentries = proto->proto_numalgs;
788 		/*
789 		 * Allocate 1 byte if there are no algorithms so a non-NULL
790 		 * return happens.
791 		 */
792 		rc = malloc((proto->proto_numalgs == 0) ? 1 :
793 		    proto->proto_numalgs * sizeof (int));
794 		if (rc != NULL) {
795 			for (i = 0; i < proto->proto_numalgs; i++)
796 				rc[i] = proto->proto_algs[i]->a_alg_num;
797 		}
798 	}
799 	rw_unlock(&proto_rw);
800 	return (rc);
801 }
802 
803 struct ipsecalgent *
804 getipsecalgbyname(const char *name, int proto_num, int *errnop)
805 {
806 	ipsec_proto_t *proto;
807 	struct ipsecalgent *rc = NULL;
808 	int i, my_errno = ENOENT;
809 	char **name_check;
810 
811 	_build_internal_algs(NULL, NULL);
812 	if (name == NULL) {
813 		my_errno = EFAULT;
814 		goto bail;
815 	}
816 
817 	rw_rdlock(&proto_rw);
818 	proto = findprotobynum(proto_num);
819 	if (proto != NULL) {
820 		for (i = 0; i < proto->proto_numalgs; i++) {
821 			for (name_check = proto->proto_algs[i]->a_names;
822 			    *name_check != NULL; name_check++) {
823 				/*
824 				 * Can use strcasecmp because our name_check
825 				 * is bounded.
826 				 */
827 				if (strcasecmp(*name_check, name) == 0) {
828 					/* found match */
829 					rc = _duplicate_alg(
830 					    proto->proto_algs[i]);
831 					my_errno = (rc == NULL) ? ENOMEM : 0;
832 					rw_unlock(&proto_rw);
833 					goto bail;
834 				}
835 			}
836 		}
837 	} else {
838 		my_errno = EINVAL;
839 	}
840 
841 	rw_unlock(&proto_rw);
842 bail:
843 	if (errnop != NULL)
844 		*errnop = my_errno;
845 	return (rc);
846 }
847 
848 struct ipsecalgent *
849 getipsecalgbynum(int alg_num, int proto_num, int *errnop)
850 {
851 	ipsec_proto_t *proto;
852 	struct ipsecalgent *rc = NULL;
853 	int i, my_errno = ENOENT;
854 
855 	_build_internal_algs(NULL, NULL);
856 
857 	rw_rdlock(&proto_rw);
858 
859 	proto = findprotobynum(proto_num);
860 	if (proto != NULL) {
861 		for (i = 0; i < proto->proto_numalgs; i++) {
862 			if (proto->proto_algs[i]->a_alg_num == alg_num) {
863 				rc = _duplicate_alg(proto->proto_algs[i]);
864 				my_errno = (rc == NULL) ? ENOMEM : 0;
865 				break;
866 			}
867 		}
868 	} else {
869 		my_errno = EINVAL;
870 	}
871 
872 	rw_unlock(&proto_rw);
873 	if (errnop != NULL)
874 		*errnop = my_errno;
875 	return (rc);
876 }
877 
878 int
879 getipsecprotobyname(const char *proto_name)
880 {
881 	int rc = -1;
882 	ipsec_proto_t *proto;
883 
884 	_build_internal_algs(NULL, NULL);
885 
886 	rw_rdlock(&proto_rw);
887 	proto = findprotobyname(proto_name);
888 	if (proto != NULL)
889 		rc = proto->proto_num;
890 	rw_unlock(&proto_rw);
891 	return (rc);
892 }
893 
894 char *
895 getipsecprotobynum(int proto_num)
896 {
897 	ipsec_proto_t *proto;
898 	char *rc = NULL;
899 
900 	_build_internal_algs(NULL, NULL);
901 
902 	rw_rdlock(&proto_rw);
903 	proto = findprotobynum(proto_num);
904 	if (proto != NULL)
905 		rc = strdup(proto->proto_name);
906 
907 	rw_unlock(&proto_rw);
908 	return (rc);
909 }
910 
911 void
912 freeipsecalgent(struct ipsecalgent *ptr)
913 {
914 	char **walker;
915 
916 	if (ptr == NULL)
917 		return;
918 
919 	if (ptr->a_names != NULL) {
920 		for (walker = ptr->a_names; *walker != NULL; walker++)
921 			free(*walker);
922 	}
923 
924 	/*
925 	 * Remember folks, free(NULL) works.
926 	 */
927 	free(ptr->a_names);
928 	free(ptr->a_mech_name);
929 	free(ptr->a_block_sizes);
930 	free(ptr->a_key_sizes);
931 	free(ptr);
932 }
933