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