diff options
Diffstat (limited to 'usr/src/lib/libdladm/common')
-rw-r--r-- | usr/src/lib/libdladm/common/libdladm.c | 274 | ||||
-rw-r--r-- | usr/src/lib/libdladm/common/libdladm.h | 72 | ||||
-rw-r--r-- | usr/src/lib/libdladm/common/libdladm_impl.h | 51 | ||||
-rw-r--r-- | usr/src/lib/libdladm/common/linkprop.c | 700 | ||||
-rw-r--r-- | usr/src/lib/libdladm/common/mapfile-vers | 14 | ||||
-rw-r--r-- | usr/src/lib/libdladm/common/secobj.c | 634 |
6 files changed, 1728 insertions, 17 deletions
diff --git a/usr/src/lib/libdladm/common/libdladm.c b/usr/src/lib/libdladm/common/libdladm.c index 679368a9ae..f7f9c1755a 100644 --- a/usr/src/lib/libdladm/common/libdladm.c +++ b/usr/src/lib/libdladm/common/libdladm.c @@ -25,19 +25,18 @@ #pragma ident "%Z%%M% %I% %E% SMI" -#include <stdio.h> -#include <sys/types.h> -#include <string.h> -#include <fcntl.h> #include <unistd.h> #include <stropts.h> -#include <stdlib.h> #include <errno.h> -#include <libdevinfo.h> -#include <libdlpi.h> -#include <libdladm.h> -#include <sys/dld.h> +#include <fcntl.h> +#include <strings.h> +#include <dirent.h> #include <net/if.h> +#include <sys/stat.h> +#include <sys/dld.h> +#include <libdlpi.h> +#include <libdevinfo.h> +#include <libdladm_impl.h> typedef struct dladm_dev { char dd_name[IFNAMSIZ]; @@ -48,11 +47,13 @@ typedef struct dladm_walk { dladm_dev_t *dw_dev_list; } dladm_walk_t; +static char dladm_rootdir[MAXPATHLEN] = "/"; + /* * Issue an ioctl to the specified file descriptor attached to the * DLD control driver interface. */ -static int +int i_dladm_ioctl(int fd, int ic_cmd, void *ic_dp, int ic_len) { struct strioctl iocb; @@ -250,3 +251,256 @@ failed: (void) close(fd); return (-1); } + +const char * +dladm_status2str(dladm_status_t status, char *buf) +{ + const char *s; + + switch (status) { + case DLADM_STATUS_OK: + s = "ok"; + break; + case DLADM_STATUS_BADARG: + s = "invalid argument"; + break; + case DLADM_STATUS_FAILED: + s = "operation failed"; + break; + case DLADM_STATUS_TOOSMALL: + s = "buffer size too small"; + break; + case DLADM_STATUS_NOTSUP: + s = "operation not supported"; + break; + case DLADM_STATUS_NOTFOUND: + s = "object not found"; + break; + case DLADM_STATUS_BADVAL: + s = "invalid value"; + break; + case DLADM_STATUS_NOMEM: + s = "insufficient memory"; + break; + case DLADM_STATUS_EXIST: + s = "object already exists"; + break; + case DLADM_STATUS_LINKINVAL: + s = "invalid link"; + break; + case DLADM_STATUS_PROPRDONLY: + s = "read-only property"; + break; + case DLADM_STATUS_BADVALCNT: + s = "invalid number of values"; + break; + case DLADM_STATUS_DBNOTFOUND: + s = "database not found"; + break; + case DLADM_STATUS_DENIED: + s = "permission denied"; + break; + case DLADM_STATUS_IOERR: + s = "I/O error"; + break; + default: + s = ""; + } + (void) snprintf(buf, DLADM_STRSIZE, "%s", s); + return (buf); +} + +/* + * Convert a unix errno to a dladm_status_t. + * We only convert errnos that are likely to be encountered. All others + * are mapped to DLADM_STATUS_FAILED. + */ +dladm_status_t +dladm_errno2status(int err) +{ + switch (err) { + case EINVAL: + return (DLADM_STATUS_BADARG); + case EEXIST: + return (DLADM_STATUS_EXIST); + case ENOENT: + return (DLADM_STATUS_NOTFOUND); + case ENOSPC: + return (DLADM_STATUS_TOOSMALL); + case ENOMEM: + return (DLADM_STATUS_NOMEM); + case ENOTSUP: + return (DLADM_STATUS_NOTSUP); + case EACCES: + return (DLADM_STATUS_DENIED); + case EIO: + return (DLADM_STATUS_IOERR); + default: + return (DLADM_STATUS_FAILED); + } +} + +/* + * These are the uid and gid of the user 'dladm'. + * The directory /etc/dladm and all files under it are owned by this user. + */ +#define DLADM_DB_OWNER 15 +#define DLADM_DB_GROUP 3 +#define LOCK_DB_PERMS S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH + +static int +i_dladm_lock_db(const char *lock_file, short type) +{ + int lock_fd; + struct flock lock; + + if ((lock_fd = open(lock_file, O_RDWR | O_CREAT | O_TRUNC, + LOCK_DB_PERMS)) < 0) + return (-1); + + lock.l_type = type; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + + if (fcntl(lock_fd, F_SETLKW, &lock) < 0) { + int err = errno; + + (void) close(lock_fd); + (void) unlink(lock_file); + errno = err; + return (-1); + } + return (lock_fd); +} + +static void +i_dladm_unlock_db(const char *lock_file, int fd) +{ + struct flock lock; + + if (fd < 0) + return; + + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + + (void) fcntl(fd, F_SETLKW, &lock); + (void) close(fd); + (void) unlink(lock_file); +} + +dladm_status_t +i_dladm_rw_db(const char *db_file, mode_t db_perms, + dladm_status_t (*process_db)(void *, FILE *, FILE *), + void *arg, boolean_t writeop) +{ + dladm_status_t status = DLADM_STATUS_OK; + FILE *fp, *nfp = NULL; + char lock[MAXPATHLEN]; + char file[MAXPATHLEN]; + char newfile[MAXPATHLEN]; + char *db_basename; + int nfd, lock_fd; + + /* + * If we are called from a boot script such as net-physical, + * it's quite likely that the root fs is still not writable. + * For this case, it's ok for the lock creation to fail since + * no one else could be accessing our configuration file. + */ + db_basename = strrchr(db_file, '/'); + if (db_basename == NULL || db_basename[1] == '\0') + return (dladm_errno2status(EINVAL)); + db_basename++; + (void) snprintf(lock, MAXPATHLEN, "/tmp/%s.lock", db_basename); + if ((lock_fd = i_dladm_lock_db + (lock, (writeop ? F_WRLCK : F_RDLCK))) < 0 && errno != EROFS) + return (dladm_errno2status(errno)); + + (void) snprintf(file, MAXPATHLEN, "%s/%s", dladm_rootdir, db_file); + if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) { + int err = errno; + + i_dladm_unlock_db(lock, lock_fd); + if (err == ENOENT) + return (DLADM_STATUS_DBNOTFOUND); + + return (dladm_errno2status(err)); + } + + if (writeop) { + (void) snprintf(newfile, MAXPATHLEN, "%s/%s.new", + dladm_rootdir, db_file); + if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC, + db_perms)) < 0) { + (void) fclose(fp); + i_dladm_unlock_db(lock, lock_fd); + return (dladm_errno2status(errno)); + } + + if ((nfp = fdopen(nfd, "w")) == NULL) { + (void) close(nfd); + (void) fclose(fp); + (void) unlink(newfile); + i_dladm_unlock_db(lock, lock_fd); + return (dladm_errno2status(errno)); + } + } + status = (*process_db)(arg, fp, nfp); + if (!writeop || status != DLADM_STATUS_OK) + goto done; + + /* + * Configuration files need to be owned by the 'dladm' user. + * If we are invoked by root, the file ownership needs to be fixed. + */ + if (getuid() == 0 || geteuid() == 0) { + if (fchown(nfd, DLADM_DB_OWNER, DLADM_DB_GROUP) < 0) { + status = dladm_errno2status(errno); + goto done; + } + } + + if (fflush(nfp) == EOF) { + status = dladm_errno2status(errno); + goto done; + } + (void) fclose(fp); + (void) fclose(nfp); + + if (rename(newfile, file) < 0) { + (void) unlink(newfile); + i_dladm_unlock_db(lock, lock_fd); + return (dladm_errno2status(errno)); + } + + i_dladm_unlock_db(lock, lock_fd); + return (DLADM_STATUS_OK); + +done: + if (nfp != NULL) { + (void) fclose(nfp); + if (status != DLADM_STATUS_OK) + (void) unlink(newfile); + } + (void) fclose(fp); + i_dladm_unlock_db(lock, lock_fd); + return (status); +} + +dladm_status_t +dladm_set_rootdir(const char *rootdir) +{ + DIR *dp; + + if (rootdir == NULL || *rootdir != '/' || + (dp = opendir(rootdir)) == NULL) + return (DLADM_STATUS_BADARG); + + (void) strncpy(dladm_rootdir, rootdir, MAXPATHLEN); + (void) closedir(dp); + return (DLADM_STATUS_OK); +} diff --git a/usr/src/lib/libdladm/common/libdladm.h b/usr/src/lib/libdladm/common/libdladm.h index 64315da820..1cf3700fc6 100644 --- a/usr/src/lib/libdladm/common/libdladm.h +++ b/usr/src/lib/libdladm/common/libdladm.h @@ -36,19 +36,77 @@ extern "C" { #endif -typedef struct dladm_attr dladm_attr_t; - -struct dladm_attr { +typedef struct dladm_attr { char da_dev[MAXNAMELEN]; uint_t da_max_sdu; uint16_t da_vid; -}; +} dladm_attr_t; + +#define DLADM_STRSIZE 256 +#define DLADM_SECOBJ_VAL_MAX 256 +#define DLADM_PROP_VAL_MAX 256 +#define DLADM_OPT_TEMP 0x00000001 +#define DLADM_OPT_CREATE 0x00000002 +#define DLADM_OPT_PERSIST 0x00000004 + +typedef enum { + DLADM_STATUS_OK = 0, + DLADM_STATUS_BADARG, + DLADM_STATUS_FAILED, + DLADM_STATUS_TOOSMALL, + DLADM_STATUS_NOTSUP, + DLADM_STATUS_NOTFOUND, + DLADM_STATUS_BADVAL, + DLADM_STATUS_NOMEM, + DLADM_STATUS_EXIST, + DLADM_STATUS_LINKINVAL, + DLADM_STATUS_PROPRDONLY, + DLADM_STATUS_BADVALCNT, + DLADM_STATUS_DBNOTFOUND, + DLADM_STATUS_DENIED, + DLADM_STATUS_IOERR +} dladm_status_t; + +typedef enum { + DLADM_PROP_VAL_CURRENT = 1, + DLADM_PROP_VAL_DEFAULT, + DLADM_PROP_VAL_MODIFIABLE, + DLADM_PROP_VAL_PERSISTENT +} dladm_prop_type_t; + +#define DLADM_SECOBJ_CLASS_WEP 0 +typedef int dladm_secobj_class_t; -extern int dladm_walk(void (*)(void *, const char *), void *); -extern int dladm_walk_vlan(void (*)(void *, - const char *), void *, const char *); +typedef void (dladm_walkcb_t)(void *, const char *); + +extern int dladm_walk(dladm_walkcb_t *, void *); +extern int dladm_walk_vlan(dladm_walkcb_t *, void *, const char *); extern int dladm_info(const char *, dladm_attr_t *); +extern dladm_status_t dladm_set_prop(const char *, const char *, + char **, uint_t, uint_t); +extern dladm_status_t dladm_get_prop(const char *, dladm_prop_type_t, + const char *, char **, uint_t *); +extern dladm_status_t dladm_walk_prop(const char *, void *, + boolean_t (*)(void *, const char *)); + +extern dladm_status_t dladm_set_secobj(const char *, dladm_secobj_class_t, + uint8_t *, uint_t, uint_t); +extern dladm_status_t dladm_get_secobj(const char *, dladm_secobj_class_t *, + uint8_t *, uint_t *, uint_t); +extern dladm_status_t dladm_unset_secobj(const char *, uint_t); +extern dladm_status_t dladm_walk_secobj(void *, + boolean_t (*)(void *, const char *), uint_t); + +extern const char *dladm_status2str(dladm_status_t, char *); +extern const char *dladm_secobjclass2str(dladm_secobj_class_t, char *); +extern dladm_status_t dladm_str2secobjclass(const char *, + dladm_secobj_class_t *); + +extern dladm_status_t dladm_init_linkprop(void); +extern dladm_status_t dladm_init_secobj(void); +extern dladm_status_t dladm_set_rootdir(const char *rootdir); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libdladm/common/libdladm_impl.h b/usr/src/lib/libdladm/common/libdladm_impl.h new file mode 100644 index 0000000000..c949219a5b --- /dev/null +++ b/usr/src/lib/libdladm/common/libdladm_impl.h @@ -0,0 +1,51 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _LIBDLADM_IMPL_H +#define _LIBDLADM_IMPL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <libdladm.h> +#include <stdio.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAXLINELEN 1024 +#define BUFLEN(lim, ptr) (((lim) > (ptr)) ? ((lim) - (ptr)) : 0) + +extern int i_dladm_ioctl(int, int, void *, int); +extern dladm_status_t dladm_errno2status(int); +extern dladm_status_t i_dladm_rw_db(const char *, mode_t, + dladm_status_t (*)(void *, FILE *, FILE *), + void *, boolean_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBDLADM_IMPL_H */ diff --git a/usr/src/lib/libdladm/common/linkprop.c b/usr/src/lib/libdladm/common/linkprop.c new file mode 100644 index 0000000000..8e1ef849e9 --- /dev/null +++ b/usr/src/lib/libdladm/common/linkprop.c @@ -0,0 +1,700 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <strings.h> +#include <errno.h> +#include <ctype.h> +#include <sys/stat.h> +#include <libwladm.h> +#include <libdladm_impl.h> + +static dladm_status_t i_dladm_set_prop_db(const char *, const char *, + char **, uint_t); +static dladm_status_t i_dladm_get_prop_db(const char *, const char *, + char **, uint_t *); + +/* + * Convert a wladm_status_t to a dladm_status_t. This is used by wrappers + * to libwladm routines (e.g. dladm_set_prop()). Note that the mapping is + * not 1-1; whenever possible we try to look for an error code with a + * similar meaning. Error codes with no suitable counterpart in libdladm + * will be mapped to DLADM_STATUS_FAILED. Clients who require clearer error + * reporting should use libwladm directly. + */ +static dladm_status_t +dladm_wladmstatus2status(wladm_status_t wstatus) +{ + switch (wstatus) { + case WLADM_STATUS_OK: + return (DLADM_STATUS_OK); + case WLADM_STATUS_FAILED: + return (DLADM_STATUS_FAILED); + case WLADM_STATUS_NOTSUP: + return (DLADM_STATUS_NOTSUP); + case WLADM_STATUS_BADARG: + return (DLADM_STATUS_BADARG); + case WLADM_STATUS_NOTFOUND: + return (DLADM_STATUS_NOTFOUND); + case WLADM_STATUS_BADVAL: + return (DLADM_STATUS_BADVAL); + case WLADM_STATUS_LINKINVAL: + return (DLADM_STATUS_LINKINVAL); + case WLADM_STATUS_NOMEM: + return (DLADM_STATUS_NOMEM); + case WLADM_STATUS_PROPRDONLY: + return (DLADM_STATUS_PROPRDONLY); + case WLADM_STATUS_TOOSMALL: + return (DLADM_STATUS_TOOSMALL); + case WLADM_STATUS_BADVALCNT: + return (DLADM_STATUS_BADVALCNT); + default: + return (DLADM_STATUS_FAILED); + } +} + +dladm_status_t +dladm_set_prop(const char *link, const char *prop_name, char **prop_val, + uint_t val_cnt, uint_t flags) +{ + dladm_status_t status = DLADM_STATUS_BADARG; + + if (link == NULL || (prop_val == NULL && val_cnt > 0) || + (prop_val != NULL && val_cnt == 0) || flags == 0) + return (DLADM_STATUS_BADARG); + + if ((flags & DLADM_OPT_TEMP) != 0) { + if (wladm_is_valid(link)) { + status = dladm_wladmstatus2status( + wladm_set_prop(link, prop_name, + prop_val, val_cnt)); + } + if (status != DLADM_STATUS_OK) + return (status); + } + if ((flags & DLADM_OPT_PERSIST) != 0) { + status = i_dladm_set_prop_db(link, prop_name, + prop_val, val_cnt); + } + return (status); +} + +dladm_status_t +dladm_walk_prop(const char *link, void *arg, + boolean_t (*func)(void *, const char *)) +{ + if (link == NULL || func == NULL) + return (DLADM_STATUS_BADARG); + + if (wladm_is_valid(link)) { + return (dladm_wladmstatus2status( + wladm_walk_prop(link, arg, func))); + } + return (DLADM_STATUS_BADARG); +} + +dladm_status_t +dladm_get_prop(const char *link, dladm_prop_type_t type, + const char *prop_name, char **prop_val, uint_t *val_cntp) +{ + if (link == NULL || prop_name == NULL || prop_val == NULL || + val_cntp == NULL || *val_cntp == 0) + return (DLADM_STATUS_BADARG); + + if (type == DLADM_PROP_VAL_PERSISTENT) { + return (i_dladm_get_prop_db(link, prop_name, + prop_val, val_cntp)); + } + + if (wladm_is_valid(link)) { + wladm_prop_type_t wtype; + + switch (type) { + case DLADM_PROP_VAL_CURRENT: + wtype = WLADM_PROP_VAL_CURRENT; + break; + case DLADM_PROP_VAL_DEFAULT: + wtype = WLADM_PROP_VAL_DEFAULT; + break; + case DLADM_PROP_VAL_MODIFIABLE: + wtype = WLADM_PROP_VAL_MODIFIABLE; + break; + default: + return (DLADM_STATUS_BADARG); + } + + return (dladm_wladmstatus2status( + wladm_get_prop(link, wtype, prop_name, + prop_val, val_cntp))); + } + return (DLADM_STATUS_BADARG); +} + +/* + * Data structures used for implementing persistent link properties + */ +typedef struct linkprop_val { + const char *lv_name; + struct linkprop_val *lv_nextval; +} linkprop_val_t; + +typedef struct linkprop_info { + const char *li_name; + struct linkprop_info *li_nextprop; + struct linkprop_val *li_val; +} linkprop_info_t; + +typedef struct linkprop_db_state linkprop_db_state_t; + +typedef boolean_t (*linkprop_db_op_t)(linkprop_db_state_t *, + char *, linkprop_info_t *, dladm_status_t *); + +struct linkprop_db_state { + linkprop_db_op_t ls_op; + const char *ls_link; + const char *ls_propname; + char **ls_propval; + uint_t *ls_valcntp; +}; + +static void +free_linkprops(linkprop_info_t *lip) +{ + linkprop_info_t *lip_next; + linkprop_val_t *lvp, *lvp_next; + + for (; lip != NULL; lip = lip_next) { + lip_next = lip->li_nextprop; + for (lvp = lip->li_val; lvp != NULL; lvp = lvp_next) { + lvp_next = lvp->lv_nextval; + free(lvp); + } + free(lip); + } +} + +/* + * Generate an entry in the link property database. + * Each entry has this format: + * <linkname> <prop0>=<val0>,...,<valn>;...;<propn>=<val0>,...,<valn>; + */ +static void +generate_linkprop_line(linkprop_db_state_t *lsp, char *buf, + linkprop_info_t *listp, dladm_status_t *statusp) +{ + char tmpbuf[MAXLINELEN]; + char *ptr, *lim = tmpbuf + MAXLINELEN; + linkprop_info_t *lip = listp; + linkprop_val_t *lvp = NULL; + + /* + * Delete line if there are no properties left. + */ + if (lip == NULL || + (lip->li_val == NULL && lip->li_nextprop == NULL)) { + buf[0] = '\0'; + return; + } + ptr = tmpbuf; + ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", lsp->ls_link); + for (; lip != NULL; lip = lip->li_nextprop) { + /* + * Skip properties without values. + */ + if (lip->li_val == NULL) + continue; + + ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s=", lip->li_name); + for (lvp = lip->li_val; lvp != NULL; lvp = lvp->lv_nextval) { + ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s%c", + lvp->lv_name, + ((lvp->lv_nextval == NULL) ? ';' : ',')); + } + } + if (ptr > lim) { + *statusp = DLADM_STATUS_TOOSMALL; + return; + } + (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf); +} + +/* + * This function is used to update or create an entry in the persistent db. + * process_linkprop_db() will first scan the db for an entry matching the + * specified link. If a match is found, this function is invoked with the + * entry's contents (buf) and its linked-list representation (listp). lsp + * holds the name and values of the property to be added or updated; this + * information will be merged with listp. Subsequently, an updated entry + * will be written to buf, which will in turn be written to disk by + * process_linkprop_db(). If no entry matches the specified link, listp + * will be NULL; a new entry will be generated in this case and it will + * contain only the property information in lsp. + */ +static boolean_t +process_linkprop_set(linkprop_db_state_t *lsp, char *buf, + linkprop_info_t *listp, dladm_status_t *statusp) +{ + dladm_status_t status; + linkprop_info_t *lastp = NULL, *lip = listp, *nlip = NULL; + linkprop_val_t **lvpp; + int i; + + if (lsp->ls_propname == NULL) { + buf[0] = '\0'; + return (B_FALSE); + } + + /* + * Find the linkprop we want to change. + */ + for (; lip != NULL; lip = lip->li_nextprop) { + if (strcmp(lip->li_name, lsp->ls_propname) == 0) + break; + + lastp = lip; + } + + if (lip == NULL) { + /* + * If the linkprop is not found, append it to the list. + */ + if ((nlip = malloc(sizeof (linkprop_info_t))) == NULL) { + status = DLADM_STATUS_NOMEM; + goto fail; + } + /* + * nlip will need to be freed later if there is no list to + * append to. + */ + if (lastp != NULL) + lastp->li_nextprop = nlip; + nlip->li_name = lsp->ls_propname; + nlip->li_nextprop = NULL; + nlip->li_val = NULL; + lvpp = &nlip->li_val; + } else { + linkprop_val_t *lvp, *lvp_next; + + /* + * If the linkprop is found, delete the existing values from it. + */ + for (lvp = lip->li_val; lvp != NULL; lvp = lvp_next) { + lvp_next = lvp->lv_nextval; + free(lvp); + } + lip->li_val = NULL; + lvpp = &lip->li_val; + } + + /* + * Fill our linkprop with the specified values. + */ + for (i = 0; i < *lsp->ls_valcntp; i++) { + if ((*lvpp = malloc(sizeof (linkprop_val_t))) == NULL) { + status = DLADM_STATUS_NOMEM; + goto fail; + } + (*lvpp)->lv_name = lsp->ls_propval[i]; + (*lvpp)->lv_nextval = NULL; + lvpp = &(*lvpp)->lv_nextval; + } + + if (listp != NULL) { + generate_linkprop_line(lsp, buf, listp, statusp); + } else { + generate_linkprop_line(lsp, buf, nlip, statusp); + free_linkprops(nlip); + } + return (B_FALSE); + +fail: + *statusp = status; + if (listp == NULL) + free_linkprops(nlip); + + return (B_FALSE); +} + +/* + * This function is used for retrieving the values for a specific property. + * It gets called if an entry matching the specified link exists in the db. + * The entry is converted into a linked-list listp. This list is then scanned + * for the specified property name; if a matching property exists, its + * associated values are copied to the array lsp->ls_propval. + */ +/* ARGSUSED */ +static boolean_t +process_linkprop_get(linkprop_db_state_t *lsp, char *buf, + linkprop_info_t *listp, dladm_status_t *statusp) +{ + linkprop_info_t *lip = listp; + linkprop_val_t *lvp; + uint_t valcnt = 0; + + /* + * Find the linkprop we want to get. + */ + for (; lip != NULL; lip = lip->li_nextprop) { + if (strcmp(lip->li_name, lsp->ls_propname) == 0) + break; + } + if (lip == NULL) { + *statusp = DLADM_STATUS_NOTFOUND; + return (B_FALSE); + } + + for (lvp = lip->li_val; lvp != NULL; lvp = lvp->lv_nextval) { + (void) strncpy(lsp->ls_propval[valcnt], lvp->lv_name, + DLADM_PROP_VAL_MAX); + + if (++valcnt >= *lsp->ls_valcntp && lvp->lv_nextval != NULL) { + *statusp = DLADM_STATUS_TOOSMALL; + return (B_FALSE); + } + } + /* + * This function is meant to be called at most once for each call + * to process_linkprop_db(). For this reason, it's ok to overwrite + * the caller's valcnt array size with the actual number of values + * returned. + */ + *lsp->ls_valcntp = valcnt; + return (B_FALSE); +} + +/* + * This is used for initializing link properties. + * Unlike the other routines, this gets called for every entry in the + * database. lsp->ls_link is not user-specified but instead is set to + * the current link being processed. + */ +/* ARGSUSED */ +static boolean_t +process_linkprop_init(linkprop_db_state_t *lsp, char *buf, + linkprop_info_t *listp, dladm_status_t *statusp) +{ + dladm_status_t status = DLADM_STATUS_OK; + linkprop_info_t *lip = listp; + linkprop_val_t *lvp; + uint_t valcnt, i; + char **propval; + + for (; lip != NULL; lip = lip->li_nextprop) { + /* + * Construct the propval array and fill it with + * values from listp. + */ + for (lvp = lip->li_val, valcnt = 0; + lvp != NULL; lvp = lvp->lv_nextval, valcnt++); + + propval = malloc(sizeof (char *) * valcnt); + if (propval == NULL) { + *statusp = DLADM_STATUS_NOMEM; + break; + } + lvp = lip->li_val; + for (i = 0; i < valcnt; i++, lvp = lvp->lv_nextval) + propval[i] = (char *)lvp->lv_name; + + status = dladm_set_prop(lsp->ls_link, lip->li_name, + propval, valcnt, DLADM_OPT_TEMP); + + /* + * We continue with initializing other properties even + * after encountering an error. This error will be + * propagated to the caller via 'statusp'. + */ + if (status != DLADM_STATUS_OK) + *statusp = status; + + free(propval); + } + return (B_TRUE); +} + +static int +parse_linkprops(char *buf, linkprop_info_t **lipp) +{ + int i, len; + char *curr; + linkprop_info_t *lip = NULL; + linkprop_info_t **tailp = lipp; + linkprop_val_t *lvp = NULL; + linkprop_val_t **vtailp = NULL; + + curr = buf; + len = strlen(buf); + for (i = 0; i < len; i++) { + char c = buf[i]; + boolean_t match = (c == '=' || c == ',' || c == ';'); + + /* + * Move to the next character if there is no match and + * if we have not reached the last character. + */ + if (!match && i != len - 1) + continue; + + if (match) { + /* + * Nul-terminate the string pointed to by 'curr'. + */ + buf[i] = '\0'; + if (*curr == '\0') + goto fail; + } + + if (lip != NULL) { + /* + * We get here after we have processed the "<prop>=" + * pattern. The pattern we are now interested in is + * "<val0>,<val1>,...,<valn>;". For each value we + * find, a linkprop_val_t will be allocated and + * added to the current 'lip'. + */ + if (c == '=') + goto fail; + + lvp = malloc(sizeof (*lvp)); + if (lvp == NULL) + goto fail; + + lvp->lv_name = curr; + lvp->lv_nextval = NULL; + *vtailp = lvp; + vtailp = &lvp->lv_nextval; + + if (c == ';') { + tailp = &lip->li_nextprop; + vtailp = NULL; + lip = NULL; + } + } else { + /* + * lip == NULL indicates that 'curr' must be refering + * to a property name. We allocate a new linkprop_info_t + * append it to the list given by the caller. + */ + if (c != '=') + goto fail; + + lip = malloc(sizeof (*lip)); + if (lip == NULL) + goto fail; + + lip->li_name = curr; + lip->li_val = NULL; + lip->li_nextprop = NULL; + *tailp = lip; + vtailp = &lip->li_val; + } + curr = buf + i + 1; + } + /* + * The list must be non-empty and the last character must be ';'. + */ + if (*lipp == NULL || lip != NULL) + goto fail; + + return (0); + +fail: + free_linkprops(*lipp); + *lipp = NULL; + return (-1); +} + +static boolean_t +process_linkprop_line(linkprop_db_state_t *lsp, char *buf, + dladm_status_t *statusp) +{ + linkprop_info_t *lip = NULL; + int i, len, llen; + char *str, *lasts; + boolean_t cont, nolink = B_FALSE; + + /* + * Skip leading spaces, blank lines, and comments. + */ + len = strlen(buf); + for (i = 0; i < len; i++) { + if (!isspace(buf[i])) + break; + } + if (i == len || buf[i] == '#') + return (B_TRUE); + + str = buf + i; + if (lsp->ls_link != NULL) { + /* + * Skip links we're not interested in. + * Note that strncmp() and isspace() are used here + * instead of strtok() and strcmp() because we don't + * want to modify buf in case it does not contain the + * specified link. + */ + llen = strlen(lsp->ls_link); + if (strncmp(str, lsp->ls_link, llen) != 0 || + !isspace(str[llen])) + return (B_TRUE); + } else { + /* + * If a link is not specified, find the link name + * and assign it to lsp->ls_link. + */ + if (strtok_r(str, " \n\t", &lasts) == NULL) + goto fail; + + llen = strlen(str); + lsp->ls_link = str; + nolink = B_TRUE; + } + str += llen + 1; + if (str >= buf + len) + goto fail; + + /* + * Now find the list of link properties. + */ + if ((str = strtok_r(str, " \n\t", &lasts)) == NULL) + goto fail; + + if (parse_linkprops(str, &lip) < 0) + goto fail; + + cont = (*lsp->ls_op)(lsp, buf, lip, statusp); + free_linkprops(lip); + if (nolink) + lsp->ls_link = NULL; + return (cont); + +fail: + free_linkprops(lip); + if (nolink) + lsp->ls_link = NULL; + + /* + * Delete corrupted line. + */ + buf[0] = '\0'; + return (B_TRUE); +} + +static dladm_status_t +process_linkprop_db(void *arg, FILE *fp, FILE *nfp) +{ + linkprop_db_state_t *lsp = arg; + dladm_status_t status = DLADM_STATUS_OK; + char buf[MAXLINELEN]; + boolean_t cont = B_TRUE; + + /* + * This loop processes each line of the configuration file. + * buf can potentially be modified by process_linkprop_line(). + * If this is a write operation and buf is not truncated, buf will + * be written to disk. process_linkprop_line() will no longer be + * called after it returns B_FALSE; at which point the remainder + * of the file will continue to be read and, if necessary, written + * to disk as well. + */ + while (fgets(buf, MAXLINELEN, fp) != NULL) { + if (cont) + cont = process_linkprop_line(lsp, buf, &status); + + if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) { + status = dladm_errno2status(errno); + break; + } + } + + if (status != DLADM_STATUS_OK || !cont) + return (status); + + if (lsp->ls_op == process_linkprop_set) { + /* + * If the specified link is not found above, we add the + * link and its properties to the configuration file. + */ + (void) (*lsp->ls_op)(lsp, buf, NULL, &status); + if (status == DLADM_STATUS_OK && fputs(buf, nfp) == EOF) + status = dladm_errno2status(errno); + } + + if (lsp->ls_op == process_linkprop_get) + status = DLADM_STATUS_NOTFOUND; + + return (status); +} + +#define LINKPROP_RW_DB(statep, writeop) \ + (i_dladm_rw_db("/etc/dladm/linkprop.conf", \ + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, process_linkprop_db, \ + (statep), (writeop))) + +static dladm_status_t +i_dladm_set_prop_db(const char *link, const char *prop_name, + char **prop_val, uint_t val_cnt) +{ + linkprop_db_state_t state; + + state.ls_op = process_linkprop_set; + state.ls_link = link; + state.ls_propname = prop_name; + state.ls_propval = prop_val; + state.ls_valcntp = &val_cnt; + + return (LINKPROP_RW_DB(&state, B_TRUE)); +} + +static dladm_status_t +i_dladm_get_prop_db(const char *link, const char *prop_name, + char **prop_val, uint_t *val_cntp) +{ + linkprop_db_state_t state; + + state.ls_op = process_linkprop_get; + state.ls_link = link; + state.ls_propname = prop_name; + state.ls_propval = prop_val; + state.ls_valcntp = val_cntp; + + return (LINKPROP_RW_DB(&state, B_FALSE)); +} + +dladm_status_t +dladm_init_linkprop(void) +{ + linkprop_db_state_t state; + + state.ls_op = process_linkprop_init; + state.ls_link = NULL; + state.ls_propname = NULL; + state.ls_propval = NULL; + state.ls_valcntp = NULL; + + return (LINKPROP_RW_DB(&state, B_FALSE)); +} diff --git a/usr/src/lib/libdladm/common/mapfile-vers b/usr/src/lib/libdladm/common/mapfile-vers index 5e0f599741..2af8201536 100644 --- a/usr/src/lib/libdladm/common/mapfile-vers +++ b/usr/src/lib/libdladm/common/mapfile-vers @@ -30,6 +30,20 @@ SUNWprivate_1.1 { dladm_info; dladm_walk; dladm_walk_vlan; + dladm_get_prop; + dladm_set_prop; + dladm_walk_prop; + dladm_get_secobj; + dladm_set_secobj; + dladm_unset_secobj; + dladm_walk_secobj; + dladm_status2str; + dladm_secobjclass2str; + dladm_str2secobjclass; + dladm_init_linkprop; + dladm_init_secobj; + dladm_set_rootdir; + local: *; }; diff --git a/usr/src/lib/libdladm/common/secobj.c b/usr/src/lib/libdladm/common/secobj.c new file mode 100644 index 0000000000..3f122ca347 --- /dev/null +++ b/usr/src/lib/libdladm/common/secobj.c @@ -0,0 +1,634 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <unistd.h> +#include <stdlib.h> +#include <strings.h> +#include <errno.h> +#include <ctype.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/dld.h> +#include <libinetutil.h> +#include <libdladm_impl.h> + +static dladm_status_t i_dladm_set_secobj_db(const char *, + dladm_secobj_class_t, uint8_t *, uint_t); +static dladm_status_t i_dladm_get_secobj_db(const char *, + dladm_secobj_class_t *, uint8_t *, uint_t *); +static dladm_status_t i_dladm_unset_secobj_db(const char *); +static dladm_status_t i_dladm_walk_secobj_db(void *, + boolean_t (*)(void *, const char *)); + +typedef struct secobj_class_info { + const char *sc_name; + dld_secobj_class_t sc_dldclass; +} secobj_class_info_t; + +static secobj_class_info_t secobj_class_table[] = { + {"wep", DLD_SECOBJ_CLASS_WEP} +}; + +#define NSECOBJCLASS \ + (sizeof (secobj_class_table) / sizeof (secobj_class_info_t)) + +static boolean_t +dladm_check_secobjclass(dladm_secobj_class_t class) +{ + return (class >= 0 && class < NSECOBJCLASS); +} + +dladm_status_t +dladm_str2secobjclass(const char *str, dladm_secobj_class_t *class) +{ + int i; + secobj_class_info_t *sp; + + for (i = 0; i < NSECOBJCLASS; i++) { + sp = &secobj_class_table[i]; + if (strcasecmp(str, sp->sc_name) == 0) { + *class = i; + return (DLADM_STATUS_OK); + } + } + return (DLADM_STATUS_BADARG); +} + +const char * +dladm_secobjclass2str(dladm_secobj_class_t class, char *buf) +{ + const char *s; + + if (!dladm_check_secobjclass(class)) + s = ""; + else + s = secobj_class_table[class].sc_name; + + (void) snprintf(buf, DLADM_STRSIZE, "%s", s); + return (buf); +} + +static boolean_t +dladm_convert_secobjclass(dladm_secobj_class_t class, + dld_secobj_class_t *dldclass) +{ + if (!dladm_check_secobjclass(class)) + return (B_FALSE); + + *dldclass = secobj_class_table[class].sc_dldclass; + return (B_TRUE); +} + +static boolean_t +dladm_convert_dldsecobjclass(dld_secobj_class_t dldclass, + dladm_secobj_class_t *class) +{ + int i; + secobj_class_info_t *sp; + + for (i = 0; i < NSECOBJCLASS; i++) { + sp = &secobj_class_table[i]; + if (dldclass == sp->sc_dldclass) { + *class = i; + return (B_TRUE); + } + } + return (B_FALSE); +} + +dladm_status_t +dladm_set_secobj(const char *obj_name, dladm_secobj_class_t class, + uint8_t *obj_val, uint_t obj_len, uint_t flags) +{ + int fd; + dladm_status_t status = DLADM_STATUS_OK; + dld_ioc_secobj_set_t secobj_set; + dld_secobj_t *objp; + + if (!dladm_check_secobjclass(class) || flags == 0 || + obj_name == NULL || strlen(obj_name) > DLD_SECOBJ_NAME_MAX || + obj_val == NULL || obj_len == 0 || obj_len > DLD_SECOBJ_VAL_MAX) + return (DLADM_STATUS_BADARG); + + if ((flags & DLADM_OPT_TEMP) == 0) + goto persist; + + bzero(&secobj_set, sizeof (secobj_set)); + objp = &secobj_set.ss_obj; + if (!dladm_convert_secobjclass(class, &objp->so_class)) + return (DLADM_STATUS_BADARG); + + (void) strlcpy(objp->so_name, obj_name, DLD_SECOBJ_NAME_MAX); + bcopy(obj_val, objp->so_val, obj_len); + objp->so_len = obj_len; + + if ((flags & DLADM_OPT_CREATE) != 0) + secobj_set.ss_flags = DLD_SECOBJ_OPT_CREATE; + + if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) + return (dladm_errno2status(errno)); + + if (i_dladm_ioctl(fd, DLDIOCSECOBJSET, &secobj_set, + sizeof (secobj_set)) < 0) + status = dladm_errno2status(errno); + + (void) close(fd); + if (status != DLADM_STATUS_OK) + return (status); + +persist: + if ((flags & DLADM_OPT_PERSIST) != 0) { + status = i_dladm_set_secobj_db(obj_name, class, + obj_val, obj_len); + } + return (status); +} + +dladm_status_t +dladm_get_secobj(const char *obj_name, dladm_secobj_class_t *classp, + uint8_t *obj_val, uint_t *obj_lenp, uint_t flags) +{ + int fd; + dladm_status_t status = DLADM_STATUS_OK; + dld_ioc_secobj_get_t secobj_get; + dld_secobj_t *objp; + + if (obj_name == NULL || strlen(obj_name) > DLD_SECOBJ_NAME_MAX || + obj_val == NULL || obj_lenp == NULL || *obj_lenp == 0 || + *obj_lenp > DLD_SECOBJ_VAL_MAX) + return (DLADM_STATUS_BADARG); + + if ((flags & DLADM_OPT_PERSIST) != 0) { + return (i_dladm_get_secobj_db(obj_name, classp, + obj_val, obj_lenp)); + } + + bzero(&secobj_get, sizeof (secobj_get)); + objp = &secobj_get.sg_obj; + (void) strlcpy(objp->so_name, obj_name, DLD_SECOBJ_NAME_MAX); + + if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) + return (dladm_errno2status(errno)); + + if (i_dladm_ioctl(fd, DLDIOCSECOBJGET, &secobj_get, + sizeof (secobj_get)) < 0) + status = dladm_errno2status(errno); + + (void) close(fd); + if (objp->so_len > *obj_lenp) + return (DLADM_STATUS_TOOSMALL); + + if (!dladm_convert_dldsecobjclass(objp->so_class, classp)) + return (DLADM_STATUS_FAILED); + + *obj_lenp = objp->so_len; + bcopy(objp->so_val, obj_val, *obj_lenp); + return (status); +} + +dladm_status_t +dladm_unset_secobj(const char *obj_name, uint_t flags) +{ + int fd; + dladm_status_t status = DLADM_STATUS_OK; + dld_ioc_secobj_unset_t secobj_unset; + + if (obj_name == NULL || strlen(obj_name) > DLD_SECOBJ_NAME_MAX || + flags == 0) + return (DLADM_STATUS_BADARG); + + if ((flags & DLADM_OPT_TEMP) == 0) + goto persist; + + bzero(&secobj_unset, sizeof (secobj_unset)); + (void) strlcpy(secobj_unset.su_name, obj_name, DLD_SECOBJ_NAME_MAX); + if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) + return (dladm_errno2status(errno)); + + if (i_dladm_ioctl(fd, DLDIOCSECOBJUNSET, &secobj_unset, + sizeof (secobj_unset)) < 0) + status = dladm_errno2status(errno); + + (void) close(fd); + if (status != DLADM_STATUS_OK) + return (status); + +persist: + if ((flags & DLADM_OPT_PERSIST) != 0) + status = i_dladm_unset_secobj_db(obj_name); + + return (status); +} + +#define SECOBJ_BUFSZ 65536 +dladm_status_t +dladm_walk_secobj(void *arg, boolean_t (*func)(void *, const char *), + uint_t flags) +{ + int fd = -1; + dladm_status_t status = DLADM_STATUS_OK; + dld_ioc_secobj_get_t *secobj_getp; + dld_secobj_t *objp; + + if ((flags & DLADM_OPT_PERSIST) != 0) + return (i_dladm_walk_secobj_db(arg, func)); + + secobj_getp = calloc(1, SECOBJ_BUFSZ); + if (secobj_getp == NULL) + return (DLADM_STATUS_NOMEM); + + if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) { + status = dladm_errno2status(errno); + goto done; + } + if (i_dladm_ioctl(fd, DLDIOCSECOBJGET, secobj_getp, + SECOBJ_BUFSZ) < 0) { + status = dladm_errno2status(errno); + goto done; + } + + objp = (dld_secobj_t *)(secobj_getp + 1); + while (secobj_getp->sg_count > 0) { + if (!func(arg, objp->so_name)) + goto done; + secobj_getp->sg_count--; + objp++; + } +done: + (void) close(fd); + free(secobj_getp); + return (status); +} + +/* + * Data structures used for implementing persistent secure objects + */ +typedef struct secobj_info { + const char *si_name; + dladm_secobj_class_t *si_classp; + uint8_t *si_val; + uint_t *si_lenp; +} secobj_info_t; + +typedef struct secobj_name { + char *sn_name; + struct secobj_name *sn_next; +} secobj_name_t; + +typedef struct secobj_db_state secobj_db_state_t; + +typedef boolean_t (*secobj_db_op_t)(struct secobj_db_state *, char *, + secobj_info_t *, dladm_status_t *); + +struct secobj_db_state { + secobj_db_op_t ss_op; + secobj_info_t ss_info; + secobj_name_t **ss_namelist; +}; + +/* + * Update or generate a secobj entry using the info in ssp->ss_info. + */ +/* ARGSUSED */ +static boolean_t +process_secobj_set(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip, + dladm_status_t *statusp) +{ + char tmpbuf[MAXLINELEN]; + char classbuf[DLADM_STRSIZE]; + char *ptr = tmpbuf, *lim = tmpbuf + MAXLINELEN; + int i; + + sip = &ssp->ss_info; + + ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", sip->si_name); + ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", + dladm_secobjclass2str(*sip->si_classp, classbuf)); + + ptr += snprintf(ptr, BUFLEN(lim, ptr), "0x"); + for (i = 0; i < *sip->si_lenp; i++) { + ptr += snprintf(ptr, BUFLEN(lim, ptr), "%02x", + sip->si_val[i] & 0xff); + } + if (ptr > lim) { + *statusp = DLADM_STATUS_TOOSMALL; + return (B_FALSE); + } + (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf); + return (B_FALSE); +} + +/* ARGSUSED */ +static boolean_t +process_secobj_get(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip, + dladm_status_t *statusp) +{ + if (*sip->si_lenp > *ssp->ss_info.si_lenp) { + *statusp = DLADM_STATUS_TOOSMALL; + return (B_FALSE); + } + bcopy(sip->si_val, ssp->ss_info.si_val, *sip->si_lenp); + *ssp->ss_info.si_lenp = *sip->si_lenp; + *ssp->ss_info.si_classp = *sip->si_classp; + return (B_FALSE); +} + +/* ARGSUSED */ +static boolean_t +process_secobj_unset(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip, + dladm_status_t *statusp) +{ + /* + * Delete line. + */ + buf[0] = '\0'; + return (B_FALSE); +} + +/* ARGSUSED */ +static boolean_t +process_secobj_walk(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip, + dladm_status_t *statusp) +{ + secobj_name_t *snp; + + if ((snp = malloc(sizeof (*snp))) == NULL) + return (B_TRUE); + + if ((snp->sn_name = strdup(sip->si_name)) == NULL) { + free(snp); + return (B_TRUE); + } + + snp->sn_next = NULL; + *ssp->ss_namelist = snp; + ssp->ss_namelist = &snp->sn_next; + return (B_TRUE); +} + +/* ARGSUSED */ +static boolean_t +process_secobj_init(secobj_db_state_t *ssp, char *buf, secobj_info_t *sip, + dladm_status_t *statusp) +{ + *statusp = dladm_set_secobj(sip->si_name, *sip->si_classp, sip->si_val, + *sip->si_lenp, DLADM_OPT_TEMP | DLADM_OPT_CREATE); + return (B_TRUE); +} + +static int +parse_secobj_val(char *buf, secobj_info_t *sip) +{ + if (strncmp(buf, "0x", 2) != 0) + return (EINVAL); + + return (hexascii_to_octet(buf + 2, strlen(buf) - 2, + sip->si_val, sip->si_lenp)); +} + +static boolean_t +process_secobj_line(secobj_db_state_t *ssp, char *buf, + dladm_status_t *statusp) +{ + secobj_info_t sinfo; + dladm_secobj_class_t class; + uint8_t val[DLADM_SECOBJ_VAL_MAX]; + uint_t vlen; + int i, len, nlen; + char *str, *lasts; + + /* + * Skip leading spaces, blank lines, and comments. + */ + len = strlen(buf); + for (i = 0; i < len; i++) { + if (!isspace(buf[i])) + break; + } + if (i == len || buf[i] == '#') + return (B_TRUE); + + str = buf + i; + if (ssp->ss_info.si_name != NULL) { + /* + * Skip objects we're not interested in. + */ + nlen = strlen(ssp->ss_info.si_name); + if (strncmp(str, ssp->ss_info.si_name, nlen) != 0 || + !isspace(str[nlen])) + return (B_TRUE); + + sinfo.si_name = ssp->ss_info.si_name; + } else { + /* + * If an object is not specified, find the object name + * and assign it to sinfo.si_name. + */ + if (strtok_r(str, " \n\t", &lasts) == NULL) + goto fail; + + nlen = strlen(str); + sinfo.si_name = str; + } + str += nlen + 1; + if (str >= buf + len) + goto fail; + + /* + * Find the class name. + */ + if ((str = strtok_r(str, " \n\t", &lasts)) == NULL) + goto fail; + + *statusp = dladm_str2secobjclass(str, &class); + if (*statusp != DLADM_STATUS_OK) + goto fail; + + /* + * Find the object value. + */ + if ((str = strtok_r(NULL, " \n\t", &lasts)) == NULL) + goto fail; + + vlen = DLADM_SECOBJ_VAL_MAX; + sinfo.si_classp = &class; + sinfo.si_val = val; + sinfo.si_lenp = &vlen; + if (parse_secobj_val(str, &sinfo) != 0) + goto fail; + + return ((*ssp->ss_op)(ssp, buf, &sinfo, statusp)); + +fail: + /* + * Delete corrupted line. + */ + buf[0] = '\0'; + return (B_TRUE); +} + +static dladm_status_t +process_secobj_db(void *arg, FILE *fp, FILE *nfp) +{ + secobj_db_state_t *ssp = arg; + dladm_status_t status = DLADM_STATUS_OK; + char buf[MAXLINELEN]; + boolean_t cont = B_TRUE; + + /* + * This loop processes each line of the configuration file. + * buf can potentially be modified by process_secobj_line(). + * If this is a write operation and buf is not truncated, buf will + * be written to disk. process_secobj_line() will no longer be + * called after it returns B_FALSE; at which point the remainder + * of the file will continue to be read and, if necessary, written + * to disk as well. + */ + while (fgets(buf, MAXLINELEN, fp) != NULL) { + if (cont) + cont = process_secobj_line(ssp, buf, &status); + + if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) { + status = dladm_errno2status(errno); + break; + } + } + if (status != DLADM_STATUS_OK || !cont) + return (status); + + if (ssp->ss_op == process_secobj_set) { + /* + * If the specified object is not found above, we add the + * object to the configuration file. + */ + (void) (*ssp->ss_op)(ssp, buf, NULL, &status); + if (status == DLADM_STATUS_OK && fputs(buf, nfp) == EOF) + status = dladm_errno2status(errno); + } + + if (ssp->ss_op == process_secobj_unset || + ssp->ss_op == process_secobj_get) + status = DLADM_STATUS_NOTFOUND; + + return (status); +} + +#define SECOBJ_RW_DB(statep, writeop) \ + (i_dladm_rw_db("/etc/dladm/secobj.conf", S_IRUSR | S_IWUSR, \ + process_secobj_db, (statep), (writeop))) + +static dladm_status_t +i_dladm_set_secobj_db(const char *obj_name, dladm_secobj_class_t class, + uint8_t *obj_val, uint_t obj_len) +{ + secobj_db_state_t state; + + state.ss_op = process_secobj_set; + state.ss_info.si_name = obj_name; + state.ss_info.si_classp = &class; + state.ss_info.si_val = obj_val; + state.ss_info.si_lenp = &obj_len; + state.ss_namelist = NULL; + + return (SECOBJ_RW_DB(&state, B_TRUE)); +} + +static dladm_status_t +i_dladm_get_secobj_db(const char *obj_name, dladm_secobj_class_t *classp, + uint8_t *obj_val, uint_t *obj_lenp) +{ + secobj_db_state_t state; + + state.ss_op = process_secobj_get; + state.ss_info.si_name = obj_name; + state.ss_info.si_classp = classp; + state.ss_info.si_val = obj_val; + state.ss_info.si_lenp = obj_lenp; + state.ss_namelist = NULL; + + return (SECOBJ_RW_DB(&state, B_FALSE)); +} + +static dladm_status_t +i_dladm_unset_secobj_db(const char *obj_name) +{ + secobj_db_state_t state; + + state.ss_op = process_secobj_unset; + state.ss_info.si_name = obj_name; + state.ss_info.si_classp = NULL; + state.ss_info.si_val = NULL; + state.ss_info.si_lenp = NULL; + state.ss_namelist = NULL; + + return (SECOBJ_RW_DB(&state, B_TRUE)); +} + +static dladm_status_t +i_dladm_walk_secobj_db(void *arg, boolean_t (*func)(void *, const char *)) +{ + secobj_db_state_t state; + secobj_name_t *snp = NULL, *fsnp; + dladm_status_t status; + boolean_t cont = B_TRUE; + + state.ss_op = process_secobj_walk; + state.ss_info.si_name = NULL; + state.ss_info.si_classp = NULL; + state.ss_info.si_val = NULL; + state.ss_info.si_lenp = NULL; + state.ss_namelist = &snp; + + status = SECOBJ_RW_DB(&state, B_FALSE); + if (status != DLADM_STATUS_OK) + return (status); + + while (snp != NULL) { + fsnp = snp; + snp = snp->sn_next; + if (cont) + cont = func(arg, fsnp->sn_name); + free(fsnp->sn_name); + free(fsnp); + } + return (status); +} + +dladm_status_t +dladm_init_secobj(void) +{ + secobj_db_state_t state; + + state.ss_op = process_secobj_init; + state.ss_info.si_name = NULL; + state.ss_info.si_classp = NULL; + state.ss_info.si_val = NULL; + state.ss_info.si_lenp = NULL; + state.ss_namelist = NULL; + + return (SECOBJ_RW_DB(&state, B_FALSE)); +} |