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