1/*
2 * Copyright (c) 2000-2001 Boris Popov
3 * All rights reserved.
4 *
5 * Portions Copyright (C) 2001 - 2012 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/smb_apple.h>
36#include <netsmb/smb.h>
37#include <netsmb/smb_2.h>
38#include <netsmb/smb_conn.h>
39#include <netsmb/smb_rq.h>
40#include <netsmb/smb_dev.h>
41#include <netsmb/smb_converter.h>
42
43int
44smb_usr_negotiate(struct smbioc_negotiate *vspec, vfs_context_t context,
45					  struct smb_dev *sdp, int searchOnly)
46{
47	struct smb_vc *vcp;
48	int error;
49	uint32_t usersMaxBufferLen = vspec->ioc_negotiate_token_len;
50
51	/* Convert any pointers over to using user_addr_t */
52	if (! vfs_context_is64bit (context)) {
53		vspec->ioc_kern_saddr = CAST_USER_ADDR_T(vspec->ioc_saddr);
54		vspec->ioc_kern_laddr = CAST_USER_ADDR_T(vspec->ioc_laddr);
55	}
56	/* Now do the real work */
57	error = smb_sm_negotiate(vspec, context, &sdp->sd_vc, sdp, searchOnly);
58	if (error) {
59		/* We always return the error in the structure, we never fail the iocl from here */
60		vspec->ioc_errno = error;
61		return 0;
62	}
63
64	vcp = sdp->sd_vc;
65	/* Return to the user the server's capablilities */
66	vspec->ioc_ret_caps = VC_CAPS(vcp);
67	/* Return to the user the vc flags */
68	vspec->ioc_ret_vc_flags = vcp->vc_flags;
69
70	/* If we got a server provide init token copy that back to the caller. */
71	vspec->ioc_negotiate_token_len = vcp->negotiate_tokenlen;
72	if ((vcp->negotiate_token) && (usersMaxBufferLen >= vcp->negotiate_tokenlen)) {
73		user_addr_t uaddr = vspec->ioc_negotiate_token;
74		error = copyout(vcp->negotiate_token, uaddr, (size_t)vcp->negotiate_tokenlen);
75		vspec->ioc_errno = error;
76	}
77	/* We are sharing the vc return the user name if there is one */
78	if (vspec->ioc_extra_flags & SMB_SHARING_VC) {
79		if (vcp->vc_username) {
80			strlcpy(vspec->ioc_user, vcp->vc_username, sizeof(vspec->ioc_user));
81		}
82		/* Return the size of the client and server principal name */
83		vspec->ioc_max_client_size = vcp->vc_gss.gss_cpn_len;
84		vspec->ioc_max_target_size = vcp->vc_gss.gss_spn_len;
85	}
86
87	return error;
88}
89
90/*
91 * Connect to the resource specified by smbioc_ossn structure.
92 * It may either find an existing connection or try to establish a new one.
93 * If no errors occured smb_vc returned locked and referenced.
94 *
95 * Called from user land so we always have a reference on the share.
96 */
97int
98smb_usr_simplerequest(struct smb_share *share, struct smbioc_rq *dp,
99					  vfs_context_t context)
100{
101	struct smb_rq rq, *rqp = &rq;
102	struct mbchain *mbp;
103	struct mdchain *mdp;
104	int32_t response_size;
105	int error;
106
107	switch (dp->ioc_cmd) {
108	    case SMB_COM_CLOSE_AND_TREE_DISC:
109	    case SMB_COM_TREE_CONNECT:
110	    case SMB_COM_TREE_DISCONNECT:
111	    case SMB_COM_NEGOTIATE:
112	    case SMB_COM_SESSION_SETUP_ANDX:
113	    case SMB_COM_LOGOFF_ANDX:
114	    case SMB_COM_TREE_CONNECT_ANDX:
115		return EPERM;
116	}
117	/* Take the 32 bit world pointers and convert them to user_addr_t. */
118	if (! vfs_context_is64bit (context)) {
119		dp->ioc_kern_twords = CAST_USER_ADDR_T(dp->ioc_twords);
120		dp->ioc_kern_tbytes = CAST_USER_ADDR_T(dp->ioc_tbytes);
121		dp->ioc_kern_rpbuf = CAST_USER_ADDR_T(dp->ioc_rpbuf);
122	}
123
124	error = smb_rq_init(rqp, SSTOCP(share), dp->ioc_cmd, dp->ioc_flags2, context);
125	if (error)
126		return error;
127    smb_rq_getrequest(rqp, &mbp);
128	smb_rq_wstart(rqp);
129	error = mb_put_user_mem(mbp, dp->ioc_kern_twords, dp->ioc_twc * 2, 0, context);
130	if (error)
131		goto bad;
132	smb_rq_wend(rqp);
133	smb_rq_bstart(rqp);
134	error = mb_put_user_mem(mbp, dp->ioc_kern_tbytes, dp->ioc_tbc, 0, context);
135	if (error)
136		goto bad;
137	smb_rq_bend(rqp);
138	error = smb_rq_simple(rqp);
139	if (error)
140		goto bad;
141    smb_rq_getreply(rqp, &mdp);
142	/*
143	 * Amount of data left in the response buffer.
144	 * Should include size of word count field + any word count data + size of
145	 * byte count field + any byte count data
146	 */
147	response_size = (int32_t)md_get_size(mdp);
148	/* Make sure we have room in the users buffer */
149	if (response_size > dp->ioc_rpbufsz) {
150		error = EBADRPC;
151		goto bad;
152	}
153	/* Copy the response into the users buffer */
154	error = md_get_user_mem(mdp, dp->ioc_kern_rpbuf, response_size, 0, context);
155	if (error)
156		goto bad;
157
158	dp->ioc_rpbufsz = response_size;
159
160bad:
161	/*
162	 * Returning an error to the IOCTL code means no other information in the
163	 * structure will be updated. The nsmb_dev_ioctl routine should only return
164	 * unexpected internal errors. We now always return the nt status code and
165	 * the errno in the ioc structure. Any internal smb error is store in the errno
166	 * field or if we get a nt status code then it holds the mapped error number.
167	 */
168	dp->ioc_ntstatus = rqp->sr_ntstatus;
169	dp->ioc_errno = error;
170	dp->ioc_flags = rqp->sr_rpflags;
171	dp->ioc_flags2 = rqp->sr_rpflags2;
172	smb_rq_done(rqp);
173	return 0;
174
175}
176
177int
178smb_cpdatain(struct mbchain *mbp, user_addr_t data, int len, vfs_context_t context)
179{
180	int error;
181
182	if (len == 0)
183		return 0;
184	error = mb_init(mbp);
185	if (! error)
186		error = mb_put_user_mem(mbp, data, len, 0, context);
187	return error;
188}
189
190/*
191* Called from user land so we always have a reference on the share.
192*/
193int
194smb_usr_t2request(struct smb_share *share, struct smbioc_t2rq *dp, vfs_context_t context)
195{
196	struct smb_t2rq t2, *t2p = &t2;
197	struct mdchain *mdp;
198	int error;
199	uint16_t len;
200
201	if (dp->ioc_setupcnt > SMB_MAXSETUPWORDS)
202		return EINVAL;
203
204	error = smb_t2_init(t2p, SSTOCP(share), dp->ioc_setup, dp->ioc_setupcnt, context);
205	if (error) {
206		return error;
207	}
208
209	len = t2p->t2_setupcount = dp->ioc_setupcnt;
210	if (len > 1)
211		t2p->t2_setupdata = dp->ioc_setup;
212
213	/* Take the 32 bit world pointers and convert them to user_addr_t. */
214	if (! vfs_context_is64bit (context)) {
215		dp->ioc_kern_name = CAST_USER_ADDR_T(dp->ioc_name);
216		dp->ioc_kern_tparam = CAST_USER_ADDR_T(dp->ioc_tparam);
217		dp->ioc_kern_tdata = CAST_USER_ADDR_T(dp->ioc_tdata);
218		dp->ioc_kern_rparam = CAST_USER_ADDR_T(dp->ioc_rparam);
219		dp->ioc_kern_rdata = CAST_USER_ADDR_T(dp->ioc_rdata);
220	}
221
222	/* ioc_name_len includes the null byte, ioc_kern_name is a c-style string */
223	if (dp->ioc_kern_name && dp->ioc_name_len) {
224		t2p->t_name = smb_memdupin(dp->ioc_kern_name, dp->ioc_name_len);
225		if (t2p->t_name == NULL) {
226			error = ENOMEM;
227			goto bad;
228		}
229	}
230	t2p->t2_maxscount = 0;
231	t2p->t2_maxpcount = dp->ioc_rparamcnt;
232	t2p->t2_maxdcount = dp->ioc_rdatacnt;
233
234	error = smb_cpdatain(&t2p->t2_tparam, dp->ioc_kern_tparam, dp->ioc_tparamcnt, context);
235	if (! error)
236		error = smb_cpdatain(&t2p->t2_tdata, dp->ioc_kern_tdata, dp->ioc_tdatacnt, context);
237	if (error) {
238		goto bad;
239	}
240
241	error = smb_t2_request(t2p);
242	/*
243	 * We now convert all non SMB_FLAGS2_ERR_STATUS errors into ntstatus
244	 * error codes, we only deal with ntstatus error here.
245	 */
246	dp->ioc_ntstatus = t2p->t2_ntstatus;
247	dp->ioc_flags2 = t2p->t2_sr_rpflags2;
248	if (error) {
249		goto bad;
250	}
251	mdp = &t2p->t2_rparam;
252	if (mdp->md_top) {
253		len = m_fixhdr(mdp->md_top);
254		if (len > dp->ioc_rparamcnt) {
255			error = EMSGSIZE;
256			goto bad;
257		}
258		dp->ioc_rparamcnt = len;
259		error = md_get_user_mem(mdp, dp->ioc_kern_rparam, len, 0, context);
260		if (error) {
261			goto bad;
262		}
263	} else
264		dp->ioc_rparamcnt = 0;
265	mdp = &t2p->t2_rdata;
266	if (mdp->md_top) {
267		len = m_fixhdr(mdp->md_top);
268		if (len > dp->ioc_rdatacnt) {
269			error = EMSGSIZE;
270			goto bad;
271		}
272		dp->ioc_rdatacnt = len;
273		error = md_get_user_mem(mdp, dp->ioc_kern_rdata, len, 0, context);
274	} else
275		dp->ioc_rdatacnt = 0;
276bad:
277	SMB_FREE(t2p->t_name, M_SMBSTR);
278	smb_t2_done(t2p);
279	return error;
280}
281
282/*
283 * Converts UTF8 string to a network style STRING. The network STRING returned
284 * may be ASCII or UTF16, depending on what was negotiated with the server. The
285 * lower level routines will handle any byte swapping issue and will set the
286 * precomosed flag. The only flag support currently is UTF_SFM_CONVERSIONS.
287 */
288int smb_usr_convert_path_to_network(struct smb_vc *vc, struct smbioc_path_convert * dp)
289{
290	size_t ntwrk_len = (size_t)dp->ioc_dest_len;
291	char *network = NULL;
292	char *utf8str = NULL;
293	int error;
294
295	utf8str = smb_memdupin(dp->ioc_kern_src, dp->ioc_src_len);
296	if (utf8str) {
297        SMB_MALLOC(network, char *, ntwrk_len, M_SMBSTR, M_WAITOK | M_ZERO);
298    }
299
300	if ((utf8str == NULL) || (network == NULL)) {
301		error = ENOMEM;
302		goto done;
303	}
304	error = smb_convert_path_to_network(utf8str, dp->ioc_src_len, network, &ntwrk_len,
305										'\\', (int)dp->ioc_flags, SMB_UNICODE_STRINGS(vc));
306	if (error) {
307		SMBERROR("converter failed : %d\n", error);
308		SMBDEBUG("utf8str = %s src len = %d dest len = %d\n", utf8str,
309				 (int)dp->ioc_src_len, (int)dp->ioc_dest_len);
310		goto done;
311	}
312
313	error = copyout(network, dp->ioc_kern_dest, ntwrk_len);
314	if (error) {
315		SMBERROR("copyout failed : %d\n", error);
316		smb_hexdump(__FUNCTION__, "dest buffer: ", (u_char *)network, ntwrk_len);
317		goto done;
318	}
319
320	dp->ioc_dest_len = (uint32_t)ntwrk_len;
321
322done:
323	if (utf8str)
324		SMB_FREE(utf8str, M_SMBSTR);
325	if (network)
326		SMB_FREE(network, M_SMBSTR);
327	return error;
328}
329
330/*
331 * Converts a network style STRING to a UTF8 string. The network STRING can be
332 * formatted as an ASCII or UTF16 string. If the network STRING is in UTF16 format
333 * then this routine expects it to be decomosed. The only flag support currently
334 * is UTF_SFM_CONVERSIONS.
335 */
336int smb_usr_convert_network_to_path(struct smb_vc *vc, struct smbioc_path_convert * dp)
337{
338	size_t utf8str_len = (size_t)dp->ioc_dest_len;
339	char *network = NULL;
340	char *utf8str = NULL;
341	int error;
342
343	network = smb_memdupin(dp->ioc_kern_src, dp->ioc_src_len);
344	if (network) {
345        SMB_MALLOC(utf8str, char *, utf8str_len, M_SMBSTR, M_WAITOK | M_ZERO);
346	}
347
348	if ((utf8str == NULL) || (network == NULL)) {
349		error = ENOMEM;
350		goto done;
351	}
352
353	error = smb_convert_network_to_path(network, dp->ioc_src_len, utf8str,
354										&utf8str_len, '\\', (int)dp->ioc_flags,
355										SMB_UNICODE_STRINGS(vc));
356	if (error) {
357		SMBERROR("converter failed : %d\n", error);
358		smb_hexdump(__FUNCTION__, "source buffer: ", (u_char *)network, dp->ioc_src_len);
359		goto done;
360	}
361
362	error = copyout(utf8str, dp->ioc_kern_dest, utf8str_len);
363	if (error) {
364		SMBERROR("copyout failed : %d\n", error);
365		SMBDEBUG("utf8str = %s src len = %d dest len = %d\n", utf8str,
366				 (int)dp->ioc_src_len, (int)dp->ioc_dest_len);
367		goto done;
368	}
369	dp->ioc_dest_len = (uint32_t)utf8str_len;
370
371done:
372	if (utf8str)
373		SMB_FREE(utf8str, M_SMBSTR);
374	if (network)
375		SMB_FREE(network, M_SMBSTR);
376	return error;
377}
378
379/*
380 * They are setting the network user identity that was obtain by lsa. Currently
381 * we only support adding the sid in the future we may want the account name
382 * and domian name.
383 */
384int smb_usr_set_network_identity(struct smb_vc *vcp, struct smbioc_ntwrk_identity *ntwrkID)
385{
386	if (vcp->vc_flags & SMBV_NETWORK_SID) {
387		/* Currently we only allow it to be set once */
388		return EEXIST;
389	}
390
391	if (ntwrkID->ioc_ntsid_len != sizeof(ntsid_t)) {
392		/* Needs to be the correct size */
393		return EINVAL;
394	}
395	memcpy(&vcp->vc_ntwrk_sid, &ntwrkID->ioc_ntsid, (size_t)ntwrkID->ioc_ntsid_len);
396	vcp->vc_flags |= SMBV_NETWORK_SID;
397	return 0;
398}
399
400/*
401 * Called from user land so we always have a reference on the share.
402 */
403int
404smb_usr_fsctl(struct smb_share *share, struct smbioc_fsctl *fsctl, vfs_context_t context)
405{
406	struct smb_ntrq * ntp = NULL;
407	int error = 0;
408
409	if (fsctl->ioc_tdatacnt > INT_MAX) {
410		error = EINVAL;
411		goto done;
412	}
413
414	fsctl->ioc_errno = 0;
415	fsctl->ioc_ntstatus = 0;
416
417	/* Take the 32 bit world pointers and convert them to user_addr_t. */
418	if (! vfs_context_is64bit (context)) {
419		fsctl->ioc_kern_tdata = CAST_USER_ADDR_T(fsctl->ioc_tdata);
420		fsctl->ioc_kern_rdata = CAST_USER_ADDR_T(fsctl->ioc_rdata);
421	}
422
423	error = smb_nt_alloc(SSTOCP(share), NT_TRANSACT_IOCTL, context, &ntp);
424	if (error) {
425		goto done;
426	}
427
428	/* The NT_TRANSACT_IOCTL setup structure is:
429	 *		uint32_t	FunctionCode
430	 *		uint16_t	FID
431	 *		uint8_t		IsFctl
432	 *		uint8_t		IsFlags
433	 */
434	mb_init(&ntp->nt_tsetup);
435	mb_put_uint32le(&ntp->nt_tsetup, fsctl->ioc_fsctl);
436	mb_put_uint16le(&ntp->nt_tsetup, fsctl->ioc_fh);
437	mb_put_uint8(&ntp->nt_tsetup, 1);
438	mb_put_uint8(&ntp->nt_tsetup, 0);
439
440	/* The fsctl arguments go in the transmit data. */
441	if (fsctl->ioc_tdatacnt) {
442		mb_init(&ntp->nt_tdata);
443		error = mb_put_user_mem(&ntp->nt_tdata, fsctl->ioc_kern_tdata,
444							fsctl->ioc_tdatacnt, 0, context);
445		if (error) {
446			goto done;
447		}
448	}
449
450	/* We don't expect any returned params from NT_TRANSACT_IOCTL. */
451	ntp->nt_maxpcount = 0;
452	ntp->nt_maxdcount = fsctl->ioc_rdatacnt;
453
454	fsctl->ioc_errno = smb_nt_request(ntp);
455	fsctl->ioc_ntstatus = ntp->nt_status;
456
457	if (fsctl->ioc_errno) {
458		error = 0;
459		goto done;
460	}
461
462	/* The data buffer wasn't big enough. Caller will have to retry. */
463	if (ntp->nt_flags & SMBT2_MOREDATA) {
464		fsctl->ioc_errno = ENOSPC;
465		error = 0;
466		goto done;
467	}
468
469	if (ntp->nt_rdata.md_top) {
470		size_t datalen = m_fixhdr(ntp->nt_rdata.md_top);
471		if (datalen > fsctl->ioc_rdatacnt) {
472			SMBERROR("datalen(%u) > ioc_rdatacnt(%u)",
473					(unsigned)datalen, (unsigned)fsctl->ioc_rdatacnt);
474			error = EMSGSIZE;
475			goto done;
476		}
477
478		fsctl->ioc_rdatacnt = (uint32_t)datalen;
479		error = md_get_user_mem(&ntp->nt_rdata, fsctl->ioc_kern_rdata,
480								(uint32_t)datalen, 0, context);
481		if (error) {
482			SMBERROR("md_get_user_mem failed with %d", error);
483			goto done;
484		}
485	} else {
486		fsctl->ioc_rdatacnt = 0;
487	}
488
489done:
490	if (ntp) {
491		smb_nt_done(ntp);
492	}
493
494	return error;
495}
496
497