1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2000-2001 Boris Popov
5 * 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 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include <sys/param.h>
33#include <sys/malloc.h>
34#include <sys/kernel.h>
35#include <sys/systm.h>
36#include <sys/conf.h>
37#include <sys/proc.h>
38#include <sys/fcntl.h>
39#include <sys/socket.h>
40#include <sys/socketvar.h>
41#include <sys/sysctl.h>
42#include <sys/mbuf.h>
43
44#include <sys/iconv.h>
45
46#include <netsmb/smb.h>
47#include <netsmb/smb_conn.h>
48#include <netsmb/smb_rq.h>
49#include <netsmb/smb_subr.h>
50#include <netsmb/smb_dev.h>
51
52/*
53 * helpers for nsmb device. Can be moved to the smb_dev.c file.
54 */
55static void smb_usr_vcspec_free(struct smb_vcspec *spec);
56
57static int
58smb_usr_vc2spec(struct smbioc_ossn *dp, struct smb_vcspec *spec)
59{
60	int flags = 0;
61
62	bzero(spec, sizeof(*spec));
63
64#ifdef NETSMB_NO_ANON_USER
65	if (dp->ioc_user[0] == 0)
66		return EINVAL;
67#endif
68
69	if (dp->ioc_server == NULL)
70		return EINVAL;
71	if (dp->ioc_localcs[0] == 0) {
72		SMBERROR("no local charset ?\n");
73		return EINVAL;
74	}
75
76	spec->sap = smb_memdupin(dp->ioc_server, dp->ioc_svlen);
77	if (spec->sap == NULL)
78		return ENOMEM;
79	if (dp->ioc_local) {
80		spec->lap = smb_memdupin(dp->ioc_local, dp->ioc_lolen);
81		if (spec->lap == NULL) {
82			smb_usr_vcspec_free(spec);
83			return ENOMEM;
84		}
85	}
86	spec->srvname = dp->ioc_srvname;
87	spec->pass = dp->ioc_password;
88	spec->domain = dp->ioc_workgroup;
89	spec->username = dp->ioc_user;
90	spec->mode = dp->ioc_mode;
91	spec->rights = dp->ioc_rights;
92	spec->owner = dp->ioc_owner;
93	spec->group = dp->ioc_group;
94	spec->localcs = dp->ioc_localcs;
95	spec->servercs = dp->ioc_servercs;
96	if (dp->ioc_opt & SMBVOPT_PRIVATE)
97		flags |= SMBV_PRIVATE;
98	if (dp->ioc_opt & SMBVOPT_SINGLESHARE)
99		flags |= SMBV_PRIVATE | SMBV_SINGLESHARE;
100	spec->flags = flags;
101	return 0;
102}
103
104static void
105smb_usr_vcspec_free(struct smb_vcspec *spec)
106{
107	if (spec->sap)
108		smb_memfree(spec->sap);
109	if (spec->lap)
110		smb_memfree(spec->lap);
111}
112
113static int
114smb_usr_share2spec(struct smbioc_oshare *dp, struct smb_sharespec *spec)
115{
116	bzero(spec, sizeof(*spec));
117	spec->mode = dp->ioc_mode;
118	spec->rights = dp->ioc_rights;
119	spec->owner = dp->ioc_owner;
120	spec->group = dp->ioc_group;
121	spec->name = dp->ioc_share;
122	spec->stype = dp->ioc_stype;
123	spec->pass = dp->ioc_password;
124	return 0;
125}
126
127int
128smb_usr_lookup(struct smbioc_lookup *dp, struct smb_cred *scred,
129	struct smb_vc **vcpp, struct smb_share **sspp)
130{
131	struct smb_vc *vcp = NULL;
132	struct smb_vcspec vspec;			/* XXX */
133	struct smb_sharespec sspec, *sspecp = NULL;	/* XXX */
134	int error;
135
136	if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE)
137		return EINVAL;
138	error = smb_usr_vc2spec(&dp->ioc_ssn, &vspec);
139	if (error)
140		return error;
141	if (dp->ioc_flags & SMBLK_CREATE)
142		vspec.flags |= SMBV_CREATE;
143
144	if (dp->ioc_level >= SMBL_SHARE) {
145		error = smb_usr_share2spec(&dp->ioc_sh, &sspec);
146		if (error)
147			goto out;
148		sspecp = &sspec;
149	}
150	error = smb_sm_lookup(&vspec, sspecp, scred, &vcp);
151	if (error == 0) {
152		*vcpp = vcp;
153		*sspp = vspec.ssp;
154	}
155out:
156	smb_usr_vcspec_free(&vspec);
157	return error;
158}
159
160/*
161 * Connect to the resource specified by smbioc_ossn structure.
162 * It may either find an existing connection or try to establish a new one.
163 * If no errors occurred smb_vc returned locked and referenced.
164 */
165int
166smb_usr_opensession(struct smbioc_ossn *dp, struct smb_cred *scred,
167	struct smb_vc **vcpp)
168{
169	struct smb_vc *vcp = NULL;
170	struct smb_vcspec vspec;
171	int error;
172
173	error = smb_usr_vc2spec(dp, &vspec);
174	if (error)
175		return error;
176	if (dp->ioc_opt & SMBVOPT_CREATE)
177		vspec.flags |= SMBV_CREATE;
178
179	error = smb_sm_lookup(&vspec, NULL, scred, &vcp);
180	smb_usr_vcspec_free(&vspec);
181	return error;
182}
183
184int
185smb_usr_openshare(struct smb_vc *vcp, struct smbioc_oshare *dp,
186	struct smb_cred *scred, struct smb_share **sspp)
187{
188	struct smb_share *ssp;
189	struct smb_sharespec shspec;
190	int error;
191
192	error = smb_usr_share2spec(dp, &shspec);
193	if (error)
194		return error;
195	error = smb_vc_lookupshare(vcp, &shspec, scred, &ssp);
196	if (error == 0) {
197		*sspp = ssp;
198		return 0;
199	}
200	if ((dp->ioc_opt & SMBSOPT_CREATE) == 0)
201		return error;
202	error = smb_share_create(vcp, &shspec, scred, &ssp);
203	if (error)
204		return error;
205	error = smb_smb_treeconnect(ssp, scred);
206	if (error) {
207		smb_share_put(ssp, scred);
208	} else
209		*sspp = ssp;
210	return error;
211}
212
213int
214smb_usr_simplerequest(struct smb_share *ssp, struct smbioc_rq *dp,
215	struct smb_cred *scred)
216{
217	struct smb_rq *rqp;
218	struct mbchain *mbp;
219	struct mdchain *mdp;
220	u_int8_t wc;
221	u_int16_t bc;
222	int error;
223
224	switch (dp->ioc_cmd) {
225	    case SMB_COM_TRANSACTION2:
226	    case SMB_COM_TRANSACTION2_SECONDARY:
227	    case SMB_COM_CLOSE_AND_TREE_DISC:
228	    case SMB_COM_TREE_CONNECT:
229	    case SMB_COM_TREE_DISCONNECT:
230	    case SMB_COM_NEGOTIATE:
231	    case SMB_COM_SESSION_SETUP_ANDX:
232	    case SMB_COM_LOGOFF_ANDX:
233	    case SMB_COM_TREE_CONNECT_ANDX:
234		return EPERM;
235	}
236	rqp = malloc(sizeof(struct smb_rq), M_SMBTEMP, M_WAITOK);
237	error = smb_rq_init(rqp, SSTOCP(ssp), dp->ioc_cmd, scred);
238	if (error) {
239		free(rqp, M_SMBTEMP);
240		return error;
241	}
242	mbp = &rqp->sr_rq;
243	smb_rq_wstart(rqp);
244	error = mb_put_mem(mbp, dp->ioc_twords, dp->ioc_twc * 2, MB_MUSER);
245	if (error)
246		goto bad;
247	smb_rq_wend(rqp);
248	smb_rq_bstart(rqp);
249	error = mb_put_mem(mbp, dp->ioc_tbytes, dp->ioc_tbc, MB_MUSER);
250	if (error)
251		goto bad;
252	smb_rq_bend(rqp);
253	error = smb_rq_simple(rqp);
254	if (error)
255		goto bad;
256	mdp = &rqp->sr_rp;
257	md_get_uint8(mdp, &wc);
258	dp->ioc_rwc = wc;
259	wc *= 2;
260	if (wc > dp->ioc_rpbufsz) {
261		error = EBADRPC;
262		goto bad;
263	}
264	error = md_get_mem(mdp, dp->ioc_rpbuf, wc, MB_MUSER);
265	if (error)
266		goto bad;
267	md_get_uint16le(mdp, &bc);
268	if ((wc + bc) > dp->ioc_rpbufsz) {
269		error = EBADRPC;
270		goto bad;
271	}
272	dp->ioc_rbc = bc;
273	error = md_get_mem(mdp, dp->ioc_rpbuf + wc, bc, MB_MUSER);
274bad:
275	dp->ioc_errclass = rqp->sr_errclass;
276	dp->ioc_serror = rqp->sr_serror;
277	dp->ioc_error = rqp->sr_error;
278	smb_rq_done(rqp);
279	free(rqp, M_SMBTEMP);
280	return error;
281
282}
283
284static int
285smb_cpdatain(struct mbchain *mbp, int len, caddr_t data)
286{
287	int error;
288
289	if (len == 0)
290		return 0;
291	error = mb_init(mbp);
292	if (error)
293		return error;
294	return mb_put_mem(mbp, data, len, MB_MUSER);
295}
296
297int
298smb_usr_t2request(struct smb_share *ssp, struct smbioc_t2rq *dp,
299	struct smb_cred *scred)
300{
301	struct smb_t2rq *t2p;
302	struct mdchain *mdp;
303	int error, len;
304
305	if (dp->ioc_setupcnt > 3)
306		return EINVAL;
307	t2p = malloc(sizeof(struct smb_t2rq), M_SMBTEMP, M_WAITOK);
308	error = smb_t2_init(t2p, SSTOCP(ssp), dp->ioc_setup[0], scred);
309	if (error) {
310		free(t2p, M_SMBTEMP);
311		return error;
312	}
313	len = t2p->t2_setupcount = dp->ioc_setupcnt;
314	if (len > 1)
315		t2p->t2_setupdata = dp->ioc_setup;
316	if (dp->ioc_name) {
317		t2p->t_name = smb_strdupin(dp->ioc_name, 128);
318		if (t2p->t_name == NULL) {
319			error = ENOMEM;
320			goto bad;
321		}
322	}
323	t2p->t2_maxscount = 0;
324	t2p->t2_maxpcount = dp->ioc_rparamcnt;
325	t2p->t2_maxdcount = dp->ioc_rdatacnt;
326	error = smb_cpdatain(&t2p->t2_tparam, dp->ioc_tparamcnt, dp->ioc_tparam);
327	if (error)
328		goto bad;
329	error = smb_cpdatain(&t2p->t2_tdata, dp->ioc_tdatacnt, dp->ioc_tdata);
330	if (error)
331		goto bad;
332	error = smb_t2_request(t2p);
333	if (error)
334		goto bad;
335	mdp = &t2p->t2_rparam;
336	if (mdp->md_top) {
337		len = m_fixhdr(mdp->md_top);
338		if (len > dp->ioc_rparamcnt) {
339			error = EMSGSIZE;
340			goto bad;
341		}
342		dp->ioc_rparamcnt = len;
343		error = md_get_mem(mdp, dp->ioc_rparam, len, MB_MUSER);
344		if (error)
345			goto bad;
346	} else
347		dp->ioc_rparamcnt = 0;
348	mdp = &t2p->t2_rdata;
349	if (mdp->md_top) {
350		len = m_fixhdr(mdp->md_top);
351		if (len > dp->ioc_rdatacnt) {
352			error = EMSGSIZE;
353			goto bad;
354		}
355		dp->ioc_rdatacnt = len;
356		error = md_get_mem(mdp, dp->ioc_rdata, len, MB_MUSER);
357	} else
358		dp->ioc_rdatacnt = 0;
359bad:
360	if (t2p->t_name)
361		smb_strfree(t2p->t_name);
362	smb_t2_done(t2p);
363	free(t2p, M_SMBTEMP);
364	return error;
365}
366