1 | /*
|
---|
2 | Unix SMB/CIFS implementation.
|
---|
3 |
|
---|
4 | SMB2 client session handling
|
---|
5 |
|
---|
6 | Copyright (C) Andrew Tridgell 2005
|
---|
7 |
|
---|
8 | This program is free software; you can redistribute it and/or modify
|
---|
9 | it under the terms of the GNU General Public License as published by
|
---|
10 | the Free Software Foundation; either version 3 of the License, or
|
---|
11 | (at your option) any later version.
|
---|
12 |
|
---|
13 | This program is distributed in the hope that it will be useful,
|
---|
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
16 | GNU General Public License for more details.
|
---|
17 |
|
---|
18 | You should have received a copy of the GNU General Public License
|
---|
19 | along with this program. If not, see <http://www.gnu.org/licenses/>.
|
---|
20 | */
|
---|
21 |
|
---|
22 | #include "includes.h"
|
---|
23 | #include "libcli/raw/libcliraw.h"
|
---|
24 | #include "libcli/smb2/smb2.h"
|
---|
25 | #include "libcli/smb2/smb2_calls.h"
|
---|
26 | #include "libcli/composite/composite.h"
|
---|
27 | #include "auth/gensec/gensec.h"
|
---|
28 |
|
---|
29 | #include <unistd.h>
|
---|
30 |
|
---|
31 | /**
|
---|
32 | initialise a smb2_session structure
|
---|
33 | */
|
---|
34 | struct smb2_session *smb2_session_init(struct smb2_transport *transport,
|
---|
35 | struct gensec_settings *settings,
|
---|
36 | TALLOC_CTX *parent_ctx, bool primary)
|
---|
37 | {
|
---|
38 | struct smb2_session *session;
|
---|
39 | NTSTATUS status;
|
---|
40 |
|
---|
41 | session = talloc_zero(parent_ctx, struct smb2_session);
|
---|
42 | if (!session) {
|
---|
43 | return NULL;
|
---|
44 | }
|
---|
45 | if (primary) {
|
---|
46 | session->transport = talloc_steal(session, transport);
|
---|
47 | } else {
|
---|
48 | session->transport = talloc_reference(session, transport);
|
---|
49 | }
|
---|
50 |
|
---|
51 | session->pid = getpid();
|
---|
52 |
|
---|
53 | /* prepare a gensec context for later use */
|
---|
54 | status = gensec_client_start(session, &session->gensec,
|
---|
55 | session->transport->socket->event.ctx,
|
---|
56 | settings);
|
---|
57 | if (!NT_STATUS_IS_OK(status)) {
|
---|
58 | talloc_free(session);
|
---|
59 | return NULL;
|
---|
60 | }
|
---|
61 |
|
---|
62 | gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
|
---|
63 |
|
---|
64 | return session;
|
---|
65 | }
|
---|
66 |
|
---|
67 | /**
|
---|
68 | send a session setup request
|
---|
69 | */
|
---|
70 | struct smb2_request *smb2_session_setup_send(struct smb2_session *session,
|
---|
71 | struct smb2_session_setup *io)
|
---|
72 | {
|
---|
73 | struct smb2_request *req;
|
---|
74 | NTSTATUS status;
|
---|
75 |
|
---|
76 | req = smb2_request_init(session->transport, SMB2_OP_SESSSETUP,
|
---|
77 | 0x18, true, io->in.secblob.length);
|
---|
78 | if (req == NULL) return NULL;
|
---|
79 |
|
---|
80 | SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, session->uid);
|
---|
81 | SCVAL(req->out.body, 0x02, io->in.vc_number);
|
---|
82 | SCVAL(req->out.body, 0x03, io->in.security_mode);
|
---|
83 | SIVAL(req->out.body, 0x04, io->in.capabilities);
|
---|
84 | SIVAL(req->out.body, 0x08, io->in.channel);
|
---|
85 | SBVAL(req->out.body, 0x10, io->in.previous_sessionid);
|
---|
86 |
|
---|
87 | req->session = session;
|
---|
88 |
|
---|
89 | status = smb2_push_o16s16_blob(&req->out, 0x0C, io->in.secblob);
|
---|
90 | if (!NT_STATUS_IS_OK(status)) {
|
---|
91 | talloc_free(req);
|
---|
92 | return NULL;
|
---|
93 | }
|
---|
94 |
|
---|
95 | smb2_transport_send(req);
|
---|
96 |
|
---|
97 | return req;
|
---|
98 | }
|
---|
99 |
|
---|
100 |
|
---|
101 | /**
|
---|
102 | recv a session setup reply
|
---|
103 | */
|
---|
104 | NTSTATUS smb2_session_setup_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
|
---|
105 | struct smb2_session_setup *io)
|
---|
106 | {
|
---|
107 | NTSTATUS status;
|
---|
108 |
|
---|
109 | if (!smb2_request_receive(req) ||
|
---|
110 | (smb2_request_is_error(req) &&
|
---|
111 | !NT_STATUS_EQUAL(req->status, NT_STATUS_MORE_PROCESSING_REQUIRED))) {
|
---|
112 | return smb2_request_destroy(req);
|
---|
113 | }
|
---|
114 |
|
---|
115 | SMB2_CHECK_PACKET_RECV(req, 0x08, true);
|
---|
116 |
|
---|
117 | io->out.session_flags = SVAL(req->in.body, 0x02);
|
---|
118 | io->out.uid = BVAL(req->in.hdr, SMB2_HDR_SESSION_ID);
|
---|
119 |
|
---|
120 | status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x04, &io->out.secblob);
|
---|
121 | if (!NT_STATUS_IS_OK(status)) {
|
---|
122 | smb2_request_destroy(req);
|
---|
123 | return status;
|
---|
124 | }
|
---|
125 |
|
---|
126 | return smb2_request_destroy(req);
|
---|
127 | }
|
---|
128 |
|
---|
129 | /*
|
---|
130 | sync session setup request
|
---|
131 | */
|
---|
132 | NTSTATUS smb2_session_setup(struct smb2_session *session,
|
---|
133 | TALLOC_CTX *mem_ctx, struct smb2_session_setup *io)
|
---|
134 | {
|
---|
135 | struct smb2_request *req = smb2_session_setup_send(session, io);
|
---|
136 | return smb2_session_setup_recv(req, mem_ctx, io);
|
---|
137 | }
|
---|
138 |
|
---|
139 |
|
---|
140 | struct smb2_session_state {
|
---|
141 | struct smb2_session_setup io;
|
---|
142 | struct smb2_request *req;
|
---|
143 | NTSTATUS gensec_status;
|
---|
144 | };
|
---|
145 |
|
---|
146 | /*
|
---|
147 | handle continuations of the spnego session setup
|
---|
148 | */
|
---|
149 | static void session_request_handler(struct smb2_request *req)
|
---|
150 | {
|
---|
151 | struct composite_context *c = talloc_get_type(req->async.private_data,
|
---|
152 | struct composite_context);
|
---|
153 | struct smb2_session_state *state = talloc_get_type(c->private_data,
|
---|
154 | struct smb2_session_state);
|
---|
155 | struct smb2_session *session = req->session;
|
---|
156 | NTSTATUS session_key_err;
|
---|
157 | DATA_BLOB session_key;
|
---|
158 | NTSTATUS peer_status;
|
---|
159 |
|
---|
160 | c->status = smb2_session_setup_recv(req, c, &state->io);
|
---|
161 | peer_status = c->status;
|
---|
162 |
|
---|
163 | if (NT_STATUS_EQUAL(peer_status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
|
---|
164 | (NT_STATUS_IS_OK(peer_status) &&
|
---|
165 | NT_STATUS_EQUAL(state->gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED))) {
|
---|
166 | c->status = gensec_update(session->gensec, c,
|
---|
167 | state->io.out.secblob,
|
---|
168 | &state->io.in.secblob);
|
---|
169 | state->gensec_status = c->status;
|
---|
170 |
|
---|
171 | session->uid = state->io.out.uid;
|
---|
172 | }
|
---|
173 |
|
---|
174 | if (!NT_STATUS_IS_OK(c->status) &&
|
---|
175 | !NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
---|
176 | composite_error(c, c->status);
|
---|
177 | return;
|
---|
178 | }
|
---|
179 |
|
---|
180 | if (NT_STATUS_EQUAL(peer_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
---|
181 | state->req = smb2_session_setup_send(session, &state->io);
|
---|
182 | if (state->req == NULL) {
|
---|
183 | composite_error(c, NT_STATUS_NO_MEMORY);
|
---|
184 | return;
|
---|
185 | }
|
---|
186 |
|
---|
187 | state->req->async.fn = session_request_handler;
|
---|
188 | state->req->async.private_data = c;
|
---|
189 | return;
|
---|
190 | }
|
---|
191 |
|
---|
192 | session_key_err = gensec_session_key(session->gensec, &session_key);
|
---|
193 | if (NT_STATUS_IS_OK(session_key_err)) {
|
---|
194 | session->session_key = session_key;
|
---|
195 | }
|
---|
196 |
|
---|
197 | if (session->transport->signing_required) {
|
---|
198 | if (session->session_key.length == 0) {
|
---|
199 | DEBUG(0,("Wrong session key length %u for SMB2 signing\n",
|
---|
200 | (unsigned)session->session_key.length));
|
---|
201 | composite_error(c, NT_STATUS_ACCESS_DENIED);
|
---|
202 | return;
|
---|
203 | }
|
---|
204 | session->signing_active = true;
|
---|
205 | }
|
---|
206 |
|
---|
207 | composite_done(c);
|
---|
208 | }
|
---|
209 |
|
---|
210 | /*
|
---|
211 | a composite function that does a full SPNEGO session setup
|
---|
212 | */
|
---|
213 | struct composite_context *smb2_session_setup_spnego_send(struct smb2_session *session,
|
---|
214 | struct cli_credentials *credentials)
|
---|
215 | {
|
---|
216 | struct composite_context *c;
|
---|
217 | struct smb2_session_state *state;
|
---|
218 | const char *chosen_oid;
|
---|
219 |
|
---|
220 | c = composite_create(session, session->transport->socket->event.ctx);
|
---|
221 | if (c == NULL) return NULL;
|
---|
222 |
|
---|
223 | state = talloc(c, struct smb2_session_state);
|
---|
224 | if (composite_nomem(state, c)) return c;
|
---|
225 | c->private_data = state;
|
---|
226 |
|
---|
227 | ZERO_STRUCT(state->io);
|
---|
228 | state->io.in.vc_number = 0;
|
---|
229 | if (session->transport->signing_required) {
|
---|
230 | state->io.in.security_mode =
|
---|
231 | SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED;
|
---|
232 | }
|
---|
233 | state->io.in.capabilities = 0;
|
---|
234 | state->io.in.channel = 0;
|
---|
235 | state->io.in.previous_sessionid = 0;
|
---|
236 |
|
---|
237 | c->status = gensec_set_credentials(session->gensec, credentials);
|
---|
238 | if (!composite_is_ok(c)) return c;
|
---|
239 |
|
---|
240 | c->status = gensec_set_target_hostname(session->gensec,
|
---|
241 | session->transport->socket->hostname);
|
---|
242 | if (!composite_is_ok(c)) return c;
|
---|
243 |
|
---|
244 | c->status = gensec_set_target_service(session->gensec, "cifs");
|
---|
245 | if (!composite_is_ok(c)) return c;
|
---|
246 |
|
---|
247 | if (session->transport->negotiate.secblob.length > 0) {
|
---|
248 | chosen_oid = GENSEC_OID_SPNEGO;
|
---|
249 | } else {
|
---|
250 | chosen_oid = GENSEC_OID_NTLMSSP;
|
---|
251 | }
|
---|
252 |
|
---|
253 | c->status = gensec_start_mech_by_oid(session->gensec, chosen_oid);
|
---|
254 | if (!composite_is_ok(c)) return c;
|
---|
255 |
|
---|
256 | c->status = gensec_update(session->gensec, c,
|
---|
257 | session->transport->negotiate.secblob,
|
---|
258 | &state->io.in.secblob);
|
---|
259 | if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
---|
260 | composite_error(c, c->status);
|
---|
261 | return c;
|
---|
262 | }
|
---|
263 | state->gensec_status = c->status;
|
---|
264 |
|
---|
265 | state->req = smb2_session_setup_send(session, &state->io);
|
---|
266 | composite_continue_smb2(c, state->req, session_request_handler, c);
|
---|
267 | return c;
|
---|
268 | }
|
---|
269 |
|
---|
270 | /*
|
---|
271 | receive a composite session setup reply
|
---|
272 | */
|
---|
273 | NTSTATUS smb2_session_setup_spnego_recv(struct composite_context *c)
|
---|
274 | {
|
---|
275 | NTSTATUS status;
|
---|
276 | status = composite_wait(c);
|
---|
277 | talloc_free(c);
|
---|
278 | return status;
|
---|
279 | }
|
---|
280 |
|
---|
281 | /*
|
---|
282 | sync version of smb2_session_setup_spnego
|
---|
283 | */
|
---|
284 | NTSTATUS smb2_session_setup_spnego(struct smb2_session *session,
|
---|
285 | struct cli_credentials *credentials)
|
---|
286 | {
|
---|
287 | struct composite_context *c = smb2_session_setup_spnego_send(session, credentials);
|
---|
288 | return smb2_session_setup_spnego_recv(c);
|
---|
289 | }
|
---|