xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_session_setup.c (revision 811599a462e8920d70cf548f4002182d3c222d13)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
14  */
15 
16 /*
17  * Dispatch function for SMB2_SESSION_SETUP
18  *
19  * Note that the Capabilities supplied in this request are an inferior
20  * subset of those given to us previously in the SMB2 Negotiate request.
21  * We need to remember the full set of capabilities from SMB2 Negotiate,
22  * and therefore ignore the subset of capabilities supplied here.
23  */
24 
25 #include <smbsrv/smb2_kproto.h>
26 
27 static void smb2_ss_adjust_credits(smb_request_t *);
28 
29 smb_sdrc_t
30 smb2_session_setup(smb_request_t *sr)
31 {
32 	smb_arg_sessionsetup_t	*sinfo;
33 	uint16_t StructureSize;
34 	uint8_t  Flags;
35 	uint8_t  SecurityMode;
36 	uint32_t Capabilities;	/* ignored - see above */
37 	uint32_t Channel;
38 	uint16_t SecBufOffset;
39 	uint16_t SecBufLength;
40 	uint64_t PrevSsnId;
41 	uint16_t SessionFlags;
42 	uint32_t status;
43 	int skip;
44 	int rc = 0;
45 
46 	sinfo = smb_srm_zalloc(sr, sizeof (smb_arg_sessionsetup_t));
47 	sr->sr_ssetup = sinfo;
48 
49 	rc = smb_mbc_decodef(
50 	    &sr->smb_data, "wbbllwwq",
51 	    &StructureSize,	/* w */
52 	    &Flags,		/* b */
53 	    &SecurityMode,	/* b */
54 	    &Capabilities,	/* l */
55 	    &Channel,		/* l */
56 	    &SecBufOffset,	/* w */
57 	    &SecBufLength,	/* w */
58 	    &PrevSsnId);	/* q */
59 	if (rc)
60 		return (SDRC_ERROR);
61 
62 	/*
63 	 * SMB3 multi-channel features are not supported.
64 	 * Once they are, this will check the dialect and
65 	 * whether multi-channel was negotiated, i.e.
66 	 *	if (sr->session->dialect < SMB_VERS_3_0 ||
67 	 *	    s->IsMultiChannelCapable == False)
68 	 *		return (error...)
69 	 */
70 	if (Flags & SMB2_SESSION_FLAG_BINDING) {
71 		status = NT_STATUS_REQUEST_NOT_ACCEPTED;
72 		goto errout;
73 	}
74 
75 	/*
76 	 * We're normally positioned at the security buffer now,
77 	 * but there could be some padding before it.
78 	 */
79 	skip = (SecBufOffset + sr->smb2_cmd_hdr) -
80 	    sr->smb_data.chain_offset;
81 	if (skip < 0)
82 		return (SDRC_ERROR);
83 	if (skip > 0)
84 		(void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
85 
86 	/*
87 	 * Get the security buffer
88 	 */
89 	sinfo->ssi_iseclen = SecBufLength;
90 	sinfo->ssi_isecblob = smb_srm_zalloc(sr, sinfo->ssi_iseclen);
91 	rc = smb_mbc_decodef(&sr->smb_data, "#c",
92 	    sinfo->ssi_iseclen, sinfo->ssi_isecblob);
93 	if (rc)
94 		return (SDRC_ERROR);
95 
96 	/*
97 	 * Decoded everything.  Dtrace probe,
98 	 * then no more early returns.
99 	 */
100 	DTRACE_SMB2_START(op__SessionSetup, smb_request_t *, sr);
101 
102 	/*
103 	 * The real auth. work happens in here.
104 	 */
105 	status = smb_authenticate_ext(sr);
106 
107 	SecBufOffset = SMB2_HDR_SIZE + 8;
108 	SecBufLength = sinfo->ssi_oseclen;
109 	SessionFlags = 0;
110 
111 	switch (status) {
112 
113 	case NT_STATUS_SUCCESS:	/* Authenticated */
114 		if (sr->uid_user->u_flags & SMB_USER_FLAG_GUEST)
115 			SessionFlags |= SMB2_SESSION_FLAG_IS_GUEST;
116 		if (sr->uid_user->u_flags & SMB_USER_FLAG_ANON)
117 			SessionFlags |= SMB2_SESSION_FLAG_IS_NULL;
118 		smb2_ss_adjust_credits(sr);
119 
120 		/*
121 		 * PrevSsnId is a session that the client is reporting as
122 		 * having gone away, and for which we might not yet have seen
123 		 * a disconnect. We need to log off the previous session so
124 		 * any durable handles in that session will become orphans
125 		 * that can be reclaimed in this new session.  Note that
126 		 * either zero or the _current_ session ID means there is
127 		 * no previous session to logoff.
128 		 */
129 		if (PrevSsnId != 0 &&
130 		    PrevSsnId != sr->smb2_ssnid)
131 			smb_server_logoff_ssnid(sr, PrevSsnId);
132 		break;
133 
134 	/*
135 	 * This is not really an error, but tells the client
136 	 * it should send another session setup request.
137 	 * Not smb2_put_error because we send a payload.
138 	 */
139 	case NT_STATUS_MORE_PROCESSING_REQUIRED:
140 		sr->smb2_status = status;
141 		break;
142 
143 	default:
144 errout:
145 		SecBufLength = 0;
146 		sr->smb2_status = status;
147 		break;
148 	}
149 
150 	/* sr->smb2_status set above */
151 	DTRACE_SMB2_DONE(op__SessionSetup, smb_request_t *, sr);
152 
153 	/*
154 	 * SMB2 Session Setup reply
155 	 */
156 
157 	rc = smb_mbc_encodef(
158 	    &sr->reply,
159 	    "wwww#c",
160 	    9,	/* StructSize */	/* w */
161 	    SessionFlags,		/* w */
162 	    SecBufOffset,		/* w */
163 	    SecBufLength,		/* w */
164 	    SecBufLength,		/* # */
165 	    sinfo->ssi_osecblob);	/* c */
166 	if (rc)
167 		sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
168 
169 	return (SDRC_SUCCESS);
170 }
171 
172 /*
173  * After a successful authentication, raise s_max_credits up to the
174  * normal maximum that clients are allowed to request.  Also, if we
175  * haven't yet given them their initial credits, do that now.
176  *
177  * Normally, clients will request some credits with session setup,
178  * but in case they don't request enough to raise s_cur_credits
179  * up to the configured initial_credits, increase the requested
180  * credits of this SR sufficiently to make that happen.  The actual
181  * increase happens in the dispatch code after we return.
182  */
183 static void
184 smb2_ss_adjust_credits(smb_request_t *sr)
185 {
186 	smb_session_t *s = sr->session;
187 
188 	mutex_enter(&s->s_credits_mutex);
189 	s->s_max_credits = s->s_cfg.skc_maximum_credits;
190 
191 	if (s->s_cur_credits < s->s_cfg.skc_initial_credits) {
192 		uint16_t grant;
193 
194 		/* How many credits we want to grant with this SR. */
195 		grant = s->s_cfg.skc_initial_credits - s->s_cur_credits;
196 
197 		/*
198 		 * Do we need to increase the smb2_credit_request?
199 		 * One might prefer to read this expression as:
200 		 *	((credit_request - credit_charge) < grant)
201 		 * but we know credit_charge == 1 and would rather not
202 		 * deal with a possibly negative value on the left,
203 		 * so adding credit_charge to both sides...
204 		 */
205 		if (sr->smb2_credit_request < (grant + 1)) {
206 			sr->smb2_credit_request = (grant + 1);
207 		}
208 	}
209 
210 	mutex_exit(&s->s_credits_mutex);
211 }
212