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