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