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