CVE-2016-2114: s4:smb2_server: fix session setup with required signing
[samba.git] / source4 / smb_server / smb2 / sesssetup.c
1 /* 
2    Unix SMB2 implementation.
3    
4    Copyright (C) Andrew Bartlett        2001-2005
5    Copyright (C) Stefan Metzmacher      2005
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include <tevent.h>
23 #include "auth/gensec/gensec.h"
24 #include "auth/auth.h"
25 #include "libcli/smb2/smb2.h"
26 #include "libcli/smb2/smb2_calls.h"
27 #include "smb_server/smb_server.h"
28 #include "smb_server/smb2/smb2_server.h"
29 #include "smbd/service_stream.h"
30 #include "lib/stream/packet.h"
31
32 static void smb2srv_sesssetup_send(struct smb2srv_request *req, union smb_sesssetup *io)
33 {
34         if (NT_STATUS_IS_OK(req->status)) {
35                 /* nothing */
36         } else if (NT_STATUS_EQUAL(req->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
37                 /* nothing */
38         } else {
39                 smb2srv_send_error(req, req->status);
40                 return;
41         }
42
43         SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, true, io->smb2.out.secblob.length));
44
45         SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID,        io->smb2.out.uid);
46
47         SSVAL(req->out.body, 0x02, io->smb2.out.session_flags);
48         SMB2SRV_CHECK(smb2_push_o16s16_blob(&req->out, 0x04, io->smb2.out.secblob));
49
50         smb2srv_send_reply(req);
51 }
52
53 struct smb2srv_sesssetup_callback_ctx {
54         struct smb2srv_request *req;
55         union smb_sesssetup *io;
56         struct smbsrv_session *smb_sess;
57 };
58
59 static void smb2srv_sesssetup_callback(struct tevent_req *subreq)
60 {
61         struct smb2srv_sesssetup_callback_ctx *ctx = tevent_req_callback_data(subreq,
62                                                      struct smb2srv_sesssetup_callback_ctx);
63         struct smb2srv_request *req = ctx->req;
64         union smb_sesssetup *io = ctx->io;
65         struct smbsrv_session *smb_sess = ctx->smb_sess;
66         struct auth_session_info *session_info = NULL;
67         enum security_user_level user_level;
68         NTSTATUS status;
69
70         packet_recv_enable(req->smb_conn->packet);
71
72         status = gensec_update_recv(subreq, req, &io->smb2.out.secblob);
73         TALLOC_FREE(subreq);
74         if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
75                 goto done;
76         } else if (!NT_STATUS_IS_OK(status)) {
77                 goto failed;
78         }
79
80         status = gensec_session_info(smb_sess->gensec_ctx, smb_sess, &session_info);
81         if (!NT_STATUS_IS_OK(status)) {
82                 goto failed;
83         }
84
85         /* Ensure this is marked as a 'real' vuid, not one
86          * simply valid for the session setup leg */
87         status = smbsrv_session_sesssetup_finished(smb_sess, session_info);
88         if (!NT_STATUS_IS_OK(status)) {
89                 goto failed;
90         }
91         req->session = smb_sess;
92
93         user_level = security_session_user_level(smb_sess->session_info, NULL);
94         if (user_level >= SECURITY_USER) {
95                 if (smb_sess->smb2_signing.required) {
96                         /* activate smb2 signing on the session */
97                         smb_sess->smb2_signing.active = true;
98                 }
99                 /* we need to sign the session setup response */
100                 req->is_signed = true;
101         }
102
103 done:
104         io->smb2.out.uid = smb_sess->vuid;
105 failed:
106         req->status = nt_status_squash(status);
107         smb2srv_sesssetup_send(req, io);
108         if (!NT_STATUS_IS_OK(status) && !
109             NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
110                 talloc_free(smb_sess);
111         }
112 }
113
114 static void smb2srv_sesssetup_backend(struct smb2srv_request *req, union smb_sesssetup *io)
115 {
116         NTSTATUS status;
117         struct smb2srv_sesssetup_callback_ctx *callback_ctx;
118         struct smbsrv_session *smb_sess = NULL;
119         uint64_t vuid;
120         struct tevent_req *subreq;
121
122         io->smb2.out.session_flags = 0;
123         io->smb2.out.uid        = 0;
124         io->smb2.out.secblob = data_blob(NULL, 0);
125
126         vuid = BVAL(req->in.hdr, SMB2_HDR_SESSION_ID);
127
128         /*
129          * only when we got '0' we should allocate a new session
130          */
131         if (vuid == 0) {
132                 struct gensec_security *gensec_ctx;
133
134                 status = samba_server_gensec_start(req,
135                                                    req->smb_conn->connection->event.ctx,
136                                                    req->smb_conn->connection->msg_ctx,
137                                                    req->smb_conn->lp_ctx,
138                                                    req->smb_conn->negotiate.server_credentials,
139                                                    "cifs",
140                                                    &gensec_ctx);
141                 if (!NT_STATUS_IS_OK(status)) {
142                         DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
143                         goto failed;
144                 }
145
146                 gensec_want_feature(gensec_ctx, GENSEC_FEATURE_SESSION_KEY);
147
148                 status = gensec_start_mech_by_oid(gensec_ctx, GENSEC_OID_SPNEGO);
149                 if (!NT_STATUS_IS_OK(status)) {
150                         DEBUG(1, ("Failed to start GENSEC SPNEGO server code: %s\n", nt_errstr(status)));
151                         goto failed;
152                 }
153
154                 /* allocate a new session */
155                 smb_sess = smbsrv_session_new(req->smb_conn, req->smb_conn, gensec_ctx);
156                 if (!smb_sess) {
157                         status = NT_STATUS_INSUFFICIENT_RESOURCES;
158                         goto failed;
159                 }
160                 status = smbsrv_smb2_init_tcons(smb_sess);
161                 if (!NT_STATUS_IS_OK(status)) {
162                         goto failed;
163                 }
164         } else {
165                 /* lookup an existing session */
166                 smb_sess = smbsrv_session_find_sesssetup(req->smb_conn, vuid);
167         }
168
169         if (!smb_sess) {
170                 status = NT_STATUS_USER_SESSION_DELETED;
171                 goto failed;
172         }
173
174         if (smb_sess->session_info) {
175                 /* see WSPP test suite - test 11 */
176                 status = NT_STATUS_REQUEST_NOT_ACCEPTED;
177                 goto failed;
178         }
179
180         if (!smb_sess->gensec_ctx) {
181                 status = NT_STATUS_INTERNAL_ERROR;
182                 DEBUG(1, ("Internal ERROR: no gensec_ctx on session: %s\n", nt_errstr(status)));
183                 goto failed;
184         }
185
186         callback_ctx = talloc(req, struct smb2srv_sesssetup_callback_ctx);
187         if (!callback_ctx) goto nomem;
188         callback_ctx->req       = req;
189         callback_ctx->io        = io;
190         callback_ctx->smb_sess  = smb_sess;
191
192         subreq = gensec_update_send(callback_ctx,
193                                     req->smb_conn->connection->event.ctx,
194                                     smb_sess->gensec_ctx,
195                                     io->smb2.in.secblob);
196         if (!subreq) goto nomem;
197         tevent_req_set_callback(subreq, smb2srv_sesssetup_callback, callback_ctx);
198
199         /* note that we ignore SMB2_NEGOTIATE_SIGNING_ENABLED from the client.
200            This is deliberate as windows does not set it even when it does 
201            set SMB2_NEGOTIATE_SIGNING_REQUIRED */
202         if (io->smb2.in.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
203                 smb_sess->smb2_signing.required = true;
204         }
205
206         /* disable receipt of more packets on this socket until we've
207            finished with the session setup. This avoids a problem with
208            crashes if we get EOF on the socket while processing a session
209            setup */
210         packet_recv_disable(req->smb_conn->packet);
211
212         return;
213 nomem:
214         status = NT_STATUS_NO_MEMORY;
215 failed:
216         talloc_free(smb_sess);
217         req->status = nt_status_squash(status);
218         smb2srv_sesssetup_send(req, io);
219 }
220
221 void smb2srv_sesssetup_recv(struct smb2srv_request *req)
222 {
223         union smb_sesssetup *io;
224
225         SMB2SRV_CHECK_BODY_SIZE(req, 0x18, true);
226         SMB2SRV_TALLOC_IO_PTR(io, union smb_sesssetup);
227
228         io->smb2.level                 = RAW_SESSSETUP_SMB2;
229         io->smb2.in.vc_number          = CVAL(req->in.body, 0x02);
230         io->smb2.in.security_mode      = CVAL(req->in.body, 0x03);
231         io->smb2.in.capabilities       = IVAL(req->in.body, 0x04);
232         io->smb2.in.channel            = IVAL(req->in.body, 0x08);
233         io->smb2.in.previous_sessionid = BVAL(req->in.body, 0x10);
234         SMB2SRV_CHECK(smb2_pull_o16s16_blob(&req->in, io, req->in.body+0x0C, &io->smb2.in.secblob));
235
236         smb2srv_sesssetup_backend(req, io);
237 }
238
239 static int smb2srv_cleanup_session_destructor(struct smbsrv_session **session)
240 {
241         /* TODO: call ntvfs backends to close file of this session */
242         DEBUG(0,("free session[%p]\n", *session));
243         talloc_free(*session);
244         return 0;
245 }
246
247 static NTSTATUS smb2srv_logoff_backend(struct smb2srv_request *req)
248 {
249         struct smbsrv_session **session_ptr;
250
251         /* we need to destroy the session after sending the reply */
252         session_ptr = talloc(req, struct smbsrv_session *);
253         NT_STATUS_HAVE_NO_MEMORY(session_ptr);
254
255         *session_ptr = req->session;
256         talloc_set_destructor(session_ptr, smb2srv_cleanup_session_destructor);
257
258         return NT_STATUS_OK;
259 }
260
261 static void smb2srv_logoff_send(struct smb2srv_request *req)
262 {
263         if (NT_STATUS_IS_ERR(req->status)) {
264                 smb2srv_send_error(req, req->status);
265                 return;
266         }
267
268         SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x04, false, 0));
269
270         SSVAL(req->out.body, 0x02, 0);
271
272         smb2srv_send_reply(req);
273 }
274
275 void smb2srv_logoff_recv(struct smb2srv_request *req)
276 {
277         SMB2SRV_CHECK_BODY_SIZE(req, 0x04, false);
278
279         req->status = smb2srv_logoff_backend(req);
280
281         if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
282                 talloc_free(req);
283                 return;
284         }
285         smb2srv_logoff_send(req);
286 }