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