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 2007 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 }; 56 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_TEMP) == 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 (i_dladm_ioctl(fd, DLDIOCSECOBJSET, &secobj_set, 157 sizeof (secobj_set)) < 0) 158 status = dladm_errno2status(errno); 159 160 (void) close(fd); 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 if (i_dladm_ioctl(fd, DLDIOCSECOBJGET, &secobj_get, 199 sizeof (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_TEMP) == 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 (i_dladm_ioctl(fd, DLDIOCSECOBJUNSET, &secobj_unset, 234 sizeof (secobj_unset)) < 0) 235 status = dladm_errno2status(errno); 236 237 (void) close(fd); 238 if (status != DLADM_STATUS_OK) 239 return (status); 240 241 persist: 242 if ((flags & DLADM_OPT_PERSIST) != 0) 243 status = i_dladm_unset_secobj_db(obj_name); 244 245 return (status); 246 } 247 248 #define SECOBJ_BUFSZ 65536 249 dladm_status_t 250 dladm_walk_secobj(void *arg, boolean_t (*func)(void *, const char *), 251 uint_t flags) 252 { 253 int fd = -1; 254 dladm_status_t status = DLADM_STATUS_OK; 255 dld_ioc_secobj_get_t *secobj_getp; 256 dld_secobj_t *objp; 257 258 if ((flags & DLADM_OPT_PERSIST) != 0) 259 return (i_dladm_walk_secobj_db(arg, func)); 260 261 secobj_getp = calloc(1, SECOBJ_BUFSZ); 262 if (secobj_getp == NULL) 263 return (DLADM_STATUS_NOMEM); 264 265 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) { 266 status = dladm_errno2status(errno); 267 goto done; 268 } 269 if (i_dladm_ioctl(fd, DLDIOCSECOBJGET, secobj_getp, 270 SECOBJ_BUFSZ) < 0) { 271 status = dladm_errno2status(errno); 272 goto done; 273 } 274 275 objp = (dld_secobj_t *)(secobj_getp + 1); 276 while (secobj_getp->sg_count > 0) { 277 if (!func(arg, objp->so_name)) 278 goto done; 279 secobj_getp->sg_count--; 280 objp++; 281 } 282 done: 283 (void) close(fd); 284 free(secobj_getp); 285 return (status); 286 } 287 288 /* 289 * Data structures used for implementing persistent secure objects 290 */ 291 typedef struct secobj_info { 292 const char *si_name; 293 dladm_secobj_class_t *si_classp; 294 uint8_t *si_val; 295 uint_t *si_lenp; 296 } secobj_info_t; 297 298 typedef struct secobj_name { 299 char *sn_name; 300 struct secobj_name *sn_next; 301 } secobj_name_t; 302 303 typedef struct secobj_db_state secobj_db_state_t; 304 305 typedef boolean_t (*secobj_db_op_t)(struct secobj_db_state *, char *, 306 secobj_info_t *, dladm_status_t *); 307 308 struct secobj_db_state { 309 secobj_db_op_t ss_op; 310 secobj_info_t ss_info; 311 secobj_name_t **ss_namelist; 312 }; 313 314 /* 315 * Update or generate a secobj entry using the info in ssp->ss_info. 316 */ 317 /* ARGSUSED */ 318 static boolean_t 319 process_secobj_set(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip, 320 dladm_status_t *statusp) 321 { 322 char tmpbuf[MAXLINELEN]; 323 char classbuf[DLADM_STRSIZE]; 324 char *ptr = tmpbuf, *lim = tmpbuf + MAXLINELEN; 325 int i; 326 327 sip = &ssp->ss_info; 328 329 ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", sip->si_name); 330 ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", 331 dladm_secobjclass2str(*sip->si_classp, classbuf)); 332 333 ptr += snprintf(ptr, BUFLEN(lim, ptr), "0x"); 334 for (i = 0; i < *sip->si_lenp; i++) { 335 ptr += snprintf(ptr, BUFLEN(lim, ptr), "%02x", 336 sip->si_val[i] & 0xff); 337 } 338 if (ptr > lim) { 339 *statusp = DLADM_STATUS_TOOSMALL; 340 return (B_FALSE); 341 } 342 (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf); 343 return (B_FALSE); 344 } 345 346 /* ARGSUSED */ 347 static boolean_t 348 process_secobj_get(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip, 349 dladm_status_t *statusp) 350 { 351 if (*sip->si_lenp > *ssp->ss_info.si_lenp) { 352 *statusp = DLADM_STATUS_TOOSMALL; 353 return (B_FALSE); 354 } 355 bcopy(sip->si_val, ssp->ss_info.si_val, *sip->si_lenp); 356 *ssp->ss_info.si_lenp = *sip->si_lenp; 357 *ssp->ss_info.si_classp = *sip->si_classp; 358 return (B_FALSE); 359 } 360 361 /* ARGSUSED */ 362 static boolean_t 363 process_secobj_unset(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip, 364 dladm_status_t *statusp) 365 { 366 /* 367 * Delete line. 368 */ 369 buf[0] = '\0'; 370 return (B_FALSE); 371 } 372 373 /* ARGSUSED */ 374 static boolean_t 375 process_secobj_walk(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip, 376 dladm_status_t *statusp) 377 { 378 secobj_name_t *snp; 379 380 if ((snp = malloc(sizeof (*snp))) == NULL) 381 return (B_TRUE); 382 383 if ((snp->sn_name = strdup(sip->si_name)) == NULL) { 384 free(snp); 385 return (B_TRUE); 386 } 387 388 snp->sn_next = NULL; 389 *ssp->ss_namelist = snp; 390 ssp->ss_namelist = &snp->sn_next; 391 return (B_TRUE); 392 } 393 394 /* ARGSUSED */ 395 static boolean_t 396 process_secobj_init(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip, 397 dladm_status_t *statusp) 398 { 399 *statusp = dladm_set_secobj(sip->si_name, *sip->si_classp, sip->si_val, 400 *sip->si_lenp, DLADM_OPT_TEMP | DLADM_OPT_CREATE); 401 return (B_TRUE); 402 } 403 404 static int 405 parse_secobj_val(char *buf, secobj_info_t *sip) 406 { 407 if (strncmp(buf, "0x", 2) != 0) 408 return (EINVAL); 409 410 return (hexascii_to_octet(buf + 2, strlen(buf) - 2, 411 sip->si_val, sip->si_lenp)); 412 } 413 414 static boolean_t 415 process_secobj_line(secobj_db_state_t *ssp, char *buf, 416 dladm_status_t *statusp) 417 { 418 secobj_info_t sinfo; 419 dladm_secobj_class_t class; 420 uint8_t val[DLADM_SECOBJ_VAL_MAX]; 421 uint_t vlen; 422 int i, len, nlen; 423 char *str, *lasts; 424 425 /* 426 * Skip leading spaces, blank lines, and comments. 427 */ 428 len = strlen(buf); 429 for (i = 0; i < len; i++) { 430 if (!isspace(buf[i])) 431 break; 432 } 433 if (i == len || buf[i] == '#') 434 return (B_TRUE); 435 436 str = buf + i; 437 if (ssp->ss_info.si_name != NULL) { 438 /* 439 * Skip objects we're not interested in. 440 */ 441 nlen = strlen(ssp->ss_info.si_name); 442 if (strncmp(str, ssp->ss_info.si_name, nlen) != 0 || 443 !isspace(str[nlen])) 444 return (B_TRUE); 445 446 sinfo.si_name = ssp->ss_info.si_name; 447 } else { 448 /* 449 * If an object is not specified, find the object name 450 * and assign it to sinfo.si_name. 451 */ 452 if (strtok_r(str, " \n\t", &lasts) == NULL) 453 goto fail; 454 455 nlen = strlen(str); 456 sinfo.si_name = str; 457 } 458 str += nlen + 1; 459 if (str >= buf + len) 460 goto fail; 461 462 /* 463 * Find the class name. 464 */ 465 if ((str = strtok_r(str, " \n\t", &lasts)) == NULL) 466 goto fail; 467 468 *statusp = dladm_str2secobjclass(str, &class); 469 if (*statusp != DLADM_STATUS_OK) 470 goto fail; 471 472 /* 473 * Find the object value. 474 */ 475 if ((str = strtok_r(NULL, " \n\t", &lasts)) == NULL) 476 goto fail; 477 478 vlen = DLADM_SECOBJ_VAL_MAX; 479 sinfo.si_classp = &class; 480 sinfo.si_val = val; 481 sinfo.si_lenp = &vlen; 482 if (parse_secobj_val(str, &sinfo) != 0) 483 goto fail; 484 485 return ((*ssp->ss_op)(ssp, buf, &sinfo, statusp)); 486 487 fail: 488 /* 489 * Delete corrupted line. 490 */ 491 buf[0] = '\0'; 492 return (B_TRUE); 493 } 494 495 static dladm_status_t 496 process_secobj_db(void *arg, FILE *fp, FILE *nfp) 497 { 498 secobj_db_state_t *ssp = arg; 499 dladm_status_t status = DLADM_STATUS_OK; 500 char buf[MAXLINELEN]; 501 boolean_t cont = B_TRUE; 502 503 /* 504 * This loop processes each line of the configuration file. 505 * buf can potentially be modified by process_secobj_line(). 506 * If this is a write operation and buf is not truncated, buf will 507 * be written to disk. process_secobj_line() will no longer be 508 * called after it returns B_FALSE; at which point the remainder 509 * of the file will continue to be read and, if necessary, written 510 * to disk as well. 511 */ 512 while (fgets(buf, MAXLINELEN, fp) != NULL) { 513 if (cont) 514 cont = process_secobj_line(ssp, buf, &status); 515 516 if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) { 517 status = dladm_errno2status(errno); 518 break; 519 } 520 } 521 if (status != DLADM_STATUS_OK || !cont) 522 return (status); 523 524 if (ssp->ss_op == process_secobj_set) { 525 /* 526 * If the specified object is not found above, we add the 527 * object to the configuration file. 528 */ 529 (void) (*ssp->ss_op)(ssp, buf, NULL, &status); 530 if (status == DLADM_STATUS_OK && fputs(buf, nfp) == EOF) 531 status = dladm_errno2status(errno); 532 } 533 534 if (ssp->ss_op == process_secobj_unset || 535 ssp->ss_op == process_secobj_get) 536 status = DLADM_STATUS_NOTFOUND; 537 538 return (status); 539 } 540 541 #define SECOBJ_RW_DB(statep, writeop) \ 542 (i_dladm_rw_db("/etc/dladm/secobj.conf", S_IRUSR | S_IWUSR, \ 543 process_secobj_db, (statep), (writeop))) 544 545 static dladm_status_t 546 i_dladm_set_secobj_db(const char *obj_name, dladm_secobj_class_t class, 547 uint8_t *obj_val, uint_t obj_len) 548 { 549 secobj_db_state_t state; 550 551 state.ss_op = process_secobj_set; 552 state.ss_info.si_name = obj_name; 553 state.ss_info.si_classp = &class; 554 state.ss_info.si_val = obj_val; 555 state.ss_info.si_lenp = &obj_len; 556 state.ss_namelist = NULL; 557 558 return (SECOBJ_RW_DB(&state, B_TRUE)); 559 } 560 561 static dladm_status_t 562 i_dladm_get_secobj_db(const char *obj_name, dladm_secobj_class_t *classp, 563 uint8_t *obj_val, uint_t *obj_lenp) 564 { 565 secobj_db_state_t state; 566 567 state.ss_op = process_secobj_get; 568 state.ss_info.si_name = obj_name; 569 state.ss_info.si_classp = classp; 570 state.ss_info.si_val = obj_val; 571 state.ss_info.si_lenp = obj_lenp; 572 state.ss_namelist = NULL; 573 574 return (SECOBJ_RW_DB(&state, B_FALSE)); 575 } 576 577 static dladm_status_t 578 i_dladm_unset_secobj_db(const char *obj_name) 579 { 580 secobj_db_state_t state; 581 582 state.ss_op = process_secobj_unset; 583 state.ss_info.si_name = obj_name; 584 state.ss_info.si_classp = NULL; 585 state.ss_info.si_val = NULL; 586 state.ss_info.si_lenp = NULL; 587 state.ss_namelist = NULL; 588 589 return (SECOBJ_RW_DB(&state, B_TRUE)); 590 } 591 592 static dladm_status_t 593 i_dladm_walk_secobj_db(void *arg, boolean_t (*func)(void *, const char *)) 594 { 595 secobj_db_state_t state; 596 secobj_name_t *snp = NULL, *fsnp; 597 dladm_status_t status; 598 boolean_t cont = B_TRUE; 599 600 state.ss_op = process_secobj_walk; 601 state.ss_info.si_name = NULL; 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 = &snp; 606 607 status = SECOBJ_RW_DB(&state, B_FALSE); 608 if (status != DLADM_STATUS_OK) 609 return (status); 610 611 while (snp != NULL) { 612 fsnp = snp; 613 snp = snp->sn_next; 614 if (cont) 615 cont = func(arg, fsnp->sn_name); 616 free(fsnp->sn_name); 617 free(fsnp); 618 } 619 return (status); 620 } 621 622 dladm_status_t 623 dladm_init_secobj(void) 624 { 625 secobj_db_state_t state; 626 627 state.ss_op = process_secobj_init; 628 state.ss_info.si_name = NULL; 629 state.ss_info.si_classp = NULL; 630 state.ss_info.si_val = NULL; 631 state.ss_info.si_lenp = NULL; 632 state.ss_namelist = NULL; 633 634 return (SECOBJ_RW_DB(&state, B_FALSE)); 635 } 636