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 * SMB transaction functions.
28 */
29
30#include <strings.h>
31
32#include <smbsrv/libsmbrdr.h>
33#include <smbsrv/smb.h>
34#include <smbrdr.h>
35
36/*
37 * The pipe filename, length (including the null terminator)
38 * and the buffer size for the transaction. Moving to unicode
39 * revealed that the length should not include the null.
40 */
41#define	TX_FILENAME		"\\PIPE\\"
42#define	TX_FILENAME_ASCII_LEN	6
43#define	TX_FILENAME_WCHAR_LEN	14
44
45
46static int prep_smb_transact(smb_msgbuf_t *, unsigned short, char *,
47    unsigned short, unsigned short, unsigned);
48static int decode_smb_transact(smb_msgbuf_t *, char *, unsigned,
49    smb_transact_rsp_t *);
50
51/*
52 * smbrdr_transact
53 *
54 * Send a SMB_COM_TRANSACTION request.
55 */
56int
57smbrdr_transact(int fid, char *out_buf, int out_len, char *in_buf, int in_len)
58{
59	struct sdb_session *session;
60	struct sdb_netuse *netuse;
61	struct sdb_ofile *ofile;
62	struct sdb_logon *logon;
63	smb_transact_rsp_t rsp;
64	smbrdr_handle_t srh;
65	smb_msgbuf_t *mb;
66	DWORD status;
67	int rc;
68	unsigned short rcv_dcnt;
69	int cur_inlen;
70	int first_rsp;
71
72	if ((ofile = smbrdr_ofile_get(fid)) == 0)
73		return (-1);
74
75	netuse = ofile->netuse;
76	session = netuse->session;
77	logon = &session->logon;
78
79	status = smbrdr_request_init(&srh, SMB_COM_TRANSACTION,
80	    session, logon, netuse);
81
82	if (status != NT_STATUS_SUCCESS) {
83		smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_transact: %s",
84		    xlate_nt_status(status));
85		smbrdr_ofile_put(ofile);
86		return (-1);
87	}
88
89	mb = &srh.srh_mbuf;
90
91	rc = prep_smb_transact(mb, ofile->fid, out_buf, out_len, in_len,
92	    session->remote_caps & CAP_UNICODE);
93	if (rc < 0) {
94		smb_log(smbrdr_log_hdl, LOG_DEBUG,
95		    "smbrdr_transact: prep failed");
96		smbrdr_handle_free(&srh);
97		smbrdr_ofile_put(ofile);
98		return (rc);
99	}
100
101	smbrdr_lock_transport();
102
103	status = smbrdr_send(&srh);
104	if (status != NT_STATUS_SUCCESS) {
105		smbrdr_unlock_transport();
106		smbrdr_handle_free(&srh);
107		smbrdr_ofile_put(ofile);
108		smb_log(smbrdr_log_hdl, LOG_DEBUG,
109		    "smbrdr_transact: send failed");
110		return (-1);
111	}
112
113	rcv_dcnt = 0;
114	cur_inlen = in_len;
115	first_rsp = 1;
116
117	do {
118		if (smbrdr_rcv(&srh, first_rsp) != NT_STATUS_SUCCESS) {
119			smb_log(smbrdr_log_hdl, LOG_DEBUG,
120			    "smbrdr_transact: nb_rcv failed");
121			rc = -1;
122			break;
123		}
124
125		rc = decode_smb_transact(mb, in_buf, cur_inlen, &rsp);
126		if (rc < 0 || rsp.TotalDataCount > in_len) {
127			smb_log(smbrdr_log_hdl, LOG_DEBUG,
128			    "smbrdr_transact: decode failed");
129			rc = -1;
130			break;
131		}
132
133		rcv_dcnt += rsp.DataCount;
134		cur_inlen -= rsp.DataCount;
135		first_rsp = 0;
136
137	} while (rcv_dcnt < rsp.TotalDataCount);
138
139	smbrdr_unlock_transport();
140	smbrdr_handle_free(&srh);
141	smbrdr_ofile_put(ofile);
142
143	return ((rc < 0) ? rc : rcv_dcnt);
144}
145
146
147/*
148 * prep_smb_transact
149 *
150 * Prepare the SMB_COM_TRANSACTION request.
151 */
152static int
153prep_smb_transact(smb_msgbuf_t *mb, unsigned short fid, char *out,
154    unsigned short out_len, unsigned short in_max, unsigned unicode)
155{
156	int data_off;
157	int rc;
158	unsigned short bcc;
159
160	/*
161	 * The byte count seems to include the pad
162	 * byte to word align the filename and two
163	 * spurious pad bytes between the filename
164	 * and the transaction data.
165	 */
166	bcc = out_len + 3;
167	bcc += (unicode) ? TX_FILENAME_WCHAR_LEN : TX_FILENAME_ASCII_LEN;
168
169	data_off  = 32;		/* sizeof SMB header up to smb_wct */
170	data_off += 1;		/* sizeof smb_wct */
171	data_off += 16*2;	/* sizeof word parameters */
172	data_off += 2;		/* sizeof smb_bcc */
173	data_off += (unicode) ? TX_FILENAME_WCHAR_LEN : TX_FILENAME_ASCII_LEN;
174	data_off += 3;
175	/* this is where data starts */
176
177	rc = smb_msgbuf_encode(mb,
178	    "(wct)b"
179	    "(tpscnt)w (tdscnt)w (mprcnt)w (mdrcnt)w (msrcnt)b"
180	    "(rsvd). (flags)w (timeo)l  (rsvd1)2."
181	    "(pscnt)w (psoff)w (dscnt)w (dsoff)w (suwcnt)b"
182	    "(rsvd2). (pipop)w (fid)w (bcc)w (fname)u",
183	    16,				/* smb_wct */
184	    0,				/* total parm bytes */
185	    out_len,			/* total data bytes */
186	    0,				/* max parm bytes to ret */
187	    in_max,			/* max data bytes to ret */
188	    0,				/* max setup words to ret */
189	    0,				/* transact flags */
190	    0,				/* transact timeout */
191	    0,				/* parameter bytes */
192	    data_off,			/* parameter offset */
193	    out_len,			/* data bytes */
194	    data_off,			/* data offset */
195	    2,				/* total setup words */
196	    0x0026,			/* OP=TransactNmPipe */
197	    fid,			/* FID */
198	    bcc,			/* byte count */
199	    TX_FILENAME);		/* file name */
200
201	/*
202	 * Transaction data - padded.
203	 */
204	rc = smb_msgbuf_encode(mb, "..#c", out_len, out);
205	return (rc);
206}
207
208
209/*
210 * decode_smb_transact
211 *
212 * Decode the response from the SMB_COM_TRANSACTION request.
213 */
214static int
215decode_smb_transact(smb_msgbuf_t *mb, char *in, unsigned in_len,
216    smb_transact_rsp_t *rsp)
217{
218	int rc;
219
220	rc = smb_msgbuf_decode(mb, "b", &rsp->WordCount);
221	if (rc <= 0 || rsp->WordCount < 10) {
222		return (-1);
223	}
224
225	rc = smb_msgbuf_decode(mb,
226	    "(tpscnt)w (tdscnt)w (rsvd)2."
227	    "(pscnt)w (psoff)w (psdisp)w (dscnt)w (dsoff)w"
228	    "(dsdisp)w (suwcnt)b (rsvd). (bcc)w",
229	    &rsp->TotalParamCount,		/* Total parm bytes */
230	    &rsp->TotalDataCount,		/* Total data bytes */
231	    &rsp->ParamCount,			/* Parm bytes this buffer */
232	    &rsp->ParamOffset,			/* Parm offset from hdr */
233	    &rsp->ParamDisplacement,		/* Parm displacement */
234	    &rsp->DataCount,			/* Data bytes this buffer */
235	    &rsp->DataOffset,			/* Data offset from hdr */
236	    &rsp->DataDisplacement,		/* Data displacement */
237	    &rsp->SetupCount,			/* Setup word count */
238	    &rsp->BCC);				/* smb_bcc */
239
240	if (rc <= 0)
241		return (-1);
242
243	if (rsp->DataCount > in_len)
244		return (-1);
245
246	bcopy(mb->base + rsp->DataOffset,
247	    in + rsp->DataDisplacement, rsp->DataCount);
248
249	return (0);
250}
251