smb_smb.c revision 4bff34e37def8a90f9194d81bc345c52ba20086a
/*
* Copyright (c) 2000-2001 Boris Popov
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Boris Popov.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: smb_smb.c,v 1.35.100.2 2005/06/02 00:55:39 lindak Exp $
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* various SMB requests. Most of the routines merely packs data into mbufs.
*/
#ifdef APPLE
#include <sys/smb_apple.h>
#else
#include <netsmb/smb_osdep.h>
#endif
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_tran.h>
/*
* Largest size to use with LARGE_READ/LARGE_WRITE.
* Specs say up to 64k data bytes, but Windows traffic
* uses 60k... no doubt for some good reason.
* (Probably to keep 4k block alignment.)
* XXX: Move to smb.h maybe?
*/
/*
* Default timeout values, all in seconds.
* Make these tunable (only via mdb for now).
*/
int smb_timo_notice = 15;
int smb_timo_open = 45;
int smb_timo_read = 45;
int smb_timo_append = 90;
struct smb_dialect {
int d_id;
const char *d_name;
};
smb_unichar smb_unieol = 0;
static struct smb_dialect smb_dialects[] = {
{SMB_DIALECT_CORE, "PC NETWORK PROGRAM 1.0"},
{SMB_DIALECT_LANMAN1_0, "LANMAN1.0"},
{SMB_DIALECT_LANMAN2_0, "LM1.2X002"},
{SMB_DIALECT_LANMAN2_1, "LANMAN2.1"},
{SMB_DIALECT_NTLM0_12, "NT LM 0.12"},
{-1, NULL}
};
#define SMB_DIALECT_MAX \
/*
* Number of seconds between 1970 and 1601 year
*/
void
{
/*
* XXX - what if we connected to the server when it was in
* to standard time, or vice versa, so that the time zone
* offset we got from the server is now wrong?
*/
/* - tz.tz_minuteswest * 60 - (wall_cmos_clock ? adjkerntz : 0) */
}
void
{
/*
* XXX - what if we connected to the server when it was in
* to standard time, or vice versa, so that the time zone
* offset we got from the server is now wrong?
*/
/* + tz.tz_minuteswest * 60 + (wall_cmos_clock ? adjkerntz : 0); */
}
/*
* Time from server comes as UTC, so no need to use tz
*/
/*ARGSUSED*/
void
{
}
/*ARGSUSED*/
void
{
long seconds;
(u_int64_t)10000000;
}
#if defined(NOICONVSUPPORT) || defined(lint)
extern int iconv_close(void *handle);
#endif
int
{
struct smb_dialect *dp;
int error;
int unicode = 0;
char *servercs;
void *servercshandle = NULL;
void *localcshandle = NULL;
/*
* Make sure SMB_FLAGS2_UNICODE is "off" so mb_put_dstring
* marshalls the dialect strings in plain ascii.
*/
if (error)
return (error);
}
/*
* This request should not wait for
* connection state changes, etc.
*/
if (error)
goto bad;
do {
if (error)
break;
if (error)
break;
if (dindex > SMB_DIALECT_MAX) {
"Don't know how to talk with server %s (%d)\n",
break;
}
if (dindex < SMB_DIALECT_MAX) {
"Server %s negotiated old dialect (%s)\n",
}
if (wc != 17)
break;
if (error)
break;
SMBERROR("server configuration requires "
"packet signing, which we dont support: "
unicode = 1;
}
/*
* They don't do NT error codes.
*
* If we send requests with
* SMB_FLAGS2_ERR_STATUS set in
* Flags2, Windows 98, at least,
* appears to send replies with that
* bit set even though it sends back
* DOS error codes. (They probably
* just use the request header as
* a template for the reply header,
* and don't bother clearing that bit.)
*
* Therefore, we clear that bit in
* our vc_hflags2 field.
*/
}
SMBSDEBUG("Win95 detected\n");
}
/*
* 3 cases here:
*
* 1) Extended security.
* Read bc bytes below for security blob.
* Note that we DON'T put the Caps flag in outtok.
* outtoklen = bc
*
* 2) No extended security, have challenge data and
* possibly a domain name (which might be zero
* bytes long, meaning "missing").
* Copy challenge stuff to vcp->vc_ch (sblen bytes),
* then copy Cap flags and domain name (bc-sblen
* bytes) to outtok.
* outtoklen = bc-sblen+4, where the 4 is for the
* Caps flag.
*
* 3) No extended security, no challenge data, just
* possibly a domain name.
* Copy Capsflags and domain name (bc) to outtok.
* outtoklen = bc+4, where 4 is for the Caps flag
*/
/*
* Sanity check: make sure the challenge length
* isn't bigger than the byte count.
*/
break;
}
(char *)vcp->vc_challenge,
sblen, MB_MSYSTEM);
if (error)
break;
}
/*
* For servers that don't support unicode
* there are 2 things we could do:
* 1) Pass the server Caps flags up to the
* user level so the logic up there will
* know whether the domain name is unicode
* (this is what I did).
* 2) Try to convert the non-unicode string
* to unicode. This doubles the length of
* the outtok buffer and would be guessing that
* the string was single-byte ascii, and that
* might be wrong. Why ask for trouble?
*/
/* Warning: NetApp may omit the GUID */
/*
* No extended security.
* Stick domain name, if present,
* and caps in outtok.
*/
/* first store server capability bits */
/*LINTED*/
/*LINTED*/
/*
* Then store the domain name if present;
* be sure to subtract 4 from the length
* for the Caps flag.
*/
if (toklen > 4) {
}
} else {
/*
* Extended security.
* Stick the rest of the buffer in outtok.
*/
}
break;
}
if (swlen > SMB_MAXCHALLENGELEN)
break;
break;
break;
(char *)vcp->vc_challenge,
swlen, MB_MSYSTEM);
if (error)
break;
}
}
} else { /* an old CORE protocol */
}
error = 0;
/*LINTED*/
} while (0);
if (error == 0) {
uint32_t x;
/*
* Maximum outstanding requests.
*/
/*
* Max VCs between server and client.
* We only use one.
*/
/*
* Maximum transfer size.
* Sanity checks:
*
* Spec. says lower limit is 1024. OK.
*
* Let's be conservative about an upper limit here.
* Win2k uses 16644 (and others) so 32k should be a
* reasonable sanity limit for this value.
*
* with CAP_LARGE_xxx, which we nearly always use.
*/
/*
* This is just the payload size, so we must
* leave room for the SMB headers, etc.
*
* With CAP_LARGE_xxx, always use 60k.
* Otherwise use the vc_txmax value, but
* reduced and rounded down. Tricky bit:
*
* Servers typically give us a value that's
* some nice "round" number, i.e 0x4000 plus
* some overhead, i.e. Win2k: 16644==0x4104
* Subtract for the SMB header (32) and the
* SMB command word and byte vectors (34?),
* then round down to a 512 byte multiple.
*/
else
else
}
/*
* If the server supports Unicode, set up to use Unicode
* when talking to them. Othewise, use code page 437.
*/
if (unicode)
servercs = "ucs-2";
else {
/*
* todo: if we can't determine the server's encoding, we
* need to try a best-guess here.
*/
servercs = "cp437";
}
#if defined(NOICONVSUPPORT) || defined(lint)
/*
* REVISIT
*/
if (error != 0)
goto bad;
if (error != 0) {
goto bad;
}
if (vcp->vc_toserver)
if (vcp->vc_tolocal)
#endif
if (unicode)
bad:
return (error);
}
static void
{
if (upper)
else
}
#ifdef APPLE
static void
{
}
#endif
/*ARGSUSED*/
static uchar_t *
{
struct ntlmv2_namehdr namehdr;
char *namebuf;
if (uppercase) {
} else {
}
} else {
uninamelen = 0;
uninamebuf = NULL;
}
if (uninamebuf != NULL) {
blobnames += uninamelen;
}
return (blobnames);
}
static uchar_t *
{
struct ntlmv2_blobhdr *blobhdr;
/*
* XXX - the information at
*
* http://davenport.sourceforge.net/ntlm.html#theNtlmv2Response
*
* says that the "target information" comes from the Type 2 message,
* but, as we're not doing NTLMSSP, we don't have that.
*
* Should we use the names from the NegProt response? Can we trust
* the NegProt response? (I've seen captures where the primary
* domain name has an extra byte in front of it.)
*
* For now, we don't trust it - we use vcp->vc_domain and
* vcp->vc_srvname, instead. We upper-case them and convert
* them to Unicode, as that's what's supposed to be in the blob.
*/
blobsize = sizeof (struct ntlmv2_blobhdr)
/*LINTED*/
/*LINTED*/
gethrestime(&now);
return (blob);
}
/*
* See radar 4134676. This define helps us avoid how a certain old server
* grants limited Guest access when we try NTLMv2, but works fine with NTLM.
* The fingerprint we are looking for here is DOS error codes and no-Unicode.
* Note XP grants Guest access but uses Unicode and NT error codes.
*/
/*
* When not doing Kerberos, we can try, in order:
*
* NTLMv2
* NTLM with the ASCII password not upper-cased
* NTLM with the ASCII password upper-cased
*
* if the server supports encrypted passwords, or
*
* plain-text with the ASCII password not upper-cased
* plain-text with the ASCII password upper-cased
*
* if it doesn't.
*/
#define STATE_NTLMV2 0
#define STATE_NOUCPW 1
#define STATE_UCPW 2
int
{
int minauth;
int error = 0;
int state;
int declinedguest = 0;
static const char NativeOS[] = "Solaris";
static const char LanMan[] = "NETSMB";
/*
* Most of the "capability" bits we offer should be copied
* from those offered by the server, with a mask applied.
* This is the mask of capabilies copied from the server.
* Some others get special handling below.
*/
/* No unicode unless server supports and encryption on */
vcp->vc_toserver = 0;
}
goto ssn_exit;
}
}
/*
* Try only plain text passwords.
*/
} else {
}
/*
* We're not doing extended security, which, for
* now, means we're not doing Kerberos.
* Fail if the minimum authentication level is
* Kerberos.
*/
if (minauth >= SMBVOPT_MINAUTH_KERBEROS) {
goto ssn_exit;
}
/*
* Server wants encrypted passwords.
*/
if (state > STATE_NTLMV2) {
/*
* We tried NTLMv2 in STATE_NTLMV2.
* Shall we allow fallback? (to NTLM)
*/
if (minauth >= SMBVOPT_MINAUTH_NTLMV2) {
goto ssn_exit;
}
}
if (state > STATE_NOUCPW) {
/*
* We tried NTLM in STATE_NOUCPW.
* No need to try it again.
*/
goto ssn_exit;
}
} else {
/*
* Plain-text passwords.
* Fail if the minimum authentication level is
* LM or better.
*/
if (minauth > SMBVOPT_MINAUTH_NTLM) {
goto ssn_exit;
}
}
}
if (error)
goto ssn_exit;
/*
* Domain name must be upper-case, as that's what's used
* when computing LMv2 and NTLMv2 responses - and, for NTLMv2,
* the domain name in the request has to be upper-cased as well.
* (That appears not to be the case for the user name. Go
* figure.)
*
* don't need to uppercase domain string. It's already uppercase UTF-8.
*/
/*
* In the share security mode password will be used
* only in the tree authentication
*/
pp = "";
plen = 1;
unipp = &smb_unieol;
uniplen = sizeof (smb_unieol);
} else {
if (state == STATE_NTLMV2) {
/*
* Compute the LMv2 and NTLMv2 responses,
* derived from the challenge, the user name,
* logging, and the Unicode password.
*/
/*
* Construct the client nonce by getting
* a bunch of random data.
*/
(void) random_get_pseudo_bytes((void *)
&client_nonce, sizeof (client_nonce));
/*
* Convert the user name to upper-case, as
* that's what's used when computing LMv2
* and NTLMv2 responses.
*/
/*
* Compute the LMv2 response, derived
* from the server challenge, the
* into which we're logging, the
* client nonce, and the NT hash.
*/
/*
* Construct the blob.
*/
/*
* Compute the NTLMv2 response, derived
* from the server challenge, the
* into which we're logging, the
* blob, and the NT hash.
*/
kmem_free((char *)ntlmv2_blob,
sizeof (struct ntlmv2_blobhdr) +
3 * sizeof (struct ntlmv2_namehdr) +
4 +
} else {
plen = 24;
/*
* Compute the LM response, derived
* from the challenge and the ASCII
* password.
*/
if (minauth < SMBVOPT_MINAUTH_NTLM) {
}
/*
* Compute the NTLM response, derived from
* the challenge and the NT hash.
*/
uniplen = 24;
}
} else {
/*
* We try w/o uppercasing first so Samba mixed case
* passwords work. If that fails, we come back and
* try uppercasing to satisfy OS/2 and Windows for
* Workgroups.
*/
0, 0);
plen--;
/*
* The uniplen is zeroed because Samba cannot deal
* with this 2nd cleartext password. This Samba
* "bug" is actually a workaround for problems in
* Microsoft clients.
*/
uniplen = 0; /* -= 2 */
}
}
/*
* If userid is null we are attempting anonymous browse login
* so passwords must be zero length.
*/
if (*up == '\0') {
}
mb_put_uint8(mbp, 0);
mb_put_uint16le(mbp, 0);
mb_put_uint32le(mbp, 0);
} else {
MB_MSYSTEM); /* security blob */
} else {
}
}
if (ntencpass) {
}
if (encpass) {
}
if (ucdp) {
}
/*
* This request should not wait for
* connection state changes, etc.
*/
if (error) {
goto bad;
}
do {
if (error)
break;
if (wc != 4)
break;
} else if (wc != 3)
break;
if (error)
break;
}
/* server OS, LANMGR, & Domain here */
error = 0;
/*LINTED*/
} while (0);
bad:
if (encpass) {
}
if (pbuf) {
}
/*
* We're doing user-level authentication (so we are actually
* sending authentication stuff over the wire), and we're
* not doing extended security, and the stuff we tried
* failed (or we we're trying to login a real user but
* got granted guest access instead.)
*/
if (!error)
declinedguest = 1;
/*
* Should we try the next type of authentication?
*/
if (state < STATE_UCPW) {
/*
* Yes, we still have more to try.
*/
state++;
goto again;
}
}
if (error && declinedguest)
SMBERROR("we declined ntlmv2 guest access. errno will be %d\n",
error);
/* Restore things we changed and return */
return (error);
}
int
{
int error;
return (0);
if (error)
return (error);
mb_put_uint8(mbp, 0);
mb_put_uint16le(mbp, 0);
/*
* Run this with a relatively short timeout.
* We don't really care about the result,
* as we're just trying to play nice and
* "say goodbye" before we hangup.
* XXX: Add SMBLOGOFFTIMO somewhere?
*/
return (error);
}
static char smb_any_share[] = "?????";
static char *
smb_share_typename(int stype)
{
char *pp;
switch (stype) {
case STYPE_DISKTREE:
pp = "A:";
break;
case STYPE_PRINTQ:
break;
case STYPE_DEVICE:
pp = "COMM";
break;
case STYPE_IPC:
pp = "IPC";
break;
default:
pp = smb_any_share;
break;
}
return (pp);
}
int
{
const char *pw;
int upper = 0;
/*
* Make this a "VC-level" request, so it will have
* rqp->sr_share == NULL, and smb_iod_sendrq()
* will send it with TID = SMB_TID_UNKNOWN
*
* This also serves to bypass the wait for
* share state changes, which this call is
* trying to carry out.
*
* No longer need to set ssp->ss_tid
* here, but it's harmless enough.
*/
if (error)
return (error);
plen = 1;
pp = "";
} else {
/*
* We try w/o uppercasing first so Samba mixed case
* passwords work. If that fails we come back and try
* uppercasing to satisfy OS/2 and Windows for Workgroups.
*/
if (upper++) {
} else {
}
#ifdef NOICONVSUPPORT
/*
* We need to convert here to the server codeset.
* Initially we will send the same stuff and see what happens
* witout the conversion. REVISIT.
*/
#endif
plen = 24;
} else {
}
}
mb_put_uint8(mbp, 0);
mb_put_uint16le(mbp, 0);
if (error) {
goto bad;
}
if (error) {
goto bad;
}
if (error) {
goto bad;
}
/* The type name is always ASCII */
if (error) {
goto bad;
}
/*
* Don't want to risk missing a successful
* tree connect response.
*/
if (error)
goto bad;
/* Success! */
bad:
if (encpass)
if (pbuf)
goto again;
return (error);
}
int
{
int error;
return (0);
/*
* Build this as a "VC-level" request, so it will
* avoid testing the _GONE flag on the share,
* which has already been set at this point.
* Add the share pointer "by hand" below, so
* smb_iod_sendrq will plug in the TID.
*/
if (error)
return (error);
#ifdef lint
#endif
/*
* Run this with a relatively short timeout. (5 sec.)
* We don't really care about the result here, but we
* do need to make sure we send this out, or we could
* "leak" active tree IDs on interrupt or timeout.
* The NOINTR_SEND flag makes this request immune to
* interrupt or timeout until the send is done.
*/
return (error);
}
static int
{
int error;
/* Fall back to the old cmd. */
}
/* Have ReadX but not large files? */
return (EFBIG);
}
if (error)
return (error);
/* (only indicates blocking) */
do {
if (timo == 0)
if (error)
break;
off = SMB_HDRLEN;
off++;
if (wc != 12) {
break;
}
off++;
off++;
off += 2;
off += 2;
off += 2;
off += 2;
off += 2;
off += 2;
off += 2;
off += 2;
if (resid == 0) {
break;
}
if (error)
break;
/*LINTED*/
} while (0);
return (error);
}
static int
{
int error;
/* Fall back to the old cmd. */
}
/* Have WriteX but not large files? */
return (EFBIG);
}
if (error)
return (error);
mb_put_uint16le(mbp, 0);
do {
if (error)
break;
if (timo == 0)
if (error)
break;
if (wc != 6) {
break;
}
/*
* if LARGE_WRITEX then there's one more bit of # written
*/
}
/*LINTED*/
} while (0);
return (error);
}
static int
{
/* This cmd is limited to 32-bit offsets. */
return (EFBIG);
if (error)
return (error);
do {
if (timo == 0)
if (error)
break;
if (wc != 5) {
break;
}
if (resid == 0) {
break;
}
if (error)
break;
/*LINTED*/
} while (0);
return (error);
}
static int
{
int error;
/* This cmd is limited to 32-bit offsets. */
return (EFBIG);
if (error)
return (error);
do {
if (error)
break;
if (timo == 0)
if (error)
break;
if (wc != 1) {
break;
}
/*LINTED*/
} while (0);
return (error);
}
/*
* Called by netsmb smb_usr_rw,
* smbfs_readvnode, smbfs_writevnode
*/
int
{
int error = 0;
while (tsize > 0) {
/* Lint: tsize may be 64-bits */
else
if (error)
break;
break;
}
timo = 0; /* only first write is special */
}
if (error) {
/*
* they imply resid is unreliable. The only safe thing is
* to pretend zero bytes made it. We needn't restore the
* iovs because callers don't depend on them in error
* paths - uio_resid and uio_offset are what matter.
*/
}
return (error);
}
int
{
int error;
if (error)
return (error);
/*
* Note: the IOD calls this, so
* this request must not wait for
* connection state changes, etc.
*/
return (error);
}
#ifdef APPLE
int
{
int error;
if (error)
return (error);
/*
* All we need to do is marshall the path: "\\"
* (the root of the share) into this request.
* We essentially in-line smbfs_fullpath() here,
* except no mb_put_padbyte (already aligned).
*/
return (error);
}
#endif /* APPLE */