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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include "mt.h" 28 #include <sys/types.h> 29 #include <sys/errno.h> 30 #include <sys/stat.h> 31 #include <ipsec_util.h> 32 #include <netdb.h> 33 #include <fcntl.h> 34 #include <unistd.h> 35 #include <synch.h> 36 #include <string.h> 37 #include <strings.h> 38 #include <stdlib.h> 39 #include <unistd.h> 40 #include <syslog.h> 41 42 /* Globals... */ 43 static rwlock_t proto_rw = DEFAULTRWLOCK; /* Protects cached algorithm list. */ 44 static time_t proto_last_update; 45 static ipsec_proto_t *protos; 46 static int num_protos; 47 48 void 49 _clean_trash(ipsec_proto_t *proto, int num) 50 { 51 int alg_offset; 52 53 if (proto == NULL) 54 return; 55 56 while (num-- != 0) { 57 free(proto[num].proto_name); 58 free(proto[num].proto_pkg); 59 for (alg_offset = 0; alg_offset < proto[num].proto_numalgs; 60 alg_offset++) 61 freeipsecalgent(proto[num].proto_algs[alg_offset]); 62 free(proto[num].proto_algs); 63 for (alg_offset = 0; alg_offset < proto[num].proto_algs_npkgs; 64 alg_offset++) 65 free(proto[num].proto_algs_pkgs[alg_offset].pkg_name); 66 free(proto[num].proto_algs_pkgs); 67 } 68 69 free(proto); 70 } 71 72 static const char *pipechar = "|"; 73 static const char *comma = ","; 74 static const char *dash = "-"; 75 static const char *slash = "/"; 76 77 /* 78 * Returns >= 0 if success (and > 0 means "increment"). 79 * Returns -1 if failure. 80 */ 81 static int 82 build_keysizes(int **sizep, char *input_string) 83 { 84 char *lasts, *token; 85 int *key_sizes = NULL, num_sizes, key_low, key_high, key_default; 86 int key_increment = 0; 87 88 /* 89 * Okay, let's check the format of the key string. It'll be either: 90 * 91 * enumeration: size1,size2...,sizeN 92 * range: defaultSize/sizeLow-sizeHi,increment 93 * 94 * In the case of an enumeration, the default key size is the 95 * first one in the list. 96 */ 97 98 if (strchr(input_string, '/') != NULL) { 99 /* key sizes specified by range */ 100 101 /* default */ 102 token = strtok_r(input_string, slash, &lasts); 103 if (token == NULL || (key_default = atoi(token)) == 0) 104 return (-1); 105 106 /* low */ 107 token = strtok_r(NULL, dash, &lasts); 108 if (token == NULL || (key_low = atoi(token)) == 0) 109 return (-1); 110 111 /* high */ 112 token = strtok_r(NULL, comma, &lasts); 113 if (token == NULL || (key_high = atoi(token)) == 0 || 114 key_high <= key_low) 115 return (-1); 116 117 /* increment */ 118 token = strtok_r(NULL, "", &lasts); 119 if (token == NULL || (key_increment = atoi(token)) == 0) 120 return (-1); 121 122 key_sizes = (int *)malloc(LIBIPSEC_ALGS_KEY_NUM_VAL * 123 sizeof (int)); 124 if (key_sizes == NULL) 125 return (-1); 126 127 key_sizes[LIBIPSEC_ALGS_KEY_DEF_IDX] = key_default; 128 key_sizes[LIBIPSEC_ALGS_KEY_MIN_IDX] = key_low; 129 key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX] = key_high; 130 key_sizes[LIBIPSEC_ALGS_KEY_MAX_IDX + 1] = 0; 131 } else { 132 /* key sizes specified by enumeration */ 133 134 key_sizes = (int *)malloc(sizeof (int)); 135 if (key_sizes == NULL) 136 return (-1); 137 num_sizes = 0; 138 139 token = strtok_r(input_string, comma, &lasts); 140 if (token == NULL) { 141 free(key_sizes); 142 return (-1); 143 } 144 *key_sizes = 0; 145 do { 146 int *nks; 147 148 nks = (int *)realloc(key_sizes, 149 sizeof (int) * ((++num_sizes) + 1)); 150 if (nks == NULL) { 151 free(key_sizes); 152 return (-1); 153 } 154 key_sizes = nks; 155 /* Can't check for atoi() == 0 here... */ 156 key_sizes[num_sizes - 1] = atoi(token); 157 key_sizes[num_sizes] = 0; 158 } while ((token = strtok_r(NULL, comma, &lasts)) != NULL); 159 } 160 *sizep = key_sizes; 161 162 return (key_increment); 163 } 164 165 /* 166 * Find the execution mode corresponding to the given string. 167 * Returns 0 on success, -1 on failure. 168 */ 169 int 170 _str_to_ipsec_exec_mode(char *str, ipsecalgs_exec_mode_t *exec_mode) 171 { 172 if (strcmp(str, "sync") == 0) { 173 *exec_mode = LIBIPSEC_ALGS_EXEC_SYNC; 174 return (0); 175 } else if (strcmp(str, "async") == 0) { 176 *exec_mode = LIBIPSEC_ALGS_EXEC_ASYNC; 177 return (0); 178 } 179 180 return (-1); 181 } 182 183 /* 184 * Given a file pointer, read all the text from the file and convert it into 185 * a bunch of ipsec_proto_t's, each with an array of struct ipsecalgent 186 * pointers - one for each algorithm. 187 */ 188 static ipsec_proto_t * 189 build_list(FILE *f, int *num) 190 { 191 char line[1024]; 192 char *token, *lasts, *alg_names, *ef_name, *key_string, *block_string; 193 char *proto_name, *params_string; 194 ipsec_proto_t *rc = NULL, *new_proto = NULL; 195 int *block_sizes = NULL, *key_sizes = NULL, *mech_params = NULL; 196 int rc_num = 0, key_increment; 197 int new_num, alg_num, num_sizes, flags = 0; 198 struct ipsecalgent *curalg, **newalglist; 199 char cur_pkg[1024]; 200 boolean_t doing_pkg = B_FALSE; 201 ipsecalgs_exec_mode_t exec_mode; 202 char diag_buf[128]; 203 204 diag_buf[0] = '\0'; 205 206 while (fgets(line, sizeof (line), f) != NULL) { 207 if (strncasecmp(line, LIBIPSEC_ALGS_LINE_PROTO, 208 sizeof (LIBIPSEC_ALGS_LINE_PROTO) - 1) != 0 && 209 strncasecmp(line, LIBIPSEC_ALGS_LINE_ALG, 210 sizeof (LIBIPSEC_ALGS_LINE_ALG) - 1) != 0 && 211 strncasecmp(line, LIBIPSEC_ALGS_LINE_PKGSTART, 212 sizeof (LIBIPSEC_ALGS_LINE_PKGSTART) - 1) != 0 && 213 strncasecmp(line, LIBIPSEC_ALGS_LINE_PKGEND, 214 sizeof (LIBIPSEC_ALGS_LINE_PKGEND) - 1) != 0) { 215 if ((token = strtok_r(line, " \t\n", &lasts)) == NULL || 216 token[0] == '#') { 217 continue; 218 } else { 219 (void) snprintf(diag_buf, sizeof (diag_buf), 220 "non-recognized start of line"); 221 goto bail; 222 } 223 } 224 225 if (strncasecmp(line, LIBIPSEC_ALGS_LINE_PROTO, 226 sizeof (LIBIPSEC_ALGS_LINE_PROTO) - 1) == 0) { 227 /* current line defines a new protocol */ 228 229 /* skip the protocol token */ 230 token = strtok_r(line, pipechar, &lasts); 231 232 /* protocol number */ 233 token = strtok_r(NULL, pipechar, &lasts); 234 if (token == NULL || (new_num = atoi(token)) == 0) { 235 (void) snprintf(diag_buf, sizeof (diag_buf), 236 "invalid protocol number"); 237 goto bail; 238 } 239 240 /* protocol name */ 241 token = strtok_r(NULL, pipechar, &lasts); 242 if (token == NULL) { 243 (void) snprintf(diag_buf, sizeof (diag_buf), 244 "cannot read protocol name"); 245 goto bail; 246 } 247 proto_name = token; 248 249 /* execution mode */ 250 token = strtok_r(NULL, pipechar, &lasts); 251 if (token == NULL) { 252 (void) snprintf(diag_buf, sizeof (diag_buf), 253 "cannot read execution mode"); 254 goto bail; 255 } 256 /* remove trailing '\n' */ 257 token[strlen(token) - 1] = '\0'; 258 if (_str_to_ipsec_exec_mode(token, &exec_mode) != 0) { 259 (void) snprintf(diag_buf, sizeof (diag_buf), 260 "invalid execution mode: \"%s\"", token); 261 goto bail; 262 } 263 264 /* initialize protocol structure */ 265 rc_num++; 266 new_proto = (ipsec_proto_t *)realloc(rc, 267 sizeof (ipsec_proto_t) * rc_num); 268 rc = new_proto; 269 if (new_proto == NULL) 270 goto bail; 271 new_proto += (rc_num - 1); 272 new_proto->proto_num = new_num; 273 new_proto->proto_algs = NULL; 274 new_proto->proto_numalgs = 0; 275 new_proto->proto_name = strdup(proto_name); 276 if (new_proto->proto_name == NULL) 277 goto bail; 278 new_proto->proto_exec_mode = exec_mode; 279 280 if (doing_pkg) { 281 /* record proto as being part of current pkg */ 282 new_proto->proto_pkg = strdup(cur_pkg); 283 if (new_proto->proto_pkg == NULL) 284 goto bail; 285 } else { 286 new_proto->proto_pkg = NULL; 287 } 288 289 new_proto->proto_algs_pkgs = NULL; 290 new_proto->proto_algs_npkgs = 0; 291 292 } else if (strncasecmp(line, LIBIPSEC_ALGS_LINE_ALG, 293 sizeof (LIBIPSEC_ALGS_LINE_ALG) - 1) == 0) { 294 /* current line defines a new algorithm */ 295 296 /* skip the algorithm token */ 297 token = strtok_r(line, pipechar, &lasts); 298 299 /* protocol number */ 300 token = strtok_r(NULL, pipechar, &lasts); 301 if (token == NULL || (new_num = atoi(token)) == 0) { 302 (void) snprintf(diag_buf, sizeof (diag_buf), 303 "invalid algorithm number"); 304 goto bail; 305 } 306 307 /* We can be O(N) for now. There aren't that many. */ 308 for (new_proto = rc; new_proto < (rc + new_num); 309 new_proto++) 310 if (new_proto->proto_num == new_num) 311 break; 312 if (new_proto == (rc + new_num)) { 313 (void) snprintf(diag_buf, sizeof (diag_buf), 314 "invalid protocol number %d for algorithm", 315 new_num); 316 goto bail; 317 } 318 319 /* algorithm number */ 320 token = strtok_r(NULL, pipechar, &lasts); 321 if (token == NULL) { 322 (void) snprintf(diag_buf, sizeof (diag_buf), 323 "cannot read algorithm number"); 324 goto bail; 325 } 326 /* Can't check for 0 here. */ 327 alg_num = atoi(token); 328 329 /* algorithm names */ 330 token = strtok_r(NULL, pipechar, &lasts); 331 if (token == NULL) { 332 (void) snprintf(diag_buf, sizeof (diag_buf), 333 "cannot read algorithm number"); 334 goto bail; 335 } 336 alg_names = token; 337 338 /* mechanism name */ 339 token = strtok_r(NULL, pipechar, &lasts); 340 if (token == NULL) { 341 (void) snprintf(diag_buf, sizeof (diag_buf), 342 "cannot read mechanism name for alg %d " 343 "(proto %d)", alg_num, 344 new_proto->proto_num); 345 goto bail; 346 } 347 ef_name = token; 348 349 /* key sizes */ 350 token = strtok_r(NULL, pipechar, &lasts); 351 if (token == NULL) { 352 (void) snprintf(diag_buf, sizeof (diag_buf), 353 "cannot read key sizes for alg %d " 354 "(proto %d)", alg_num, 355 new_proto->proto_num); 356 goto bail; 357 } 358 key_string = token; 359 360 /* block sizes */ 361 token = strtok_r(NULL, pipechar, &lasts); 362 if (token == NULL) { 363 (void) snprintf(diag_buf, sizeof (diag_buf), 364 "cannot read block sizes for alg %d " 365 "(proto %d)", alg_num, 366 new_proto->proto_num); 367 goto bail; 368 } 369 block_string = token; 370 371 /* 372 * Check for mechanism params and flags. As these 373 * are optional, we won't bail if they don't exist. 374 */ 375 token = strtok_r(NULL, pipechar, &lasts); 376 params_string = token; 377 378 token = strtok_r(NULL, pipechar, &lasts); 379 if (token != NULL) 380 flags = atoi(token); 381 382 /* extract key sizes */ 383 key_increment = build_keysizes(&key_sizes, key_string); 384 if (key_increment == -1) { 385 (void) snprintf(diag_buf, sizeof (diag_buf), 386 "invalid key sizes for alg %d (proto %d)", 387 alg_num, new_proto->proto_num); 388 goto bail; 389 } 390 391 /* extract block sizes */ 392 block_sizes = (int *)malloc(sizeof (int)); 393 if (block_sizes == NULL) { 394 goto bail; 395 } 396 num_sizes = 0; 397 token = strtok_r(block_string, comma, &lasts); 398 if (token == NULL) { 399 (void) snprintf(diag_buf, sizeof (diag_buf), 400 "invalid block sizes for alg %d (proto %d)", 401 alg_num, new_proto->proto_num); 402 goto bail; 403 } 404 *block_sizes = 0; 405 do { 406 int *nbk; 407 408 nbk = (int *)realloc(block_sizes, 409 sizeof (int) * ((++num_sizes) + 1)); 410 if (nbk == NULL) { 411 goto bail; 412 } 413 block_sizes = nbk; 414 /* Can't check for 0 here... */ 415 block_sizes[num_sizes - 1] = atoi(token); 416 block_sizes[num_sizes] = 0; 417 } while ((token = strtok_r(NULL, comma, &lasts)) != 418 NULL); 419 420 /* extract mech params */ 421 mech_params = (int *)malloc(sizeof (int)); 422 if (mech_params == NULL) { 423 goto bail; 424 } 425 *mech_params = 0; 426 num_sizes = 0; 427 if (params_string != NULL) { 428 token = strtok_r(params_string, comma, &lasts); 429 if (token == NULL) { 430 (void) snprintf(diag_buf, 431 sizeof (diag_buf), "invalid mech " 432 "params for alg %d (proto %d)", 433 alg_num, new_proto->proto_num); 434 goto bail; 435 } 436 do { 437 int *nbk; 438 439 nbk = (int *)realloc(mech_params, 440 sizeof (int) * ((++num_sizes) + 1)); 441 if (nbk == NULL) { 442 goto bail; 443 } 444 mech_params = nbk; 445 /* Can't check for 0 here... */ 446 mech_params[num_sizes - 1] = 447 atoi(token); 448 mech_params[num_sizes] = 0; 449 } while ((token = strtok_r(NULL, comma, &lasts)) 450 != NULL); 451 } 452 /* Allocate a new struct ipsecalgent. */ 453 curalg = (struct ipsecalgent *)calloc( 454 sizeof (struct ipsecalgent), 1); 455 if (curalg == NULL) { 456 goto bail; 457 } 458 curalg->a_proto_num = new_num; 459 curalg->a_alg_num = alg_num; 460 curalg->a_block_sizes = block_sizes; 461 curalg->a_alg_flags = flags; 462 curalg->a_mech_params = mech_params; 463 curalg->a_key_sizes = key_sizes; 464 curalg->a_key_increment = key_increment; 465 if ((curalg->a_mech_name = strdup(ef_name)) == NULL) { 466 freeipsecalgent(curalg); 467 goto bail; 468 } 469 /* Set names. */ 470 curalg->a_names = (char **)malloc(sizeof (char *)); 471 num_sizes = 0; /* Recycle "sizes" */ 472 token = strtok_r(alg_names, comma, &lasts); 473 if (curalg->a_names == NULL || token == NULL) { 474 freeipsecalgent(curalg); 475 goto bail; 476 } 477 do { 478 char **nnames; 479 480 nnames = (char **)realloc(curalg->a_names, 481 sizeof (char *) * ((++num_sizes) + 1)); 482 if (nnames == NULL) { 483 freeipsecalgent(curalg); 484 goto bail; 485 } 486 curalg->a_names = nnames; 487 curalg->a_names[num_sizes] = NULL; 488 curalg->a_names[num_sizes - 1] = 489 strdup(token); 490 if (curalg->a_names[num_sizes - 1] == NULL) { 491 freeipsecalgent(curalg); 492 goto bail; 493 } 494 } while ((token = strtok_r(NULL, comma, &lasts)) != 495 NULL); 496 497 if (doing_pkg) { 498 /* record alg as being part of current pkg */ 499 int npkgs = new_proto->proto_algs_npkgs; 500 501 new_proto->proto_algs_pkgs = realloc( 502 new_proto->proto_algs_pkgs, 503 (npkgs + 1) * sizeof (ipsecalgs_pkg_t)); 504 if (new_proto->proto_algs_pkgs == NULL) 505 goto bail; 506 507 new_proto->proto_algs_pkgs[npkgs].alg_num = 508 curalg->a_alg_num; 509 new_proto->proto_algs_pkgs[npkgs].pkg_name = 510 strdup(cur_pkg); 511 if (new_proto->proto_algs_pkgs[npkgs].pkg_name 512 == NULL) 513 goto bail; 514 515 new_proto->proto_algs_npkgs = npkgs + 1; 516 } 517 518 /* add new alg to protocol */ 519 newalglist = realloc(new_proto->proto_algs, 520 (new_proto->proto_numalgs + 1) * 521 sizeof (struct ipsecalgent *)); 522 if (newalglist == NULL) { 523 freeipsecalgent(curalg); 524 goto bail; 525 } 526 newalglist[new_proto->proto_numalgs] = curalg; 527 new_proto->proto_numalgs++; 528 new_proto->proto_algs = newalglist; 529 530 } else if (strncasecmp(line, LIBIPSEC_ALGS_LINE_PKGSTART, 531 sizeof (LIBIPSEC_ALGS_LINE_PKGSTART) - 1) == 0) { 532 /* start of package delimiter */ 533 if (doing_pkg) { 534 (void) snprintf(diag_buf, sizeof (diag_buf), 535 "duplicate package start delimiters"); 536 goto bail; 537 } 538 (void) strncpy(cur_pkg, line + 539 (sizeof (LIBIPSEC_ALGS_LINE_PKGSTART) - 1), 540 sizeof (cur_pkg)); 541 /* remove trailing '\n' */ 542 cur_pkg[strlen(cur_pkg) - 1] = '\0'; 543 doing_pkg = B_TRUE; 544 545 } else { 546 /* end of package delimiter */ 547 char tmp_pkg[1024]; 548 549 if (!doing_pkg) { 550 (void) snprintf(diag_buf, sizeof (diag_buf), 551 "end package delimiter without start"); 552 goto bail; 553 } 554 /* 555 * Get specified pkg name, fail if it doesn't match 556 * the package specified by the last # Begin. 557 */ 558 (void) strncpy(tmp_pkg, line + 559 (sizeof (LIBIPSEC_ALGS_LINE_PKGEND) - 1), 560 sizeof (tmp_pkg)); 561 /* remove trailing '\n' */ 562 tmp_pkg[strlen(tmp_pkg) - 1] = '\0'; 563 if (strncmp(cur_pkg, tmp_pkg, sizeof (cur_pkg)) != 0) 564 goto bail; 565 doing_pkg = B_FALSE; 566 } 567 } 568 569 *num = rc_num; 570 return (rc); 571 572 bail: 573 if (strlen(diag_buf) > 0) { 574 syslog(LOG_ERR, "possibly corrupt %s file: %s\n", 575 INET_IPSECALGSFILE, diag_buf); 576 } 577 free(key_sizes); 578 free(block_sizes); 579 free(mech_params); 580 _clean_trash(rc, rc_num); 581 return (NULL); 582 } 583 584 /* 585 * If alg_context is NULL, update the library's cached copy of 586 * INET_IPSECALGSFILE. If alg_context is non-NULL, hang a 587 * library-internal representation of a cached copy. The latter is useful 588 * for routines in libipsecutil that _write_ the contents out. 589 */ 590 void 591 _build_internal_algs(ipsec_proto_t **alg_context, int *alg_nums) 592 { 593 FILE *f; 594 int rc, trash_num; 595 ipsec_proto_t *new_protos = NULL, *trash; 596 time_t filetime; 597 struct stat statbuf; 598 599 /* 600 * Construct new_protos from the file. 601 */ 602 if (alg_context == NULL) { 603 /* 604 * Check the time w/o holding the lock. This is just a 605 * cache reality check. We'll do it again for real if this 606 * surface check fails. 607 */ 608 if (stat(INET_IPSECALGSFILE, &statbuf) == -1 || 609 (statbuf.st_mtime < proto_last_update && protos != NULL)) 610 return; 611 (void) rw_wrlock(&proto_rw); 612 } 613 614 f = fopen(INET_IPSECALGSFILE, "rF"); 615 if (f != NULL) { 616 rc = fstat(fileno(f), &statbuf); 617 if (rc != -1) { 618 /* 619 * Update if the file is newer than our 620 * last cached copy. 621 */ 622 filetime = statbuf.st_mtime; 623 if (alg_context != NULL || 624 filetime > proto_last_update) 625 new_protos = build_list(f, &rc); 626 } 627 /* Since f is read-only, can avoid all of the failures... */ 628 (void) fclose(f); 629 } 630 631 if (alg_context == NULL) { 632 /* 633 * If we have failed anywhere above, new_protoss will be NULL. 634 * This way, the previous cached protos will still be intact. 635 */ 636 if (new_protos != NULL) { 637 proto_last_update = filetime; 638 trash = protos; 639 trash_num = num_protos; 640 protos = new_protos; 641 num_protos = rc; 642 } else { 643 /* 644 * Else the original protocols and algorithms lists 645 * remains the same. 646 */ 647 trash = NULL; 648 } 649 (void) rw_unlock(&proto_rw); 650 _clean_trash(trash, trash_num); 651 } else { 652 /* 653 * Assume caller has done the appropriate locking, 654 * cleanup, etc. And if new_protos is NULL, it's the caller's 655 * problem. 656 */ 657 *alg_context = new_protos; 658 *alg_nums = rc; 659 } 660 661 } 662 663 /* 664 * Assume input is 0-terminated. 665 */ 666 static int * 667 duplicate_intarr(int *orig) 668 { 669 size_t allocsize = sizeof (int); 670 int *iwalker = orig; 671 672 if (orig == NULL) 673 return (NULL); 674 675 while (*iwalker != 0) { 676 allocsize += sizeof (int); 677 iwalker++; 678 } 679 680 iwalker = malloc(allocsize); 681 if (iwalker != NULL) 682 (void) memcpy(iwalker, orig, allocsize); 683 684 return (iwalker); 685 } 686 687 /* 688 * Assume input is NULL terminated. 689 */ 690 static char ** 691 duplicate_strarr(char **orig) 692 { 693 int i; 694 char **swalker; 695 char **newbie; 696 697 if (orig == NULL) 698 return (NULL); 699 700 /* count number of elements in source array */ 701 for (swalker = orig; *swalker != NULL; swalker++) 702 ; 703 704 /* use calloc() to get NULL-initialization */ 705 newbie = calloc(swalker - orig + 1, sizeof (char *)); 706 707 if (newbie != NULL) { 708 /* do the copy */ 709 for (i = 0; orig[i] != NULL; i++) { 710 newbie[i] = strdup(orig[i]); 711 if (newbie[i] == NULL) { 712 for (swalker = newbie; *swalker != NULL; 713 swalker++) 714 free(*swalker); 715 free(newbie); 716 return (NULL); 717 } 718 } 719 } 720 721 return (newbie); 722 } 723 724 struct ipsecalgent * 725 _duplicate_alg(struct ipsecalgent *orig) 726 { 727 struct ipsecalgent *rc; 728 729 /* use calloc() to get NULL-initialization. */ 730 rc = calloc(1, sizeof (struct ipsecalgent)); 731 if (rc == NULL) 732 return (NULL); 733 734 rc->a_proto_num = orig->a_proto_num; 735 rc->a_alg_num = orig->a_alg_num; 736 rc->a_key_increment = orig->a_key_increment; 737 rc->a_mech_name = strdup(orig->a_mech_name); 738 rc->a_alg_flags = orig->a_alg_flags; 739 rc->a_block_sizes = duplicate_intarr(orig->a_block_sizes); 740 rc->a_mech_params = duplicate_intarr(orig->a_mech_params); 741 rc->a_key_sizes = duplicate_intarr(orig->a_key_sizes); 742 rc->a_names = duplicate_strarr(orig->a_names); 743 744 if (rc->a_mech_name == NULL || rc->a_block_sizes == NULL || 745 rc->a_key_sizes == NULL || rc->a_names == NULL || 746 rc->a_mech_params == NULL) { 747 freeipsecalgent(rc); 748 return (NULL); 749 } 750 751 return (rc); 752 } 753 754 /* 755 * Assume the rwlock is held for reading. 756 */ 757 static ipsec_proto_t * 758 findprotobynum(int proto_num) 759 { 760 int i; 761 762 for (i = 0; i < num_protos; i++) { 763 if (protos[i].proto_num == proto_num) 764 return (protos + i); 765 } 766 767 return (NULL); 768 } 769 770 static ipsec_proto_t * 771 findprotobyname(const char *name) 772 { 773 int i; 774 775 if (name == NULL) 776 return (NULL); 777 778 for (i = 0; i < num_protos; i++) { 779 /* Can use strcasecmp because our proto_name is bounded. */ 780 if (strcasecmp(protos[i].proto_name, name) == 0) 781 return (protos + i); 782 } 783 784 return (NULL); 785 } 786 787 int * 788 _real_getipsecprotos(int *nentries) 789 { 790 int *rc, i; 791 792 if (nentries == NULL) 793 return (NULL); 794 795 _build_internal_algs(NULL, NULL); 796 797 (void) rw_rdlock(&proto_rw); 798 *nentries = num_protos; 799 /* 800 * Allocate 1 byte if there are no protocols so a non-NULL return 801 * happens. 802 */ 803 rc = malloc((num_protos == 0) ? 1 : num_protos * sizeof (int)); 804 if (rc != NULL) { 805 for (i = 0; i < num_protos; i++) 806 rc[i] = protos[i].proto_num; 807 } 808 (void) rw_unlock(&proto_rw); 809 return (rc); 810 } 811 812 int * 813 _real_getipsecalgs(int *nentries, int proto_num) 814 { 815 int *rc = NULL, i; 816 ipsec_proto_t *proto; 817 818 if (nentries == NULL) 819 return (NULL); 820 821 _build_internal_algs(NULL, NULL); 822 823 (void) rw_rdlock(&proto_rw); 824 proto = findprotobynum(proto_num); 825 if (proto != NULL) { 826 *nentries = proto->proto_numalgs; 827 /* 828 * Allocate 1 byte if there are no algorithms so a non-NULL 829 * return happens. 830 */ 831 rc = malloc((proto->proto_numalgs == 0) ? 1 : 832 proto->proto_numalgs * sizeof (int)); 833 if (rc != NULL) { 834 for (i = 0; i < proto->proto_numalgs; i++) 835 rc[i] = proto->proto_algs[i]->a_alg_num; 836 } 837 } 838 (void) rw_unlock(&proto_rw); 839 return (rc); 840 } 841 842 struct ipsecalgent * 843 getipsecalgbyname(const char *name, int proto_num, int *errnop) 844 { 845 ipsec_proto_t *proto; 846 struct ipsecalgent *rc = NULL; 847 int i, my_errno = ENOENT; 848 char **name_check; 849 850 _build_internal_algs(NULL, NULL); 851 if (name == NULL) { 852 my_errno = EFAULT; 853 goto bail; 854 } 855 856 (void) rw_rdlock(&proto_rw); 857 proto = findprotobynum(proto_num); 858 if (proto != NULL) { 859 for (i = 0; i < proto->proto_numalgs; i++) { 860 for (name_check = proto->proto_algs[i]->a_names; 861 *name_check != NULL; name_check++) { 862 /* 863 * Can use strcasecmp because our name_check 864 * is bounded. 865 */ 866 if (strcasecmp(*name_check, name) == 0) { 867 /* found match */ 868 rc = _duplicate_alg( 869 proto->proto_algs[i]); 870 my_errno = (rc == NULL) ? ENOMEM : 0; 871 (void) rw_unlock(&proto_rw); 872 goto bail; 873 } 874 } 875 } 876 } else { 877 my_errno = EINVAL; 878 } 879 880 (void) rw_unlock(&proto_rw); 881 bail: 882 if (errnop != NULL) 883 *errnop = my_errno; 884 return (rc); 885 } 886 887 struct ipsecalgent * 888 getipsecalgbynum(int alg_num, int proto_num, int *errnop) 889 { 890 ipsec_proto_t *proto; 891 struct ipsecalgent *rc = NULL; 892 int i, my_errno = ENOENT; 893 894 _build_internal_algs(NULL, NULL); 895 896 (void) rw_rdlock(&proto_rw); 897 898 proto = findprotobynum(proto_num); 899 if (proto != NULL) { 900 for (i = 0; i < proto->proto_numalgs; i++) { 901 if (proto->proto_algs[i]->a_alg_num == alg_num) { 902 rc = _duplicate_alg(proto->proto_algs[i]); 903 my_errno = (rc == NULL) ? ENOMEM : 0; 904 break; 905 } 906 } 907 } else { 908 my_errno = EINVAL; 909 } 910 911 (void) rw_unlock(&proto_rw); 912 if (errnop != NULL) 913 *errnop = my_errno; 914 return (rc); 915 } 916 917 int 918 getipsecprotobyname(const char *proto_name) 919 { 920 int rc = -1; 921 ipsec_proto_t *proto; 922 923 _build_internal_algs(NULL, NULL); 924 925 (void) rw_rdlock(&proto_rw); 926 proto = findprotobyname(proto_name); 927 if (proto != NULL) 928 rc = proto->proto_num; 929 (void) rw_unlock(&proto_rw); 930 return (rc); 931 } 932 933 char * 934 getipsecprotobynum(int proto_num) 935 { 936 ipsec_proto_t *proto; 937 char *rc = NULL; 938 939 _build_internal_algs(NULL, NULL); 940 941 (void) rw_rdlock(&proto_rw); 942 proto = findprotobynum(proto_num); 943 if (proto != NULL) 944 rc = strdup(proto->proto_name); 945 946 (void) rw_unlock(&proto_rw); 947 return (rc); 948 } 949 950 void 951 freeipsecalgent(struct ipsecalgent *ptr) 952 { 953 char **walker; 954 955 if (ptr == NULL) 956 return; 957 958 if (ptr->a_names != NULL) { 959 for (walker = ptr->a_names; *walker != NULL; walker++) 960 free(*walker); 961 } 962 963 /* 964 * Remember folks, free(NULL) works. 965 */ 966 free(ptr->a_names); 967 free(ptr->a_mech_name); 968 free(ptr->a_block_sizes); 969 free(ptr->a_mech_params); 970 free(ptr->a_key_sizes); 971 free(ptr); 972 } 973