1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * This file provides some common functionality for SMB Redirector
28 * module.
29 */
30
31 #include <unistd.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <smbrdr.h>
35
36 smb_log_hdl_t smbrdr_log_hdl;
37
38 static DWORD smbrdr_handle_setup(smbrdr_handle_t *, unsigned char,
39 struct sdb_session *, struct sdb_logon *, struct sdb_netuse *);
40 static int smbrdr_hdr_setup(smbrdr_handle_t *);
41 static DWORD smbrdr_hdr_process(smbrdr_handle_t *, smb_hdr_t *);
42 static int smbrdr_sign(smb_sign_ctx_t *, smb_msgbuf_t *);
43 static int smbrdr_sign_chk(smb_sign_ctx_t *, smb_msgbuf_t *, unsigned char *);
44
smbrdr_lock_transport()45 void smbrdr_lock_transport() { nb_lock(); }
smbrdr_unlock_transport()46 void smbrdr_unlock_transport() { nb_unlock(); }
47
48 #pragma init(_smbrdr_init)
49
50 void
_smbrdr_init(void)51 _smbrdr_init(void)
52 {
53 smbrdr_log_hdl = smb_log_create(SMBRDR_LOG_MAXCNT, SMBRDR_LOG_NAME);
54 }
55
56 /*
57 * smbrdr_request_init
58 *
59 * Setup a handle with given information and then
60 * setup a SMB header structure.
61 *
62 * Returns:
63 *
64 * NT_STATUS_NO_MEMORY no memory for creating request
65 * NT_STATUS_INTERNAL_ERROR header encode failed or crypto failed
66 * NT_STATUS_SUCCESS successful
67 */
68 DWORD
smbrdr_request_init(smbrdr_handle_t * srh,unsigned char cmd,struct sdb_session * session,struct sdb_logon * logon,struct sdb_netuse * netuse)69 smbrdr_request_init(smbrdr_handle_t *srh,
70 unsigned char cmd,
71 struct sdb_session *session,
72 struct sdb_logon *logon,
73 struct sdb_netuse *netuse)
74 {
75 DWORD status;
76
77 status = smbrdr_handle_setup(srh, cmd, session, logon, netuse);
78 if (status != NT_STATUS_SUCCESS)
79 return (status);
80
81 if (smbrdr_hdr_setup(srh) < SMB_HEADER_LEN) {
82 smbrdr_handle_free(srh);
83 return (NT_STATUS_INTERNAL_ERROR);
84 }
85
86 return (NT_STATUS_SUCCESS);
87 }
88
89 /*
90 * smbrdr_send
91 *
92 * Send the SMB packet pointed by the given handle over
93 * network.
94 *
95 * Returns:
96 *
97 * NT_STATUS_INTERNAL_ERROR crypto framework failure
98 * NT_STATUS_UNEXPECTED_NETWORK_ERROR send failed
99 * NT_STATUS_SUCCESS successful
100 */
101 DWORD
smbrdr_send(smbrdr_handle_t * srh)102 smbrdr_send(smbrdr_handle_t *srh)
103 {
104 int rc;
105
106 if (smbrdr_sign(&srh->srh_session->sign_ctx, &srh->srh_mbuf) !=
107 SMBAUTH_SUCCESS) {
108 smb_log(smbrdr_log_hdl, LOG_ERR,
109 "smbrdr_send[%d]: signing failed", srh->srh_cmd);
110 return (NT_STATUS_INTERNAL_ERROR);
111 }
112
113 rc = nb_send(srh->srh_session->sock, srh->srh_buf,
114 smb_msgbuf_used(&srh->srh_mbuf));
115
116 if (rc < 0) {
117 /*
118 * Make the sequence number of the next SMB request even
119 * to avoid DC from failing the next SMB request with
120 * ACCESS_DENIED.
121 */
122 smb_mac_dec_seqnum(&srh->srh_session->sign_ctx);
123 smb_log(smbrdr_log_hdl, LOG_ERR,
124 "smbrdr_send[%d]: send failed (%d)", srh->srh_cmd, rc);
125 return (NT_STATUS_UNEXPECTED_NETWORK_ERROR);
126 }
127
128 return (NT_STATUS_SUCCESS);
129 }
130
131 /*
132 * smbrdr_rcv
133 *
134 * Receive a SMB response and decode the packet header.
135 *
136 * "Implementing CIFS" book, SMB requests always have an even sequence
137 * number and replies always have an odd.
138 *
139 * With the original code, if the SMB Redirector skip the counter increment
140 * in the event of any failure during SmbSessionSetupAndX, it causes the
141 * domain controller to fail the next SMB request(odd sequence number)
142 * with ACCESS_DENIED.
143 *
144 * Smbrdr module should use the same sequence number (i.e. ssc_seqnum of the
145 * SMB Sign context) for generating the MAC signature for all incoming
146 * responses per SmbTransact request. Otherwise, the validation will fail.
147 * It is now fixed by decrementing the sequence number prior to validating
148 * the subsequent responses for a single request.
149 *
150 * Returns:
151 *
152 * status code returned by smbrdr_hdr_process()
153 * NT_STATUS_UNEXPECTED_NETWORK_ERROR receive failed
154 * NT_STATUS_SUCCESS successful
155 */
156 DWORD
smbrdr_rcv(smbrdr_handle_t * srh,int is_first_rsp)157 smbrdr_rcv(smbrdr_handle_t *srh, int is_first_rsp)
158 {
159 smb_hdr_t smb_hdr;
160 DWORD status;
161 int rc;
162 smb_sign_ctx_t *sign_ctx = &srh->srh_session->sign_ctx;
163
164 rc = nb_rcv(srh->srh_session->sock, srh->srh_buf, SMBRDR_REQ_BUFSZ, 0);
165 if (rc < 0) {
166 smb_mac_inc_seqnum(sign_ctx);
167 smb_log(smbrdr_log_hdl, LOG_ERR,
168 "smbrdr_rcv[%d]: receive failed (%d)", srh->srh_cmd, rc);
169 return (NT_STATUS_UNEXPECTED_NETWORK_ERROR);
170 }
171
172 smb_msgbuf_init(&srh->srh_mbuf, srh->srh_buf, rc, srh->srh_mbflags);
173
174 status = smbrdr_hdr_process(srh, &smb_hdr);
175 if (status != NT_STATUS_SUCCESS) {
176 smb_mac_inc_seqnum(sign_ctx);
177 smb_log(smbrdr_log_hdl, LOG_ERR,
178 "smbrdr_rcv[%d]: failed (%s)", srh->srh_cmd,
179 xlate_nt_status(status));
180 return (status);
181 }
182
183 if (!is_first_rsp)
184 smb_mac_dec_seqnum(sign_ctx);
185
186 if (!smbrdr_sign_chk(sign_ctx,
187 &srh->srh_mbuf, smb_hdr.extra.extra.security_sig)) {
188 smb_log(smbrdr_log_hdl, LOG_ERR,
189 "smbrdr_rcv[%d]: bad signature", srh->srh_cmd);
190 return (NT_STATUS_INVALID_NETWORK_RESPONSE);
191 }
192
193 return (NT_STATUS_SUCCESS);
194 }
195
196 /*
197 * smbrdr_exchange
198 *
199 * Send the SMB packet pointed by the given handle over
200 * network. Receive the response and decode the packet header.
201 *
202 * From "Implementing CIFS" book, SMB requests always have an even sequence
203 * number and replies always have an odd.
204 *
205 * With the original code, if the SMB Redirector skips the counter increment
206 * in the event of any failure during SmbSessionSetupAndX, it causes the
207 * domain controller to fail the next SMB request(odd sequence number)
208 * with ACCESS_DENIED.
209 *
210 * Returns:
211 *
212 * status code returned by smbrdr_hdr_process()
213 * NT_STATUS_INTERNAL_ERROR crypto framework failure
214 * NT_STATUS_UNEXPECTED_NETWORK_ERROR send/receive failed
215 * NT_STATUS_SUCCESS successful
216 */
217 DWORD
smbrdr_exchange(smbrdr_handle_t * srh,smb_hdr_t * smb_hdr,long timeout)218 smbrdr_exchange(smbrdr_handle_t *srh, smb_hdr_t *smb_hdr, long timeout)
219 {
220 smb_sign_ctx_t *sign_ctx;
221 smb_msgbuf_t *mb;
222 DWORD status;
223 int rc;
224
225 smbrdr_lock_transport();
226
227 mb = &srh->srh_mbuf;
228 sign_ctx = &srh->srh_session->sign_ctx;
229
230 if (smbrdr_sign(sign_ctx, mb) != SMBAUTH_SUCCESS) {
231 smb_log(smbrdr_log_hdl, LOG_ERR,
232 "smbrdr_exchange[%d]: signing failed", srh->srh_cmd);
233 smbrdr_unlock_transport();
234 return (NT_STATUS_INTERNAL_ERROR);
235 }
236
237 rc = nb_exchange(srh->srh_session->sock,
238 srh->srh_buf, smb_msgbuf_used(mb),
239 srh->srh_buf, SMBRDR_REQ_BUFSZ, timeout);
240
241 if (rc < 0) {
242 smb_log(smbrdr_log_hdl, LOG_ERR,
243 "smbrdr_exchange[%d]: failed (%d)", srh->srh_cmd, rc);
244
245 if (srh->srh_cmd != SMB_COM_ECHO) {
246 /*
247 * Since SMB echo is used to check the session
248 * status then don't destroy the session if it's
249 * SMB echo.
250 */
251 srh->srh_session->state = SDB_SSTATE_STALE;
252 }
253 smb_mac_inc_seqnum(sign_ctx);
254 smbrdr_unlock_transport();
255 return (NT_STATUS_UNEXPECTED_NETWORK_ERROR);
256 }
257
258 /* initialize for processing response */
259 smb_msgbuf_init(mb, srh->srh_buf, rc, srh->srh_mbflags);
260
261 status = smbrdr_hdr_process(srh, smb_hdr);
262 if (status != NT_STATUS_SUCCESS) {
263 smb_log(smbrdr_log_hdl, LOG_ERR,
264 "smbrdr_exchange[%d]: failed (%s)", srh->srh_cmd,
265 xlate_nt_status(status));
266 smb_mac_inc_seqnum(sign_ctx);
267 smbrdr_unlock_transport();
268 return (status);
269 }
270
271 /* Signature validation */
272 if (!smbrdr_sign_chk(sign_ctx, mb, smb_hdr->extra.extra.security_sig)) {
273 smb_log(smbrdr_log_hdl, LOG_ERR,
274 "smbrdr_exchange[%d]: bad signature", srh->srh_cmd);
275 smbrdr_unlock_transport();
276 return (NT_STATUS_INVALID_NETWORK_RESPONSE);
277 }
278
279 smbrdr_unlock_transport();
280 return (NT_STATUS_SUCCESS);
281 }
282
283 /*
284 * smbrdr_handle_free
285 *
286 * Frees the memories allocated for the given handle.
287 */
288 void
smbrdr_handle_free(smbrdr_handle_t * srh)289 smbrdr_handle_free(smbrdr_handle_t *srh)
290 {
291 if (srh) {
292 smb_msgbuf_term(&srh->srh_mbuf);
293 free(srh->srh_buf);
294 }
295 }
296
297
298 /*
299 * smbrdr_sign_init
300 *
301 * This function is called from SessionSetup and initialize the
302 * signing context for the session if the connected user isn't
303 * anonymous. This has to call before smbrdr_request_init()
304 * because it modifies smb_flags2.
305 *
306 * The following description is taken out from the "Implementing CIFS"
307 * book(pg. 304):
308 *
309 * "Once the MAC signing has been initialized within a session, all
310 * messages are numbered using the same counters and signed using
311 * the same Session Key. This is true even if additional SESSION
312 * SETUP ANDX exchanges occur."
313 *
314 * The original SMB packet signing implementation calculates a MAC
315 * key each time the SMB Redirector sends the SmbSessionSetupAndx
316 * request for any non-anonymous/non-guest user which is not desired
317 * whenever there is a change in the user session key.
318 *
319 * If NTLMv2 authentication is used, the MAC key generated for each
320 * SessionSetup is unique. Since the domain controller expects the
321 * signature of all incoming requests are signed by the same MAC key
322 * (i.e. the one that generated for the first non-anonymous SessionSetup),
323 * access denied is returned for any subsequent SmbSessionSetupAndX
324 * request.
325 */
326 int
smbrdr_sign_init(struct sdb_session * session,struct sdb_logon * logon)327 smbrdr_sign_init(struct sdb_session *session, struct sdb_logon *logon)
328 {
329 smb_sign_ctx_t *sign_ctx;
330 int rc = 0;
331
332 sign_ctx = &session->sign_ctx;
333
334 if ((sign_ctx->ssc_flags & SMB_SCF_REQUIRED) &&
335 !(sign_ctx->ssc_flags & SMB_SCF_STARTED) &&
336 (logon->type != SDB_LOGON_ANONYMOUS)) {
337 if (smb_mac_init(sign_ctx, &logon->auth) != SMBAUTH_SUCCESS)
338 return (-1);
339
340 sign_ctx->ssc_flags |=
341 (SMB_SCF_STARTED | SMB_SCF_KEY_ISSET_THIS_LOGON);
342 session->smb_flags2 |= SMB_FLAGS2_SMB_SECURITY_SIGNATURE;
343 rc = 1;
344 }
345
346 return (rc);
347 }
348
349 /*
350 * smbrdr_sign_fini
351 *
352 * Invalidate the MAC key if the first non-anonymous/non-guest user logon
353 * fail.
354 */
355 void
smbrdr_sign_fini(struct sdb_session * session)356 smbrdr_sign_fini(struct sdb_session *session)
357 {
358 smb_sign_ctx_t *sign_ctx = &session->sign_ctx;
359
360 if (sign_ctx->ssc_flags & SMB_SCF_KEY_ISSET_THIS_LOGON) {
361 sign_ctx->ssc_flags &= ~SMB_SCF_STARTED;
362 sign_ctx->ssc_flags &= ~SMB_SCF_KEY_ISSET_THIS_LOGON;
363 sign_ctx->ssc_seqnum = 0;
364 }
365 }
366
367 /*
368 * smbrdr_sign_unset_key
369 *
370 * The SMB_SCF_KEY_ISSET_THIS_LOGON should be unset upon the successful
371 * SmbSessionSetupAndX request for the first non-anonymous/non-guest
372 * logon.
373 */
374 void
smbrdr_sign_unset_key(struct sdb_session * session)375 smbrdr_sign_unset_key(struct sdb_session *session)
376 {
377 smb_sign_ctx_t *sign_ctx = &session->sign_ctx;
378
379 sign_ctx->ssc_flags &= ~SMB_SCF_KEY_ISSET_THIS_LOGON;
380 }
381
382 /*
383 * smbrdr_handle_setup
384 *
385 * Allocates a buffer for sending/receiving a SMB request.
386 * Initialize a smb_msgbuf structure with the allocated buffer.
387 * Setup given handle (srh) with the specified information.
388 *
389 * Returns:
390 *
391 * NT_STATUS_NO_MEMORY not enough memory
392 * NT_STATUS_SUCCESS successful
393 */
394 static DWORD
smbrdr_handle_setup(smbrdr_handle_t * srh,unsigned char cmd,struct sdb_session * session,struct sdb_logon * logon,struct sdb_netuse * netuse)395 smbrdr_handle_setup(smbrdr_handle_t *srh,
396 unsigned char cmd,
397 struct sdb_session *session,
398 struct sdb_logon *logon,
399 struct sdb_netuse *netuse)
400 {
401 srh->srh_buf = (unsigned char *)malloc(SMBRDR_REQ_BUFSZ);
402 if (srh->srh_buf == NULL)
403 return (NT_STATUS_NO_MEMORY);
404
405 bzero(srh->srh_buf, SMBRDR_REQ_BUFSZ);
406
407 srh->srh_mbflags = (session->remote_caps & CAP_UNICODE)
408 ? SMB_MSGBUF_UNICODE : 0;
409
410 smb_msgbuf_init(&srh->srh_mbuf, srh->srh_buf,
411 SMBRDR_REQ_BUFSZ, srh->srh_mbflags);
412
413 srh->srh_cmd = cmd;
414 srh->srh_session = session;
415 srh->srh_user = logon;
416 srh->srh_tree = netuse;
417
418 return (NT_STATUS_SUCCESS);
419 }
420
421 /*
422 * smbrdr_hdr_setup
423 *
424 * Build an SMB header based on the information in the given handle.
425 * The SMB header is described in section 3.2 of the CIFS spec.
426 * As this is a canned function, no error checking is performed here.
427 * The return value from smb_msgbuf_encode is simply returned to the caller.
428 */
429 static int
smbrdr_hdr_setup(smbrdr_handle_t * srh)430 smbrdr_hdr_setup(smbrdr_handle_t *srh)
431 {
432 static unsigned short my_pid = 0;
433
434 if (!my_pid)
435 my_pid = getpid();
436
437 return (smb_msgbuf_encode(&srh->srh_mbuf, "Mb4.bw12.wwww",
438 srh->srh_cmd,
439 srh->srh_session->smb_flags,
440 srh->srh_session->smb_flags2,
441 (srh->srh_tree) ? srh->srh_tree->tid : 0,
442 my_pid,
443 (srh->srh_user) ? srh->srh_user->uid : 0,
444 0 /* mid */));
445 }
446
447 /*
448 * Canned SMB header decode.
449 */
450 static int
smb_decode_nt_hdr(smb_msgbuf_t * mb,smb_hdr_t * hdr)451 smb_decode_nt_hdr(smb_msgbuf_t *mb, smb_hdr_t *hdr)
452 {
453 return (smb_msgbuf_decode(mb, SMB_HEADER_NT_FMT,
454 &hdr->command,
455 &hdr->status.ntstatus,
456 &hdr->flags,
457 &hdr->flags2,
458 &hdr->pid_high,
459 SMB_SIG_SIZE,
460 &hdr->extra.extra.security_sig,
461 &hdr->tid,
462 &hdr->pid,
463 &hdr->uid,
464 &hdr->mid));
465 }
466
467 /*
468 * smbrdr_hdr_process
469 *
470 * Assuming 'srh->srh_mbuf' contains a response from a Windows client,
471 * decodes the 32 bytes SMB header.
472 *
473 * Buffer overflow typically means that the server has more data than
474 * it could fit in the response buffer. The client can use subsequent
475 * SmbReadX requests to obtain the remaining data (KB 193839).
476 *
477 * Returns:
478 *
479 * NT_STATUS_INVALID_NETWORK_RESPONSE error decoding the header
480 * NT_STATUS_REPLY_MESSAGE_MISMATCH response doesn't match the request
481 * NT_STATUS_SUCCESS successful
482 * smb_hdr->status.ntstatus error returned by server
483 */
484 static DWORD
smbrdr_hdr_process(smbrdr_handle_t * srh,smb_hdr_t * smb_hdr)485 smbrdr_hdr_process(smbrdr_handle_t *srh, smb_hdr_t *smb_hdr)
486 {
487 int rc;
488
489 rc = smb_decode_nt_hdr(&srh->srh_mbuf, smb_hdr);
490 if (rc < SMB_HEADER_LEN) {
491 smb_log(smbrdr_log_hdl, LOG_DEBUG,
492 "smbrdr_hdr_process[%d]: invalid header (%d)",
493 srh->srh_cmd, rc);
494 return (NT_STATUS_INVALID_NETWORK_RESPONSE);
495 }
496
497 switch (NT_SC_VALUE(smb_hdr->status.ntstatus)) {
498 case NT_STATUS_SUCCESS:
499 case NT_STATUS_BUFFER_OVERFLOW:
500 break;
501
502 default:
503 smb_log(smbrdr_log_hdl, LOG_DEBUG,
504 "smbrdr_hdr_process[%d]: request failed (%s)",
505 srh->srh_cmd, xlate_nt_status(smb_hdr->status.ntstatus));
506 return (smb_hdr->status.ntstatus);
507 }
508
509 if (smb_hdr->command != srh->srh_cmd) {
510 smb_log(smbrdr_log_hdl, LOG_DEBUG,
511 "smbrdr_hdr_process[%d]: reply mismatch (%d)",
512 srh->srh_cmd, smb_hdr->command);
513 return (NT_STATUS_REPLY_MESSAGE_MISMATCH);
514 }
515
516 return (NT_STATUS_SUCCESS);
517 }
518
519 /*
520 * smbrdr_sign
521 *
522 * Signs the given outgoing packet according to the
523 * specified signing context.
524 *
525 * The client and server each maintain an integer counter
526 * which they initialize to zero. Both counters are
527 * incremented for every SMB message - that's once for a
528 * request and once for a reply. As a result, requests sent
529 * by SMB Redirector always have an even sequence number
530 * and replies from the Windows server always have an odd
531 * number.
532 *
533 * Based on the observed Windows 2003 behavior, any SMB
534 * request will fail with NT_STATUS_ACCESS_DENIED if its
535 * sequence number is not even.
536 *
537 * The function can fail if there is trouble with the cryptographic
538 * framework and if that happens SMBAUTH_FAILURE is returned. In the
539 * normal case SMBAUTH_SUCCESS is returned.
540 */
541 static int
smbrdr_sign(smb_sign_ctx_t * sign_ctx,smb_msgbuf_t * mb)542 smbrdr_sign(smb_sign_ctx_t *sign_ctx, smb_msgbuf_t *mb)
543 {
544 if (sign_ctx->ssc_flags & SMB_SCF_STARTED) {
545 if (sign_ctx->ssc_seqnum % 2) {
546 smb_log(smbrdr_log_hdl, LOG_DEBUG,
547 "smbrdr_sign: invalid sequence (%d)",
548 sign_ctx->ssc_seqnum);
549 }
550 if (smb_mac_sign(sign_ctx, smb_msgbuf_base(mb),
551 smb_msgbuf_used(mb)) != SMBAUTH_SUCCESS)
552 return (SMBAUTH_FAILURE);
553 sign_ctx->ssc_seqnum++;
554 }
555 return (SMBAUTH_SUCCESS);
556 }
557
558
559 /*
560 * smbrdr_sign_chk
561 *
562 * Validates SMB MAC signature in the in-coming message.
563 * Return 1 if the signature are match; otherwise, return 0;
564 *
565 * When packet signing is enabled, the sequence number kept in the
566 * sign_ctx structure will be incremented when a SMB request is
567 * sent and upon the receipt of the first SmbTransact response
568 * if SMB fragmentation occurs.
569 */
570 static int
smbrdr_sign_chk(smb_sign_ctx_t * sign_ctx,smb_msgbuf_t * mb,unsigned char * signature)571 smbrdr_sign_chk(smb_sign_ctx_t *sign_ctx, smb_msgbuf_t *mb,
572 unsigned char *signature)
573 {
574 int sign_ok = 1;
575
576 if (sign_ctx->ssc_flags & SMB_SCF_STARTED) {
577 (void) memcpy(sign_ctx->ssc_sign, signature, SMB_SIG_SIZE);
578 sign_ok = smb_mac_chk(sign_ctx, smb_msgbuf_base(mb),
579 smb_msgbuf_size(mb));
580 sign_ctx->ssc_seqnum++;
581 }
582
583 return (sign_ok);
584 }
585