1/*
2 * Copyright (c) 2000, Boris Popov
3 * All rights reserved.
4 *
5 * Portions Copyright (C) 2001 - 2010 Apple Inc. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *    This product includes software developed by Boris Popov.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 */
35#include <sys/param.h>
36#include <sys/ioctl.h>
37#include <sys/errno.h>
38#include <sys/stat.h>
39#include <ctype.h>
40#include <err.h>
41#include <stdio.h>
42#include <unistd.h>
43#include <strings.h>
44#include <stdlib.h>
45#include <sysexits.h>
46#include <sys/types.h>
47
48#include <netsmb/upi_mbuf.h>
49#include <sys/smb_byte_order.h>
50#include <sys/mchain.h>
51#include <netsmb/smb_lib.h>
52#include <netsmb/rq.h>
53#include <netsmb/smb_conn.h>
54#include <smbclient/ntstatus.h>
55
56struct smb_usr_rq {
57	u_char			rq_cmd;
58	struct mbchain	rq_rq;
59	struct mdchain	rq_rp;
60	struct smb_ctx *rq_ctx;
61	uint8_t			*wcount;
62	uint16_t		*bcount;
63	uint8_t			flags;
64	uint16_t		flags2;
65	uint32_t		nt_error;
66};
67
68/*
69 * Takes a Window style UTF-16 string and converts it to a Mac style UTF-8 string
70 * that is not null terminated.
71 */
72int smb_ntwrkpath_to_localpath(struct smb_ctx *ctx,
73							   const char *ntwrkstr, size_t ntwrk_len,
74							   char *utf8str, size_t *utf8_len,
75							   uint32_t flags)
76{
77	struct smbioc_path_convert rq;
78
79	bzero(&rq, sizeof(rq));
80	rq.ioc_version = SMB_IOC_STRUCT_VERSION;
81	rq.ioc_direction = NETWORK_TO_LOCAL;
82	rq.ioc_flags = flags;
83	rq.ioc_src_len = (uint32_t)ntwrk_len;
84	rq.ioc_src = ntwrkstr;
85	rq.ioc_dest_len = *utf8_len;
86	rq.ioc_dest = utf8str;
87	if (smb_ioctl_call(ctx->ct_fd, SMBIOC_CONVERT_PATH, &rq) == 0) {
88		*utf8_len = (size_t)rq.ioc_dest_len;
89		/*
90		 * Currently the kernel doesn't null terminate, we may want to change
91		 * this later. Since utf8_len has to be less than the utf8str buffer
92		 * size this is safe.
93		 */
94		utf8str[*utf8_len] = 0;
95		return 0;
96	}
97	return -1;
98}
99
100/*
101 * Takes a Mac style UTF-8 path string and converts it to a Window style UTF-16
102 * that is not null terminated.
103 */
104int smb_localpath_to_ntwrkpath(struct smb_ctx *ctx,
105							   const char *utf8str, size_t utf8_len,
106							   char *ntwrkstr, size_t *ntwrk_len,
107							   uint32_t flags)
108{
109	struct smbioc_path_convert rq;
110
111	bzero(&rq, sizeof(rq));
112	rq.ioc_version = SMB_IOC_STRUCT_VERSION;
113	rq.ioc_direction = LOCAL_TO_NETWORK;
114	rq.ioc_flags = flags;
115	rq.ioc_src_len = (uint32_t)utf8_len;
116	rq.ioc_src = utf8str;
117	rq.ioc_dest_len = *ntwrk_len;
118	rq.ioc_dest = ntwrkstr;
119	if (smb_ioctl_call(ctx->ct_fd, SMBIOC_CONVERT_PATH, &rq) == 0) {
120		*ntwrk_len = (size_t)rq.ioc_dest_len;
121		return 0;
122	}
123	return -1;
124}
125
126int smb_usr_rq_init(struct smb_ctx *ctx, u_char cmd, uint16_t flags2, struct smb_usr_rq **rqpp)
127{
128	struct smb_usr_rq *rqp;
129
130	rqp = malloc(sizeof(*rqp));
131	if (rqp == NULL)
132		return ENOMEM;
133	bzero(rqp, sizeof(*rqp));
134	rqp->rq_cmd = cmd;
135	rqp->rq_ctx = ctx;
136	smb_usr_rq_setflags2(rqp, flags2);
137	mb_init(&rqp->rq_rq);
138	md_init(&rqp->rq_rp);
139	*rqpp = rqp;
140	return 0;
141}
142
143int smb_usr_rq_init_rcvsize(struct smb_ctx *ctx, u_char cmd, uint16_t flags2,
144						size_t rpbufsz, struct smb_usr_rq **rqpp)
145{
146	struct smb_usr_rq *rqp;
147
148	rqp = malloc(sizeof(*rqp));
149	if (rqp == NULL)
150		return ENOMEM;
151	bzero(rqp, sizeof(*rqp));
152	rqp->rq_cmd = cmd;
153	rqp->rq_ctx = ctx;
154	smb_usr_rq_setflags2(rqp, flags2);
155	mb_init(&rqp->rq_rq);
156	md_init_rcvsize(&rqp->rq_rp, rpbufsz);
157	*rqpp = rqp;
158	return 0;
159}
160
161void
162smb_usr_rq_done(struct smb_usr_rq *rqp)
163{
164	mb_done((mbchain_t)&rqp->rq_rp);
165	md_done((mdchain_t)&rqp->rq_rq);
166	free(rqp);
167}
168
169mbchain_t smb_usr_rq_getrequest(struct smb_usr_rq *rqp)
170{
171	return &rqp->rq_rq;
172}
173
174
175mdchain_t smb_usr_rq_getreply(struct smb_usr_rq *rqp)
176{
177	return &rqp->rq_rp;
178}
179
180uint32_t smb_usr_rq_get_error(struct smb_usr_rq *rqp)
181{
182	return rqp->nt_error;
183}
184
185uint32_t smb_usr_rq_flags2(struct smb_usr_rq *rqp)
186{
187	return rqp->flags2;
188}
189
190uint32_t smb_usr_rq_nt_error(struct smb_usr_rq *rqp)
191{
192	return rqp->nt_error;
193}
194
195void smb_usr_rq_setflags2(struct smb_usr_rq *rqp, uint32_t flags2)
196{
197	/* We only support SMB_FLAGS2_DFS, log the failure reset the flags to zero */
198	if (flags2 && (flags2 != SMB_FLAGS2_DFS)) {
199		smb_log_info("%s to 0x%x, syserr = %s", ASL_LEVEL_DEBUG, __FUNCTION__,
200					 flags2, strerror(ENOTSUP));
201		flags2 = 0;
202	}
203	rqp->flags2 = flags2;
204}
205
206void smb_usr_rq_wstart(struct smb_usr_rq *rqp)
207{
208	rqp->wcount = (uint8_t *)mb_reserve(&rqp->rq_rq, sizeof(uint8_t));
209	rqp->rq_rq.mb_count = 0;
210}
211
212void smb_usr_rq_wend(struct smb_usr_rq *rqp)
213{
214	if (rqp->wcount == NULL) {
215		smb_log_info("%s: no word count pointer?", ASL_LEVEL_ERR, __FUNCTION__);
216		return;
217	}
218	if (rqp->rq_rq.mb_count & 1)
219		smb_log_info("%s: odd word count", ASL_LEVEL_ERR, __FUNCTION__);
220	*rqp->wcount = rqp->rq_rq.mb_count / 2;
221}
222
223void smb_usr_rq_bstart(struct smb_usr_rq *rqp)
224{
225	rqp->bcount = (uint16_t *)mb_reserve(&rqp->rq_rq, sizeof(uint16_t));
226	rqp->rq_rq.mb_count = 0;
227}
228
229void smb_usr_rq_bend(struct smb_usr_rq *rqp)
230{
231	uint16_t bcnt;
232
233	if (rqp->bcount == NULL) {
234		smb_log_info("%s: no byte count pointer?", ASL_LEVEL_ERR, __FUNCTION__);
235		return;
236	}
237	/*
238	 * Byte Count field should be ignored when dealing with  SMB_CAP_LARGE_WRITEX
239	 * or SMB_CAP_LARGE_READX messages. So if byte cound becomes to big we set
240	 * it to zero and hope the calling process knows what it is doing.
241	 */
242	if (rqp->rq_rq.mb_count > 0x0ffff)
243		bcnt = 0; /* Set the byte count to zero here */
244	else
245		bcnt = (uint16_t)rqp->rq_rq.mb_count;
246
247	*rqp->bcount = htoles(bcnt);
248}
249
250/*
251 * Given a UTF8 string, convert it to a network string and place it in
252 * the buffer.
253 */
254int smb_usr_put_dmem(struct smb_ctx *ctx, mbchain_t mbp, const char *src,
255            			size_t src_size, int flags, size_t *lenp)
256{
257	size_t dest_size = (src_size * 2) + 2 + 2;
258	void *dst = NULL;
259	int error = 0;
260
261	/* Nothing to do here */
262	if (src_size == 0)
263		goto done;
264
265    mb_put_padbyte(mbp);
266
267	dst = mb_getbuffer(mbp, dest_size);
268	if (dst == NULL) {
269		smb_log_info("%s: mb_getbuffer failed, syserr = %s", ASL_LEVEL_DEBUG,
270					 __FUNCTION__, strerror(ENOMEM));
271		error = ENOMEM;
272		goto done;
273	}
274	memset(dst, 0, dest_size);
275
276	if (smb_localpath_to_ntwrkpath(ctx, src, src_size, dst, &dest_size, flags)) {
277		smb_log_info("%s: local to ntwrk path failed, syserr = %s",
278					 ASL_LEVEL_DEBUG, __FUNCTION__, strerror(errno));
279		error = errno;
280		goto done;
281	}
282	mb_consume(mbp, dest_size);
283	if (lenp)
284		*lenp += dest_size;
285done:
286	return error;
287}
288
289/*
290 * Given a UTF8 string, convert it to a network string and place it in
291 * the buffer. Now add the NULL byte/bytes.
292 */
293int smb_usr_rq_put_dstring(struct smb_ctx *ctx, mbchain_t mbp, const char *src,
294					size_t maxlen, int flags, size_t *lenp)
295{
296	int error;
297
298	error = smb_usr_put_dmem(ctx, mbp, src, maxlen, flags, lenp);
299	if (error)
300		return error;
301
302    if (lenp )
303        *lenp += 2;
304    return mb_put_uint16le(mbp, 0);
305}
306
307int
308smb_usr_rq_simple(struct smb_usr_rq *rqp)
309{
310	struct smbioc_rq krq;
311	mbchain_t mbp;
312	mdchain_t mdp;
313
314	mbp = smb_usr_rq_getrequest(rqp);
315	mb_pullup(mbp);
316	bzero(&krq, sizeof(krq));
317	krq.ioc_version = SMB_IOC_STRUCT_VERSION;
318	krq.ioc_cmd = rqp->rq_cmd;
319	krq.ioc_twc = (rqp->wcount) ? *(rqp->wcount) : 0;
320	krq.ioc_twords = (rqp->wcount) ? rqp->wcount+sizeof(*rqp->wcount) : NULL;
321	krq.ioc_tbc = (rqp->bcount) ? letohs(*(rqp->bcount)) : 0;
322	krq.ioc_tbytes = (rqp->bcount) ? (uint8_t *)rqp->bcount+sizeof(*rqp->bcount) : NULL;
323	krq.ioc_flags2 = rqp->flags2;
324
325	mdp = smb_usr_rq_getreply(rqp);
326	krq.ioc_rpbufsz = (int32_t)mbuf_maxlen(mdp->md_top);
327	krq.ioc_rpbuf = mbuf_data(mdp->md_top);
328
329	if (smb_ioctl_call(rqp->rq_ctx->ct_fd, SMBIOC_REQUEST, &krq) == -1) {
330		smb_log_info("%s: smb_ioctl_call, syserr = %s", ASL_LEVEL_DEBUG,
331					 __FUNCTION__, strerror(errno));
332		return errno;
333	}
334	mbuf_setlen(mdp->md_top, krq.ioc_rpbufsz);
335	rqp->flags = krq.ioc_flags;
336	rqp->flags2 = krq.ioc_flags2;
337	rqp->nt_error = krq.ioc_ntstatus;
338	/*
339	 * Returning an error to the IOCTL code means no other information in the
340	 * structure will be updated. The nsmb_dev_ioctl routine should only return
341	 * unexpected internal errors. We should return all errors in the ioctl
342	 * structure and not through the ioctl call. This will be corrected by the
343	 * following:
344	 * <rdar://problem/7082077> "SMB error handling is just confusing and needs to be rewritten"
345	 */
346	return krq.ioc_errno;
347}
348
349int
350smb_usr_t2_request(struct smb_ctx *ctx, int setupcount, uint16_t *setup,
351			   const char *name,
352			   uint16_t tparamcnt, const void *tparam,
353			   uint16_t tdatacnt, const void *tdata,
354			   uint16_t *rparamcnt, void *rparam,
355			   uint16_t *rdatacnt, void *rdata,
356			   uint32_t *buffer_oflow)
357{
358	struct smbioc_t2rq krq;
359	int i;
360
361	if (setupcount < 0 || setupcount > SMB_MAXSETUPWORDS) {
362		/* Bogus setup count, or too many setup words */
363		return EINVAL;
364	}
365	bzero(&krq, sizeof(krq));
366	krq.ioc_version = SMB_IOC_STRUCT_VERSION;
367	for (i = 0; i < setupcount; i++)
368		krq.ioc_setup[i] = setup[i];
369	krq.ioc_setupcnt = setupcount;
370	krq.ioc_name = name;
371	/*
372	 * Now when passing the name into the kernel we also send the length
373	 * of the name. The ioc_name_len needs to contain the name length and
374	 * the null byte.
375	  */
376	if (name)
377		krq.ioc_name_len = (uint32_t)strlen(name) + 1;
378	else
379		krq.ioc_name_len = 0;
380	krq.ioc_tparamcnt = tparamcnt;
381	krq.ioc_tparam = (void *)tparam;
382	krq.ioc_tdatacnt = tdatacnt;
383	krq.ioc_tdata = (void *)tdata;
384	krq.ioc_rparamcnt = *rparamcnt;
385	krq.ioc_rparam = rparam;
386	krq.ioc_rdatacnt = *rdatacnt;
387	krq.ioc_rdata = rdata;
388
389	if (smb_ioctl_call(ctx->ct_fd, SMBIOC_T2RQ, &krq) == -1) {
390		return errno;
391	}
392	*rparamcnt = krq.ioc_rparamcnt;
393	*rdatacnt = krq.ioc_rdatacnt;
394	*buffer_oflow = (krq.ioc_flags2 & SMB_FLAGS2_ERR_STATUS) &&
395					(krq.ioc_ntstatus == STATUS_BUFFER_OVERFLOW);
396	return 0;
397}
398