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,42 ****
* $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.
*/
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/time.h>
--- 32,42 ----
* $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 2018 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/time.h>
*** 146,159 ****
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");
}
--- 146,155 ----
*** 167,176 ****
--- 163,174 ----
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,271 ****
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_owner = SMBM_ANY_OWNER;
ctx->ct_authflags = SMB_AT_DEFAULT;
ctx->ct_minauth = SMB_AT_MINAUTH;
/*
* Default domain, user, ...
*/
strlcpy(ctx->ct_domain, default_domain,
--- 250,269 ----
if (error)
return (error);
ctx->ct_dev_fd = -1;
ctx->ct_door_fd = -1;
ctx->ct_parsedlevel = SMBL_NONE;
ctx->ct_minlevel = SMBL_NONE;
ctx->ct_maxlevel = SMBL_PATH;
/* Fill in defaults */
! 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,341 ****
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);
if (opt == -1)
break;
arg = cf_optarg;
/* NB: handle most in smb_ctx_opt */
switch (opt) {
--- 329,344 ----
ctx->ct_shtype_req = sharetype;
cf_opt_lock();
/* Careful: no return/goto before cf_opt_unlock! */
while (error == 0) {
! /*
! * 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,416 ****
{
rpc_cleanup_smbctx(ctx);
if (ctx->ct_dev_fd != -1) {
! 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) {
--- 399,415 ----
{
rpc_cleanup_smbctx(ctx);
if (ctx->ct_dev_fd != -1) {
! 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_srvaddr_s) {
free(ctx->ct_srvaddr_s);
ctx->ct_srvaddr_s = NULL;
}
if (ctx->ct_nb) {
*** 439,460 ****
}
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_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]]"
--- 438,451 ----
}
if (ctx->ct_rpath) {
free(ctx->ct_rpath);
ctx->ct_rpath = NULL;
}
! if (ctx->ct_ssnkey_buf) {
! free(ctx->ct_ssnkey_buf);
! ctx->ct_ssnkey_buf = NULL;
}
}
/*
* Parse the UNC path. Here we expect something like
* "//[[domain;]user[:password]@]host[/share[/path]]"
*** 866,875 ****
--- 857,897 ----
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,912 ****
return (0);
}
/*
! * Suport a securty options arg, i.e. -S noext,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 },
--- 919,933 ----
return (0);
}
/*
! * Suport a securty options arg, i.e. -S lm,ntlm
* for testing various type of authenticators.
*/
static struct nv
sectype_table[] = {
{ "anon", SMB_AT_ANON },
{ "lm", SMB_AT_LM1 },
{ "ntlm", SMB_AT_NTLM1 },
{ "ntlm2", SMB_AT_NTLM2 },
{ "krb5", SMB_AT_KRB5 },
*** 928,944 ****
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;
--- 949,958 ----
*** 1115,1124 ****
--- 1129,1151 ----
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,1141 ****
/*
* 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);
/*
* 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);
}
--- 1152,1173 ----
/*
* Have a user name.
* If we don't have a p/w yet,
* try the keychain.
*/
! 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,1154 ****
--- 1177,1190 ----
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,1181 ****
return (fd);
}
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);
ctx->ct_dev_fd = -1;
- ctx->ct_flags &= ~SMBCF_SSNACTIVE;
}
fd = smb_open_driver();
if (fd < 0) {
err = errno;
--- 1198,1229 ----
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);
! nsmb_close(ctx->ct_dev_fd);
ctx->ct_dev_fd = -1;
}
fd = smb_open_driver();
if (fd < 0) {
err = errno;
*** 1185,1200 ****
}
/*
* Check the driver version (paranoia)
*/
! if (ioctl(fd, SMBIOC_GETVERS, &version) < 0)
version = 0;
if (version != NSMB_VERSION) {
smb_error(dgettext(TEXT_DOMAIN,
"incorrect driver version"), 0);
! close(fd);
return (ENODEV);
}
ctx->ct_dev_fd = fd;
return (0);
--- 1233,1248 ----
}
/*
* Check the driver version (paranoia)
*/
! if (nsmb_ioctl(fd, SMBIOC_GETVERS, &version) < 0)
version = 0;
if (version != NSMB_VERSION) {
smb_error(dgettext(TEXT_DOMAIN,
"incorrect driver version"), 0);
! nsmb_close(fd);
return (ENODEV);
}
ctx->ct_dev_fd = fd;
return (0);
*** 1210,1233 ****
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 {
/*
* This calls the IOD to create a new session.
*/
DPRINT("setup a new VC");
err = smb_ctx_newvc(ctx);
if (err != 0)
--- 1258,1285 ----
int err = 0;
if ((ctx->ct_flags & SMBCF_RESOLVED) == 0)
return (EINVAL);
/*
* 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,1285 ****
/*
* Todo: share passwords for share-level security.
*
* The driver does the actual TCON call.
*/
! if (ioctl(ctx->ct_dev_fd, cmd, tcon) == -1) {
err = errno;
goto out;
}
/*
--- 1327,1337 ----
/*
* Todo: share passwords for share-level security.
*
* The driver does the actual TCON call.
*/
! if (nsmb_ioctl(ctx->ct_dev_fd, cmd, tcon) == -1) {
err = errno;
goto out;
}
/*
*** 1306,1316 ****
int
smb_ctx_flags2(struct smb_ctx *ctx)
{
uint16_t flags2;
! if (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);
--- 1358,1368 ----
int
smb_ctx_flags2(struct smb_ctx *ctx)
{
uint16_t flags2;
! 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,1334 ****
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)
return (errno);
return (0);
}
--- 1376,1386 ----
smb_fh_getssnkey(int dev_fd, uchar_t *key, size_t len)
{
if (len < SMBIOC_HASH_SZ)
return (EINVAL);
! if (nsmb_ioctl(dev_fd, SMBIOC_GETSSNKEY, key) == -1)
return (errno);
return (0);
}
*** 1346,1356 ****
--- 1398,1437 ----
{ "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,1368 ****
--- 1439,1451 ----
*/
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,1398 ****
#endif
if (level <= 1) {
/* Section is: [default] or [server] */
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 {
/*
* Unknown minimum authentication level.
*/
smb_error(dgettext(TEXT_DOMAIN,
"invalid minimum authentication level \"%s\" specified in the section %s"),
--- 1459,1541 ----
#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.
*/
! 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"),