예제 #1
0
/*
 * Starts the creation of a new printer file, which will be deleted
 * automatically once it has been closed and printed.
 *
 * SetupLength is the number of bytes in the first part of the resulting
 * print spool file which contains printer-specific control strings.
 *
 * Mode can have the following values:
 *      0     Text mode.  The server may optionally
 *            expand tabs to a series of spaces.
 *      1     Graphics mode.  No conversion of data
 *            should be done by the server.
 *
 * IdentifierString can be used by the server to provide some sort of
 * per-client identifying component to the print file.
 *
 * When the file is closed, it will be sent to the spooler and printed.
 */
smb_sdrc_t
smb_pre_open_print_file(smb_request_t *sr)
{
	struct open_param	*op = &sr->arg.open;
	char			*path;
	char			*identifier;
	uint32_t		new_id;
	uint16_t		setup;
	uint16_t		mode;
	int			rc;
	static uint32_t		tmp_id = 10000;

	bzero(op, sizeof (sr->arg.open));
	rc = smbsr_decode_vwv(sr, "ww", &setup, &mode);
	if (rc == 0)
		rc = smbsr_decode_data(sr, "%S", sr, &identifier);

	if (rc == 0) {
		path = smb_srm_zalloc(sr, MAXPATHLEN);
		op->fqi.fq_path.pn_path = path;
		new_id = atomic_inc_32_nv(&tmp_id);
		(void) snprintf(path, MAXPATHLEN, "%s%05u", identifier, new_id);
	}

	op->create_disposition = FILE_OVERWRITE_IF;
	op->create_options = FILE_NON_DIRECTORY_FILE;
	DTRACE_SMB_2(op__OpenPrintFile__start, smb_request_t *, sr,
	    struct open_param *, op);

	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
예제 #2
0
smb_sdrc_t
smb_com_create_temporary(smb_request_t *sr)
{
	static uint16_t tmp_id = 10000;
	struct open_param *op = &sr->arg.open;
	char name[SMB_CREATE_NAMEBUF_SZ];
	char *buf;
	uint16_t bcc;

	++tmp_id;
	bcc = 1; /* null terminator */
	bcc += snprintf(name, SMB_CREATE_NAMEBUF_SZ, "tt%05d.tmp", tmp_id);

	buf = smb_srm_zalloc(sr, MAXPATHLEN);
	(void) snprintf(buf, MAXPATHLEN, "%s\\%s",
	    op->fqi.fq_path.pn_path, name);
	op->fqi.fq_path.pn_path = buf;

	if (smb_common_create(sr) != NT_STATUS_SUCCESS)
		return (SDRC_ERROR);

	if (smbsr_encode_result(sr, 1, VAR_BCC, "bww%S", 1, sr->smb_fid,
	    VAR_BCC, sr, name))
		return (SDRC_ERROR);

	return (SDRC_SUCCESS);
}
예제 #3
0
/*
 * smb_pathname_strdup
 *
 * Duplicate NULL terminated string s.
 *
 * The new string is allocated using request specific storage and will
 * be free'd when the sr is destroyed.
 */
static char *
smb_pathname_strdup(smb_request_t *sr, const char *s)
{
	char *s2;
	size_t n;

	n = strlen(s) + 1;
	s2 = smb_srm_zalloc(sr, n);
	(void) strlcpy(s2, s, n);
	return (s2);
}
예제 #4
0
smb_sdrc_t
smb_com_echo(struct smb_request *sr)
{
	unsigned short necho;
	unsigned short nbytes;
	unsigned short i;
	struct mbuf_chain reply;
	char *data;

	if (smbsr_decode_vwv(sr, "w", &necho) != 0)
		return (SDRC_ERROR);

	nbytes = sr->smb_bcc;
	data = smb_srm_zalloc(sr, nbytes);

	if (smb_mbc_decodef(&sr->smb_data, "#c", nbytes, data))
		return (SDRC_ERROR);

	for (i = 1; i <= necho; ++i) {
		MBC_INIT(&reply, SMB_HEADER_ED_LEN + 10 + nbytes);

		(void) smb_mbc_encodef(&reply, SMB_HEADER_ED_FMT,
		    sr->first_smb_com,
		    sr->smb_rcls,
		    sr->smb_reh,
		    sr->smb_err,
		    sr->smb_flg | SMB_FLAGS_REPLY,
		    sr->smb_flg2,
		    sr->smb_pid_high,
		    sr->smb_sig,
		    sr->smb_tid,
		    sr->smb_pid,
		    sr->smb_uid,
		    sr->smb_mid);

		(void) smb_mbc_encodef(&reply, "bww#c", 1, i,
		    nbytes, nbytes, data);

		if (sr->session->signing.flags & SMB_SIGNING_ENABLED)
			smb_sign_reply(sr, &reply);

		(void) smb_session_send(sr->session, 0, &reply);
	}

	return (SDRC_NO_REPLY);
}
예제 #5
0
smb_sdrc_t
smb_pre_negotiate(smb_request_t *sr)
{
	smb_kmod_cfg_t		*skc;
	smb_arg_negotiate_t	*negprot;
	int			dialect;
	int			pos;
	int			rc = 0;

	skc = &sr->session->s_cfg;
	negprot = smb_srm_zalloc(sr, sizeof (smb_arg_negotiate_t));
	negprot->ni_index = -1;
	sr->sr_negprot = negprot;

	for (pos = 0; smbsr_decode_data_avail(sr); pos++) {
		if (smbsr_decode_data(sr, "%L", sr, &negprot->ni_name) != 0) {
			smbsr_error(sr, 0, ERRSRV, ERRerror);
			rc = -1;
			break;
		}

		if ((dialect = smb_xlate_dialect(negprot->ni_name)) < 0)
			continue;

		/*
		 * Conditionally recognize the SMB2 dialects.
		 */
		if (dialect >= DIALECT_SMB2002 &&
		    skc->skc_max_protocol < SMB_VERS_2_BASE)
			continue;

		if (negprot->ni_dialect < dialect) {
			negprot->ni_dialect = dialect;
			negprot->ni_index = pos;
		}
	}

	DTRACE_SMB_2(op__Negotiate__start, smb_request_t *, sr,
	    smb_arg_negotiate_t, negprot);

	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
예제 #6
0
smb_sdrc_t
smb_pre_write_raw(smb_request_t *sr)
{
	smb_rw_param_t *param;
	uint32_t off_low;
	uint32_t timeout;
	uint32_t off_high;
	uint16_t datalen;
	uint16_t total;
	int rc;

	param = smb_srm_zalloc(sr, sizeof (smb_rw_param_t));
	sr->arg.rw = param;
	param->rw_magic = SMB_RW_MAGIC;

	if (sr->smb_wct == 12) {
		rc = smbsr_decode_vwv(sr, "ww2.llw4.ww", &sr->smb_fid, &total,
		    &off_low, &timeout, &param->rw_mode, &datalen,
		    &param->rw_dsoff);

		param->rw_offset = (uint64_t)off_low;
		param->rw_dsoff -= 59;
	} else {
		rc = smbsr_decode_vwv(sr, "ww2.llw4.wwl", &sr->smb_fid, &total,
		    &off_low, &timeout, &param->rw_mode, &datalen,
		    &param->rw_dsoff, &off_high);

		param->rw_offset = ((uint64_t)off_high << 32) | off_low;
		param->rw_dsoff -= 63;
	}

	param->rw_count = (uint32_t)datalen;
	param->rw_total = (uint32_t)total;
	param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;

	DTRACE_SMB_2(op__WriteRaw__start, smb_request_t *, sr,
	    smb_rw_param_t *, sr->arg.rw);

	smb_rwx_rwenter(&sr->session->s_lock, RW_WRITER);

	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
예제 #7
0
smb_sdrc_t
smb_com_echo(struct smb_request *sr)
{
	unsigned short necho;
	unsigned short nbytes;
	unsigned short i;
	struct mbuf_chain reply;
	char *data;
	uint16_t	pid_hi, pid_lo;

	pid_hi = sr->smb_pid >> 16;
	pid_lo = (uint16_t)sr->smb_pid;

	if (smbsr_decode_vwv(sr, "w", &necho) != 0)
		return (SDRC_ERROR);

	/*
	 * Don't let the client fool us into doing
	 * more work than is "reasonable".
	 */
	if (necho > smb_max_echo)
		necho = smb_max_echo;

	nbytes = sr->smb_bcc;
	data = smb_srm_zalloc(sr, nbytes);

	if (smb_mbc_decodef(&sr->smb_data, "#c", nbytes, data))
		return (SDRC_ERROR);

	for (i = 1; i <= necho; ++i) {

		/*
		 * According to [MS-CIFS] 3.3.5.32 echo is
		 * subject to cancellation.
		 */
		if (sr->sr_state != SMB_REQ_STATE_ACTIVE)
			break;

		MBC_INIT(&reply, SMB_HEADER_ED_LEN + 10 + nbytes);

		(void) smb_mbc_encodef(&reply, SMB_HEADER_ED_FMT,
		    sr->first_smb_com,
		    sr->smb_rcls,
		    sr->smb_reh,
		    sr->smb_err,
		    sr->smb_flg | SMB_FLAGS_REPLY,
		    sr->smb_flg2,
		    pid_hi,
		    sr->smb_sig,
		    sr->smb_tid,
		    pid_lo,
		    sr->smb_uid,
		    sr->smb_mid);

		(void) smb_mbc_encodef(&reply, "bww#c", 1, i,
		    nbytes, nbytes, data);

		if (sr->session->signing.flags & SMB_SIGNING_ENABLED)
			smb_sign_reply(sr, &reply);

		(void) smb_session_send(sr->session, 0, &reply);

		delay(MSEC_TO_TICK(100));
	}

	return (SDRC_NO_REPLY);
}
예제 #8
0
smb_sdrc_t
smb2_read(smb_request_t *sr)
{
	smb_ofile_t *of = NULL;
	smb_vdb_t *vdb = NULL;
	struct mbuf *m = NULL;
	uint16_t StructSize;
	uint8_t Padding;
	uint8_t DataOff;
	uint32_t Length;
	uint64_t Offset;
	smb2fid_t smb2fid;
	uint32_t MinCount;
	uint32_t Channel;
	uint32_t Remaining;
	uint16_t ChanInfoOffset;
	uint16_t ChanInfoLength;
	uint32_t XferCount;
	uint32_t status;
	int rc = 0;

	/*
	 * SMB2 Read request
	 */
	rc = smb_mbc_decodef(
	    &sr->smb_data,
	    "wb.lqqqlllww",
	    &StructSize,		/* w */
	    &Padding,			/* b. */
	    &Length,			/* l */
	    &Offset,			/* q */
	    &smb2fid.persistent,	/* q */
	    &smb2fid.temporal,		/* q */
	    &MinCount,			/* l */
	    &Channel,			/* l */
	    &Remaining,			/* l */
	    &ChanInfoOffset,		/* w */
	    &ChanInfoLength);		/* w */
	if (rc)
		return (SDRC_ERROR);
	if (StructSize != 49)
		return (SDRC_ERROR);

	status = smb2sr_lookup_fid(sr, &smb2fid);
	if (status) {
		smb2sr_put_error(sr, status);
		return (SDRC_SUCCESS);
	}
	of = sr->fid_ofile;

	if (Length > smb2_max_rwsize) {
		smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
		return (SDRC_SUCCESS);
	}
	if (MinCount > Length)
		MinCount = Length;

	/* This is automatically free'd. */
	vdb = smb_srm_zalloc(sr, sizeof (*vdb));
	vdb->vdb_tag = 0;
	vdb->vdb_uio.uio_iov = &vdb->vdb_iovec[0];
	vdb->vdb_uio.uio_iovcnt = MAX_IOVEC;
	vdb->vdb_uio.uio_resid = Length;
	vdb->vdb_uio.uio_loffset = (offset_t)Offset;
	vdb->vdb_uio.uio_segflg = UIO_SYSSPACE;
	vdb->vdb_uio.uio_extflg = UIO_COPY_DEFAULT;

	sr->raw_data.max_bytes = Length;
	m = smb_mbuf_allocate(&vdb->vdb_uio);

	switch (of->f_tree->t_res_type & STYPE_MASK) {
	case STYPE_DISKTREE:
		if (!smb_node_is_dir(of->f_node)) {
			/* Check for conflicting locks. */
			rc = smb_lock_range_access(sr, of->f_node,
			    Offset, Length, B_FALSE);
			if (rc) {
				rc = ERANGE;
				break;
			}
		}
		rc = smb_fsop_read(sr, of->f_cr, of->f_node, &vdb->vdb_uio);
		break;
	case STYPE_IPC:
		rc = smb_opipe_read(sr, &vdb->vdb_uio);
		break;
	default:
	case STYPE_PRINTQ:
		rc = EACCES;
		break;
	}

	/* How much data we moved. */
	XferCount = Length - vdb->vdb_uio.uio_resid;

	sr->raw_data.max_bytes = XferCount;
	smb_mbuf_trim(m, XferCount);
	MBC_ATTACH_MBUF(&sr->raw_data, m);

	/*
	 * Checking the error return _after_ dealing with
	 * the returned data so that if m was allocated,
	 * it will be free'd via sr->raw_data cleanup.
	 */
	if (rc) {
		smb2sr_put_errno(sr, rc);
		return (SDRC_SUCCESS);
	}

	/*
	 * SMB2 Read reply
	 */
	DataOff = SMB2_HDR_SIZE + 16;
	rc = smb_mbc_encodef(
	    &sr->reply,
	    "wb.lllC",
	    17,	/* StructSize */	/* w */
	    DataOff,			/* b. */
	    XferCount,			/* l */
	    0, /* DataRemaining */	/* l */
	    0, /* reserved */		/* l */
	    &sr->raw_data);		/* C */
	if (rc)
		return (SDRC_ERROR);

	mutex_enter(&of->f_mutex);
	of->f_seek_pos = Offset + XferCount;
	mutex_exit(&of->f_mutex);

	return (SDRC_SUCCESS);
}
예제 #9
0
smb_sdrc_t
smb2_session_setup(smb_request_t *sr)
{
	smb_arg_sessionsetup_t	*sinfo;
	uint16_t StructureSize;
	uint8_t  Flags;
	uint8_t  SecurityMode;
	uint32_t Capabilities;	/* ignored - see above */
	uint32_t Channel;
	uint16_t SecBufOffset;
	uint16_t SecBufLength;
	uint64_t PrevSessionId;
	uint16_t SessionFlags;
	uint32_t status;
	int skip;
	int rc = 0;

	sinfo = smb_srm_zalloc(sr, sizeof (smb_arg_sessionsetup_t));
	sr->sr_ssetup = sinfo;

	rc = smb_mbc_decodef(
	    &sr->smb_data, "wbbllwwq",
	    &StructureSize,	/* w */
	    &Flags,		/* b */
	    &SecurityMode,	/* b */
	    &Capabilities,	/* l */
	    &Channel,		/* l */
	    &SecBufOffset,	/* w */
	    &SecBufLength,	/* w */
	    &PrevSessionId);	/* q */
	if (rc)
		return (SDRC_ERROR);

	/*
	 * We're normally positioned at the security buffer now,
	 * but there could be some padding before it.
	 */
	skip = (SecBufOffset + sr->smb2_cmd_hdr) -
	    sr->smb_data.chain_offset;
	if (skip < 0)
		return (SDRC_ERROR);
	if (skip > 0)
		(void) smb_mbc_decodef(&sr->smb_data, "#.", skip);

	/*
	 * Get the security buffer
	 */
	sinfo->ssi_iseclen = SecBufLength;
	sinfo->ssi_isecblob = smb_srm_zalloc(sr, sinfo->ssi_iseclen);
	rc = smb_mbc_decodef(&sr->smb_data, "#c",
	    sinfo->ssi_iseclen, sinfo->ssi_isecblob);
	if (rc)
		return (SDRC_ERROR);

	/*
	 * The real auth. work happens in here.
	 */
	status = smb_authenticate_ext(sr);

	SecBufOffset = SMB2_HDR_SIZE + 8;
	SecBufLength = sinfo->ssi_oseclen;
	SessionFlags = 0;

	switch (status) {

	case NT_STATUS_SUCCESS:	/* Authenticated */
		if (sr->uid_user->u_flags & SMB_USER_FLAG_GUEST)
			SessionFlags |= SMB2_SESSION_FLAG_IS_GUEST;
		if (sr->uid_user->u_flags & SMB_USER_FLAG_ANON)
			SessionFlags |= SMB2_SESSION_FLAG_IS_NULL;
		smb2_ss_adjust_credits(sr);
		break;

	/*
	 * This is not really an error, but tells the client
	 * it should send another session setup request.
	 * Not smb2_put_error because we send a payload.
	 */
	case NT_STATUS_MORE_PROCESSING_REQUIRED:
		sr->smb2_status = status;
		break;

	default:
		SecBufLength = 0;
		sr->smb2_status = status;
		break;
	}

	/*
	 * SMB2 Session Setup reply
	 */

	rc = smb_mbc_encodef(
	    &sr->reply,
	    "wwww#c",
	    9,	/* StructSize */	/* w */
	    SessionFlags,		/* w */
	    SecBufOffset,		/* w */
	    SecBufLength,		/* w */
	    SecBufLength,		/* # */
	    sinfo->ssi_osecblob);	/* c */
	if (rc)
		return (SDRC_ERROR);

	return (SDRC_SUCCESS);
}
예제 #10
0
smb_sdrc_t
smb_com_negotiate(smb_request_t *sr)
{
	smb_session_t 		*session = sr->session;
	smb_arg_negotiate_t	*negprot = sr->sr_negprot;
	uint16_t		secmode;
	uint32_t		sesskey;
	char			*nbdomain;
	uint8_t			*wcbuf;
	int			wclen;
	smb_msgbuf_t		mb;
	int			rc;

	if (session->s_state != SMB_SESSION_STATE_ESTABLISHED) {
		/* The protocol has already been negotiated. */
		smbsr_error(sr, 0, ERRSRV, ERRerror);
		return (SDRC_ERROR);
	}

	/*
	 * Special case for negotiating SMB2 from SMB1.  The client
	 * includes the  "SMB 2..." dialects in the SMB1 negotiate,
	 * and if SMB2 is enabled, we choose one of those and then
	 * send an SMB2 reply to that SMB1 request.  Yes, it's very
	 * strange, but this SMB1 request can have an SMB2 reply!
	 * To accomplish this, we let the SMB2 code send the reply
	 * and return the special code SDRC_NO_REPLY to the SMB1
	 * dispatch logic so it will NOT send an SMB1 reply.
	 * (Or possibly send an SMB1 error reply.)
	 */
	if (negprot->ni_dialect >= DIALECT_SMB2002) {
		rc = smb1_negotiate_smb2(sr);
		ASSERT(rc == SDRC_NO_REPLY ||
		    rc == SDRC_DROP_VC || rc == SDRC_ERROR);
		return (rc);
	}

	session->secmode = NEGOTIATE_ENCRYPT_PASSWORDS |
	    NEGOTIATE_USER_SECURITY;
	secmode = session->secmode;
	sesskey = session->sesskey;

	negprot->ni_servertime.tv_sec = gethrestime_sec();
	negprot->ni_servertime.tv_nsec = 0;
	negprot->ni_tzcorrection = sr->sr_gmtoff / 60;
	negprot->ni_maxmpxcount = sr->sr_cfg->skc_maxworkers;
	negprot->ni_keylen = SMB_CHALLENGE_SZ;
	bcopy(&session->challenge_key, negprot->ni_key, SMB_CHALLENGE_SZ);
	nbdomain = sr->sr_cfg->skc_nbdomain;

	/*
	 * UNICODE support is required for long share names,
	 * long file names and streams.  Note: CAP_RAW_MODE
	 * is not supported because it does nothing to help
	 * modern clients and causes nasty complications.
	 */
	negprot->ni_capabilities = CAP_LARGE_FILES
	    | CAP_UNICODE
	    | CAP_NT_SMBS
	    | CAP_STATUS32
	    | CAP_NT_FIND
	    | CAP_LEVEL_II_OPLOCKS
	    | CAP_LOCK_AND_READ
	    | CAP_RPC_REMOTE_APIS
	    | CAP_LARGE_READX
	    | CAP_LARGE_WRITEX
	    | CAP_DFS;

	if (smb_cap_passthru)
		negprot->ni_capabilities |= CAP_INFOLEVEL_PASSTHRU;
	else
		cmn_err(CE_NOTE, "smbsrv: cap passthru is %s",
		    (negprot->ni_capabilities & CAP_INFOLEVEL_PASSTHRU) ?
		    "enabled" : "disabled");

	switch (negprot->ni_dialect) {
	case PC_NETWORK_PROGRAM_1_0:	/* core */
		(void) ksocket_setsockopt(session->sock, SOL_SOCKET,
		    SO_RCVBUF, (const void *)&smb_dos_tcp_rcvbuf,
		    sizeof (smb_dos_tcp_rcvbuf), CRED());
		rc = smbsr_encode_result(sr, 1, 0, "bww", 1,
		    negprot->ni_index, 0);
		break;

	case Windows_for_Workgroups_3_1a:
	case PCLAN1_0:
	case MICROSOFT_NETWORKS_1_03:
	case MICROSOFT_NETWORKS_3_0:
	case LANMAN1_0:
	case LM1_2X002:
	case DOS_LM1_2X002:
		(void) ksocket_setsockopt(session->sock, SOL_SOCKET,
		    SO_RCVBUF, (const void *)&smb_dos_tcp_rcvbuf,
		    sizeof (smb_dos_tcp_rcvbuf), CRED());
		sr->smb_flg |= SMB_FLAGS_LOCK_AND_READ_OK;
		rc = smbsr_encode_result(sr, 13, VAR_BCC,
		    "bwwwwwwlYww2.w#c",
		    13,				/* wct */
		    negprot->ni_index,		/* dialect index */
		    secmode,			/* security mode */
		    SMB_DOS_MAXBUF,		/* max buffer size */
		    1,				/* max MPX */
		    1,				/* max VCs */
		    0,				/* read/write raw */
		    sesskey,			/* session key */
		    negprot->ni_servertime.tv_sec, /* server date/time */
		    negprot->ni_tzcorrection,
		    (uint16_t)negprot->ni_keylen, /* encryption key length */
						/* reserved field handled 2. */
		    VAR_BCC,
		    (int)negprot->ni_keylen,
		    negprot->ni_key);		/* encryption key */
		break;

	case DOS_LANMAN2_1:
	case LANMAN2_1:
		(void) ksocket_setsockopt(session->sock, SOL_SOCKET,
		    SO_RCVBUF, (const void *)&smb_dos_tcp_rcvbuf,
		    sizeof (smb_dos_tcp_rcvbuf), CRED());
		sr->smb_flg |= SMB_FLAGS_LOCK_AND_READ_OK;
		rc = smbsr_encode_result(sr, 13, VAR_BCC,
		    "bwwwwwwlYww2.w#cs",
		    13,				/* wct */
		    negprot->ni_index,		/* dialect index */
		    secmode,			/* security mode */
		    SMB_DOS_MAXBUF,		/* max buffer size */
		    1,				/* max MPX */
		    1,				/* max VCs */
		    0,				/* read/write raw */
		    sesskey,			/* session key */
		    negprot->ni_servertime.tv_sec, /* server date/time */
		    negprot->ni_tzcorrection,
		    (uint16_t)negprot->ni_keylen, /* encryption key length */
						/* reserved field handled 2. */
		    VAR_BCC,
		    (int)negprot->ni_keylen,
		    negprot->ni_key,		/* encryption key */
		    nbdomain);
		break;

	case NT_LM_0_12:
		(void) ksocket_setsockopt(session->sock, SOL_SOCKET,
		    SO_RCVBUF, (const void *)&smb_nt_tcp_rcvbuf,
		    sizeof (smb_nt_tcp_rcvbuf), CRED());

		/*
		 * Allow SMB signatures if using encrypted passwords
		 */
		if ((secmode & NEGOTIATE_ENCRYPT_PASSWORDS) &&
		    sr->sr_cfg->skc_signing_enable) {
			secmode |= NEGOTIATE_SECURITY_SIGNATURES_ENABLED;
			if (sr->sr_cfg->skc_signing_required)
				secmode |=
				    NEGOTIATE_SECURITY_SIGNATURES_REQUIRED;

			session->secmode = secmode;
		}

		/*
		 * Does the client want Extended Security?
		 * (and if we have it enabled)
		 * If so, handle as if a different dialect.
		 */
		if ((sr->smb_flg2 & SMB_FLAGS2_EXT_SEC) && smb_cap_ext_sec)
			goto NT_LM_0_12_ext_sec;
		sr->smb_flg2 &= ~SMB_FLAGS2_EXT_SEC;

		/*
		 * nbdomain is not expected to be aligned.
		 * Use temporary buffer to avoid alignment padding
		 */
		wclen = smb_wcequiv_strlen(nbdomain) + sizeof (smb_wchar_t);
		wcbuf = smb_srm_zalloc(sr, wclen);
		smb_msgbuf_init(&mb, wcbuf, wclen, SMB_MSGBUF_UNICODE);
		if (smb_msgbuf_encode(&mb, "U", nbdomain) < 0) {
			smb_msgbuf_term(&mb);
			smbsr_error(sr, 0, ERRSRV, ERRerror);
			return (SDRC_ERROR);
		}

		rc = smbsr_encode_result(sr, 17, VAR_BCC,
		    "bwbwwllllTwbw#c#c",
		    17,				/* wct */
		    negprot->ni_index,		/* dialect index */
		    secmode,			/* security mode */
		    negprot->ni_maxmpxcount,	/* max MPX */
		    1,				/* max VCs */
		    (DWORD)smb_maxbufsize,	/* max buffer size */
		    0xFFFF,			/* max raw size */
		    sesskey,			/* session key */
		    negprot->ni_capabilities,
		    &negprot->ni_servertime,	/* system time */
		    negprot->ni_tzcorrection,
		    negprot->ni_keylen,		/* encryption key length */
		    VAR_BCC,
		    (int)negprot->ni_keylen,
		    negprot->ni_key,		/* encryption key */
		    wclen,
		    wcbuf);			/* nbdomain (unicode) */

		smb_msgbuf_term(&mb);
		break;

NT_LM_0_12_ext_sec:
		/*
		 * This is the "Extended Security" variant of
		 * dialect NT_LM_0_12.
		 */
		negprot->ni_capabilities |= CAP_EXTENDED_SECURITY;

		rc = smbsr_encode_result(sr, 17, VAR_BCC,
		    "bwbwwllllTwbw#c#c",
		    17,				/* wct */
		    negprot->ni_index,		/* dialect index */
		    secmode,			/* security mode */
		    negprot->ni_maxmpxcount,	/* max MPX */
		    1,				/* max VCs */
		    (DWORD)smb_maxbufsize,	/* max buffer size */
		    0xFFFF,			/* max raw size */
		    sesskey,			/* session key */
		    negprot->ni_capabilities,
		    &negprot->ni_servertime,	/* system time */
		    negprot->ni_tzcorrection,
		    0,		/* encryption key length (MBZ) */
		    VAR_BCC,
		    UUID_LEN,
		    sr->sr_cfg->skc_machine_uuid,
		    sr->sr_cfg->skc_negtok_len,
		    sr->sr_cfg->skc_negtok);
		break;


	default:
		rc = smbsr_encode_result(sr, 1, 0, "bww", 1, -1, 0);
		break;
	}

	if (rc != 0)
		return (SDRC_ERROR);

	/*
	 * Save the agreed dialect. Note that the state is also
	 * used to detect and reject attempts to re-negotiate.
	 */
	session->dialect = negprot->ni_dialect;
	session->s_state = SMB_SESSION_STATE_NEGOTIATED;

	/* Allow normal SMB1 requests now. */
	session->newrq_func = smb1sr_newrq;

	return (SDRC_SUCCESS);
}