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