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 static boolean_t 322 process_secobj_set(dladm_handle_t handle __unused, secobj_db_state_t *ssp, 323 char *buf, 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 static boolean_t 350 process_secobj_get(dladm_handle_t handle __unused, secobj_db_state_t *ssp, 351 char *buf __unused, secobj_info_t *sip, dladm_status_t *statusp) 352 { 353 if (*sip->si_lenp > *ssp->ss_info.si_lenp) { 354 *statusp = DLADM_STATUS_TOOSMALL; 355 return (B_FALSE); 356 } 357 bcopy(sip->si_val, ssp->ss_info.si_val, *sip->si_lenp); 358 *ssp->ss_info.si_lenp = *sip->si_lenp; 359 *ssp->ss_info.si_classp = *sip->si_classp; 360 return (B_FALSE); 361 } 362 363 static boolean_t 364 process_secobj_unset(dladm_handle_t handle __unused, 365 secobj_db_state_t *ssp __unused, char *buf, 366 secobj_info_t *sip __unused, dladm_status_t *statusp __unused) 367 { 368 /* 369 * Delete line. 370 */ 371 buf[0] = '\0'; 372 return (B_FALSE); 373 } 374 375 static boolean_t 376 process_secobj_walk(dladm_handle_t handle __unused, secobj_db_state_t *ssp, 377 char *buf __unused, secobj_info_t *sip, dladm_status_t *statusp __unused) 378 { 379 secobj_name_t *snp; 380 381 if ((snp = malloc(sizeof (*snp))) == NULL) 382 return (B_TRUE); 383 384 if ((snp->sn_name = strdup(sip->si_name)) == NULL) { 385 free(snp); 386 return (B_TRUE); 387 } 388 389 snp->sn_next = NULL; 390 *ssp->ss_namelist = snp; 391 ssp->ss_namelist = &snp->sn_next; 392 return (B_TRUE); 393 } 394 395 static boolean_t 396 process_secobj_init(dladm_handle_t handle, secobj_db_state_t *ssp __unused, 397 char *buf __unused, secobj_info_t *sip, dladm_status_t *statusp) 398 { 399 *statusp = dladm_set_secobj(handle, sip->si_name, *sip->si_classp, 400 sip->si_val, *sip->si_lenp, 401 DLADM_OPT_ACTIVE | DLADM_OPT_CREATE); 402 return (B_TRUE); 403 } 404 405 static int 406 parse_secobj_val(char *buf, secobj_info_t *sip) 407 { 408 if (strncmp(buf, "0x", 2) != 0) 409 return (EINVAL); 410 411 return (hexascii_to_octet(buf + 2, strlen(buf) - 2, 412 sip->si_val, sip->si_lenp)); 413 } 414 415 static boolean_t 416 process_secobj_line(dladm_handle_t handle, secobj_db_state_t *ssp, char *buf, 417 dladm_status_t *statusp) 418 { 419 secobj_info_t sinfo; 420 dladm_secobj_class_t class; 421 uint8_t val[DLADM_SECOBJ_VAL_MAX]; 422 uint_t vlen; 423 int i, len, nlen; 424 char *str, *lasts; 425 426 /* 427 * Skip leading spaces, blank lines, and comments. 428 */ 429 len = strlen(buf); 430 for (i = 0; i < len; i++) { 431 if (!isspace(buf[i])) 432 break; 433 } 434 if (i == len || buf[i] == '#') 435 return (B_TRUE); 436 437 str = buf + i; 438 if (ssp->ss_info.si_name != NULL) { 439 /* 440 * Skip objects we're not interested in. 441 */ 442 nlen = strlen(ssp->ss_info.si_name); 443 if (strncmp(str, ssp->ss_info.si_name, nlen) != 0 || 444 !isspace(str[nlen])) 445 return (B_TRUE); 446 447 sinfo.si_name = ssp->ss_info.si_name; 448 } else { 449 /* 450 * If an object is not specified, find the object name 451 * and assign it to sinfo.si_name. 452 */ 453 if (strtok_r(str, " \n\t", &lasts) == NULL) 454 goto fail; 455 456 nlen = strlen(str); 457 sinfo.si_name = str; 458 } 459 str += nlen + 1; 460 if (str >= buf + len) 461 goto fail; 462 463 /* 464 * Find the class name. 465 */ 466 if ((str = strtok_r(str, " \n\t", &lasts)) == NULL) 467 goto fail; 468 469 *statusp = dladm_str2secobjclass(str, &class); 470 if (*statusp != DLADM_STATUS_OK) 471 goto fail; 472 473 /* 474 * Find the object value. 475 */ 476 if ((str = strtok_r(NULL, " \n\t", &lasts)) == NULL) 477 goto fail; 478 479 vlen = DLADM_SECOBJ_VAL_MAX; 480 sinfo.si_classp = &class; 481 sinfo.si_val = val; 482 sinfo.si_lenp = &vlen; 483 if (parse_secobj_val(str, &sinfo) != 0) 484 goto fail; 485 486 return ((*ssp->ss_op)(handle, ssp, buf, &sinfo, statusp)); 487 488 fail: 489 /* 490 * Delete corrupted line. 491 */ 492 buf[0] = '\0'; 493 return (B_TRUE); 494 } 495 496 static dladm_status_t 497 process_secobj_db(dladm_handle_t handle, void *arg, FILE *fp, FILE *nfp) 498 { 499 secobj_db_state_t *ssp = arg; 500 dladm_status_t status = DLADM_STATUS_OK; 501 char buf[MAXLINELEN]; 502 boolean_t cont = B_TRUE; 503 504 /* 505 * This loop processes each line of the configuration file. 506 * buf can potentially be modified by process_secobj_line(). 507 * If this is a write operation and buf is not truncated, buf will 508 * be written to disk. process_secobj_line() will no longer be 509 * called after it returns B_FALSE; at which point the remainder 510 * of the file will continue to be read and, if necessary, written 511 * to disk as well. 512 */ 513 while (fgets(buf, MAXLINELEN, fp) != NULL) { 514 if (cont) 515 cont = process_secobj_line(handle, ssp, buf, &status); 516 517 if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) { 518 status = dladm_errno2status(errno); 519 break; 520 } 521 } 522 if (status != DLADM_STATUS_OK || !cont) 523 return (status); 524 525 if (ssp->ss_op == process_secobj_set) { 526 /* 527 * If the specified object is not found above, we add the 528 * object to the configuration file. 529 */ 530 (void) (*ssp->ss_op)(handle, ssp, buf, NULL, &status); 531 if (status == DLADM_STATUS_OK && fputs(buf, nfp) == EOF) 532 status = dladm_errno2status(errno); 533 } 534 535 if (ssp->ss_op == process_secobj_unset || 536 ssp->ss_op == process_secobj_get) 537 status = DLADM_STATUS_NOTFOUND; 538 539 return (status); 540 } 541 542 #define SECOBJ_RW_DB(handle, statep, writeop) \ 543 (i_dladm_rw_db(handle, "/etc/dladm/secobj.conf", \ 544 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, \ 545 process_secobj_db, (statep), (writeop))) 546 547 static dladm_status_t 548 i_dladm_set_secobj_db(dladm_handle_t handle, const char *obj_name, 549 dladm_secobj_class_t class, uint8_t *obj_val, uint_t obj_len) 550 { 551 secobj_db_state_t state; 552 553 state.ss_op = process_secobj_set; 554 state.ss_info.si_name = obj_name; 555 state.ss_info.si_classp = &class; 556 state.ss_info.si_val = obj_val; 557 state.ss_info.si_lenp = &obj_len; 558 state.ss_namelist = NULL; 559 560 return (SECOBJ_RW_DB(handle, &state, B_TRUE)); 561 } 562 563 static dladm_status_t 564 i_dladm_get_secobj_db(dladm_handle_t handle, const char *obj_name, 565 dladm_secobj_class_t *classp, uint8_t *obj_val, uint_t *obj_lenp) 566 { 567 secobj_db_state_t state; 568 569 state.ss_op = process_secobj_get; 570 state.ss_info.si_name = obj_name; 571 state.ss_info.si_classp = classp; 572 state.ss_info.si_val = obj_val; 573 state.ss_info.si_lenp = obj_lenp; 574 state.ss_namelist = NULL; 575 576 return (SECOBJ_RW_DB(handle, &state, B_FALSE)); 577 } 578 579 static dladm_status_t 580 i_dladm_unset_secobj_db(dladm_handle_t handle, const char *obj_name) 581 { 582 secobj_db_state_t state; 583 584 state.ss_op = process_secobj_unset; 585 state.ss_info.si_name = obj_name; 586 state.ss_info.si_classp = NULL; 587 state.ss_info.si_val = NULL; 588 state.ss_info.si_lenp = NULL; 589 state.ss_namelist = NULL; 590 591 return (SECOBJ_RW_DB(handle, &state, B_TRUE)); 592 } 593 594 static dladm_status_t 595 i_dladm_walk_secobj_db(dladm_handle_t handle, void *arg, 596 boolean_t (*func)(dladm_handle_t, 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(handle, &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(handle, 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(dladm_handle_t handle) 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(handle, &state, B_FALSE)); 638 } 639 640 boolean_t 641 dladm_valid_secobj_name(const char *secobj_name) 642 { 643 size_t len = strlen(secobj_name); 644 const char *cp; 645 646 if (len + 1 > DLADM_SECOBJ_NAME_MAX) 647 return (B_FALSE); 648 649 /* 650 * The legal characters in a secobj name are: 651 * alphanumeric (a-z, A-Z, 0-9), '.', '_', '-'. 652 */ 653 for (cp = secobj_name; *cp != '\0'; cp++) { 654 if (!isalnum(*cp) && 655 (*cp != '.') && (*cp != '_') && (*cp != '-')) 656 return (B_FALSE); 657 } 658 659 return (B_TRUE); 660 } 661