Print this page
NEX-14666 Need to provide SMB 2.1 Client
NEX-17187 panic in smbfs_acl_store
NEX-17231 smbfs create xattr files finds wrong file
NEX-17224 smbfs lookup EINVAL should be ENOENT
NEX-17260 SMB1 client fails to list directory after NEX-14666
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
and: (cleanup)
NEX-16824 SMB client connection setup rework
NEX-17232 SMB client reconnect failures
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
and: (improve debug)
NEX-16818 Add fksmbcl development tool
NEX-17264 SMB client test tp_smbutil_013 fails after NEX-14666
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
and: (fix ref leaks)
NEX-16805 Add smbutil discon command
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
re #12739 rb4173 AD join with lmauth_level=2 fails
re #12394 rb3934 Even NULL sessions should use SPNEGO
@@ -32,11 +32,11 @@
* $Id: ctx.c,v 1.32.70.2 2005/06/02 00:55:40 lindak Exp $
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/time.h>
@@ -146,14 +146,10 @@
printf("BROWSEOK ");
if (flags & SMBCF_AUTHREQ)
printf("AUTHREQ ");
if (flags & SMBCF_KCSAVE)
printf("KCSAVE ");
- if (flags & SMBCF_XXX)
- printf("XXX ");
- if (flags & SMBCF_SSNACTIVE)
- printf("SSNACTIVE ");
if (flags & SMBCF_KCDOMAIN)
printf("KCDOMAIN ");
printf("\n");
}
@@ -167,10 +163,12 @@
dump_sockaddr(&ssn->ssn_srvaddr.sa);
printf(" dom=\"%s\", user=\"%s\"\n",
ssn->ssn_domain, ssn->ssn_user);
printf(" ct_vopt=0x%x, ct_owner=%d\n",
ssn->ssn_vopt, ssn->ssn_owner);
+ printf(" ct_minver=0x%x, ct_maxver=0x%x\n",
+ ssn->ssn_minver, ssn->ssn_maxver);
printf(" ct_authflags=0x%x\n", is->iod_authflags);
printf(" ct_nthash:");
if (bcmp(zeros, &is->iod_nthash, NTLM_HASH_SZ))
smb_hexdump(&is->iod_nthash, NTLM_HASH_SZ);
@@ -252,20 +250,20 @@
if (error)
return (error);
ctx->ct_dev_fd = -1;
ctx->ct_door_fd = -1;
- ctx->ct_tran_fd = -1;
ctx->ct_parsedlevel = SMBL_NONE;
ctx->ct_minlevel = SMBL_NONE;
ctx->ct_maxlevel = SMBL_PATH;
/* Fill in defaults */
- ctx->ct_vopt = SMBVOPT_EXT_SEC;
+ ctx->ct_vopt = SMBVOPT_SIGNING_ENABLED;
ctx->ct_owner = SMBM_ANY_OWNER;
ctx->ct_authflags = SMB_AT_DEFAULT;
ctx->ct_minauth = SMB_AT_MINAUTH;
+ ctx->ct_maxver = SMB2_DIALECT_MAX;
/*
* Default domain, user, ...
*/
strlcpy(ctx->ct_domain, default_domain,
@@ -331,11 +329,16 @@
ctx->ct_shtype_req = sharetype;
cf_opt_lock();
/* Careful: no return/goto before cf_opt_unlock! */
while (error == 0) {
- opt = cf_getopt(argc, argv, STDPARAM_OPT);
+ /*
+ * Leading ':' tells this to skip unknown opts.
+ * Just get -A and -U here so we know the user
+ * for config file parsing.
+ */
+ opt = cf_getopt(argc, argv, ":AU:");
if (opt == -1)
break;
arg = cf_optarg;
/* NB: handle most in smb_ctx_opt */
switch (opt) {
@@ -396,21 +399,17 @@
{
rpc_cleanup_smbctx(ctx);
if (ctx->ct_dev_fd != -1) {
- close(ctx->ct_dev_fd);
+ nsmb_close(ctx->ct_dev_fd);
ctx->ct_dev_fd = -1;
}
if (ctx->ct_door_fd != -1) {
close(ctx->ct_door_fd);
ctx->ct_door_fd = -1;
}
- if (ctx->ct_tran_fd != -1) {
- close(ctx->ct_tran_fd);
- ctx->ct_tran_fd = -1;
- }
if (ctx->ct_srvaddr_s) {
free(ctx->ct_srvaddr_s);
ctx->ct_srvaddr_s = NULL;
}
if (ctx->ct_nb) {
@@ -439,22 +438,14 @@
}
if (ctx->ct_rpath) {
free(ctx->ct_rpath);
ctx->ct_rpath = NULL;
}
- if (ctx->ct_srv_OS) {
- free(ctx->ct_srv_OS);
- ctx->ct_srv_OS = NULL;
+ if (ctx->ct_ssnkey_buf) {
+ free(ctx->ct_ssnkey_buf);
+ ctx->ct_ssnkey_buf = NULL;
}
- if (ctx->ct_srv_LM) {
- free(ctx->ct_srv_LM);
- ctx->ct_srv_LM = NULL;
- }
- if (ctx->ct_mackey) {
- free(ctx->ct_mackey);
- ctx->ct_mackey = NULL;
- }
}
/*
* Parse the UNC path. Here we expect something like
* "//[[domain;]user[:password]@]host[/share[/path]]"
@@ -866,10 +857,41 @@
ctx->ct_vopt |= SMBVOPT_SIGNING_REQUIRED;
}
return (0);
}
+/*
+ * Handle .nsmbrc "minver" option.
+ * Must be <= maxver
+ */
+int
+smb_ctx_setminver(struct smb_ctx *ctx, int ver)
+{
+ if (ver < 0 || ver > ctx->ct_maxver)
+ return (EINVAL);
+ ctx->ct_minver = (uint16_t)ver;
+ return (0);
+}
+
+/*
+ * Handle .nsmbrc "maxver" option.
+ * Must be >= minver
+ *
+ * Any "too high" value is just clamped, so the caller
+ * doesn't need to know what's the highest we support.
+ */
+int
+smb_ctx_setmaxver(struct smb_ctx *ctx, int ver)
+{
+ if (ver < 1 || ver < ctx->ct_minver)
+ return (EINVAL);
+ if (ver > SMB2_DIALECT_MAX)
+ ver = SMB2_DIALECT_MAX;
+ ctx->ct_maxver = (uint16_t)ver;
+ return (0);
+}
+
static int
smb_parse_owner(char *pair, uid_t *uid, gid_t *gid)
{
struct group gr;
struct passwd pw;
@@ -897,16 +919,15 @@
return (0);
}
/*
- * Suport a securty options arg, i.e. -S noext,lm,ntlm
+ * Suport a securty options arg, i.e. -S lm,ntlm
* for testing various type of authenticators.
*/
static struct nv
sectype_table[] = {
- /* noext - handled below */
{ "anon", SMB_AT_ANON },
{ "lm", SMB_AT_LM1 },
{ "ntlm", SMB_AT_NTLM1 },
{ "ntlm2", SMB_AT_NTLM2 },
{ "krb5", SMB_AT_KRB5 },
@@ -928,17 +949,10 @@
nlen = strcspn(p, sep);
if (nlen == 0)
break;
- if (nlen == 5 && 0 == strncmp(p, "noext", nlen)) {
- /* Don't offer extended security. */
- ctx->ct_vopt &= ~SMBVOPT_EXT_SEC;
- p += nlen;
- continue;
- }
-
/* This is rarely called, so not optimized. */
for (nv = sectype_table; nv->name; nv++) {
tlen = strlen(nv->name);
if (tlen == nlen && 0 == strncmp(p, nv->name, tlen))
break;
@@ -1115,10 +1129,23 @@
return (ENODATA);
}
assert(ctx->ct_addrinfo != NULL);
/*
+ * Empty user name means an explicit request for
+ * NULL session setup, which is a special case.
+ * (No SMB signing, per [MS-SMB] 3.3.5.3)
+ */
+ if (ctx->ct_user[0] == '\0') {
+ /* Null user should have null domain too. */
+ ctx->ct_domain[0] = '\0';
+ ctx->ct_authflags = SMB_AT_ANON;
+ ctx->ct_vopt |= SMBVOPT_ANONYMOUS;
+ ctx->ct_vopt &= ~SMBVOPT_SIGNING_REQUIRED;
+ }
+
+ /*
* If we have a user name but no password,
* check for a keychain entry.
* XXX: Only for auth NTLM?
*/
if (ctx->ct_user[0] != '\0') {
@@ -1125,17 +1152,22 @@
/*
* Have a user name.
* If we don't have a p/w yet,
* try the keychain.
*/
- if (ctx->ct_password[0] == '\0')
- (void) smb_get_keychain(ctx);
+ if (ctx->ct_password[0] == '\0' &&
+ smb_get_keychain(ctx) == 0) {
+ strlcpy(ctx->ct_password, "$HASH",
+ sizeof (ctx->ct_password));
+ }
+
/*
* Mask out disallowed auth types.
*/
ctx->ct_authflags &= ctx->ct_minauth;
}
+
if (ctx->ct_authflags == 0) {
smb_error(dgettext(TEXT_DOMAIN,
"no valid auth. types"), 0);
return (ENOTSUP);
}
@@ -1145,10 +1177,14 @@
dump_ctx("after smb_ctx_resolve", ctx);
return (0);
}
+/*
+ * Note: The next three have NODIRECT binding so the
+ * "fksmbcl" development tool can provide its own.
+ */
int
smb_open_driver()
{
int fd;
@@ -1162,20 +1198,32 @@
return (fd);
}
int
+nsmb_close(int fd)
+{
+ return (close(fd));
+}
+
+int
+nsmb_ioctl(int fd, int cmd, void *arg)
+{
+ return (ioctl(fd, cmd, arg));
+}
+
+
+int
smb_ctx_gethandle(struct smb_ctx *ctx)
{
int fd, err;
uint32_t version;
if (ctx->ct_dev_fd != -1) {
rpc_cleanup_smbctx(ctx);
- close(ctx->ct_dev_fd);
+ nsmb_close(ctx->ct_dev_fd);
ctx->ct_dev_fd = -1;
- ctx->ct_flags &= ~SMBCF_SSNACTIVE;
}
fd = smb_open_driver();
if (fd < 0) {
err = errno;
@@ -1185,16 +1233,16 @@
}
/*
* Check the driver version (paranoia)
*/
- if (ioctl(fd, SMBIOC_GETVERS, &version) < 0)
+ if (nsmb_ioctl(fd, SMBIOC_GETVERS, &version) < 0)
version = 0;
if (version != NSMB_VERSION) {
smb_error(dgettext(TEXT_DOMAIN,
"incorrect driver version"), 0);
- close(fd);
+ nsmb_close(fd);
return (ENODEV);
}
ctx->ct_dev_fd = fd;
return (0);
@@ -1210,24 +1258,28 @@
int err = 0;
if ((ctx->ct_flags & SMBCF_RESOLVED) == 0)
return (EINVAL);
- if (ctx->ct_dev_fd < 0) {
- if ((err = smb_ctx_gethandle(ctx)))
- return (err);
- }
-
/*
* Check whether the driver already has a VC
* we can use. If so, we're done!
*/
err = smb_ctx_findvc(ctx);
if (err == 0) {
DPRINT("found an existing VC");
} else {
/*
+ * If we're authenticating (real user, not NULL session)
+ * and we don't yet have a password, return EAUTH and
+ * the caller will prompt for it and call again.
+ */
+ if (ctx->ct_user[0] != '\0' &&
+ ctx->ct_password[0] == '\0')
+ return (EAUTH);
+
+ /*
* This calls the IOD to create a new session.
*/
DPRINT("setup a new VC");
err = smb_ctx_newvc(ctx);
if (err != 0)
@@ -1275,11 +1327,11 @@
/*
* Todo: share passwords for share-level security.
*
* The driver does the actual TCON call.
*/
- if (ioctl(ctx->ct_dev_fd, cmd, tcon) == -1) {
+ if (nsmb_ioctl(ctx->ct_dev_fd, cmd, tcon) == -1) {
err = errno;
goto out;
}
/*
@@ -1306,11 +1358,11 @@
int
smb_ctx_flags2(struct smb_ctx *ctx)
{
uint16_t flags2;
- if (ioctl(ctx->ct_dev_fd, SMBIOC_FLAGS2, &flags2) == -1) {
+ if (nsmb_ioctl(ctx->ct_dev_fd, SMBIOC_FLAGS2, &flags2) == -1) {
smb_error(dgettext(TEXT_DOMAIN,
"can't get flags2 for a session"), errno);
return (-1);
}
return (flags2);
@@ -1324,11 +1376,11 @@
smb_fh_getssnkey(int dev_fd, uchar_t *key, size_t len)
{
if (len < SMBIOC_HASH_SZ)
return (EINVAL);
- if (ioctl(dev_fd, SMBIOC_GETSSNKEY, key) == -1)
+ if (nsmb_ioctl(dev_fd, SMBIOC_GETSSNKEY, key) == -1)
return (errno);
return (0);
}
@@ -1346,11 +1398,40 @@
{ "none", SMB_AT_KRB5|SMB_AT_NTLM2|SMB_AT_NTLM1|SMB_AT_LM1|
SMB_AT_ANON },
{ NULL }
};
+int
+smb_cf_minauth_from_str(char *str)
+{
+ struct nv *nvp;
+ for (nvp = minauth_table; nvp->name; nvp++)
+ if (strcmp(nvp->name, str) == 0)
+ return (nvp->value);
+ return (-1);
+}
+
+
+static struct nv
+smbver_table[] = {
+ { "2.1", SMB2_DIALECT_0210 },
+ { "1", 1 },
+ { NULL, 0 }
+};
+
+int
+smb_cf_version_from_str(char *str)
+{
+ struct nv *nvp;
+
+ for (nvp = smbver_table; nvp->name; nvp++)
+ if (strcmp(nvp->name, str) == 0)
+ return (nvp->value);
+ return (-1);
+}
+
/*
* level values:
* 0 - default
* 1 - server
* 2 - server:user
@@ -1358,11 +1439,13 @@
*/
static int
smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level)
{
char *p;
+ int ival;
int error;
+ int minver, maxver;
#ifdef KICONV_SUPPORT
if (level > 0) {
rc_getstringptr(smb_rc, sname, "charsets", &p);
if (p) {
@@ -1376,23 +1459,83 @@
#endif
if (level <= 1) {
/* Section is: [default] or [server] */
+ /*
+ * Handle min_protocol, max_protocol
+ * (SMB protocol versions)
+ */
+ minver = -1;
+ rc_getstringptr(smb_rc, sname, "min_protocol", &p);
+ if (p != NULL) {
+ minver = smb_cf_version_from_str(p);
+ if (minver == -1) {
+ smb_error(dgettext(TEXT_DOMAIN,
+"invalid min_protocol value \"%s\" specified in the section %s"),
+ 0, p, sname);
+ }
+ }
+ maxver = -1;
+ rc_getstringptr(smb_rc, sname, "max_protocol", &p);
+ if (p != NULL) {
+ maxver = smb_cf_version_from_str(p);
+ if (maxver == -1) {
+ smb_error(dgettext(TEXT_DOMAIN,
+"invalid max_protocol value \"%s\" specified in the section %s"),
+ 0, p, sname);
+ }
+ }
+
+ /*
+ * If setting both min/max protocol,
+ * validate against each other
+ */
+ if (minver != -1 && maxver != -1) {
+ if (minver > maxver) {
+ smb_error(dgettext(TEXT_DOMAIN,
+"invalid min/max protocol combination in the section %s"),
+ 0, sname);
+ } else {
+ ctx->ct_minver = minver;
+ ctx->ct_maxver = maxver;
+ }
+ }
+
+ /*
+ * Setting just min or max, validate against
+ * current settings
+ */
+ if (minver != -1) {
+ if (minver > ctx->ct_maxver) {
+ smb_error(dgettext(TEXT_DOMAIN,
+"invalid min/max protocol combination in the section %s"),
+ 0, sname);
+ } else {
+ ctx->ct_minver = minver;
+ }
+ }
+ if (maxver != -1) {
+ if (maxver < ctx->ct_minver) {
+ smb_error(dgettext(TEXT_DOMAIN,
+"invalid min/max protocol combination in the section %s"),
+ 0, sname);
+ } else {
+ ctx->ct_maxver = maxver;
+ }
+ }
+
rc_getstringptr(smb_rc, sname, "minauth", &p);
if (p) {
/*
* "minauth" was set in this section; override
* the current minimum authentication setting.
*/
- struct nv *nvp;
- for (nvp = minauth_table; nvp->name; nvp++)
- if (strcmp(p, nvp->name) == 0)
- break;
- if (nvp->name)
- ctx->ct_minauth = nvp->value;
- else {
+ ival = smb_cf_minauth_from_str(p);
+ if (ival != -1) {
+ ctx->ct_minauth = ival;
+ } else {
/*
* Unknown minimum authentication level.
*/
smb_error(dgettext(TEXT_DOMAIN,
"invalid minimum authentication level \"%s\" specified in the section %s"),