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