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
parse_flag(char * flag_str,uint_t flag)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
usage(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
bail_nomem(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
num_sizes(int * sizes)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
synch_emit_alg(struct ipsecalgent * alg)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
synch_emit_proto(uint_t proto_num)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
kernel_synch(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
list_kernel_algs(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 *
parse_intlist(char * args,int * num_args)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
new_alg(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
new_proto(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
remove_alg(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
remove_proto(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
set_exec_mode(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
dump_alg(struct ipsecalgent * alg)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
dump_proto(uint_t proto_id)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
algs_walker(void (* alg_action)(struct ipsecalgent *),void (* proto_action)(uint_t))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
show_algs(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
try_int(char * optarg,const char * what)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
try_cmd(cmd_t newcmd)1022 try_cmd(cmd_t newcmd)
1023 {
1024 if (cmd != CMD_NONE)
1025 usage();
1026 cmd = newcmd;
1027 }
1028
1029 int
main(int argc,char * argv[])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