summaryrefslogtreecommitdiff
path: root/usr/src/lib/libshare/smb/libshare_smb.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libshare/smb/libshare_smb.c')
-rw-r--r--usr/src/lib/libshare/smb/libshare_smb.c1641
1 files changed, 1641 insertions, 0 deletions
diff --git a/usr/src/lib/libshare/smb/libshare_smb.c b/usr/src/lib/libshare/smb/libshare_smb.c
new file mode 100644
index 0000000000..7d705e0369
--- /dev/null
+++ b/usr/src/lib/libshare/smb/libshare_smb.c
@@ -0,0 +1,1641 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * SMB specific functions
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <zone.h>
+#include <errno.h>
+#include <locale.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include "libshare.h"
+#include "libshare_impl.h"
+#include <pwd.h>
+#include <limits.h>
+#include <libscf.h>
+#include <strings.h>
+#include "libshare_smb.h"
+#include <rpcsvc/daemon_utils.h>
+#include <smbsrv/lmshare.h>
+#include <smbsrv/lmshare_door.h>
+#include <smbsrv/smbinfo.h>
+#include <smbsrv/libsmb.h>
+
+/* internal functions */
+static int smb_share_init(void);
+static void smb_share_fini(void);
+static int smb_enable_share(sa_share_t);
+static int smb_share_changed(sa_share_t);
+static int smb_resource_changed(sa_resource_t);
+static int smb_rename_resource(sa_handle_t, sa_resource_t, char *);
+static int smb_disable_share(sa_share_t share, char *);
+static int smb_validate_property(sa_property_t, sa_optionset_t);
+static int smb_set_proto_prop(sa_property_t);
+static sa_protocol_properties_t smb_get_proto_set(void);
+static char *smb_get_status(void);
+static int smb_parse_optstring(sa_group_t, char *);
+static char *smb_format_options(sa_group_t, int);
+
+static int smb_enable_service(void);
+
+static int range_check_validator(int, char *);
+static int range_check_validator_zero_ok(int, char *);
+static int string_length_check_validator(int, char *);
+static int true_false_validator(int, char *);
+static int ip_address_validator_empty_ok(int, char *);
+static int ip_address_csv_list_validator_empty_ok(int, char *);
+static int ipc_mode_validator(int, char *);
+static int path_validator(int, char *);
+
+static int smb_enable_resource(sa_resource_t);
+static int smb_disable_resource(sa_resource_t);
+static uint64_t smb_share_features(void);
+static int smb_list_transient(sa_handle_t);
+
+/* size of basic format allocation */
+#define OPT_CHUNK 1024
+
+/*
+ * Indexes of entries in smb_proto_options table.
+ * Changes to smb_proto_options table may require
+ * an update to these values.
+ */
+#define PROTO_OPT_WINS1 6
+#define PROTO_OPT_WINS_EXCLUDE 8
+
+
+/*
+ * ops vector that provides the protocol specific info and operations
+ * for share management.
+ */
+
+struct sa_plugin_ops sa_plugin_ops = {
+ SA_PLUGIN_VERSION,
+ SMB_PROTOCOL_NAME,
+ smb_share_init,
+ smb_share_fini,
+ smb_enable_share,
+ smb_disable_share,
+ smb_validate_property,
+ NULL,
+ NULL,
+ smb_parse_optstring,
+ smb_format_options,
+ smb_set_proto_prop,
+ smb_get_proto_set,
+ smb_get_status,
+ NULL,
+ NULL,
+ NULL,
+ smb_share_changed,
+ smb_enable_resource,
+ smb_disable_resource,
+ smb_share_features,
+ smb_list_transient,
+ smb_resource_changed,
+ smb_rename_resource,
+ NULL,
+ NULL
+};
+
+/*
+ * option definitions. Make sure to keep the #define for the option
+ * index just before the entry it is the index for. Changing the order
+ * can cause breakage.
+ */
+
+struct option_defs optdefs[] = {
+ {SHOPT_AD_CONTAINER, OPT_TYPE_STRING},
+ {SHOPT_NAME, OPT_TYPE_NAME},
+ {NULL, NULL},
+};
+
+/*
+ * findopt(name)
+ *
+ * Lookup option "name" in the option table and return the table
+ * index.
+ */
+
+static int
+findopt(char *name)
+{
+ int i;
+ if (name != NULL) {
+ for (i = 0; optdefs[i].tag != NULL; i++) {
+ if (strcmp(optdefs[i].tag, name) == 0)
+ return (i);
+ }
+ }
+ return (-1);
+}
+
+/*
+ * is_a_number(number)
+ *
+ * is the string a number in one of the forms we want to use?
+ */
+
+static int
+is_a_number(char *number)
+{
+ int ret = 1;
+ int hex = 0;
+
+ if (strncmp(number, "0x", 2) == 0) {
+ number += 2;
+ hex = 1;
+ } else if (*number == '-') {
+ number++; /* skip the minus */
+ }
+
+ while (ret == 1 && *number != '\0') {
+ if (hex) {
+ ret = isxdigit(*number++);
+ } else {
+ ret = isdigit(*number++);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * validresource(name)
+ *
+ * Check that name only has valid characters in it. The current valid
+ * set are the printable characters but not including:
+ * " / \ [ ] : | < > + ; , ? * = \t
+ * Note that space is included and there is a maximum length.
+ */
+static int
+validresource(const char *name)
+{
+ const char *cp;
+ size_t len;
+
+ if (name == NULL)
+ return (B_FALSE);
+
+ len = strlen(name);
+ if (len == 0 || len > SA_MAX_RESOURCE_NAME)
+ return (B_FALSE);
+
+ if (strpbrk(name, "\"/\\[]:|<>+;,?*=\t") != NULL) {
+ return (B_FALSE);
+ }
+
+ for (cp = name; *cp != '\0'; cp++)
+ if (iscntrl(*cp))
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+/*
+ * smb_isonline()
+ *
+ * Determine if the SMF service instance is in the online state or
+ * not. A number of operations depend on this state.
+ */
+static boolean_t
+smb_isonline(void)
+{
+ char *str;
+ boolean_t ret = B_FALSE;
+
+ if ((str = smf_get_state(SMBD_DEFAULT_INSTANCE_FMRI)) != NULL) {
+ ret = (strcmp(str, SCF_STATE_STRING_ONLINE) == 0);
+ free(str);
+ }
+ return (ret);
+}
+
+/*
+ * smb_enable_share tells the implementation that it is to enable the share.
+ * This entails converting the path and options into the appropriate ioctl
+ * calls. It is assumed that all error checking of paths, etc. were
+ * done earlier.
+ */
+static int
+smb_enable_share(sa_share_t share)
+{
+ char *path;
+ char *rname;
+ lmshare_info_t si;
+ sa_resource_t resource;
+ boolean_t iszfs;
+ boolean_t privileged;
+ int err = SA_OK;
+ priv_set_t *priv_effective;
+ boolean_t online;
+
+ priv_effective = priv_allocset();
+ (void) getppriv(PRIV_EFFECTIVE, priv_effective);
+ privileged = (priv_isfullset(priv_effective) == B_TRUE);
+ priv_freeset(priv_effective);
+
+ /* get the path since it is important in several places */
+ path = sa_get_share_attr(share, "path");
+ if (path == NULL)
+ return (SA_NO_SUCH_PATH);
+
+ online = smb_isonline();
+
+ iszfs = sa_path_is_zfs(path);
+
+ if (iszfs) {
+
+ if (privileged == B_FALSE && !online) {
+
+ if (!online) {
+ (void) printf(dgettext(TEXT_DOMAIN,
+ "SMB: Cannot share remove "
+ "file system: %s\n"), path);
+ (void) printf(dgettext(TEXT_DOMAIN,
+ "SMB: Service needs to be enabled "
+ "by a privileged user\n"));
+ err = SA_NO_PERMISSION;
+ errno = EPERM;
+ }
+ if (err) {
+ sa_free_attr_string(path);
+ return (err);
+ }
+
+ }
+ }
+
+ if (privileged == B_TRUE && !online) {
+ err = smb_enable_service();
+ if (err != SA_OK) {
+ (void) printf(dgettext(TEXT_DOMAIN,
+ "SMB: Unable to enable service\n"));
+ /*
+ * For now, it is OK to not be able to enable
+ * the service.
+ */
+ if (err == SA_BUSY)
+ err = SA_OK;
+ } else {
+ online = B_TRUE;
+ }
+ }
+
+ /*
+ * Don't bother trying to start shares if the service isn't
+ * running.
+ */
+ if (!online)
+ goto done;
+
+ /* Each share can have multiple resources */
+ for (resource = sa_get_share_resource(share, NULL);
+ resource != NULL;
+ resource = sa_get_next_resource(resource)) {
+ sa_optionset_t opts;
+ bzero(&si, sizeof (lmshare_info_t));
+ rname = sa_get_resource_attr(resource, "name");
+ if (rname == NULL) {
+ sa_free_attr_string(path);
+ return (SA_NO_SUCH_RESOURCE);
+ }
+
+ opts = sa_get_derived_optionset(resource, SMB_PROTOCOL_NAME, 1);
+ smb_build_lmshare_info(rname, path, opts, &si);
+ sa_free_attr_string(rname);
+
+ sa_free_derived_optionset(opts);
+ if (!iszfs) {
+ err = lmshrd_add(&si);
+ } else {
+ share_t sh;
+
+ sa_sharetab_fill_zfs(share, &sh, "smb");
+ err = sa_share_zfs(share, (char *)path, &sh,
+ &si, ZFS_SHARE_SMB);
+
+ sa_emptyshare(&sh);
+ }
+ }
+ if (!iszfs)
+ (void) sa_update_sharetab(share, "smb");
+done:
+ sa_free_attr_string(path);
+
+ return (err == NERR_DuplicateShare ? 0 : err);
+}
+
+/*
+ * This is the share for CIFS all shares have resource names.
+ * Enable tells the smb server to update its hash. If it fails
+ * because smb server is down, we just ignore as smb server loads
+ * the resources from sharemanager at startup.
+ */
+
+static int
+smb_enable_resource(sa_resource_t resource)
+{
+ char *path;
+ char *rname;
+ sa_optionset_t opts;
+ sa_share_t share;
+ lmshare_info_t si;
+ int ret;
+
+ share = sa_get_resource_parent(resource);
+ if (share == NULL)
+ return (SA_NO_SUCH_PATH);
+ path = sa_get_share_attr(share, "path");
+ if (path == NULL)
+ return (SA_SYSTEM_ERR);
+ rname = sa_get_resource_attr(resource, "name");
+ if (rname == NULL) {
+ sa_free_attr_string(path);
+ return (SA_NO_SUCH_RESOURCE);
+ }
+
+ ret = smb_enable_service();
+
+ if (!smb_isonline()) {
+ ret = SA_OK;
+ goto done;
+ }
+
+ opts = sa_get_derived_optionset(resource, SMB_PROTOCOL_NAME, 1);
+ smb_build_lmshare_info(rname, path, opts, &si);
+ sa_free_attr_string(path);
+ sa_free_attr_string(rname);
+ sa_free_derived_optionset(opts);
+ if (lmshrd_add(&si) != NERR_Success)
+ return (SA_NOT_SHARED);
+ (void) sa_update_sharetab(share, "smb");
+
+done:
+ return (ret);
+}
+
+/*
+ * Remove it from smb server hash.
+ */
+static int
+smb_disable_resource(sa_resource_t resource)
+{
+ char *rname;
+ DWORD res;
+ sa_share_t share;
+
+ rname = sa_get_resource_attr(resource, "name");
+ if (rname == NULL)
+ return (SA_NO_SUCH_RESOURCE);
+
+ if (smb_isonline()) {
+ res = lmshrd_delete(rname);
+ if (res != NERR_Success) {
+ sa_free_attr_string(rname);
+ return (SA_CONFIG_ERR);
+ }
+ sa_free_attr_string(rname);
+ rname = NULL;
+ }
+ share = sa_get_resource_parent(resource);
+ if (share != NULL) {
+ rname = sa_get_share_attr(share, "path");
+ if (rname != NULL) {
+ (void) sa_delete_sharetab(rname, "smb");
+ sa_free_attr_string(rname);
+ rname = NULL;
+ }
+ }
+ if (rname != NULL)
+ sa_free_attr_string(rname);
+ /*
+ * Always return OK as smb/server may be down and
+ * Shares will be picked up when loaded.
+ */
+ return (SA_OK);
+}
+
+/*
+ * smb_share_changed(sa_share_t share)
+ *
+ * The specified share has changed.
+ */
+static int
+smb_share_changed(sa_share_t share)
+{
+ char *path;
+ sa_resource_t resource;
+
+ /* get the path since it is important in several places */
+ path = sa_get_share_attr(share, "path");
+ if (path == NULL)
+ return (SA_NO_SUCH_PATH);
+ for (resource = sa_get_share_resource(share, NULL);
+ resource != NULL;
+ resource = sa_get_next_resource(resource))
+ (void) smb_resource_changed(resource);
+
+ sa_free_attr_string(path);
+
+ return (SA_OK);
+}
+
+/*
+ * smb_resource_changed(sa_resource_t resource)
+ *
+ * The specified resource has changed.
+ */
+static int
+smb_resource_changed(sa_resource_t resource)
+{
+ DWORD res;
+ lmshare_info_t si;
+ lmshare_info_t new_si;
+ char *rname, *path;
+ sa_optionset_t opts;
+ sa_share_t share;
+
+ rname = sa_get_resource_attr(resource, "name");
+ if (rname == NULL)
+ return (SA_NO_SUCH_RESOURCE);
+
+ share = sa_get_resource_parent(resource);
+ if (share == NULL) {
+ sa_free_attr_string(rname);
+ return (SA_CONFIG_ERR);
+ }
+
+ path = sa_get_share_attr(share, "path");
+ if (path == NULL) {
+ sa_free_attr_string(rname);
+ return (SA_NO_SUCH_PATH);
+ }
+
+ if (!smb_isonline()) {
+ sa_free_attr_string(rname);
+ return (SA_OK);
+ }
+
+ /* Update the share cache in smb/server */
+ res = lmshrd_getinfo(rname, &si);
+ if (res != NERR_Success) {
+ sa_free_attr_string(path);
+ sa_free_attr_string(rname);
+ return (SA_CONFIG_ERR);
+ }
+
+ opts = sa_get_derived_optionset(resource, SMB_PROTOCOL_NAME, 1);
+ smb_build_lmshare_info(rname, path, opts, &new_si);
+ sa_free_derived_optionset(opts);
+ sa_free_attr_string(path);
+ sa_free_attr_string(rname);
+
+ /*
+ * Update all fields from sa_share_t
+ * Get derived values.
+ */
+ if (lmshrd_setinfo(&new_si) != LMSHR_DOOR_SRV_SUCCESS)
+ return (SA_CONFIG_ERR);
+ return (smb_enable_service());
+}
+
+/*
+ * smb_disable_share(sa_share_t share)
+ *
+ * Unshare the specified share.
+ */
+static int
+smb_disable_share(sa_share_t share, char *path)
+{
+ char *rname;
+ sa_resource_t resource;
+ boolean_t iszfs;
+ int err = SA_OK;
+
+ iszfs = sa_path_is_zfs(path);
+ if (!smb_isonline())
+ goto done;
+
+ for (resource = sa_get_share_resource(share, NULL);
+ resource != NULL;
+ resource = sa_get_next_resource(resource)) {
+ rname = sa_get_resource_attr(resource, "name");
+ if (rname == NULL) {
+ continue;
+ }
+ if (!iszfs) {
+ err = lmshrd_delete(rname);
+ switch (err) {
+ case NERR_NetNameNotFound:
+ case NERR_Success:
+ err = SA_OK;
+ break;
+ default:
+ err = SA_CONFIG_ERR;
+ break;
+ }
+ } else {
+ share_t sh;
+
+ sa_sharetab_fill_zfs(share, &sh, "smb");
+ err = sa_share_zfs(share, (char *)path, &sh,
+ rname, ZFS_UNSHARE_SMB);
+ sa_emptyshare(&sh);
+ }
+ sa_free_attr_string(rname);
+ }
+done:
+ if (!iszfs)
+ (void) sa_delete_sharetab(path, "smb");
+ return (err);
+}
+
+/*
+ * smb_validate_property(property, parent)
+ *
+ * Check that the property has a legitimate value for its type.
+ */
+
+static int
+smb_validate_property(sa_property_t property, sa_optionset_t parent)
+{
+ int ret = SA_OK;
+ char *propname;
+ int optindex;
+ sa_group_t parent_group;
+ char *value;
+
+ propname = sa_get_property_attr(property, "type");
+
+ if ((optindex = findopt(propname)) < 0)
+ ret = SA_NO_SUCH_PROP;
+
+ /* need to validate value range here as well */
+ if (ret == SA_OK) {
+ parent_group = sa_get_parent_group((sa_share_t)parent);
+ if (optdefs[optindex].share && !sa_is_share(parent_group))
+ ret = SA_PROP_SHARE_ONLY;
+ }
+ if (ret != SA_OK) {
+ if (propname != NULL)
+ sa_free_attr_string(propname);
+ return (ret);
+ }
+
+ value = sa_get_property_attr(property, "value");
+ if (value != NULL) {
+ /* first basic type checking */
+ switch (optdefs[optindex].type) {
+ case OPT_TYPE_NUMBER:
+ /* check that the value is all digits */
+ if (!is_a_number(value))
+ ret = SA_BAD_VALUE;
+ break;
+ case OPT_TYPE_BOOLEAN:
+ if (strlen(value) == 0 ||
+ strcasecmp(value, "true") == 0 ||
+ strcmp(value, "1") == 0 ||
+ strcasecmp(value, "false") == 0 ||
+ strcmp(value, "0") == 0) {
+ ret = SA_OK;
+ } else {
+ ret = SA_BAD_VALUE;
+ }
+ break;
+ case OPT_TYPE_NAME:
+ /*
+ * Make sure no invalid characters
+ */
+ if (validresource(value) == B_FALSE)
+ ret = SA_BAD_VALUE;
+ break;
+ case OPT_TYPE_STRING:
+ /* whatever is here should be ok */
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (value != NULL)
+ sa_free_attr_string(value);
+ if (ret == SA_OK && optdefs[optindex].check != NULL)
+ /* do the property specific check */
+ ret = optdefs[optindex].check(property);
+
+ if (propname != NULL)
+ sa_free_attr_string(propname);
+ return (ret);
+}
+
+/*
+ * Protocol management functions
+ *
+ * properties defined in the default files are defined in
+ * proto_option_defs for parsing and validation.
+ */
+
+struct smb_proto_option_defs {
+ char *name; /* display name -- remove protocol identifier */
+ int smb_index;
+ int32_t minval;
+ int32_t maxval; /* In case of length of string this should be max */
+ int (*validator)(int, char *);
+ int32_t refresh;
+} smb_proto_options[] = {
+ { SMB_CD_SYS_CMNT,
+ SMB_CI_SYS_CMNT, 0, MAX_VALUE_BUFLEN,
+ string_length_check_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_MAX_WORKERS,
+ SMB_CI_MAX_WORKERS, 64, 1024, range_check_validator,
+ SMB_REFRESH_REFRESH},
+ { SMB_CD_NBSCOPE,
+ SMB_CI_NBSCOPE, 0, MAX_VALUE_BUFLEN,
+ string_length_check_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_RDR_IPCMODE,
+ SMB_CI_RDR_IPCMODE, 0, 0, ipc_mode_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_LM_LEVEL,
+ SMB_CI_LM_LEVEL, 2, 5, range_check_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_KEEPALIVE,
+ SMB_CI_KEEPALIVE, 20, 5400, range_check_validator_zero_ok,
+ SMB_REFRESH_REFRESH},
+ { SMB_CD_WINS_SRV1,
+ SMB_CI_WINS_SRV1, 0, MAX_VALUE_BUFLEN,
+ ip_address_validator_empty_ok, SMB_REFRESH_REFRESH},
+ { SMB_CD_WINS_SRV2,
+ SMB_CI_WINS_SRV2, 0, MAX_VALUE_BUFLEN,
+ ip_address_validator_empty_ok, SMB_REFRESH_REFRESH},
+ { SMB_CD_WINS_EXCL,
+ SMB_CI_WINS_EXCL, 0, MAX_VALUE_BUFLEN,
+ ip_address_csv_list_validator_empty_ok, SMB_REFRESH_REFRESH},
+ { SMB_CD_SIGNING_ENABLE,
+ SMB_CI_SIGNING_ENABLE, 0, 0, true_false_validator,
+ SMB_REFRESH_REFRESH},
+ { SMB_CD_SIGNING_REQD,
+ SMB_CI_SIGNING_REQD, 0, 0, true_false_validator,
+ SMB_REFRESH_REFRESH},
+ { SMB_CD_RESTRICT_ANON,
+ SMB_CI_RESTRICT_ANON, 0, 0, true_false_validator,
+ SMB_REFRESH_REFRESH},
+ { SMB_CD_DOMAIN_SRV,
+ SMB_CI_DOMAIN_SRV, 0, MAX_VALUE_BUFLEN,
+ ip_address_validator_empty_ok, SMB_REFRESH_REFRESH},
+ { SMB_CD_ADS_ENABLE,
+ SMB_CI_ADS_ENABLE, 0, 0, true_false_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_ADS_USER,
+ SMB_CI_ADS_USER, 0, MAX_VALUE_BUFLEN,
+ string_length_check_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_ADS_USER_CONTAINER,
+ SMB_CI_ADS_USER_CONTAINER, 0, MAX_VALUE_BUFLEN,
+ string_length_check_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_ADS_DOMAIN,
+ SMB_CI_ADS_DOMAIN, 0, MAX_VALUE_BUFLEN,
+ string_length_check_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_ADS_PASSWD,
+ SMB_CI_ADS_PASSWD, 0, MAX_VALUE_BUFLEN,
+ string_length_check_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_ADS_IPLOOKUP,
+ SMB_CI_ADS_IPLOOKUP, 0, 0, true_false_validator,
+ SMB_REFRESH_REFRESH},
+ { SMB_CD_ADS_SITE,
+ SMB_CI_ADS_SITE, 0, MAX_VALUE_BUFLEN,
+ string_length_check_validator, SMB_REFRESH_REFRESH},
+ { SMB_CD_DYNDNS_ENABLE,
+ SMB_CI_DYNDNS_ENABLE, 0, 0, true_false_validator,
+ SMB_REFRESH_REFRESH},
+ { SMB_CD_DYNDNS_RETRY_SEC,
+ SMB_CI_DYNDNS_RETRY_SEC, 0, 20, range_check_validator,
+ SMB_REFRESH_REFRESH},
+ { SMB_CD_DYNDNS_RETRY_COUNT,
+ SMB_CI_DYNDNS_RETRY_COUNT, 3, 5, range_check_validator,
+ SMB_REFRESH_REFRESH},
+ { SMB_CD_AUTOHOME_MAP,
+ SMB_CI_AUTOHOME_MAP, 0, MAX_VALUE_BUFLEN,
+ path_validator},
+ {NULL, -1, 0, 0, NULL}
+};
+
+/*
+ * Check the range of value as int range.
+ */
+static int
+range_check_validator(int index, char *value)
+{
+ int ret = SA_OK;
+
+ if (!is_a_number(value)) {
+ ret = SA_BAD_VALUE;
+ } else {
+ int val;
+ val = strtoul(value, NULL, 0);
+ if (val < smb_proto_options[index].minval ||
+ val > smb_proto_options[index].maxval)
+ ret = SA_BAD_VALUE;
+ }
+ return (ret);
+}
+
+/*
+ * Check the range of value as int range.
+ */
+static int
+range_check_validator_zero_ok(int index, char *value)
+{
+ int ret = SA_OK;
+
+ if (!is_a_number(value)) {
+ ret = SA_BAD_VALUE;
+ } else {
+ int val;
+ val = strtoul(value, NULL, 0);
+ if (val == 0)
+ ret = SA_OK;
+ else {
+ if (val < smb_proto_options[index].minval ||
+ val > smb_proto_options[index].maxval)
+ ret = SA_BAD_VALUE;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * Check the length of the string
+ */
+static int
+string_length_check_validator(int index, char *value)
+{
+ int ret = SA_OK;
+
+ if (value == NULL)
+ return (SA_BAD_VALUE);
+ if (strlen(value) > smb_proto_options[index].maxval)
+ ret = SA_BAD_VALUE;
+ return (ret);
+}
+
+/*
+ * Check yes/no
+ */
+/*ARGSUSED*/
+static int
+true_false_validator(int index, char *value)
+{
+ if (value == NULL)
+ return (SA_BAD_VALUE);
+ if ((strcasecmp(value, "true") == 0) ||
+ (strcasecmp(value, "false") == 0))
+ return (SA_OK);
+ return (SA_BAD_VALUE);
+}
+
+/*
+ * Check IP address.
+ */
+/*ARGSUSED*/
+static int
+ip_address_validator_empty_ok(int index, char *value)
+{
+ char sbytes[16];
+ int len;
+
+ if (value == NULL)
+ return (SA_OK);
+ len = strlen(value);
+ if (len == 0)
+ return (SA_OK);
+ if (inet_pton(AF_INET, value, (void *)sbytes) != 1)
+ return (SA_BAD_VALUE);
+
+ return (SA_OK);
+}
+
+/*
+ * Check IP address list
+ */
+/*ARGSUSED*/
+static int
+ip_address_csv_list_validator_empty_ok(int index, char *value)
+{
+ char sbytes[16];
+ char *ip, *tmp, *ctx;
+
+ if (value == NULL || *value == '\0')
+ return (SA_OK);
+
+ if (strlen(value) > MAX_VALUE_BUFLEN)
+ return (SA_BAD_VALUE);
+
+ if ((tmp = strdup(value)) == NULL)
+ return (SA_NO_MEMORY);
+
+ ip = strtok_r(tmp, ",", &ctx);
+ while (ip) {
+ if (strlen(ip) == 0) {
+ free(tmp);
+ return (SA_BAD_VALUE);
+ }
+ if (*ip != 0) {
+ if (inet_pton(AF_INET, ip,
+ (void *)sbytes) != 1) {
+ free(tmp);
+ return (SA_BAD_VALUE);
+ }
+ }
+ ip = strtok_r(0, ",", &ctx);
+ }
+
+ free(tmp);
+ return (SA_OK);
+}
+
+/*
+ * Check IPC mode
+ */
+/*ARGSUSED*/
+static int
+ipc_mode_validator(int index, char *value)
+{
+ if (value == NULL)
+ return (SA_BAD_VALUE);
+ if (strcasecmp(value, "anon") == 0)
+ return (SA_OK);
+ if (strcasecmp(value, "auth") == 0)
+ return (SA_OK);
+ return (SA_BAD_VALUE);
+}
+
+/*
+ * Check path
+ */
+/*ARGSUSED*/
+static int
+path_validator(int index, char *value)
+{
+ struct stat buffer;
+ int fd, status;
+
+ if (value == NULL)
+ return (SA_BAD_VALUE);
+
+ fd = open(value, O_RDONLY);
+ if (fd < 0)
+ return (SA_BAD_VALUE);
+
+ status = fstat(fd, &buffer);
+ (void) close(fd);
+
+ if (status < 0)
+ return (SA_BAD_VALUE);
+
+ if (buffer.st_mode & S_IFDIR)
+ return (SA_OK);
+ return (SA_BAD_VALUE);
+}
+
+/*
+ * the protoset holds the defined options so we don't have to read
+ * them multiple times
+ */
+static sa_protocol_properties_t protoset;
+
+static int
+findprotoopt(char *name)
+{
+ int i;
+ for (i = 0; smb_proto_options[i].name != NULL; i++) {
+ if (strcasecmp(smb_proto_options[i].name, name) == 0)
+ return (i);
+ }
+ return (-1);
+}
+
+/*
+ * smb_load_proto_properties()
+ *
+ * read the smb config values from SMF.
+ */
+
+static int
+smb_load_proto_properties()
+{
+ sa_property_t prop;
+ int index;
+ char *value;
+
+ protoset = sa_create_protocol_properties(SMB_PROTOCOL_NAME);
+ if (protoset == NULL)
+ return (SA_NO_MEMORY);
+
+ if (smb_config_load() != 0)
+ return (SA_CONFIG_ERR);
+ for (index = 0; smb_proto_options[index].name != NULL; index++) {
+ value = smb_config_getenv(smb_proto_options[index].smb_index);
+ prop = sa_create_property(
+ smb_proto_options[index].name, value);
+ (void) sa_add_protocol_property(protoset, prop);
+ }
+ return (SA_OK);
+}
+
+/*
+ * smb_share_init()
+ *
+ * Initialize the smb plugin.
+ */
+
+static int
+smb_share_init(void)
+{
+ int ret = SA_OK;
+
+ if (sa_plugin_ops.sa_init != smb_share_init)
+ return (SA_SYSTEM_ERR);
+
+ if (smb_load_proto_properties() != SA_OK)
+ return (SA_SYSTEM_ERR);
+
+ return (ret);
+}
+
+/*
+ * smb_share_fini()
+ *
+ */
+static void
+smb_share_fini(void)
+{
+ xmlFreeNode(protoset);
+ protoset = NULL;
+}
+
+/*
+ * smb_get_proto_set()
+ *
+ * Return an optionset with all the protocol specific properties in
+ * it.
+ */
+static sa_protocol_properties_t
+smb_get_proto_set(void)
+{
+ return (protoset);
+}
+
+/*
+ * How long to wait for service to come online
+ */
+#define WAIT_FOR_SERVICE 15
+
+/*
+ * smb_enable_service()
+ *
+ */
+static int
+smb_enable_service(void)
+{
+ int i;
+ int ret = SA_OK;
+
+ if (!smb_isonline()) {
+ if (smf_enable_instance(SMBD_DEFAULT_INSTANCE_FMRI, 0) != 0) {
+ (void) fprintf(stderr,
+ dgettext(TEXT_DOMAIN,
+ "%s failed to restart: %s\n"),
+ scf_strerror(scf_error()));
+ return (SA_CONFIG_ERR);
+ }
+
+ /* Wait for service to come online */
+ for (i = 0; i < WAIT_FOR_SERVICE; i++) {
+ if (smb_isonline()) {
+ ret = SA_OK;
+ break;
+ } else {
+ ret = SA_BUSY;
+ (void) sleep(1);
+ }
+ }
+ }
+ return (ret);
+}
+
+/*
+ * smb_validate_proto_prop(index, name, value)
+ *
+ * Verify that the property specified by name can take the new
+ * value. This is a sanity check to prevent bad values getting into
+ * the default files.
+ */
+static int
+smb_validate_proto_prop(int index, char *name, char *value)
+{
+ if ((name == NULL) || (index < 0))
+ return (SA_BAD_VALUE);
+
+ if (smb_proto_options[index].validator == NULL)
+ return (SA_OK);
+
+ if (smb_proto_options[index].validator(index, value) == SA_OK)
+ return (SA_OK);
+ return (SA_BAD_VALUE);
+}
+
+/*
+ * smb_set_proto_prop(prop)
+ *
+ * check that prop is valid.
+ */
+/*ARGSUSED*/
+static int
+smb_set_proto_prop(sa_property_t prop)
+{
+ int ret = SA_OK;
+ char *name;
+ char *value;
+ int index = -1;
+
+ name = sa_get_property_attr(prop, "type");
+ value = sa_get_property_attr(prop, "value");
+ if (name != NULL && value != NULL) {
+ index = findprotoopt(name);
+ if (index >= 0) {
+ /* should test for valid value */
+ ret = smb_validate_proto_prop(index, name, value);
+ if (ret == SA_OK) {
+ /* Save to SMF */
+ smb_config_setenv(
+ smb_proto_options[index].smb_index, value);
+ /*
+ * Specialized refresh mechanisms can
+ * be flagged in the proto_options and
+ * processed here.
+ */
+ if (smb_proto_options[index].refresh &
+ SMB_REFRESH_REFRESH)
+ (void) smf_refresh_instance(
+ SMBD_DEFAULT_INSTANCE_FMRI);
+ else if (smb_proto_options[index].refresh &
+ SMB_REFRESH_RESTART)
+ (void) smf_restart_instance(
+ SMBD_DEFAULT_INSTANCE_FMRI);
+ }
+ }
+ }
+ if (name != NULL)
+ sa_free_attr_string(name);
+ if (value != NULL)
+ sa_free_attr_string(value);
+
+ return (ret);
+}
+
+/*
+ * smb_get_status()
+ *
+ * What is the current status of the smbd? We use the SMF state here.
+ * Caller must free the returned value.
+ */
+
+static char *
+smb_get_status(void)
+{
+ char *state = NULL;
+ state = smf_get_state(SMBD_DEFAULT_INSTANCE_FMRI);
+ return (state != NULL ? state : "-");
+}
+
+/*
+ * This protocol plugin require resource names
+ */
+static uint64_t
+smb_share_features(void)
+{
+ return (SA_FEATURE_RESOURCE | SA_FEATURE_ALLOWSUBDIRS |
+ SA_FEATURE_ALLOWPARDIRS);
+}
+
+/*
+ * This should be used to convert lmshare_info to sa_resource_t
+ * Should only be needed to build temp shares/resources to be
+ * supplied to sharemanager to display temp shares.
+ */
+static int
+smb_build_tmp_sa_resource(sa_handle_t handle, lmshare_info_t *si)
+{
+ int err;
+ sa_share_t share;
+ sa_group_t group;
+ sa_resource_t resource;
+
+ if (si == NULL)
+ return (SA_INVALID_NAME);
+
+ /*
+ * First determine if the "share path" is already shared
+ * somewhere. If it is, we have to use it as the authority on
+ * where the transient share lives so will use it's parent
+ * group. If it doesn't exist, it needs to land in "smb".
+ */
+
+ share = sa_find_share(handle, si->directory);
+ if (share != NULL) {
+ group = sa_get_parent_group(share);
+ } else {
+ group = smb_get_smb_share_group(handle);
+ if (group == NULL)
+ return (SA_NO_SUCH_GROUP);
+ share = sa_get_share(group, si->directory);
+ if (share == NULL) {
+ share = sa_add_share(group, si->directory,
+ SA_SHARE_TRANSIENT, &err);
+ if (share == NULL)
+ return (SA_NO_SUCH_PATH);
+ }
+ }
+
+ /*
+ * Now handle the resource. Make sure that the resource is
+ * transient and added to the share.
+ */
+ resource = sa_get_share_resource(share, si->share_name);
+ if (resource == NULL) {
+ resource = sa_add_resource(share,
+ si->share_name, SA_SHARE_TRANSIENT, &err);
+ if (resource == NULL)
+ return (SA_NO_SUCH_RESOURCE);
+ }
+
+ /* set resource attributes now */
+ (void) sa_set_resource_attr(resource, "description", si->comment);
+ (void) sa_set_resource_attr(resource, SHOPT_AD_CONTAINER,
+ si->container);
+
+ return (SA_OK);
+}
+
+/*
+ * Return smb transient shares. Note that we really want to look at
+ * all current shares from SMB in order to determine this. Transient
+ * shares should be those that don't appear in either the SMF or ZFS
+ * configurations. Those that are in the repositories will be
+ * filtered out by smb_build_tmp_sa_resource.
+ */
+static int
+smb_list_transient(sa_handle_t handle)
+{
+ int i, offset, num;
+ lmshare_list_t list;
+ int res;
+
+ num = lmshrd_num_shares();
+ if (num <= 0)
+ return (SA_OK);
+ offset = 0;
+ while (lmshrd_list(offset, &list) != NERR_InternalError) {
+ if (list.no == 0)
+ break;
+ for (i = 0; i < list.no; i++) {
+ res = smb_build_tmp_sa_resource(handle,
+ &(list.smbshr[i]));
+ if (res != SA_OK)
+ return (res);
+ }
+ offset += list.no;
+ }
+
+ return (SA_OK);
+}
+
+/*
+ * fix_resource_name(share, name, prefix)
+ *
+ * Construct a name where the ZFS dataset has the prefix replaced with "name".
+ */
+static char *
+fix_resource_name(sa_share_t share, char *name, char *prefix)
+{
+ char *dataset = NULL;
+ char *newname = NULL;
+ size_t psize;
+ size_t nsize;
+
+ dataset = sa_get_share_attr(share, "dataset");
+
+ if (dataset != NULL && strcmp(dataset, prefix) != 0) {
+ psize = strlen(prefix);
+ if (strncmp(dataset, prefix, psize) == 0) {
+ /* need string plus ',' and NULL */
+ nsize = (strlen(dataset) - psize) + strlen(name) + 2;
+ newname = calloc(nsize, 1);
+ if (newname != NULL) {
+ (void) snprintf(newname, nsize, "%s%s", name,
+ dataset + psize);
+ sa_fix_resource_name(newname);
+ }
+ sa_free_attr_string(dataset);
+ return (newname);
+ }
+ }
+ if (dataset != NULL)
+ sa_free_attr_string(dataset);
+ return (strdup(name));
+}
+
+/*
+ * smb_parse_optstring(group, options)
+ *
+ * parse a compact option string into individual options. This allows
+ * ZFS sharesmb and sharemgr "share" command to work. group can be a
+ * group, a share or a resource.
+ */
+static int
+smb_parse_optstring(sa_group_t group, char *options)
+{
+ char *dup;
+ char *base;
+ char *lasts;
+ char *token;
+ sa_optionset_t optionset;
+ sa_group_t parent = NULL;
+ sa_resource_t resource = NULL;
+ int iszfs = 0;
+ int persist = 0;
+ int need_optionset = 0;
+ int ret = SA_OK;
+ sa_property_t prop;
+
+ /*
+ * In order to not attempt to change ZFS properties unless
+ * absolutely necessary, we never do it in the legacy parsing
+ * so we need to keep track of this.
+ */
+ if (sa_is_share(group)) {
+ char *zfs;
+
+ parent = sa_get_parent_group(group);
+ if (parent != NULL) {
+ zfs = sa_get_group_attr(parent, "zfs");
+ if (zfs != NULL) {
+ sa_free_attr_string(zfs);
+ iszfs = 1;
+ }
+ }
+ } else {
+ iszfs = sa_group_is_zfs(group);
+ /*
+ * If a ZFS group, then we need to see if a resource
+ * name is being set. If so, bail with
+ * SA_PROP_SHARE_ONLY, so we come back in with a share
+ * instead of a group.
+ */
+ if (strncmp(options, "name=", sizeof ("name=") - 1) == 0 ||
+ strstr(options, ",name=") != NULL) {
+ return (SA_PROP_SHARE_ONLY);
+ }
+ }
+
+ /* do we have an existing optionset? */
+ optionset = sa_get_optionset(group, "smb");
+ if (optionset == NULL) {
+ /* didn't find existing optionset so create one */
+ optionset = sa_create_optionset(group, "smb");
+ if (optionset == NULL)
+ return (SA_NO_MEMORY);
+ } else {
+ /*
+ * If an optionset already exists, we've come through
+ * twice so ignore the second time.
+ */
+ return (ret);
+ }
+
+ /* We need a copy of options for the next part. */
+ dup = strdup(options);
+ if (dup == NULL)
+ return (SA_NO_MEMORY);
+
+ /*
+ * SMB properties are straightforward and are strings,
+ * integers or booleans. Properties are separated by
+ * commas. It will be necessary to parse quotes due to some
+ * strings not having a restricted characters set.
+ *
+ * Note that names will create a resource. For now, if there
+ * is a set of properties "before" the first name="", those
+ * properties will be placed on the group.
+ */
+ persist = sa_is_persistent(group);
+ base = dup;
+ token = dup;
+ lasts = NULL;
+ while (token != NULL && ret == SA_OK) {
+ ret = SA_OK;
+ token = strtok_r(base, ",", &lasts);
+ base = NULL;
+ if (token != NULL) {
+ char *value;
+ /*
+ * All SMB properties have values so there
+ * MUST be an '=' character. If it doesn't,
+ * it is a syntax error.
+ */
+ value = strchr(token, '=');
+ if (value != NULL) {
+ *value++ = '\0';
+ } else {
+ ret = SA_SYNTAX_ERR;
+ break;
+ }
+ /*
+ * We may need to handle a "name" property
+ * that is a ZFS imposed resource name. Each
+ * name would trigger getting a new "resource"
+ * to put properties on. For now, assume no
+ * "name" property for special handling.
+ */
+
+ if (strcmp(token, "name") == 0) {
+ char *prefix;
+ char *name = NULL;
+ /*
+ * We have a name, so now work on the
+ * resource level. We have a "share"
+ * in "group" due to the caller having
+ * added it. If we are called with a
+ * group, the check for group/share
+ * at the beginning of this function
+ * will bail out the parse if there is a
+ * "name" but no share.
+ */
+ if (!iszfs) {
+ ret = SA_SYNTAX_ERR;
+ break;
+ }
+ /*
+ * Make sure the parent group has the
+ * "prefix" property since we will
+ * need to use this for constructing
+ * inherited name= values.
+ */
+ prefix = sa_get_group_attr(parent, "prefix");
+ if (prefix == NULL) {
+ prefix = sa_get_group_attr(parent,
+ "name");
+ if (prefix != NULL) {
+ (void) sa_set_group_attr(parent,
+ "prefix", prefix);
+ }
+ }
+ name = fix_resource_name((sa_share_t)group,
+ value, prefix);
+ if (name != NULL) {
+ resource = sa_add_resource(
+ (sa_share_t)group, name,
+ SA_SHARE_TRANSIENT, &ret);
+ sa_free_attr_string(name);
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ if (prefix != NULL)
+ sa_free_attr_string(prefix);
+
+ /* A resource level optionset is needed */
+
+ need_optionset = 1;
+ if (resource == NULL) {
+ ret = SA_NO_MEMORY;
+ break;
+ }
+ continue;
+ }
+
+ if (need_optionset) {
+ optionset = sa_create_optionset(resource,
+ "smb");
+ need_optionset = 0;
+ }
+
+ prop = sa_create_property(token, value);
+ if (prop == NULL)
+ ret = SA_NO_MEMORY;
+ else
+ ret = sa_add_property(optionset, prop);
+ if (ret != SA_OK)
+ break;
+ if (!iszfs)
+ ret = sa_commit_properties(optionset, !persist);
+ }
+ }
+ free(dup);
+ return (ret);
+}
+
+/*
+ * smb_sprint_option(rbuff, rbuffsize, incr, prop, sep)
+ *
+ * provides a mechanism to format SMB properties into legacy output
+ * format. If the buffer would overflow, it is reallocated and grown
+ * as appropriate. Special cases of converting internal form of values
+ * to those used by "share" are done. this function does one property
+ * at a time.
+ */
+
+static void
+smb_sprint_option(char **rbuff, size_t *rbuffsize, size_t incr,
+ sa_property_t prop, int sep)
+{
+ char *name;
+ char *value;
+ int curlen;
+ char *buff = *rbuff;
+ size_t buffsize = *rbuffsize;
+
+ name = sa_get_property_attr(prop, "type");
+ value = sa_get_property_attr(prop, "value");
+ if (buff != NULL)
+ curlen = strlen(buff);
+ else
+ curlen = 0;
+ if (name != NULL) {
+ int len;
+ len = strlen(name) + sep;
+
+ /*
+ * A future RFE would be to replace this with more
+ * generic code and to possibly handle more types.
+ *
+ * For now, everything else is treated as a string. If
+ * we get any properties that aren't exactly
+ * name/value pairs, we may need to
+ * interpret/transform.
+ */
+ if (value != NULL)
+ len += 1 + strlen(value);
+
+ while (buffsize <= (curlen + len)) {
+ /* need more room */
+ buffsize += incr;
+ buff = realloc(buff, buffsize);
+ *rbuff = buff;
+ *rbuffsize = buffsize;
+ if (buff == NULL) {
+ /* realloc failed so free everything */
+ if (*rbuff != NULL)
+ free(*rbuff);
+ goto err;
+ }
+ }
+ if (buff == NULL)
+ goto err;
+ (void) snprintf(buff + curlen, buffsize - curlen,
+ "%s%s=%s", sep ? "," : "",
+ name, value != NULL ? value : "\"\"");
+
+ }
+err:
+ if (name != NULL)
+ sa_free_attr_string(name);
+ if (value != NULL)
+ sa_free_attr_string(value);
+}
+
+/*
+ * smb_format_resource_options(resource, hier)
+ *
+ * format all the options on the group into a flattened option
+ * string. If hier is non-zero, walk up the tree to get inherited
+ * options.
+ */
+
+static char *
+smb_format_options(sa_group_t group, int hier)
+{
+ sa_optionset_t options = NULL;
+ sa_property_t prop;
+ int sep = 0;
+ char *buff;
+ size_t buffsize;
+
+
+ buff = malloc(OPT_CHUNK);
+ if (buff == NULL)
+ return (NULL);
+
+ buff[0] = '\0';
+ buffsize = OPT_CHUNK;
+
+ /*
+ * We may have a an optionset relative to this item. format
+ * these if we find them and then add any security definitions.
+ */
+
+ options = sa_get_derived_optionset(group, "smb", hier);
+
+ /*
+ * do the default set first but skip any option that is also
+ * in the protocol specific optionset.
+ */
+ if (options != NULL) {
+ for (prop = sa_get_property(options, NULL);
+ prop != NULL; prop = sa_get_next_property(prop)) {
+ /*
+ * use this one since we skipped any
+ * of these that were also in
+ * optdefault
+ */
+ smb_sprint_option(&buff, &buffsize, OPT_CHUNK,
+ prop, sep);
+ if (buff == NULL) {
+ /*
+ * buff could become NULL if there
+ * isn't enough memory for
+ * smb_sprint_option to realloc()
+ * as necessary. We can't really
+ * do anything about it at this
+ * point so we return NULL. The
+ * caller should handle the
+ * failure.
+ */
+ if (options != NULL)
+ sa_free_derived_optionset(
+ options);
+ return (buff);
+ }
+ sep = 1;
+ }
+ }
+
+ if (options != NULL)
+ sa_free_derived_optionset(options);
+ return (buff);
+}
+
+/*
+ * smb_rename_resource(resource, newname)
+ *
+ * Change the current exported name of the resource to newname.
+ */
+/*ARGSUSED*/
+int
+smb_rename_resource(sa_handle_t handle, sa_resource_t resource, char *newname)
+{
+ int ret = SA_OK;
+ int err;
+ char *oldname;
+
+ oldname = sa_get_resource_attr(resource, "name");
+ if (oldname == NULL)
+ return (SA_NO_SUCH_RESOURCE);
+
+ err = lmshrd_rename(oldname, newname);
+
+ /* improve error values somewhat */
+ switch (err) {
+ case NERR_Success:
+ break;
+ case NERR_InternalError:
+ ret = SA_SYSTEM_ERR;
+ break;
+ case NERR_DuplicateShare:
+ ret = SA_DUPLICATE_NAME;
+ break;
+ default:
+ ret = SA_CONFIG_ERR;
+ break;
+ }
+
+ return (ret);
+}