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"),