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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <unistd.h> 27 #include <stdlib.h> 28 #include <strings.h> 29 #include <errno.h> 30 #include <ctype.h> 31 #include <fcntl.h> 32 #include <sys/stat.h> 33 #include <sys/dld.h> 34 #include <libinetutil.h> 35 #include <libdllink.h> 36 #include <libdladm_impl.h> 37 38 static dladm_status_t i_dladm_set_secobj_db(const char *, 39 dladm_secobj_class_t, uint8_t *, uint_t); 40 static dladm_status_t i_dladm_get_secobj_db(const char *, 41 dladm_secobj_class_t *, uint8_t *, uint_t *); 42 static dladm_status_t i_dladm_unset_secobj_db(const char *); 43 static dladm_status_t i_dladm_walk_secobj_db(void *, 44 boolean_t (*)(void *, const char *)); 45 46 typedef struct secobj_class_info { 47 const char *sc_name; 48 dld_secobj_class_t sc_dldclass; 49 } secobj_class_info_t; 50 51 static secobj_class_info_t secobj_class_table[] = { 52 {"wep", DLD_SECOBJ_CLASS_WEP}, 53 {"wpa", DLD_SECOBJ_CLASS_WPA} 54 }; 55 56 #define SECOBJ_MAXBUFSZ 65536 57 #define NSECOBJCLASS \ 58 (sizeof (secobj_class_table) / sizeof (secobj_class_info_t)) 59 60 static boolean_t 61 dladm_check_secobjclass(dladm_secobj_class_t class) 62 { 63 return (class >= 0 && class < NSECOBJCLASS); 64 } 65 66 dladm_status_t 67 dladm_str2secobjclass(const char *str, dladm_secobj_class_t *class) 68 { 69 int i; 70 secobj_class_info_t *sp; 71 72 for (i = 0; i < NSECOBJCLASS; i++) { 73 sp = &secobj_class_table[i]; 74 if (strcasecmp(str, sp->sc_name) == 0) { 75 *class = i; 76 return (DLADM_STATUS_OK); 77 } 78 } 79 return (DLADM_STATUS_BADARG); 80 } 81 82 const char * 83 dladm_secobjclass2str(dladm_secobj_class_t class, char *buf) 84 { 85 const char *s; 86 87 if (!dladm_check_secobjclass(class)) 88 s = ""; 89 else 90 s = secobj_class_table[class].sc_name; 91 92 (void) snprintf(buf, DLADM_STRSIZE, "%s", s); 93 return (buf); 94 } 95 96 static boolean_t 97 dladm_convert_secobjclass(dladm_secobj_class_t class, 98 dld_secobj_class_t *dldclass) 99 { 100 if (!dladm_check_secobjclass(class)) 101 return (B_FALSE); 102 103 *dldclass = secobj_class_table[class].sc_dldclass; 104 return (B_TRUE); 105 } 106 107 static boolean_t 108 dladm_convert_dldsecobjclass(dld_secobj_class_t dldclass, 109 dladm_secobj_class_t *class) 110 { 111 int i; 112 secobj_class_info_t *sp; 113 114 for (i = 0; i < NSECOBJCLASS; i++) { 115 sp = &secobj_class_table[i]; 116 if (dldclass == sp->sc_dldclass) { 117 *class = i; 118 return (B_TRUE); 119 } 120 } 121 return (B_FALSE); 122 } 123 124 dladm_status_t 125 dladm_set_secobj(const char *obj_name, dladm_secobj_class_t class, 126 uint8_t *obj_val, uint_t obj_len, uint_t flags) 127 { 128 int fd; 129 dladm_status_t status = DLADM_STATUS_OK; 130 dld_ioc_secobj_set_t secobj_set; 131 dld_secobj_t *objp; 132 133 if (!dladm_valid_secobj_name(obj_name)) 134 return (DLADM_STATUS_BADARG); 135 136 if (!dladm_check_secobjclass(class) || flags == 0 || 137 obj_name == NULL || strlen(obj_name) > DLD_SECOBJ_NAME_MAX || 138 obj_val == NULL || obj_len == 0 || obj_len > DLD_SECOBJ_VAL_MAX) 139 return (DLADM_STATUS_BADARG); 140 141 if ((flags & DLADM_OPT_ACTIVE) == 0) 142 goto persist; 143 144 bzero(&secobj_set, sizeof (secobj_set)); 145 objp = &secobj_set.ss_obj; 146 if (!dladm_convert_secobjclass(class, &objp->so_class)) 147 return (DLADM_STATUS_BADARG); 148 149 (void) strlcpy(objp->so_name, obj_name, DLD_SECOBJ_NAME_MAX); 150 bcopy(obj_val, objp->so_val, obj_len); 151 objp->so_len = obj_len; 152 153 if ((flags & DLADM_OPT_CREATE) != 0) 154 secobj_set.ss_flags = DLD_SECOBJ_OPT_CREATE; 155 156 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 157 return (dladm_errno2status(errno)); 158 159 if (ioctl(fd, DLDIOC_SECOBJ_SET, &secobj_set) < 0) 160 status = dladm_errno2status(errno); 161 162 (void) close(fd); 163 164 if (status != DLADM_STATUS_OK) 165 return (status); 166 167 persist: 168 if ((flags & DLADM_OPT_PERSIST) != 0) { 169 status = i_dladm_set_secobj_db(obj_name, class, 170 obj_val, obj_len); 171 } 172 return (status); 173 } 174 175 dladm_status_t 176 dladm_get_secobj(const char *obj_name, dladm_secobj_class_t *classp, 177 uint8_t *obj_val, uint_t *obj_lenp, uint_t flags) 178 { 179 int fd; 180 dladm_status_t status = DLADM_STATUS_OK; 181 dld_ioc_secobj_get_t secobj_get; 182 dld_secobj_t *objp; 183 184 if (obj_name == NULL || strlen(obj_name) > DLD_SECOBJ_NAME_MAX || 185 obj_val == NULL || obj_lenp == NULL || *obj_lenp == 0 || 186 *obj_lenp > DLD_SECOBJ_VAL_MAX) 187 return (DLADM_STATUS_BADARG); 188 189 if ((flags & DLADM_OPT_PERSIST) != 0) { 190 return (i_dladm_get_secobj_db(obj_name, classp, 191 obj_val, obj_lenp)); 192 } 193 194 bzero(&secobj_get, sizeof (secobj_get)); 195 objp = &secobj_get.sg_obj; 196 (void) strlcpy(objp->so_name, obj_name, DLD_SECOBJ_NAME_MAX); 197 198 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 199 return (dladm_errno2status(errno)); 200 201 secobj_get.sg_size = sizeof (secobj_get); 202 if (ioctl(fd, DLDIOC_SECOBJ_GET, &secobj_get) < 0) 203 status = dladm_errno2status(errno); 204 205 (void) close(fd); 206 if (objp->so_len > *obj_lenp) 207 return (DLADM_STATUS_TOOSMALL); 208 209 if (!dladm_convert_dldsecobjclass(objp->so_class, classp)) 210 return (DLADM_STATUS_FAILED); 211 212 *obj_lenp = objp->so_len; 213 bcopy(objp->so_val, obj_val, *obj_lenp); 214 return (status); 215 } 216 217 dladm_status_t 218 dladm_unset_secobj(const char *obj_name, uint_t flags) 219 { 220 int fd; 221 dladm_status_t status = DLADM_STATUS_OK; 222 dld_ioc_secobj_unset_t secobj_unset; 223 224 if (obj_name == NULL || strlen(obj_name) > DLD_SECOBJ_NAME_MAX || 225 flags == 0) 226 return (DLADM_STATUS_BADARG); 227 228 if ((flags & DLADM_OPT_ACTIVE) == 0) 229 goto persist; 230 231 bzero(&secobj_unset, sizeof (secobj_unset)); 232 (void) strlcpy(secobj_unset.su_name, obj_name, DLD_SECOBJ_NAME_MAX); 233 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 234 return (dladm_errno2status(errno)); 235 236 if (ioctl(fd, DLDIOC_SECOBJ_UNSET, &secobj_unset) < 0) 237 status = dladm_errno2status(errno); 238 239 (void) close(fd); 240 if (status != DLADM_STATUS_OK) 241 return (status); 242 243 persist: 244 if ((flags & DLADM_OPT_PERSIST) != 0) 245 status = i_dladm_unset_secobj_db(obj_name); 246 247 return (status); 248 } 249 250 dladm_status_t 251 dladm_walk_secobj(void *arg, boolean_t (*func)(void *, const char *), 252 uint_t flags) 253 { 254 int fd = -1; 255 dladm_status_t status = DLADM_STATUS_OK; 256 dld_ioc_secobj_get_t *secobj_getp; 257 dld_secobj_t *objp; 258 size_t secobj_bufsz; 259 260 if ((flags & DLADM_OPT_PERSIST) != 0) 261 return (i_dladm_walk_secobj_db(arg, func)); 262 263 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 264 return (dladm_errno2status(errno)); 265 266 /* Start with enough room for 10 objects, increase if necessary. */ 267 secobj_bufsz = sizeof (*secobj_getp) + (10 * sizeof (*objp)); 268 secobj_getp = calloc(1, secobj_bufsz); 269 if (secobj_getp == NULL) { 270 status = dladm_errno2status(errno); 271 goto done; 272 } 273 274 tryagain: 275 secobj_getp->sg_size = secobj_bufsz; 276 if (ioctl(fd, DLDIOC_SECOBJ_GET, secobj_getp) < 0) { 277 if (errno == ENOSPC) { 278 /* Increase the buffer size and try again. */ 279 secobj_bufsz *= 2; 280 if (secobj_bufsz > SECOBJ_MAXBUFSZ) { 281 status = dladm_errno2status(errno); 282 goto done; 283 } 284 secobj_getp = realloc(secobj_getp, secobj_bufsz); 285 if (secobj_getp == NULL) { 286 status = dladm_errno2status(errno); 287 goto done; 288 } 289 bzero(secobj_getp, secobj_bufsz); 290 goto tryagain; 291 } 292 status = dladm_errno2status(errno); 293 goto done; 294 } 295 296 objp = (dld_secobj_t *)(secobj_getp + 1); 297 while (secobj_getp->sg_count > 0) { 298 if (!func(arg, objp->so_name)) 299 goto done; 300 secobj_getp->sg_count--; 301 objp++; 302 } 303 done: 304 (void) close(fd); 305 free(secobj_getp); 306 return (status); 307 } 308 309 /* 310 * Data structures used for implementing persistent secure objects 311 */ 312 typedef struct secobj_info { 313 const char *si_name; 314 dladm_secobj_class_t *si_classp; 315 uint8_t *si_val; 316 uint_t *si_lenp; 317 } secobj_info_t; 318 319 typedef struct secobj_name { 320 char *sn_name; 321 struct secobj_name *sn_next; 322 } secobj_name_t; 323 324 typedef struct secobj_db_state secobj_db_state_t; 325 326 typedef boolean_t (*secobj_db_op_t)(struct secobj_db_state *, char *, 327 secobj_info_t *, dladm_status_t *); 328 329 struct secobj_db_state { 330 secobj_db_op_t ss_op; 331 secobj_info_t ss_info; 332 secobj_name_t **ss_namelist; 333 }; 334 335 /* 336 * Update or generate a secobj entry using the info in ssp->ss_info. 337 */ 338 /* ARGSUSED */ 339 static boolean_t 340 process_secobj_set(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip, 341 dladm_status_t *statusp) 342 { 343 char tmpbuf[MAXLINELEN]; 344 char classbuf[DLADM_STRSIZE]; 345 char *ptr = tmpbuf, *lim = tmpbuf + MAXLINELEN; 346 int i; 347 348 sip = &ssp->ss_info; 349 350 ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", sip->si_name); 351 ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", 352 dladm_secobjclass2str(*sip->si_classp, classbuf)); 353 354 ptr += snprintf(ptr, BUFLEN(lim, ptr), "0x"); 355 for (i = 0; i < *sip->si_lenp; i++) { 356 ptr += snprintf(ptr, BUFLEN(lim, ptr), "%02x", 357 sip->si_val[i] & 0xff); 358 } 359 if (ptr > lim) { 360 *statusp = DLADM_STATUS_TOOSMALL; 361 return (B_FALSE); 362 } 363 (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf); 364 return (B_FALSE); 365 } 366 367 /* ARGSUSED */ 368 static boolean_t 369 process_secobj_get(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip, 370 dladm_status_t *statusp) 371 { 372 if (*sip->si_lenp > *ssp->ss_info.si_lenp) { 373 *statusp = DLADM_STATUS_TOOSMALL; 374 return (B_FALSE); 375 } 376 bcopy(sip->si_val, ssp->ss_info.si_val, *sip->si_lenp); 377 *ssp->ss_info.si_lenp = *sip->si_lenp; 378 *ssp->ss_info.si_classp = *sip->si_classp; 379 return (B_FALSE); 380 } 381 382 /* ARGSUSED */ 383 static boolean_t 384 process_secobj_unset(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip, 385 dladm_status_t *statusp) 386 { 387 /* 388 * Delete line. 389 */ 390 buf[0] = '\0'; 391 return (B_FALSE); 392 } 393 394 /* ARGSUSED */ 395 static boolean_t 396 process_secobj_walk(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip, 397 dladm_status_t *statusp) 398 { 399 secobj_name_t *snp; 400 401 if ((snp = malloc(sizeof (*snp))) == NULL) 402 return (B_TRUE); 403 404 if ((snp->sn_name = strdup(sip->si_name)) == NULL) { 405 free(snp); 406 return (B_TRUE); 407 } 408 409 snp->sn_next = NULL; 410 *ssp->ss_namelist = snp; 411 ssp->ss_namelist = &snp->sn_next; 412 return (B_TRUE); 413 } 414 415 /* ARGSUSED */ 416 static boolean_t 417 process_secobj_init(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip, 418 dladm_status_t *statusp) 419 { 420 *statusp = dladm_set_secobj(sip->si_name, *sip->si_classp, sip->si_val, 421 *sip->si_lenp, DLADM_OPT_ACTIVE | DLADM_OPT_CREATE); 422 return (B_TRUE); 423 } 424 425 static int 426 parse_secobj_val(char *buf, secobj_info_t *sip) 427 { 428 if (strncmp(buf, "0x", 2) != 0) 429 return (EINVAL); 430 431 return (hexascii_to_octet(buf + 2, strlen(buf) - 2, 432 sip->si_val, sip->si_lenp)); 433 } 434 435 static boolean_t 436 process_secobj_line(secobj_db_state_t *ssp, char *buf, 437 dladm_status_t *statusp) 438 { 439 secobj_info_t sinfo; 440 dladm_secobj_class_t class; 441 uint8_t val[DLADM_SECOBJ_VAL_MAX]; 442 uint_t vlen; 443 int i, len, nlen; 444 char *str, *lasts; 445 446 /* 447 * Skip leading spaces, blank lines, and comments. 448 */ 449 len = strlen(buf); 450 for (i = 0; i < len; i++) { 451 if (!isspace(buf[i])) 452 break; 453 } 454 if (i == len || buf[i] == '#') 455 return (B_TRUE); 456 457 str = buf + i; 458 if (ssp->ss_info.si_name != NULL) { 459 /* 460 * Skip objects we're not interested in. 461 */ 462 nlen = strlen(ssp->ss_info.si_name); 463 if (strncmp(str, ssp->ss_info.si_name, nlen) != 0 || 464 !isspace(str[nlen])) 465 return (B_TRUE); 466 467 sinfo.si_name = ssp->ss_info.si_name; 468 } else { 469 /* 470 * If an object is not specified, find the object name 471 * and assign it to sinfo.si_name. 472 */ 473 if (strtok_r(str, " \n\t", &lasts) == NULL) 474 goto fail; 475 476 nlen = strlen(str); 477 sinfo.si_name = str; 478 } 479 str += nlen + 1; 480 if (str >= buf + len) 481 goto fail; 482 483 /* 484 * Find the class name. 485 */ 486 if ((str = strtok_r(str, " \n\t", &lasts)) == NULL) 487 goto fail; 488 489 *statusp = dladm_str2secobjclass(str, &class); 490 if (*statusp != DLADM_STATUS_OK) 491 goto fail; 492 493 /* 494 * Find the object value. 495 */ 496 if ((str = strtok_r(NULL, " \n\t", &lasts)) == NULL) 497 goto fail; 498 499 vlen = DLADM_SECOBJ_VAL_MAX; 500 sinfo.si_classp = &class; 501 sinfo.si_val = val; 502 sinfo.si_lenp = &vlen; 503 if (parse_secobj_val(str, &sinfo) != 0) 504 goto fail; 505 506 return ((*ssp->ss_op)(ssp, buf, &sinfo, statusp)); 507 508 fail: 509 /* 510 * Delete corrupted line. 511 */ 512 buf[0] = '\0'; 513 return (B_TRUE); 514 } 515 516 static dladm_status_t 517 process_secobj_db(void *arg, FILE *fp, FILE *nfp) 518 { 519 secobj_db_state_t *ssp = arg; 520 dladm_status_t status = DLADM_STATUS_OK; 521 char buf[MAXLINELEN]; 522 boolean_t cont = B_TRUE; 523 524 /* 525 * This loop processes each line of the configuration file. 526 * buf can potentially be modified by process_secobj_line(). 527 * If this is a write operation and buf is not truncated, buf will 528 * be written to disk. process_secobj_line() will no longer be 529 * called after it returns B_FALSE; at which point the remainder 530 * of the file will continue to be read and, if necessary, written 531 * to disk as well. 532 */ 533 while (fgets(buf, MAXLINELEN, fp) != NULL) { 534 if (cont) 535 cont = process_secobj_line(ssp, buf, &status); 536 537 if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) { 538 status = dladm_errno2status(errno); 539 break; 540 } 541 } 542 if (status != DLADM_STATUS_OK || !cont) 543 return (status); 544 545 if (ssp->ss_op == process_secobj_set) { 546 /* 547 * If the specified object is not found above, we add the 548 * object to the configuration file. 549 */ 550 (void) (*ssp->ss_op)(ssp, buf, NULL, &status); 551 if (status == DLADM_STATUS_OK && fputs(buf, nfp) == EOF) 552 status = dladm_errno2status(errno); 553 } 554 555 if (ssp->ss_op == process_secobj_unset || 556 ssp->ss_op == process_secobj_get) 557 status = DLADM_STATUS_NOTFOUND; 558 559 return (status); 560 } 561 562 #define SECOBJ_RW_DB(statep, writeop) \ 563 (i_dladm_rw_db("/etc/dladm/secobj.conf", S_IRUSR | S_IWUSR, \ 564 process_secobj_db, (statep), (writeop))) 565 566 static dladm_status_t 567 i_dladm_set_secobj_db(const char *obj_name, dladm_secobj_class_t class, 568 uint8_t *obj_val, uint_t obj_len) 569 { 570 secobj_db_state_t state; 571 572 state.ss_op = process_secobj_set; 573 state.ss_info.si_name = obj_name; 574 state.ss_info.si_classp = &class; 575 state.ss_info.si_val = obj_val; 576 state.ss_info.si_lenp = &obj_len; 577 state.ss_namelist = NULL; 578 579 return (SECOBJ_RW_DB(&state, B_TRUE)); 580 } 581 582 static dladm_status_t 583 i_dladm_get_secobj_db(const char *obj_name, dladm_secobj_class_t *classp, 584 uint8_t *obj_val, uint_t *obj_lenp) 585 { 586 secobj_db_state_t state; 587 588 state.ss_op = process_secobj_get; 589 state.ss_info.si_name = obj_name; 590 state.ss_info.si_classp = classp; 591 state.ss_info.si_val = obj_val; 592 state.ss_info.si_lenp = obj_lenp; 593 state.ss_namelist = NULL; 594 595 return (SECOBJ_RW_DB(&state, B_FALSE)); 596 } 597 598 static dladm_status_t 599 i_dladm_unset_secobj_db(const char *obj_name) 600 { 601 secobj_db_state_t state; 602 603 state.ss_op = process_secobj_unset; 604 state.ss_info.si_name = obj_name; 605 state.ss_info.si_classp = NULL; 606 state.ss_info.si_val = NULL; 607 state.ss_info.si_lenp = NULL; 608 state.ss_namelist = NULL; 609 610 return (SECOBJ_RW_DB(&state, B_TRUE)); 611 } 612 613 static dladm_status_t 614 i_dladm_walk_secobj_db(void *arg, boolean_t (*func)(void *, const char *)) 615 { 616 secobj_db_state_t state; 617 secobj_name_t *snp = NULL, *fsnp; 618 dladm_status_t status; 619 boolean_t cont = B_TRUE; 620 621 state.ss_op = process_secobj_walk; 622 state.ss_info.si_name = NULL; 623 state.ss_info.si_classp = NULL; 624 state.ss_info.si_val = NULL; 625 state.ss_info.si_lenp = NULL; 626 state.ss_namelist = &snp; 627 628 status = SECOBJ_RW_DB(&state, B_FALSE); 629 if (status != DLADM_STATUS_OK) 630 return (status); 631 632 while (snp != NULL) { 633 fsnp = snp; 634 snp = snp->sn_next; 635 if (cont) 636 cont = func(arg, fsnp->sn_name); 637 free(fsnp->sn_name); 638 free(fsnp); 639 } 640 return (status); 641 } 642 643 dladm_status_t 644 dladm_init_secobj(void) 645 { 646 secobj_db_state_t state; 647 648 state.ss_op = process_secobj_init; 649 state.ss_info.si_name = NULL; 650 state.ss_info.si_classp = NULL; 651 state.ss_info.si_val = NULL; 652 state.ss_info.si_lenp = NULL; 653 state.ss_namelist = NULL; 654 655 return (SECOBJ_RW_DB(&state, B_FALSE)); 656 } 657 658 boolean_t 659 dladm_valid_secobj_name(const char *secobj_name) 660 { 661 size_t len = strlen(secobj_name); 662 const char *cp; 663 664 if (len + 1 > DLADM_SECOBJ_NAME_MAX) 665 return (B_FALSE); 666 667 /* 668 * The legal characters in a secobj name are: 669 * alphanumeric (a-z, A-Z, 0-9), '.', '_', '-'. 670 */ 671 for (cp = secobj_name; *cp != '\0'; cp++) { 672 if (!isalnum(*cp) && 673 (*cp != '.') && (*cp != '_') && (*cp != '-')) 674 return (B_FALSE); 675 } 676 677 return (B_TRUE); 678 } 679