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