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