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