xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/ipsecutils/ipsecalgs.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <ipsec_util.h>
30 #include <netdb.h>
31 #include <locale.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <assert.h>
36 #include <unistd.h>
37 #include <net/pfpolicy.h>
38 #include <strings.h>
39 #include <errno.h>
40 #include <sys/crypto/common.h>
41 #include <zone.h>
42 
43 #define	SPDSOCK_DIAG_BUF_LEN	128
44 
45 typedef enum cmd_s {
46 	CMD_NONE = 0,
47 	CMD_ADD,
48 	CMD_ADD_PROTO,
49 	CMD_DEL,
50 	CMD_DEL_PROTO,
51 	CMD_EXEC_MODE,
52 	CMD_LIST_KERNEL
53 } cmd_t;
54 
55 static const char *comma = ",";
56 static int adddel_flags, increment = 0, default_keylen;
57 static boolean_t synch_kernel;
58 static cmd_t cmd = CMD_NONE;
59 static int proto_number = -1, alg_number = -1;
60 static char *proto_name, *alg_names_string, *block_sizes_string;
61 static char *key_sizes_string, *mech_name, *exec_mode_string;
62 static ipsecalgs_exec_mode_t proto_exec_mode = LIBIPSEC_ALGS_EXEC_SYNC;
63 
64 /*
65  * Used by the algorithm walker callback to populate a SPD_UPDATEALGS
66  * request.
67  */
68 
69 #define	SYNC_REQ_SIZE	2048
70 
71 static uint64_t sync_req_buf[SYNC_REQ_SIZE];
72 static struct spd_attribute *sync_req_attr;
73 static uint_t sync_req_alg_count, sync_req_proto_count;
74 
75 #define	EMIT(ap, tag, value) {					\
76 		(ap)->spd_attr_tag = (tag);			\
77 		(ap)->spd_attr_value = (value);			\
78 		(ap)++;						\
79 		if ((char *)(ap) + sizeof (*ap) -		\
80 		    (char *)sync_req_buf > SYNC_REQ_SIZE)	\
81 			bail_nomem();				\
82 	}
83 
84 static void dump_alg(struct ipsecalgent *);
85 static void algs_walker(void (*)(struct ipsecalgent *), void (*)(uint_t));
86 
87 static void
88 usage(void)
89 {
90 	errx(EXIT_FAILURE, gettext("Usage:\tipsecalgs\n"
91 		"\tipsecalgs -l\n"
92 		"\tipsecalgs -s\n"
93 		"\tipsecalgs -a [-P protocol-number | -p protocol-name]\n"
94 		"\t\t-k keylen-list [-i inc]\n"
95 		"\t\t[-K default-keylen] -b blocklen-list\n"
96 		"\t\t-n alg-names -N alg-number -m mech-name [-f] [-s]\n"
97 		"\tipsecalgs -P protocol-number -p protocol-name\n"
98 		"\t\t[-e exec-mode] [-f] [-s]\n"
99 		"\tipsecalgs -r -p protocol-name -n alg-name [-s]\n"
100 		"\tipsecalgs -r -p protocol-name -N alg-number [-s]\n"
101 		"\tipsecalgs -R -P protocol-number [-s]\n"
102 		"\tipsecalgs -R -p protocol-name [-s]\n"
103 		"\tipsecalgs -e exec-mode -P protocol-number [-s]\n"
104 		"\tipsecalgs -e exec-mode -p protocol-number [-s]"));
105 }
106 
107 static void
108 bail_nomem(void)
109 {
110 	errx(EXIT_FAILURE, gettext("Out of memory."));
111 }
112 
113 /*
114  * Return the number of key or block sizes in the specified array.
115  */
116 static uint_t
117 num_sizes(int *sizes)
118 {
119 	uint_t nsizes = 0;
120 
121 	while (sizes[nsizes] != 0)
122 		nsizes++;
123 
124 	return (nsizes);
125 }
126 
127 /*
128  * Algorithms walker callback. Adds an algorithm to the current SPD_UPDATEALGS
129  * request.
130  */
131 static void
132 synch_emit_alg(struct ipsecalgent *alg)
133 {
134 	uint_t nkey_sizes, nblock_sizes, i;
135 
136 	EMIT(sync_req_attr, SPD_ATTR_ALG_ID, alg->a_alg_num);
137 	EMIT(sync_req_attr, SPD_ATTR_ALG_PROTO, alg->a_proto_num);
138 	EMIT(sync_req_attr, SPD_ATTR_ALG_INCRBITS, alg->a_key_increment);
139 
140 	nkey_sizes = num_sizes(alg->a_key_sizes);
141 	EMIT(sync_req_attr, SPD_ATTR_ALG_NKEYSIZES, nkey_sizes);
142 	for (i = 0; i < nkey_sizes; i++)
143 		EMIT(sync_req_attr, SPD_ATTR_ALG_KEYSIZE, alg->a_key_sizes[i]);
144 
145 	nblock_sizes = num_sizes(alg->a_block_sizes);
146 	EMIT(sync_req_attr, SPD_ATTR_ALG_NBLOCKSIZES, nblock_sizes);
147 	for (i = 0; i < nblock_sizes; i++) {
148 		EMIT(sync_req_attr, SPD_ATTR_ALG_BLOCKSIZE,
149 		    alg->a_block_sizes[i]);
150 	}
151 
152 	EMIT(sync_req_attr, SPD_ATTR_ALG_MECHNAME, CRYPTO_MAX_MECH_NAME);
153 	(void) strncpy((char *)sync_req_attr, alg->a_mech_name,
154 	    CRYPTO_MAX_MECH_NAME);
155 	sync_req_attr = (struct spd_attribute *)((uint64_t *)sync_req_attr +
156 	    SPD_8TO64(CRYPTO_MAX_MECH_NAME));
157 
158 	EMIT(sync_req_attr, SPD_ATTR_NEXT, 0);
159 
160 	sync_req_alg_count++;
161 }
162 
163 /*
164  * Protocol walker callback. Add protocol related info to the current
165  * SPD_UPDATEALGS request.
166  */
167 static void
168 synch_emit_proto(uint_t proto_num)
169 {
170 	ipsecalgs_exec_mode_t exec_mode;
171 	uint32_t exec_mode_spdval;
172 
173 	EMIT(sync_req_attr, SPD_ATTR_PROTO_ID, proto_num);
174 
175 	/* execution mode */
176 	if (ipsecproto_get_exec_mode(proto_num, &exec_mode) != 0) {
177 		errx(EXIT_FAILURE, gettext("cannot get execution mode for "
178 		    "proto %d"), proto_num);
179 	}
180 
181 	switch (exec_mode) {
182 	case LIBIPSEC_ALGS_EXEC_SYNC:
183 		exec_mode_spdval = SPD_ALG_EXEC_MODE_SYNC;
184 		break;
185 	case LIBIPSEC_ALGS_EXEC_ASYNC:
186 		exec_mode_spdval = SPD_ALG_EXEC_MODE_ASYNC;
187 		break;
188 	}
189 	EMIT(sync_req_attr, SPD_ATTR_PROTO_EXEC_MODE, exec_mode_spdval);
190 
191 	EMIT(sync_req_attr, SPD_ATTR_NEXT, 0);
192 
193 	sync_req_proto_count++;
194 }
195 
196 /*
197  * Causes the kernel to be re-synched with the contents of /etc/inet/algs
198  */
199 static void
200 kernel_synch(void)
201 {
202 	int sfd = socket(PF_POLICY, SOCK_RAW, PF_POLICY_V1);
203 	int cnt, req_len;
204 	struct spd_msg *msg;
205 	struct spd_ext_actions *act;
206 	struct spd_attribute *attr;
207 
208 	if (sfd < 0) {
209 		err(EXIT_FAILURE, gettext("Unable to open policy socket"));
210 	}
211 
212 	/*
213 	 * Initialize the SPD message header and action. Some fields
214 	 * are set after having walked through the algorithms (number
215 	 * of algorithms, sizes, etc.)
216 	 */
217 	msg = (struct spd_msg *)sync_req_buf;
218 	(void) memset(msg, 0, sizeof (*msg));
219 	msg->spd_msg_version = PF_POLICY_V1;
220 	msg->spd_msg_type = SPD_UPDATEALGS;
221 
222 	act = (struct spd_ext_actions *)(msg + 1);
223 	act->spd_actions_exttype = SPD_EXT_ACTION;
224 	act->spd_actions_reserved = 0;
225 
226 	/*
227 	 * Walk through the algorithms defined and populate the
228 	 * request buffer.
229 	 */
230 	sync_req_alg_count = 0;
231 	sync_req_proto_count = 0;
232 	sync_req_attr = (struct spd_attribute *)(act + 1);
233 	algs_walker(synch_emit_alg, synch_emit_proto);
234 	act->spd_actions_count = sync_req_alg_count + sync_req_proto_count;
235 
236 	/*
237 	 * Replace the last SPD_ATTR_NEXT attribute by a SPD_ATTR_END.
238 	 */
239 	attr = sync_req_attr - 1;
240 	attr->spd_attr_tag = SPD_ATTR_END;
241 
242 	/*
243 	 * Now that the message is built, compute its total length and
244 	 * update the length fields that depend on this value.
245 	 */
246 	req_len = (char *)sync_req_attr - (char *)sync_req_buf;
247 	msg->spd_msg_len = SPD_8TO64(req_len);
248 	act->spd_actions_len = SPD_8TO64(req_len - sizeof (*msg));
249 
250 	/* ship the update request to spdsock */
251 	cnt = write(sfd, sync_req_buf, req_len);
252 	if (cnt != req_len) {
253 		if (cnt < 0) {
254 			err(EXIT_FAILURE, gettext("algs update write failed"));
255 		} else {
256 			errx(EXIT_FAILURE, gettext("algs update short write"));
257 		}
258 		/* err/errx call exit(). */
259 	}
260 
261 	cnt = read(sfd, sync_req_buf, req_len);
262 
263 	if (cnt == -1) {
264 		err(EXIT_FAILURE, gettext("algs update read failed"));
265 	}
266 
267 	if (cnt < sizeof (struct spd_msg)) {
268 		errx(EXIT_FAILURE, gettext(
269 		    "algs update failed while reading reply (short read)"));
270 	}
271 
272 	msg = (struct spd_msg *)sync_req_buf;
273 	if (msg->spd_msg_errno != 0) {
274 		errno = msg->spd_msg_errno;
275 		warn(gettext("algs update failed"));
276 		if (msg->spd_msg_diagnostic != 0) {
277 			warnx("%s", spdsock_diag(msg->spd_msg_diagnostic));
278 		}
279 		exit(EXIT_FAILURE);
280 	}
281 
282 	(void) close(sfd);
283 }
284 
285 static void
286 list_kernel_algs(void)
287 {
288 	int sfd = socket(PF_POLICY, SOCK_RAW, PF_POLICY_V1);
289 	int cnt, retval;
290 	uint64_t reply_buf[2048];
291 	spd_ext_t *exts[SPD_EXT_MAX+1];
292 	struct spd_msg msg;
293 	struct spd_ext_actions *actp;
294 	struct spd_attribute *attr, *endattr;
295 	uint64_t *start, *end;
296 	struct ipsecalgent alg;
297 	uint_t cur_key, cur_block;
298 	uint_t nkey_sizes, nblock_sizes;
299 	char diag_buf[SPDSOCK_DIAG_BUF_LEN];
300 
301 	if (sfd < 0) {
302 		err(EXIT_FAILURE, gettext("Unable to open policy socket"));
303 	}
304 
305 	(void) memset(&msg, 0, sizeof (msg));
306 	msg.spd_msg_version = PF_POLICY_V1;
307 	msg.spd_msg_type = SPD_DUMPALGS;
308 	msg.spd_msg_len = SPD_8TO64(sizeof (msg));
309 
310 	cnt = write(sfd, &msg, sizeof (msg));
311 	if (cnt != sizeof (msg)) {
312 		if (cnt < 0) {
313 			err(EXIT_FAILURE, gettext("dump algs write failed"));
314 		} else {
315 			errx(EXIT_FAILURE, gettext("dump algs short write"));
316 		}
317 		/* err/errx call exit(). */
318 	}
319 
320 	cnt = read(sfd, reply_buf, sizeof (reply_buf));
321 
322 	if (cnt == -1) {
323 		err(EXIT_FAILURE, gettext("dump algs read failed"));
324 	}
325 
326 	if (cnt < sizeof (struct spd_msg)) {
327 		errx(EXIT_FAILURE, gettext(
328 		    "dump algs failed while reading reply (short read)"));
329 	}
330 
331 	(void) close(sfd);
332 
333 	retval = spdsock_get_ext(exts, (spd_msg_t *)reply_buf, SPD_8TO64(cnt),
334 	    diag_buf, SPDSOCK_DIAG_BUF_LEN);
335 
336 	if (retval == KGE_LEN && exts[0]->spd_ext_len == 0) {
337 		/*
338 		 * No algorithms are defined in the kernel, which caused
339 		 * the extension length to be zero, and spdsock_get_ext()
340 		 * to fail with a KGE_LEN error. This is not an error
341 		 * condition, so we return nicely.
342 		 */
343 		return;
344 	} else if (retval != 0) {
345 		if (strlen(diag_buf) != 0)
346 			warnx("%s", diag_buf);
347 		errx(EXIT_FAILURE, gettext("invalid extension "
348 		    "in dump algs reply (%d)"), retval);
349 	}
350 
351 	if (exts[SPD_EXT_ACTION] == NULL) {
352 		errx(EXIT_FAILURE,
353 		    gettext("action missing in dump algs reply"));
354 	}
355 
356 	actp = (struct spd_ext_actions *)exts[SPD_EXT_ACTION];
357 	start = (uint64_t *)actp;
358 	end = (start + actp->spd_actions_len);
359 	endattr = (struct spd_attribute *)end;
360 	attr = (struct spd_attribute *)&actp[1];
361 
362 	bzero(&alg, sizeof (alg));
363 	nkey_sizes = nblock_sizes = 0;
364 
365 	(void) printf("Kernel list of algorithms:\n\n");
366 
367 	while (attr < endattr) {
368 		switch (attr->spd_attr_tag) {
369 		case SPD_ATTR_NOP:
370 		case SPD_ATTR_EMPTY:
371 			break;
372 		case SPD_ATTR_END:
373 			attr = endattr;
374 			/* FALLTHRU */
375 		case SPD_ATTR_NEXT:
376 			/*
377 			 * Note that if the message received from the spdsock
378 			 * has a premature SPD_ATTR_END or SPD_ATTR_NEXT, this
379 			 * could cause the current algorithm to be only
380 			 * partially initialized.
381 			 */
382 			dump_alg(&alg);
383 			free(alg.a_key_sizes);
384 			free(alg.a_block_sizes);
385 			free(alg.a_mech_name);
386 			bzero(&alg, sizeof (alg));
387 			nkey_sizes = nblock_sizes = 0;
388 			break;
389 
390 		case SPD_ATTR_ALG_ID:
391 			alg.a_alg_num = attr->spd_attr_value;
392 			break;
393 
394 		case SPD_ATTR_ALG_PROTO:
395 			alg.a_proto_num = attr->spd_attr_value;
396 			break;
397 
398 		case SPD_ATTR_ALG_INCRBITS:
399 			alg.a_key_increment = attr->spd_attr_value;
400 			break;
401 
402 		case SPD_ATTR_ALG_NKEYSIZES:
403 			nkey_sizes = attr->spd_attr_value;
404 			if (alg.a_key_sizes != NULL) {
405 				errx(EXIT_FAILURE, gettext("duplicate number "
406 				    "of keys in dump algs reply"));
407 			}
408 			alg.a_key_sizes = calloc(nkey_sizes + 1, sizeof (int));
409 			if (alg.a_key_sizes == NULL)
410 				bail_nomem();
411 			cur_key = 0;
412 			break;
413 
414 		case SPD_ATTR_ALG_KEYSIZE:
415 			if (cur_key >= nkey_sizes) {
416 				errx(EXIT_FAILURE, gettext("too many key sizes"
417 				    " in dump algs reply"));
418 			}
419 			alg.a_key_sizes[cur_key++] = attr->spd_attr_value;
420 			break;
421 
422 		case SPD_ATTR_ALG_NBLOCKSIZES:
423 			nblock_sizes = attr->spd_attr_value;
424 			if (alg.a_block_sizes != NULL) {
425 				errx(EXIT_FAILURE, gettext("duplicate number "
426 				    "of blocks in dump algs reply"));
427 			}
428 			alg.a_block_sizes = calloc(nblock_sizes + 1,
429 			    sizeof (int));
430 			if (alg.a_block_sizes == NULL)
431 				bail_nomem();
432 			cur_block = 0;
433 			break;
434 
435 		case SPD_ATTR_ALG_BLOCKSIZE:
436 			if (cur_block >= nblock_sizes) {
437 				errx(EXIT_FAILURE, gettext("too many block "
438 				    "sizes in dump algs reply"));
439 			}
440 			alg.a_block_sizes[cur_block++] = attr->spd_attr_value;
441 			break;
442 
443 		case SPD_ATTR_ALG_MECHNAME: {
444 			char *mech_name;
445 
446 			if (alg.a_mech_name != NULL) {
447 				errx(EXIT_FAILURE, gettext(
448 				    "duplicate mech name in dump algs reply"));
449 			}
450 
451 			alg.a_mech_name = malloc(attr->spd_attr_value);
452 			if (alg.a_mech_name == NULL)
453 				bail_nomem();
454 
455 			mech_name = (char *)(attr + 1);
456 			bcopy(mech_name, alg.a_mech_name, attr->spd_attr_value);
457 			attr = (struct spd_attribute *)((uint64_t *)attr +
458 			    SPD_8TO64(attr->spd_attr_value));
459 			break;
460 		}
461 		}
462 		attr++;
463 	}
464 
465 }
466 
467 
468 static int *
469 parse_intlist(char *args, int *num_args)
470 {
471 	int *rc = NULL;
472 	char *holder = NULL;
473 
474 	while ((holder = strtok((holder == NULL) ? args : NULL, comma)) !=
475 	    NULL) {
476 		(*num_args)++;
477 		rc = realloc(rc, ((*num_args) + 1) * sizeof (int));
478 		if (rc == NULL)
479 			bail_nomem();
480 		rc[(*num_args) - 1] = atoi(holder);
481 		if (rc[(*num_args) - 1] == 0)
482 			usage();	/* Malformed integer list! */
483 		rc[*num_args] = 0;
484 	}
485 
486 	return (rc);
487 }
488 
489 static void
490 new_alg(void)
491 {
492 	struct ipsecalgent newbie;
493 	int num_names = 0, num_block_sizes = 0, num_key_sizes = 0;
494 	int i, rc;
495 	char *holder = NULL;
496 
497 	/* Parameter reality check... */
498 	if (proto_number == -1) {
499 		if (proto_name == NULL) {
500 			warnx(gettext("Missing protocol number."));
501 			usage();
502 		}
503 		proto_number = getipsecprotobyname(proto_name);
504 		if (proto_number == -1) {
505 			warnx(gettext("Unknown protocol."));
506 			usage();
507 		}
508 	}
509 	if (alg_number == -1) {
510 		warnx(gettext("Missing algorithm number."));
511 		usage();
512 	}
513 	if (key_sizes_string == NULL) {
514 		warnx(gettext("Missing key size(s)."));
515 		usage();
516 	}
517 	if (alg_names_string == NULL) {
518 		warnx(gettext("Missing algorithm name(s)."));
519 		usage();
520 	}
521 	if (block_sizes_string == NULL) {
522 		warnx(gettext("Missing block/MAC lengths"));
523 		usage();
524 	}
525 	if (mech_name == NULL) {
526 		warnx(gettext("Missing mechanism name."));
527 		usage();
528 	}
529 
530 	newbie.a_proto_num = proto_number;
531 	newbie.a_alg_num = alg_number;
532 	newbie.a_key_increment = increment;
533 	newbie.a_mech_name = mech_name;
534 
535 	newbie.a_names = NULL;
536 	while ((holder = strtok((holder == NULL) ? alg_names_string : NULL,
537 		    comma)) != NULL) {
538 		newbie.a_names = realloc(newbie.a_names,
539 		    sizeof (char *) * ((++num_names) + 1));
540 		if (newbie.a_names == NULL)
541 			bail_nomem();
542 		newbie.a_names[num_names - 1] = holder;
543 		newbie.a_names[num_names] = NULL;
544 	}
545 
546 	/* Extract block sizes. */
547 	newbie.a_block_sizes = parse_intlist(block_sizes_string,
548 	    &num_block_sizes);
549 
550 	/* Extract key sizes. */
551 	if ((holder = strchr(key_sizes_string, '-')) != NULL) {
552 		/* key sizes by range, key size increment required */
553 		if (newbie.a_key_increment == 0) {
554 			warnx(gettext("Missing key increment"));
555 			usage();
556 		}
557 		newbie.a_key_sizes = calloc(sizeof (int),
558 		    LIBIPSEC_ALGS_KEY_NUM_VAL);
559 		if (newbie.a_key_sizes == NULL)
560 			bail_nomem();
561 		*holder = '\0';
562 		holder++;
563 		/*
564 		 * At this point, holder points to high, key_sizes_string
565 		 * points to low.
566 		 */
567 		newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX] =
568 		    atoi(key_sizes_string);
569 		if (newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX] == 0) {
570 			warnx(gettext("Invalid lower key size range"));
571 			usage();
572 		}
573 		newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX] = atoi(holder);
574 		if (newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX] == 0) {
575 			warnx(gettext("Invalid higher key size range"));
576 			usage();
577 		}
578 
579 		/* sanity check key range consistency */
580 		if (newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX] >=
581 		    newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX]) {
582 			warnx(gettext("Invalid key size range (min >= max)"));
583 			usage();
584 		}
585 
586 		/* check key increment vs key range */
587 		if (((newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX] -
588 		    newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX]) %
589 		    newbie.a_key_increment) != 0) {
590 			warnx(gettext("Key size increment"
591 			    " not consistent with key size range"));
592 			usage();
593 		}
594 
595 		/* default key size */
596 		if (default_keylen != 0) {
597 			/* check specified default key size */
598 			if (default_keylen <
599 			    newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX] ||
600 			    default_keylen >
601 			    newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX] ||
602 			    ((default_keylen -
603 			    newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX]) %
604 			    newbie.a_key_increment) != 0) {
605 				warnx(gettext("Default key size not consistent"
606 				    " with key size range"));
607 				usage();
608 			}
609 			newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_DEF_IDX] =
610 			    default_keylen;
611 		} else {
612 			/* min key size in range if not specified */
613 			newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_DEF_IDX] =
614 				newbie.a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX];
615 		}
616 	} else {
617 		/* key sizes by enumeration */
618 		if (newbie.a_key_increment != 0) {
619 			warnx(gettext("Key increment must "
620 			    "not be specified with key sizes enumeration"));
621 			usage();
622 		}
623 		newbie.a_key_sizes = parse_intlist(key_sizes_string,
624 		    &num_key_sizes);
625 
626 		/* default key size */
627 		if (default_keylen != 0 && default_keylen !=
628 		    newbie.a_key_sizes[0]) {
629 			/*
630 			 * The default key size is not at the front of the
631 			 * list. Swap it with the first element of the list.
632 			 */
633 			for (i = 1; i < num_key_sizes; i++) {
634 				if (newbie.a_key_sizes[i] == default_keylen)
635 					break;
636 				if (i >= num_key_sizes) {
637 					warnx(gettext("Default key size not "
638 					    "in list of key sizes"));
639 					usage();
640 				}
641 				newbie.a_key_sizes[i] = newbie.a_key_sizes[0];
642 				newbie.a_key_sizes[0] = default_keylen;
643 			}
644 		}
645 	}
646 
647 	/* Call things! */
648 	if ((rc = addipsecalg(&newbie, adddel_flags)) != 0) {
649 		errx(EXIT_FAILURE, gettext("addipsecalg() call failed: "
650 		    "%s"), ipsecalgs_diag(rc));
651 	}
652 
653 	free(newbie.a_names);
654 	free(newbie.a_block_sizes);
655 	free(newbie.a_key_sizes);
656 }
657 
658 static void
659 new_proto(void)
660 {
661 	int rc;
662 
663 	if ((rc = addipsecproto(proto_name, proto_number, proto_exec_mode,
664 	    adddel_flags))
665 	    != 0) {
666 		errx(EXIT_FAILURE, gettext(
667 		    "Cannot add protocol %1$d \"%2$s\": %3$s"), proto_number,
668 		    proto_name, ipsecalgs_diag(rc));
669 	}
670 }
671 
672 static void
673 remove_alg(void)
674 {
675 	int rc;
676 
677 	if (proto_number == -1) {
678 		if (proto_name == NULL) {
679 			warnx(gettext("Missing protocol number."));
680 			usage();
681 		}
682 		proto_number = getipsecprotobyname(proto_name);
683 		if (proto_number == -1) {
684 			errx(EXIT_FAILURE, gettext(
685 			    "Unknown protocol \"%s\"."), proto_name);
686 		}
687 	}
688 
689 	if (alg_number == -1) {
690 		if (alg_names_string == NULL) {
691 			errx(EXIT_FAILURE, gettext("Missing algorithm ID."));
692 		}
693 		if (strchr(alg_names_string, ',') != NULL) {
694 			errx(EXIT_FAILURE, gettext(
695 				"Specify a single algorithm name for removal, "
696 				"not a list."));
697 		}
698 		if ((rc = delipsecalgbyname(alg_names_string, proto_number))
699 		    != 0) {
700 			errx(EXIT_FAILURE, gettext(
701 			    "Could not remove algorithm %1$s: %2$s"),
702 			    alg_names_string, ipsecalgs_diag(rc));
703 		}
704 	} else {
705 		if ((rc = delipsecalgbynum(alg_number, proto_number)) != 0) {
706 			errx(EXIT_FAILURE, gettext(
707 			    "Could not remove algorithm %1$d: %2$s"),
708 			    alg_number, ipsecalgs_diag(rc));
709 		}
710 	}
711 }
712 
713 static void
714 remove_proto(void)
715 {
716 	int rc;
717 
718 	if (proto_number == -1) {
719 		if (proto_name == NULL) {
720 			warnx(gettext("Please specify protocol to remove."));
721 			usage();
722 		}
723 		if ((rc = delipsecprotobyname(proto_name)) != 0) {
724 			errx(EXIT_FAILURE, gettext(
725 			    "Could not remove protocol %1$s: %2$s"),
726 			    proto_name, ipsecalgs_diag(rc));
727 		}
728 	} else {
729 		if ((rc = delipsecprotobynum(proto_number)) != 0) {
730 			errx(EXIT_FAILURE, gettext(
731 			    "Could not remove protocol %1$d: %2$s"),
732 			    proto_number, ipsecalgs_diag(rc));
733 		}
734 	}
735 }
736 
737 static void
738 set_exec_mode(void)
739 {
740 	int rc;
741 
742 	if (proto_number == -1) {
743 		if (proto_name == NULL) {
744 			warnx(gettext(
745 			    "Please specify protocol name or number."));
746 			usage();
747 		}
748 		proto_number = getipsecprotobyname(proto_name);
749 		if (proto_number == -1) {
750 			errx(EXIT_FAILURE, gettext("Unknown protocol %s"),
751 			    proto_name);
752 		}
753 	}
754 
755 	if ((rc = ipsecproto_set_exec_mode(proto_number, proto_exec_mode))
756 	    != 0) {
757 		errx(EXIT_FAILURE, gettext("Cannot set execution mode: %s"),
758 		    ipsecalgs_diag(rc));
759 	}
760 }
761 
762 /*
763  * Print a description of an algorithm to standard output.
764  */
765 static void
766 dump_alg(struct ipsecalgent *alg)
767 {
768 	int *ifloater;
769 	char **floater;
770 
771 	/* protocol number */
772 	(void) printf(gettext("\tProtocol number: %d\n"), alg->a_proto_num);
773 
774 	/* algorithm number */
775 	(void) printf(gettext("\tAlgorithm number: %d\n"), alg->a_alg_num);
776 
777 	/* algorithm name(s) */
778 	if (alg->a_names != NULL) {
779 		(void) printf(gettext("\tAlgorithm names: "));
780 		floater = alg->a_names;
781 		assert(floater != NULL && *floater != NULL);
782 		do {
783 			/* Assume at least one string. */
784 			(void) printf("%s", *floater);
785 			if (*(++floater) != NULL)
786 				(void) putchar(',');
787 		} while (*floater != NULL);
788 		(void) putchar('\n');
789 	}
790 
791 	/* mechanism name */
792 	(void) printf(gettext("\tMechanism Name: %s\n"), alg->a_mech_name);
793 
794 	/* block/MAC sizes */
795 	(void) printf(gettext("\tBlock sizes or MAC sizes: "));
796 	ifloater = alg->a_block_sizes;
797 	(void) list_ints(stdout, ifloater);
798 	(void) putchar('\n');
799 
800 	/* key sizes */
801 	(void) printf(gettext("\tKey sizes: "));
802 	if (alg->a_key_increment != 0)
803 		/* key specified by range */
804 		(void) printf(gettext(
805 		    "%1$d-%2$d, increment %3$d, default %4$d"),
806 		    alg->a_key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX],
807 		    alg->a_key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX],
808 		    alg->a_key_increment,
809 		    alg->a_key_sizes[LIBIPSEC_ALGS_KEY_DEF_IDX]);
810 	else
811 		/* key specified by enumeration */
812 		(void) list_ints(stdout, alg->a_key_sizes);
813 	(void) putchar('\n');
814 
815 	(void) putchar('\n');
816 }
817 
818 /*
819  * Print the description of a protocol.
820  */
821 static void
822 dump_proto(uint_t proto_id)
823 {
824 	char *proto_name;
825 	ipsecalgs_exec_mode_t exec_mode;
826 
827 	/* protocol name and number */
828 	proto_name = getipsecprotobynum(proto_id);
829 	(void) printf(gettext("Protocol %1$d/%2$s "),
830 	    proto_id, proto_name != NULL ? proto_name : gettext("<unknown>"));
831 
832 	/* execution mode */
833 	(void) printf("(%s", gettext("execution mode: "));
834 
835 	if (ipsecproto_get_exec_mode(proto_id, &exec_mode) != 0) {
836 		(void) printf(gettext("<unknown>"));
837 	} else {
838 		switch (exec_mode) {
839 		case LIBIPSEC_ALGS_EXEC_SYNC:
840 			(void) printf("sync");
841 			break;
842 		case LIBIPSEC_ALGS_EXEC_ASYNC:
843 			(void) printf("async");
844 			break;
845 		}
846 	}
847 
848 	(void) printf(")\n\n");
849 
850 	free(proto_name);
851 }
852 
853 
854 /*
855  * Algorithm walker table. Call proto_action() for each protocol,
856  * and alg_action() for each algorithm.
857  */
858 static void
859 algs_walker(void (*alg_action)(struct ipsecalgent *),
860     void (*proto_action)(uint_t))
861 {
862 	int *proto_nums, proto_count, i;
863 	int *alg_nums, alg_count, j;
864 	struct ipsecalgent *alg;
865 
866 	proto_nums = getipsecprotos(&proto_count);
867 	if (proto_nums == NULL) {
868 		errx(EXIT_FAILURE, gettext("getipsecprotos() failed."));
869 	}
870 
871 	for (i = 0; i < proto_count; i++) {
872 
873 		if (proto_action != NULL)
874 			proto_action(proto_nums[i]);
875 
876 		alg_nums = getipsecalgs(&alg_count, proto_nums[i]);
877 		if (alg_nums == NULL) {
878 			free(proto_nums);
879 			errx(EXIT_FAILURE, gettext("getipsecalgs() failed."));
880 		}
881 
882 		for (j = 0; j < alg_count; j++) {
883 			alg = getipsecalgbynum(alg_nums[j], proto_nums[i],
884 			    NULL);
885 			if (alg == NULL)
886 				continue;
887 			if (alg_action != NULL)
888 				alg_action(alg);
889 			freeipsecalgent(alg);
890 		}
891 		free(alg_nums);
892 	}
893 	free(proto_nums);
894 }
895 
896 /*
897  * Use just the libnsl/libipsecutil APIs to dump out all of the algorithms.
898  */
899 static void
900 show_algs(void)
901 {
902 	/* Yes, I'm aware that this'll produce TWO newlines. */
903 	(void) puts(gettext(
904 	    "List of algorithms, grouped by IPsec protocol:\n"));
905 
906 	algs_walker(dump_alg, dump_proto);
907 }
908 
909 static int
910 try_int(char *optarg, const char *what)
911 {
912 	int rc = atoi(optarg);
913 
914 	if (rc <= 0) {
915 		warnx(gettext("Invalid %s value"), what);
916 		usage();
917 	}
918 	return (rc);
919 }
920 
921 static void
922 try_cmd(cmd_t newcmd)
923 {
924 	if (cmd != CMD_NONE)
925 		usage();
926 	cmd = newcmd;
927 }
928 
929 int
930 main(int argc, char *argv[])
931 {
932 	int c;
933 	zoneid_t zoneid;
934 	ushort_t flags;
935 
936 	(void) setlocale(LC_ALL, "");
937 #if !defined(TEXT_DOMAIN)
938 #define	TEXT_DOMAIN "SYS_TEST"
939 #endif
940 	(void) textdomain(TEXT_DOMAIN);
941 
942 	if (argc == 1) {
943 		show_algs();
944 		return (EXIT_SUCCESS);
945 	}
946 
947 	while ((c = getopt(argc, argv,
948 		    "aflrRsb:p:P:i:k:K:m:n:N:e:")) != EOF) {
949 		switch (c) {
950 		case 'a':
951 			try_cmd(CMD_ADD);
952 			break;
953 		case 'f':
954 			/* multiple occurences of -f are harmless */
955 			adddel_flags = LIBIPSEC_ALGS_ADD_FORCE;
956 			break;
957 		case 'l':
958 			try_cmd(CMD_LIST_KERNEL);
959 			break;
960 		case 'r':
961 			try_cmd(CMD_DEL);
962 			break;
963 		case 'R':
964 			try_cmd(CMD_DEL_PROTO);
965 			break;
966 		case 's':
967 			/* multiple occurences of -s are harmless */
968 			synch_kernel = B_TRUE;
969 			break;
970 		case 'n':
971 			if (alg_names_string != NULL)
972 				usage();
973 			alg_names_string = optarg;
974 			break;
975 		case 'b':
976 			if (block_sizes_string != NULL)
977 				usage();
978 			block_sizes_string = optarg;
979 			break;
980 		case 'p':
981 			if (proto_name != NULL)
982 				usage();
983 			proto_name = optarg;
984 			break;
985 		case 'P':
986 			if (proto_number != -1)
987 				usage();
988 			proto_number = try_int(optarg,
989 			    gettext("protocol number"));
990 			break;
991 		case 'e':
992 			if (exec_mode_string != NULL)
993 				usage();
994 			exec_mode_string = optarg;
995 			if (_str_to_ipsec_exec_mode(exec_mode_string,
996 			    &proto_exec_mode) == -1) {
997 				warnx(gettext("Invalid execution mode \"%s\""),
998 				    exec_mode_string);
999 				usage();
1000 			}
1001 			break;
1002 		case 'i':
1003 			if (increment != 0)
1004 				usage();
1005 			increment = try_int(optarg,
1006 			    gettext("key size increment"));
1007 			break;
1008 		case 'k':
1009 			if (key_sizes_string != NULL)
1010 				usage();
1011 			key_sizes_string = optarg;
1012 			break;
1013 		case 'K':
1014 			if (default_keylen != 0)
1015 				usage();
1016 			default_keylen = try_int(optarg,
1017 			    gettext("default key size"));
1018 			break;
1019 		case 'm':
1020 			if (mech_name != NULL)
1021 				usage();
1022 			mech_name = optarg;
1023 			break;
1024 		case 'N':
1025 			if (alg_number != -1)
1026 				usage();
1027 			alg_number = try_int(optarg,
1028 			    gettext("algorithm number"));
1029 			break;
1030 		}
1031 	}
1032 
1033 	/*
1034 	 * When both protocol name (-p) and protocol number (-P) are
1035 	 * specified, a new protocol is being defined.
1036 	 */
1037 	if (proto_number != -1 && proto_name != NULL)
1038 		try_cmd(CMD_ADD_PROTO);
1039 	else if (exec_mode_string != NULL)
1040 		try_cmd(CMD_EXEC_MODE);
1041 
1042 	/*
1043 	 * Process specified command.
1044 	 */
1045 	switch (cmd) {
1046 	case CMD_ADD:
1047 		new_alg();
1048 		break;
1049 	case CMD_ADD_PROTO:
1050 		new_proto();
1051 		break;
1052 	case CMD_DEL:
1053 		remove_alg();
1054 		break;
1055 	case CMD_DEL_PROTO:
1056 		remove_proto();
1057 		break;
1058 	case CMD_EXEC_MODE:
1059 		set_exec_mode();
1060 		break;
1061 	case CMD_LIST_KERNEL:
1062 		if (synch_kernel)
1063 			usage();
1064 		list_kernel_algs();
1065 		break;
1066 	default:
1067 		if (!synch_kernel)
1068 			usage();
1069 	}
1070 
1071 	if (synch_kernel) {
1072 		/*
1073 		 * This will only work in the global zone or
1074 		 * a zone with an exclusive IP stack.
1075 		 */
1076 		if ((zoneid = getzoneid()) == 0) {
1077 			kernel_synch();
1078 		} else {
1079 			if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &flags,
1080 			    sizeof (flags)) < 0) {
1081 				err(EXIT_FAILURE, gettext(
1082 				    "Unable to determine zone IP type"));
1083 			}
1084 			if (flags & ZF_NET_EXCL) {
1085 				kernel_synch();
1086 			} else {
1087 				(void) printf(gettext("Synchronization with "
1088 				    "kernel not appropriate in this zone.\n"));
1089 			}
1090 		}
1091 	}
1092 
1093 	return (EXIT_SUCCESS);
1094 }
1095