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 "auth/credentials/credentials.h"
|
---|
23 | #include "auth/auth.h"
|
---|
24 | #include "auth/gensec/gensec.h"
|
---|
25 | #include "libcli/raw/libcliraw.h"
|
---|
26 | #include "libcli/raw/raw_proto.h"
|
---|
27 | #include "libcli/smb2/smb2.h"
|
---|
28 | #include "libcli/smb2/smb2_calls.h"
|
---|
29 | #include "smb_server/smb_server.h"
|
---|
30 | #include "smb_server/smb2/smb2_server.h"
|
---|
31 | #include "smbd/service_stream.h"
|
---|
32 | #include "param/param.h"
|
---|
33 |
|
---|
34 | static NTSTATUS smb2srv_negprot_secblob(struct smb2srv_request *req, DATA_BLOB *_blob)
|
---|
35 | {
|
---|
36 | struct gensec_security *gensec_security;
|
---|
37 | DATA_BLOB null_data_blob = data_blob(NULL, 0);
|
---|
38 | DATA_BLOB blob;
|
---|
39 | NTSTATUS nt_status;
|
---|
40 | struct cli_credentials *server_credentials;
|
---|
41 |
|
---|
42 | server_credentials = cli_credentials_init(req);
|
---|
43 | if (!server_credentials) {
|
---|
44 | smbsrv_terminate_connection(req->smb_conn, "Failed to init server credentials\n");
|
---|
45 | return NT_STATUS_NO_MEMORY;
|
---|
46 | }
|
---|
47 |
|
---|
48 | cli_credentials_set_conf(server_credentials, req->smb_conn->lp_ctx);
|
---|
49 | nt_status = cli_credentials_set_machine_account(server_credentials, req->smb_conn->lp_ctx);
|
---|
50 | if (!NT_STATUS_IS_OK(nt_status)) {
|
---|
51 | DEBUG(10, ("Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(nt_status)));
|
---|
52 | talloc_free(server_credentials);
|
---|
53 | server_credentials = NULL;
|
---|
54 | }
|
---|
55 |
|
---|
56 | req->smb_conn->negotiate.server_credentials = talloc_steal(req->smb_conn, server_credentials);
|
---|
57 |
|
---|
58 | nt_status = samba_server_gensec_start(req,
|
---|
59 | req->smb_conn->connection->event.ctx,
|
---|
60 | req->smb_conn->connection->msg_ctx,
|
---|
61 | req->smb_conn->lp_ctx,
|
---|
62 | server_credentials,
|
---|
63 | "cifs",
|
---|
64 | &gensec_security);
|
---|
65 | if (!NT_STATUS_IS_OK(nt_status)) {
|
---|
66 | DEBUG(0, ("Failed to start GENSEC: %s\n", nt_errstr(nt_status)));
|
---|
67 | smbsrv_terminate_connection(req->smb_conn, "Failed to start GENSEC\n");
|
---|
68 | return nt_status;
|
---|
69 | }
|
---|
70 |
|
---|
71 | gensec_set_target_service(gensec_security, "cifs");
|
---|
72 |
|
---|
73 | gensec_set_credentials(gensec_security, server_credentials);
|
---|
74 |
|
---|
75 | nt_status = gensec_start_mech_by_oid(gensec_security, GENSEC_OID_SPNEGO);
|
---|
76 | if (!NT_STATUS_IS_OK(nt_status)) {
|
---|
77 | DEBUG(0, ("Failed to start SPNEGO: %s\n", nt_errstr(nt_status)));
|
---|
78 | smbsrv_terminate_connection(req->smb_conn, "Failed to start SPNEGO\n");
|
---|
79 | return nt_status;
|
---|
80 | }
|
---|
81 |
|
---|
82 | nt_status = gensec_update(gensec_security, req, null_data_blob, &blob);
|
---|
83 | if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
---|
84 | DEBUG(0, ("Failed to get SPNEGO to give us the first token: %s\n", nt_errstr(nt_status)));
|
---|
85 | smbsrv_terminate_connection(req->smb_conn, "Failed to start SPNEGO - no first token\n");
|
---|
86 | return nt_status;
|
---|
87 | }
|
---|
88 |
|
---|
89 | *_blob = blob;
|
---|
90 | return NT_STATUS_OK;
|
---|
91 | }
|
---|
92 |
|
---|
93 | static NTSTATUS smb2srv_negprot_backend(struct smb2srv_request *req, struct smb2_negprot *io)
|
---|
94 | {
|
---|
95 | NTSTATUS status;
|
---|
96 | struct timeval current_time;
|
---|
97 | struct timeval boot_time;
|
---|
98 | uint16_t i;
|
---|
99 | uint16_t dialect = 0;
|
---|
100 |
|
---|
101 | /* we only do one dialect for now */
|
---|
102 | if (io->in.dialect_count < 1) {
|
---|
103 | return NT_STATUS_NOT_SUPPORTED;
|
---|
104 | }
|
---|
105 | for (i=0; i < io->in.dialect_count; i++) {
|
---|
106 | dialect = io->in.dialects[i];
|
---|
107 | if (dialect == SMB2_DIALECT_REVISION_202) {
|
---|
108 | break;
|
---|
109 | }
|
---|
110 | }
|
---|
111 | if (dialect != SMB2_DIALECT_REVISION_202) {
|
---|
112 | DEBUG(0,("Got unexpected SMB2 dialect %u\n", dialect));
|
---|
113 | return NT_STATUS_NOT_SUPPORTED;
|
---|
114 | }
|
---|
115 |
|
---|
116 | req->smb_conn->negotiate.protocol = PROTOCOL_SMB2;
|
---|
117 |
|
---|
118 | current_time = timeval_current(); /* TODO: handle timezone?! */
|
---|
119 | boot_time = timeval_current(); /* TODO: fix me */
|
---|
120 |
|
---|
121 | ZERO_STRUCT(io->out);
|
---|
122 | switch (lp_server_signing(req->smb_conn->lp_ctx)) {
|
---|
123 | case SMB_SIGNING_OFF:
|
---|
124 | io->out.security_mode = 0;
|
---|
125 | break;
|
---|
126 | case SMB_SIGNING_SUPPORTED:
|
---|
127 | case SMB_SIGNING_AUTO:
|
---|
128 | io->out.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED;
|
---|
129 | break;
|
---|
130 | case SMB_SIGNING_REQUIRED:
|
---|
131 | io->out.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED;
|
---|
132 | /* force signing on immediately */
|
---|
133 | req->smb_conn->smb2_signing_required = true;
|
---|
134 | break;
|
---|
135 | }
|
---|
136 | io->out.dialect_revision = dialect;
|
---|
137 | io->out.capabilities = 0;
|
---|
138 | io->out.max_transact_size = lp_parm_ulong(req->smb_conn->lp_ctx, NULL,
|
---|
139 | "smb2", "max transaction size", 0x10000);
|
---|
140 | io->out.max_read_size = lp_parm_ulong(req->smb_conn->lp_ctx, NULL,
|
---|
141 | "smb2", "max read size", 0x10000);
|
---|
142 | io->out.max_write_size = lp_parm_ulong(req->smb_conn->lp_ctx, NULL,
|
---|
143 | "smb2", "max write size", 0x10000);
|
---|
144 | io->out.system_time = timeval_to_nttime(¤t_time);
|
---|
145 | io->out.server_start_time = timeval_to_nttime(&boot_time);
|
---|
146 | io->out.reserved2 = 0;
|
---|
147 | status = smb2srv_negprot_secblob(req, &io->out.secblob);
|
---|
148 | NT_STATUS_NOT_OK_RETURN(status);
|
---|
149 |
|
---|
150 | return NT_STATUS_OK;
|
---|
151 | }
|
---|
152 |
|
---|
153 | static void smb2srv_negprot_send(struct smb2srv_request *req, struct smb2_negprot *io)
|
---|
154 | {
|
---|
155 | NTSTATUS status;
|
---|
156 | enum ndr_err_code ndr_err;
|
---|
157 |
|
---|
158 | if (NT_STATUS_IS_ERR(req->status)) {
|
---|
159 | smb2srv_send_error(req, req->status); /* TODO: is this correct? */
|
---|
160 | return;
|
---|
161 | }
|
---|
162 |
|
---|
163 | status = smb2srv_setup_reply(req, 0x40, true, io->out.secblob.length);
|
---|
164 | if (!NT_STATUS_IS_OK(status)) {
|
---|
165 | smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
|
---|
166 | talloc_free(req);
|
---|
167 | return;
|
---|
168 | }
|
---|
169 |
|
---|
170 | SSVAL(req->out.body, 0x02, io->out.security_mode);
|
---|
171 | SIVAL(req->out.body, 0x04, io->out.dialect_revision);
|
---|
172 | SIVAL(req->out.body, 0x06, io->out.reserved);
|
---|
173 | ndr_err = smbcli_push_guid(req->out.body, 0x08, &io->out.server_guid);
|
---|
174 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
---|
175 | smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
|
---|
176 | talloc_free(req);
|
---|
177 | return;
|
---|
178 | }
|
---|
179 | SIVAL(req->out.body, 0x18, io->out.capabilities);
|
---|
180 | SIVAL(req->out.body, 0x1C, io->out.max_transact_size);
|
---|
181 | SIVAL(req->out.body, 0x20, io->out.max_read_size);
|
---|
182 | SIVAL(req->out.body, 0x24, io->out.max_write_size);
|
---|
183 | push_nttime(req->out.body, 0x28, io->out.system_time);
|
---|
184 | push_nttime(req->out.body, 0x30, io->out.server_start_time);
|
---|
185 | SIVAL(req->out.body, 0x3C, io->out.reserved2);
|
---|
186 | status = smb2_push_o16s16_blob(&req->out, 0x38, io->out.secblob);
|
---|
187 | if (!NT_STATUS_IS_OK(status)) {
|
---|
188 | smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
|
---|
189 | talloc_free(req);
|
---|
190 | return;
|
---|
191 | }
|
---|
192 |
|
---|
193 | smb2srv_send_reply(req);
|
---|
194 | }
|
---|
195 |
|
---|
196 | void smb2srv_negprot_recv(struct smb2srv_request *req)
|
---|
197 | {
|
---|
198 | struct smb2_negprot *io;
|
---|
199 | int i;
|
---|
200 | enum ndr_err_code ndr_err;
|
---|
201 |
|
---|
202 | if (req->in.body_size < 0x26) {
|
---|
203 | smbsrv_terminate_connection(req->smb_conn, "Bad body size in SMB2 negprot");
|
---|
204 | return;
|
---|
205 | }
|
---|
206 |
|
---|
207 | io = talloc(req, struct smb2_negprot);
|
---|
208 | if (!io) {
|
---|
209 | smbsrv_terminate_connection(req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
|
---|
210 | talloc_free(req);
|
---|
211 | return;
|
---|
212 | }
|
---|
213 |
|
---|
214 | io->in.dialect_count = SVAL(req->in.body, 0x02);
|
---|
215 | io->in.security_mode = SVAL(req->in.body, 0x04);
|
---|
216 | io->in.reserved = SVAL(req->in.body, 0x06);
|
---|
217 | io->in.capabilities = IVAL(req->in.body, 0x08);
|
---|
218 | ndr_err = smbcli_pull_guid(req->in.body, 0xC, &io->in.client_guid);
|
---|
219 | if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
---|
220 | smbsrv_terminate_connection(req->smb_conn, "Bad GUID in SMB2 negprot");
|
---|
221 | talloc_free(req);
|
---|
222 | return;
|
---|
223 | }
|
---|
224 | io->in.start_time = smbcli_pull_nttime(req->in.body, 0x1C);
|
---|
225 |
|
---|
226 | io->in.dialects = talloc_array(req, uint16_t, io->in.dialect_count);
|
---|
227 | if (io->in.dialects == NULL) {
|
---|
228 | smbsrv_terminate_connection(req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
|
---|
229 | talloc_free(req);
|
---|
230 | return;
|
---|
231 | }
|
---|
232 | for (i=0;i<io->in.dialect_count;i++) {
|
---|
233 | io->in.dialects[i] = SVAL(req->in.body, 0x24+i*2);
|
---|
234 | }
|
---|
235 |
|
---|
236 | req->status = smb2srv_negprot_backend(req, io);
|
---|
237 |
|
---|
238 | if (req->control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY) {
|
---|
239 | talloc_free(req);
|
---|
240 | return;
|
---|
241 | }
|
---|
242 | smb2srv_negprot_send(req, io);
|
---|
243 | }
|
---|
244 |
|
---|
245 | /*
|
---|
246 | * reply to a SMB negprot request with dialect "SMB 2.002"
|
---|
247 | */
|
---|
248 | void smb2srv_reply_smb_negprot(struct smbsrv_request *smb_req)
|
---|
249 | {
|
---|
250 | struct smb2srv_request *req;
|
---|
251 | uint32_t body_fixed_size = 0x26;
|
---|
252 |
|
---|
253 | req = talloc_zero(smb_req->smb_conn, struct smb2srv_request);
|
---|
254 | if (!req) goto nomem;
|
---|
255 | req->smb_conn = smb_req->smb_conn;
|
---|
256 | req->request_time = smb_req->request_time;
|
---|
257 | talloc_steal(req, smb_req);
|
---|
258 |
|
---|
259 | req->in.size = NBT_HDR_SIZE+SMB2_HDR_BODY+body_fixed_size;
|
---|
260 | req->in.allocated = req->in.size;
|
---|
261 | req->in.buffer = talloc_array(req, uint8_t, req->in.allocated);
|
---|
262 | if (!req->in.buffer) goto nomem;
|
---|
263 | req->in.hdr = req->in.buffer + NBT_HDR_SIZE;
|
---|
264 | req->in.body = req->in.hdr + SMB2_HDR_BODY;
|
---|
265 | req->in.body_size = body_fixed_size;
|
---|
266 | req->in.dynamic = NULL;
|
---|
267 |
|
---|
268 | smb2srv_setup_bufinfo(req);
|
---|
269 |
|
---|
270 | SIVAL(req->in.hdr, 0, SMB2_MAGIC);
|
---|
271 | SSVAL(req->in.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
|
---|
272 | SSVAL(req->in.hdr, SMB2_HDR_EPOCH, 0);
|
---|
273 | SIVAL(req->in.hdr, SMB2_HDR_STATUS, 0);
|
---|
274 | SSVAL(req->in.hdr, SMB2_HDR_OPCODE, SMB2_OP_NEGPROT);
|
---|
275 | SSVAL(req->in.hdr, SMB2_HDR_CREDIT, 0);
|
---|
276 | SIVAL(req->in.hdr, SMB2_HDR_FLAGS, 0);
|
---|
277 | SIVAL(req->in.hdr, SMB2_HDR_NEXT_COMMAND, 0);
|
---|
278 | SBVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID, 0);
|
---|
279 | SIVAL(req->in.hdr, SMB2_HDR_PID, 0);
|
---|
280 | SIVAL(req->in.hdr, SMB2_HDR_TID, 0);
|
---|
281 | SBVAL(req->in.hdr, SMB2_HDR_SESSION_ID, 0);
|
---|
282 | memset(req->in.hdr+SMB2_HDR_SIGNATURE, 0, 16);
|
---|
283 |
|
---|
284 | /* this seems to be a bug, they use 0x24 but the length is 0x26 */
|
---|
285 | SSVAL(req->in.body, 0x00, 0x24);
|
---|
286 |
|
---|
287 | SSVAL(req->in.body, 0x02, 1);
|
---|
288 | memset(req->in.body+0x04, 0, 32);
|
---|
289 | SSVAL(req->in.body, 0x24, SMB2_DIALECT_REVISION_202);
|
---|
290 |
|
---|
291 | smb2srv_negprot_recv(req);
|
---|
292 | return;
|
---|
293 | nomem:
|
---|
294 | smbsrv_terminate_connection(smb_req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
|
---|
295 | talloc_free(req);
|
---|
296 | return;
|
---|
297 | }
|
---|