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
36smb_log_hdl_t smbrdr_log_hdl;
37
38static DWORD smbrdr_handle_setup(smbrdr_handle_t *, unsigned char,
39    struct sdb_session *, struct sdb_logon *, struct sdb_netuse *);
40static int smbrdr_hdr_setup(smbrdr_handle_t *);
41static DWORD smbrdr_hdr_process(smbrdr_handle_t *, smb_hdr_t *);
42static int smbrdr_sign(smb_sign_ctx_t *, smb_msgbuf_t *);
43static int smbrdr_sign_chk(smb_sign_ctx_t *, smb_msgbuf_t *, unsigned char *);
44
45void smbrdr_lock_transport() { nb_lock(); }
46void smbrdr_unlock_transport() { nb_unlock(); }
47
48#pragma init(_smbrdr_init)
49
50void
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 */
68DWORD
69smbrdr_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 */
101DWORD
102smbrdr_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 */
156DWORD
157smbrdr_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 */
217DWORD
218smbrdr_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 */
288void
289smbrdr_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 */
326int
327smbrdr_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 */
355void
356smbrdr_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 */
374void
375smbrdr_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 */
394static DWORD
395smbrdr_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 */
429static int
430smbrdr_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 */
450static int
451smb_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 */
484static DWORD
485smbrdr_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 */
541static int
542smbrdr_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 */
570static int
571smbrdr_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