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(dladm_handle_t, const char *, 39 dladm_secobj_class_t, uint8_t *, uint_t); 40 static dladm_status_t i_dladm_get_secobj_db(dladm_handle_t, const char *, 41 dladm_secobj_class_t *, uint8_t *, uint_t *); 42 static dladm_status_t i_dladm_unset_secobj_db(dladm_handle_t, const char *); 43 static dladm_status_t i_dladm_walk_secobj_db(dladm_handle_t, void *, 44 boolean_t (*)(dladm_handle_t, void *, 45 const char *)); 46 47 typedef struct secobj_class_info { 48 const char *sc_name; 49 dld_secobj_class_t sc_dldclass; 50 } secobj_class_info_t; 51 52 static secobj_class_info_t secobj_class_table[] = { 53 {"wep", DLD_SECOBJ_CLASS_WEP}, 54 {"wpa", DLD_SECOBJ_CLASS_WPA} 55 }; 56 57 #define SECOBJ_MAXBUFSZ 65536 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(dladm_handle_t handle, const char *obj_name, 127 dladm_secobj_class_t class, uint8_t *obj_val, uint_t obj_len, uint_t flags) 128 { 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 (ioctl(dladm_dld_fd(handle), DLDIOC_SECOBJ_SET, &secobj_set) < 0) 157 status = dladm_errno2status(errno); 158 159 if (status != DLADM_STATUS_OK) 160 return (status); 161 162 persist: 163 if ((flags & DLADM_OPT_PERSIST) != 0) { 164 status = i_dladm_set_secobj_db(handle, obj_name, class, 165 obj_val, obj_len); 166 } 167 return (status); 168 } 169 170 dladm_status_t 171 dladm_get_secobj(dladm_handle_t handle, const char *obj_name, 172 dladm_secobj_class_t *classp, uint8_t *obj_val, uint_t *obj_lenp, 173 uint_t flags) 174 { 175 dladm_status_t status = DLADM_STATUS_OK; 176 dld_ioc_secobj_get_t secobj_get; 177 dld_secobj_t *objp; 178 179 if (obj_name == NULL || strlen(obj_name) > DLD_SECOBJ_NAME_MAX || 180 obj_val == NULL || obj_lenp == NULL || *obj_lenp == 0 || 181 *obj_lenp > DLD_SECOBJ_VAL_MAX) 182 return (DLADM_STATUS_BADARG); 183 184 if ((flags & DLADM_OPT_PERSIST) != 0) { 185 return (i_dladm_get_secobj_db(handle, obj_name, classp, 186 obj_val, obj_lenp)); 187 } 188 189 bzero(&secobj_get, sizeof (secobj_get)); 190 objp = &secobj_get.sg_obj; 191 (void) strlcpy(objp->so_name, obj_name, DLD_SECOBJ_NAME_MAX); 192 193 secobj_get.sg_size = sizeof (secobj_get); 194 if (ioctl(dladm_dld_fd(handle), DLDIOC_SECOBJ_GET, &secobj_get) < 0) 195 status = dladm_errno2status(errno); 196 197 if (objp->so_len > *obj_lenp) 198 return (DLADM_STATUS_TOOSMALL); 199 200 if (!dladm_convert_dldsecobjclass(objp->so_class, classp)) 201 return (DLADM_STATUS_FAILED); 202 203 *obj_lenp = objp->so_len; 204 bcopy(objp->so_val, obj_val, *obj_lenp); 205 return (status); 206 } 207 208 dladm_status_t 209 dladm_unset_secobj(dladm_handle_t handle, const char *obj_name, uint_t flags) 210 { 211 dladm_status_t status = DLADM_STATUS_OK; 212 dld_ioc_secobj_unset_t secobj_unset; 213 214 if (obj_name == NULL || strlen(obj_name) > DLD_SECOBJ_NAME_MAX || 215 flags == 0) 216 return (DLADM_STATUS_BADARG); 217 218 if ((flags & DLADM_OPT_ACTIVE) == 0) 219 goto persist; 220 221 bzero(&secobj_unset, sizeof (secobj_unset)); 222 (void) strlcpy(secobj_unset.su_name, obj_name, DLD_SECOBJ_NAME_MAX); 223 224 if (ioctl(dladm_dld_fd(handle), DLDIOC_SECOBJ_UNSET, &secobj_unset) < 0) 225 status = dladm_errno2status(errno); 226 227 if (status != DLADM_STATUS_OK) 228 return (status); 229 230 persist: 231 if ((flags & DLADM_OPT_PERSIST) != 0) 232 status = i_dladm_unset_secobj_db(handle, obj_name); 233 234 return (status); 235 } 236 237 dladm_status_t 238 dladm_walk_secobj(dladm_handle_t handle, void *arg, 239 boolean_t (*func)(dladm_handle_t, void *, const char *), uint_t flags) 240 { 241 dladm_status_t status = DLADM_STATUS_OK; 242 dld_ioc_secobj_get_t *secobj_getp; 243 dld_secobj_t *objp; 244 size_t secobj_bufsz; 245 246 if ((flags & DLADM_OPT_PERSIST) != 0) 247 return (i_dladm_walk_secobj_db(handle, arg, func)); 248 249 /* Start with enough room for 10 objects, increase if necessary. */ 250 secobj_bufsz = sizeof (*secobj_getp) + (10 * sizeof (*objp)); 251 secobj_getp = calloc(1, secobj_bufsz); 252 if (secobj_getp == NULL) { 253 status = dladm_errno2status(errno); 254 goto done; 255 } 256 257 tryagain: 258 secobj_getp->sg_size = secobj_bufsz; 259 if (ioctl(dladm_dld_fd(handle), DLDIOC_SECOBJ_GET, secobj_getp) < 0) { 260 if (errno == ENOSPC) { 261 /* Increase the buffer size and try again. */ 262 secobj_bufsz *= 2; 263 if (secobj_bufsz > SECOBJ_MAXBUFSZ) { 264 status = dladm_errno2status(errno); 265 goto done; 266 } 267 secobj_getp = realloc(secobj_getp, secobj_bufsz); 268 if (secobj_getp == NULL) { 269 status = dladm_errno2status(errno); 270 goto done; 271 } 272 bzero(secobj_getp, secobj_bufsz); 273 goto tryagain; 274 } 275 status = dladm_errno2status(errno); 276 goto done; 277 } 278 279 objp = (dld_secobj_t *)(secobj_getp + 1); 280 while (secobj_getp->sg_count > 0) { 281 if (!func(handle, arg, objp->so_name)) 282 goto done; 283 secobj_getp->sg_count--; 284 objp++; 285 } 286 done: 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)(dladm_handle_t, struct secobj_db_state *, 309 char *, 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(dladm_handle_t handle, secobj_db_state_t *ssp, char *buf, 323 secobj_info_t *sip, 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(dladm_handle_t handle, secobj_db_state_t *ssp, char *buf, 352 secobj_info_t *sip, 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(dladm_handle_t handle, secobj_db_state_t *ssp, char *buf, 367 secobj_info_t *sip, 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(dladm_handle_t handle, secobj_db_state_t *ssp, char *buf, 379 secobj_info_t *sip, 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(dladm_handle_t handle, secobj_db_state_t *ssp, char *buf, 400 secobj_info_t *sip, dladm_status_t *statusp) 401 { 402 *statusp = dladm_set_secobj(handle, sip->si_name, *sip->si_classp, 403 sip->si_val, *sip->si_lenp, 404 DLADM_OPT_ACTIVE | DLADM_OPT_CREATE); 405 return (B_TRUE); 406 } 407 408 static int 409 parse_secobj_val(char *buf, secobj_info_t *sip) 410 { 411 if (strncmp(buf, "0x", 2) != 0) 412 return (EINVAL); 413 414 return (hexascii_to_octet(buf + 2, strlen(buf) - 2, 415 sip->si_val, sip->si_lenp)); 416 } 417 418 static boolean_t 419 process_secobj_line(dladm_handle_t handle, secobj_db_state_t *ssp, char *buf, 420 dladm_status_t *statusp) 421 { 422 secobj_info_t sinfo; 423 dladm_secobj_class_t class; 424 uint8_t val[DLADM_SECOBJ_VAL_MAX]; 425 uint_t vlen; 426 int i, len, nlen; 427 char *str, *lasts; 428 429 /* 430 * Skip leading spaces, blank lines, and comments. 431 */ 432 len = strlen(buf); 433 for (i = 0; i < len; i++) { 434 if (!isspace(buf[i])) 435 break; 436 } 437 if (i == len || buf[i] == '#') 438 return (B_TRUE); 439 440 str = buf + i; 441 if (ssp->ss_info.si_name != NULL) { 442 /* 443 * Skip objects we're not interested in. 444 */ 445 nlen = strlen(ssp->ss_info.si_name); 446 if (strncmp(str, ssp->ss_info.si_name, nlen) != 0 || 447 !isspace(str[nlen])) 448 return (B_TRUE); 449 450 sinfo.si_name = ssp->ss_info.si_name; 451 } else { 452 /* 453 * If an object is not specified, find the object name 454 * and assign it to sinfo.si_name. 455 */ 456 if (strtok_r(str, " \n\t", &lasts) == NULL) 457 goto fail; 458 459 nlen = strlen(str); 460 sinfo.si_name = str; 461 } 462 str += nlen + 1; 463 if (str >= buf + len) 464 goto fail; 465 466 /* 467 * Find the class name. 468 */ 469 if ((str = strtok_r(str, " \n\t", &lasts)) == NULL) 470 goto fail; 471 472 *statusp = dladm_str2secobjclass(str, &class); 473 if (*statusp != DLADM_STATUS_OK) 474 goto fail; 475 476 /* 477 * Find the object value. 478 */ 479 if ((str = strtok_r(NULL, " \n\t", &lasts)) == NULL) 480 goto fail; 481 482 vlen = DLADM_SECOBJ_VAL_MAX; 483 sinfo.si_classp = &class; 484 sinfo.si_val = val; 485 sinfo.si_lenp = &vlen; 486 if (parse_secobj_val(str, &sinfo) != 0) 487 goto fail; 488 489 return ((*ssp->ss_op)(handle, ssp, buf, &sinfo, statusp)); 490 491 fail: 492 /* 493 * Delete corrupted line. 494 */ 495 buf[0] = '\0'; 496 return (B_TRUE); 497 } 498 499 static dladm_status_t 500 process_secobj_db(dladm_handle_t handle, void *arg, FILE *fp, FILE *nfp) 501 { 502 secobj_db_state_t *ssp = arg; 503 dladm_status_t status = DLADM_STATUS_OK; 504 char buf[MAXLINELEN]; 505 boolean_t cont = B_TRUE; 506 507 /* 508 * This loop processes each line of the configuration file. 509 * buf can potentially be modified by process_secobj_line(). 510 * If this is a write operation and buf is not truncated, buf will 511 * be written to disk. process_secobj_line() will no longer be 512 * called after it returns B_FALSE; at which point the remainder 513 * of the file will continue to be read and, if necessary, written 514 * to disk as well. 515 */ 516 while (fgets(buf, MAXLINELEN, fp) != NULL) { 517 if (cont) 518 cont = process_secobj_line(handle, ssp, buf, &status); 519 520 if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) { 521 status = dladm_errno2status(errno); 522 break; 523 } 524 } 525 if (status != DLADM_STATUS_OK || !cont) 526 return (status); 527 528 if (ssp->ss_op == process_secobj_set) { 529 /* 530 * If the specified object is not found above, we add the 531 * object to the configuration file. 532 */ 533 (void) (*ssp->ss_op)(handle, ssp, buf, NULL, &status); 534 if (status == DLADM_STATUS_OK && fputs(buf, nfp) == EOF) 535 status = dladm_errno2status(errno); 536 } 537 538 if (ssp->ss_op == process_secobj_unset || 539 ssp->ss_op == process_secobj_get) 540 status = DLADM_STATUS_NOTFOUND; 541 542 return (status); 543 } 544 545 #define SECOBJ_RW_DB(handle, statep, writeop) \ 546 (i_dladm_rw_db(handle, "/etc/dladm/secobj.conf", S_IRUSR | S_IWUSR, \ 547 process_secobj_db, (statep), (writeop))) 548 549 static dladm_status_t 550 i_dladm_set_secobj_db(dladm_handle_t handle, const char *obj_name, 551 dladm_secobj_class_t class, uint8_t *obj_val, uint_t obj_len) 552 { 553 secobj_db_state_t state; 554 555 state.ss_op = process_secobj_set; 556 state.ss_info.si_name = obj_name; 557 state.ss_info.si_classp = &class; 558 state.ss_info.si_val = obj_val; 559 state.ss_info.si_lenp = &obj_len; 560 state.ss_namelist = NULL; 561 562 return (SECOBJ_RW_DB(handle, &state, B_TRUE)); 563 } 564 565 static dladm_status_t 566 i_dladm_get_secobj_db(dladm_handle_t handle, const char *obj_name, 567 dladm_secobj_class_t *classp, uint8_t *obj_val, uint_t *obj_lenp) 568 { 569 secobj_db_state_t state; 570 571 state.ss_op = process_secobj_get; 572 state.ss_info.si_name = obj_name; 573 state.ss_info.si_classp = classp; 574 state.ss_info.si_val = obj_val; 575 state.ss_info.si_lenp = obj_lenp; 576 state.ss_namelist = NULL; 577 578 return (SECOBJ_RW_DB(handle, &state, B_FALSE)); 579 } 580 581 static dladm_status_t 582 i_dladm_unset_secobj_db(dladm_handle_t handle, const char *obj_name) 583 { 584 secobj_db_state_t state; 585 586 state.ss_op = process_secobj_unset; 587 state.ss_info.si_name = obj_name; 588 state.ss_info.si_classp = NULL; 589 state.ss_info.si_val = NULL; 590 state.ss_info.si_lenp = NULL; 591 state.ss_namelist = NULL; 592 593 return (SECOBJ_RW_DB(handle, &state, B_TRUE)); 594 } 595 596 static dladm_status_t 597 i_dladm_walk_secobj_db(dladm_handle_t handle, void *arg, 598 boolean_t (*func)(dladm_handle_t, void *, const char *)) 599 { 600 secobj_db_state_t state; 601 secobj_name_t *snp = NULL, *fsnp; 602 dladm_status_t status; 603 boolean_t cont = B_TRUE; 604 605 state.ss_op = process_secobj_walk; 606 state.ss_info.si_name = NULL; 607 state.ss_info.si_classp = NULL; 608 state.ss_info.si_val = NULL; 609 state.ss_info.si_lenp = NULL; 610 state.ss_namelist = &snp; 611 612 status = SECOBJ_RW_DB(handle, &state, B_FALSE); 613 if (status != DLADM_STATUS_OK) 614 return (status); 615 616 while (snp != NULL) { 617 fsnp = snp; 618 snp = snp->sn_next; 619 if (cont) 620 cont = func(handle, arg, fsnp->sn_name); 621 free(fsnp->sn_name); 622 free(fsnp); 623 } 624 return (status); 625 } 626 627 dladm_status_t 628 dladm_init_secobj(dladm_handle_t handle) 629 { 630 secobj_db_state_t state; 631 632 state.ss_op = process_secobj_init; 633 state.ss_info.si_name = NULL; 634 state.ss_info.si_classp = NULL; 635 state.ss_info.si_val = NULL; 636 state.ss_info.si_lenp = NULL; 637 state.ss_namelist = NULL; 638 639 return (SECOBJ_RW_DB(handle, &state, B_FALSE)); 640 } 641 642 boolean_t 643 dladm_valid_secobj_name(const char *secobj_name) 644 { 645 size_t len = strlen(secobj_name); 646 const char *cp; 647 648 if (len + 1 > DLADM_SECOBJ_NAME_MAX) 649 return (B_FALSE); 650 651 /* 652 * The legal characters in a secobj name are: 653 * alphanumeric (a-z, A-Z, 0-9), '.', '_', '-'. 654 */ 655 for (cp = secobj_name; *cp != '\0'; cp++) { 656 if (!isalnum(*cp) && 657 (*cp != '.') && (*cp != '_') && (*cp != '-')) 658 return (B_FALSE); 659 } 660 661 return (B_TRUE); 662 } 663