1 /*
2  * Copyright (c) 2000-2001 Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *    This product includes software developed by Boris Popov.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $Id: smb_usr.c,v 1.15 2004/12/13 00:25:18 lindak Exp $
33  */
34 
35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
36 
37 #include <sys/param.h>
38 #include <sys/kmem.h>
39 #include <sys/systm.h>
40 #include <sys/policy.h>
41 #include <sys/conf.h>
42 #include <sys/proc.h>
43 #include <sys/fcntl.h>
44 #include <sys/socket.h>
45 #include <sys/cmn_err.h>
46 
47 #ifdef APPLE
48 #include <sys/smb_apple.h>
49 #include <sys/smb_iconv.h>
50 #else
51 #include <netsmb/smb_osdep.h>
52 #endif
53 
54 #include <netsmb/smb.h>
55 #include <netsmb/smb_conn.h>
56 #include <netsmb/smb_rq.h>
57 #include <netsmb/smb_subr.h>
58 #include <netsmb/smb_dev.h>
59 
60 /*
61  * helpers for nsmb device. Can be moved to the smb_dev.c file.
62  */
63 static void smb_usr_vcspec_free(struct smb_vcspec *spec);
64 
65 /*
66  * Moved the access checks here, just becuase
67  * this was a more convenient place to do it
68  * than in every function calling this.
69  */
70 static int
71 smb_usr_ioc2vcspec(struct smbioc_ossn *dp, struct smb_vcspec *spec)
72 {
73 	cred_t *cr = CRED();
74 	uid_t realuid;
75 
76 	/*
77 	 * Only superuser can specify a UID or GID.
78 	 */
79 	realuid = crgetruid(cr);
80 	if (dp->ioc_owner == SMBM_ANY_OWNER)
81 		spec->owner = realuid;
82 	else {
83 		/*
84 		 * Do we have the privilege to create with the
85 		 * specified uid?  (does uid == cr->cr_uid, etc.)
86 		 * MacOS would want suser(), or similar here.
87 		 */
88 		if (secpolicy_vnode_owner(cr, dp->ioc_owner))
89 			return (EPERM);
90 		spec->owner = dp->ioc_owner;
91 	}
92 	if (dp->ioc_group == SMBM_ANY_GROUP)
93 		spec->group = crgetgid(cr);
94 	else {
95 		/*
96 		 * Do we have the privilege to create with the
97 		 * specified gid?  (one of our groups?)
98 		 */
99 		if (groupmember(dp->ioc_group, cr) ||
100 		    secpolicy_vnode_create_gid(cr) == 0)
101 			spec->group = dp->ioc_group;
102 		else
103 			return (EPERM);
104 	}
105 
106 	/*
107 	 * Valid codesets?  XXX
108 	 */
109 	if (dp->ioc_localcs[0] == 0) {
110 		spec->localcs = "ISO8859-1";
111 #ifdef NOTYETRESOLVED
112 		SMBERROR("no local charset ? dp->ioc_localcs[0]: %d\n",
113 		    dp->ioc_localcs[0]);
114 		return (EINVAL);
115 #endif
116 	} else
117 		spec->localcs = spec->localcs;
118 
119 	/*
120 	 * Check for valid sa_family.
121 	 * XXX: Just NetBIOS for now.
122 	 */
123 	if (dp->ioc_server.sa.sa_family != AF_NETBIOS)
124 		return (EINVAL);
125 	spec->sap = &dp->ioc_server.sa;
126 
127 	if (dp->ioc_local.sa.sa_family) {
128 		/* If specified, local AF must be the same. */
129 		if (dp->ioc_local.sa.sa_family !=
130 		    dp->ioc_server.sa.sa_family)
131 			return (EINVAL);
132 		spec->lap = &dp->ioc_local.sa;
133 	}
134 
135 	if (dp->ioc_intok) {
136 		spec->tok = smb_memdupin(dp->ioc_intok, dp->ioc_intoklen);
137 		if (spec->tok == NULL)
138 			return (EFAULT);
139 		spec->toklen = dp->ioc_intoklen;
140 	}
141 
142 	spec->srvname = dp->ioc_srvname;
143 	spec->pass = dp->ioc_password;
144 	spec->domain = dp->ioc_workgroup;
145 	spec->username = dp->ioc_user;
146 	spec->mode = dp->ioc_mode;
147 	spec->rights = dp->ioc_rights;
148 	spec->servercs = dp->ioc_servercs;
149 	spec->optflags = dp->ioc_opt;
150 
151 	return (0);
152 }
153 
154 static void
155 smb_usr_shspec_free(struct smb_sharespec *sspec)
156 {
157 	kmem_free(sspec, sizeof (struct smb_sharespec));
158 }
159 
160 static void
161 smb_usr_vcspec_free(struct smb_vcspec *spec)
162 {
163 
164 	if (spec->tok) {
165 		kmem_free(spec->tok, spec->toklen);
166 	}
167 	kmem_free(spec, sizeof (*spec));
168 }
169 
170 static int
171 smb_usr_ioc2sharespec(struct smbioc_oshare *dp, struct smb_sharespec *spec)
172 {
173 	bzero(spec, sizeof (*spec));
174 	spec->name = dp->ioc_share;
175 	spec->pass = dp->ioc_password;
176 	spec->mode = dp->ioc_mode;
177 	spec->rights = dp->ioc_rights;
178 	spec->owner = dp->ioc_owner;
179 	spec->group = dp->ioc_group;
180 	spec->stype = dp->ioc_stype;
181 	spec->optflags = dp->ioc_opt;
182 	return (0);
183 }
184 
185 int
186 smb_usr_negotiate(struct smbioc_lookup *dp, struct smb_cred *scred,
187 	struct smb_vc **vcpp)
188 {
189 	struct smb_vc *vcp = NULL;
190 	struct smb_vcspec *vspec = NULL;
191 	struct smb_sharespec *sspecp = NULL;
192 	int error = 0;
193 
194 	if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE)
195 		return (EINVAL);
196 	vspec = kmem_zalloc(sizeof (struct smb_vcspec), KM_SLEEP);
197 	error = smb_usr_ioc2vcspec(&dp->ioc_ssn, vspec);
198 	if (error)
199 		return (error);
200 	if (dp->ioc_flags & SMBLK_CREATE)
201 		vspec->optflags |= SMBVOPT_CREATE;
202 	if (dp->ioc_level >= SMBL_SHARE) {
203 		sspecp = kmem_alloc(sizeof (*sspecp), KM_SLEEP);
204 		error = smb_usr_ioc2sharespec(&dp->ioc_sh, sspecp);
205 		if (error)
206 			goto out;
207 	}
208 	error = smb_sm_negotiate(vspec, scred, &vcp);
209 	if (error == 0) {
210 		*vcpp =  vcp;
211 		/*
212 		 * Used to copyout ioc_outtok, outtoklen here,
213 		 * but that's now in smb_dev. (our caller)
214 		 *
215 		 * If this call asked for extended security and
216 		 * the server does not support it, clear the
217 		 * flag so the caller knows this.
218 		 *
219 		 * XXX: Should just add sv_caps to ioc_ssn,
220 		 * set the new sv_caps field here, and let
221 		 * let the copyout of ioc_ssn handle it.
222 		 */
223 		if (!(vcp->vc_sopt.sv_caps & SMB_CAP_EXT_SECURITY) &&
224 		    (dp->ioc_ssn.ioc_opt & SMBVOPT_EXT_SEC)) {
225 			dp->ioc_ssn.ioc_opt &= ~SMBVOPT_EXT_SEC;
226 			SMBSDEBUG("turned off extended security");
227 		}
228 	}
229 out:
230 	smb_usr_vcspec_free(vspec);
231 	smb_usr_shspec_free(sspecp);
232 	return (error);
233 }
234 
235 int
236 smb_usr_ssnsetup(struct smbioc_lookup *dp, struct smb_cred *scred,
237 	struct smb_vc *vcp)
238 {
239 	struct smb_vcspec *vspec = NULL;
240 	int error;
241 
242 	if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE)
243 		return (EINVAL);
244 
245 	vspec = kmem_zalloc(sizeof (struct smb_vcspec), KM_SLEEP);
246 	error = smb_usr_ioc2vcspec(&dp->ioc_ssn, vspec);
247 	if (error)
248 		goto out;
249 
250 	error = smb_sm_ssnsetup(vspec, scred, vcp);
251 	/*
252 	 * Moved the copyout of ioc_outtok to
253 	 * smb_dev.c (our caller)
254 	 */
255 
256 out:
257 	smb_usr_vcspec_free(vspec);
258 	return (error);
259 }
260 
261 
262 int
263 smb_usr_tcon(struct smbioc_lookup *dp, struct smb_cred *scred,
264 	struct smb_vc *vcp, struct smb_share **sspp)
265 {
266 	struct smb_sharespec *sspecp = NULL;
267 	int error;
268 
269 	if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE)
270 		return (EINVAL);
271 
272 	if (dp->ioc_level >= SMBL_SHARE) {
273 		sspecp = kmem_alloc(sizeof (*sspecp), KM_SLEEP);
274 		error = smb_usr_ioc2sharespec(&dp->ioc_sh, sspecp);
275 		if (error)
276 			goto out;
277 	}
278 	error = smb_sm_tcon(sspecp, scred, vcp, sspp);
279 
280 out:
281 	if (sspecp)
282 		smb_usr_shspec_free(sspecp);
283 
284 	return (error);
285 }
286 
287 /*
288  * Connect to the resource specified by smbioc_ossn structure.
289  * It may either find an existing connection or try to establish a new one.
290  * If no errors occured smb_vc returned locked and referenced.
291  */
292 
293 int
294 smb_usr_simplerequest(struct smb_share *ssp, struct smbioc_rq *dp,
295 	struct smb_cred *scred)
296 {
297 	struct smb_rq rq, *rqp = &rq;
298 	struct mbchain *mbp;
299 	struct mdchain *mdp;
300 	char *p;
301 	size_t wc2;
302 	u_int8_t wc;
303 	u_int16_t bc;
304 	int error;
305 
306 	switch (dp->ioc_cmd) {
307 	case SMB_COM_TRANSACTION2:
308 	case SMB_COM_TRANSACTION2_SECONDARY:
309 	case SMB_COM_CLOSE_AND_TREE_DISC:
310 	case SMB_COM_TREE_CONNECT:
311 	case SMB_COM_TREE_DISCONNECT:
312 	case SMB_COM_NEGOTIATE:
313 	case SMB_COM_SESSION_SETUP_ANDX:
314 	case SMB_COM_LOGOFF_ANDX:
315 	case SMB_COM_TREE_CONNECT_ANDX:
316 		return (EPERM);
317 	}
318 	error = smb_rq_init(rqp, SSTOCP(ssp), dp->ioc_cmd, scred);
319 	if (error)
320 		return (error);
321 	mbp = &rqp->sr_rq;
322 	smb_rq_wstart(rqp);
323 	error = mb_put_mem(mbp, dp->ioc_twords,
324 	    dp->ioc_twc * 2, MB_MUSER);
325 	if (error)
326 		goto bad;
327 	smb_rq_wend(rqp);
328 	smb_rq_bstart(rqp);
329 	error = mb_put_mem(mbp, dp->ioc_tbytes,
330 	    dp->ioc_tbc, MB_MUSER);
331 	if (error)
332 		goto bad;
333 	smb_rq_bend(rqp);
334 	error = smb_rq_simple(rqp);
335 	if (error)
336 		goto bad;
337 	mdp = &rqp->sr_rp;
338 	md_get_uint8(mdp, &wc);
339 	dp->ioc_rwc = wc;
340 	wc2 = wc * 2;
341 	if (wc2 > dp->ioc_rpbufsz) {
342 		error = EBADRPC;
343 		goto bad;
344 	}
345 	error = md_get_mem(mdp, dp->ioc_rpbuf, wc2, MB_MUSER);
346 	if (error)
347 		goto bad;
348 	md_get_uint16le(mdp, &bc);
349 	if ((wc2 + bc) > dp->ioc_rpbufsz) {
350 		error = EBADRPC;
351 		goto bad;
352 	}
353 	dp->ioc_rbc = bc;
354 	p = dp->ioc_rpbuf;
355 	error = md_get_mem(mdp, p + wc2, bc, MB_MUSER);
356 bad:
357 	dp->ioc_errclass = rqp->sr_errclass;
358 	dp->ioc_serror = rqp->sr_serror;
359 	dp->ioc_error = rqp->sr_error;
360 	smb_rq_done(rqp);
361 	return (error);
362 
363 }
364 
365 static int
366 smb_cpdatain(struct mbchain *mbp, int len, char *data)
367 {
368 	int error;
369 
370 	if (len == 0)
371 		return (0);
372 	error = mb_init(mbp);
373 	if (error)
374 		return (error);
375 	return (mb_put_mem(mbp, data, len, MB_MUSER));
376 }
377 
378 int
379 smb_usr_t2request(struct smb_share *ssp, smbioc_t2rq_t *dp,
380 	struct smb_cred *scred)
381 {
382 	struct smb_t2rq t2, *t2p = &t2;
383 	struct mdchain *mdp;
384 	int error, len;
385 
386 	if (dp->ioc_setupcnt > SMB_MAXSETUPWORDS)
387 		return (EINVAL);
388 
389 	t2p = (struct smb_t2rq *)kmem_alloc(sizeof (struct smb_t2rq), KM_SLEEP);
390 	if (t2p == NULL)
391 		return (ENOMEM);
392 	error = smb_t2_init(t2p, SSTOCP(ssp), dp->ioc_setup, dp->ioc_setupcnt,
393 	    scred);
394 	if (error)
395 		return (error);
396 	len = t2p->t2_setupcount = dp->ioc_setupcnt;
397 	if (len > 1)
398 		t2p->t2_setupdata = dp->ioc_setup;
399 	if (dp->ioc_name) {
400 		bcopy(dp->ioc_name, t2p->t_name, 128);
401 		if (t2p->t_name == NULL) {
402 			error = ENOMEM;
403 			goto bad;
404 		}
405 	}
406 	t2p->t2_maxscount = 0;
407 	t2p->t2_maxpcount = dp->ioc_rparamcnt;
408 	t2p->t2_maxdcount = dp->ioc_rdatacnt;
409 	error = smb_cpdatain(&t2p->t2_tparam, dp->ioc_tparamcnt,
410 	    dp->ioc_tparam);
411 	if (error)
412 		goto bad;
413 	error = smb_cpdatain(&t2p->t2_tdata,
414 	    dp->ioc_tdatacnt, dp->ioc_tdata);
415 	if (error)
416 		goto bad;
417 	error = smb_t2_request(t2p);
418 	dp->ioc_errclass = t2p->t2_sr_errclass;
419 	dp->ioc_serror = t2p->t2_sr_serror;
420 	dp->ioc_error = t2p->t2_sr_error;
421 	dp->ioc_rpflags2 = t2p->t2_sr_rpflags2;
422 	if (error)
423 		goto bad;
424 	mdp = &t2p->t2_rparam;
425 	if (mdp->md_top) {
426 		mblk_t *m = mdp->md_top;
427 #ifdef lint
428 		m = m;
429 #endif
430 		len = m_fixhdr(mdp->md_top);
431 		if (len > dp->ioc_rparamcnt) {
432 			error = EMSGSIZE;
433 			goto bad;
434 		}
435 		dp->ioc_rparamcnt = (ushort_t)len;
436 		error = md_get_mem(mdp, dp->ioc_rparam,
437 		    len, MB_MUSER);
438 		if (error) {
439 			goto bad;
440 		}
441 	} else
442 		dp->ioc_rparamcnt = 0;
443 	mdp = &t2p->t2_rdata;
444 	if (mdp->md_top) {
445 		mblk_t *m = mdp->md_top;
446 #ifdef lint
447 		m = m;
448 #endif
449 		len = m_fixhdr(mdp->md_top);
450 		if (len > dp->ioc_rdatacnt) {
451 			error = EMSGSIZE;
452 			goto bad;
453 		}
454 		dp->ioc_rdatacnt = (ushort_t)len;
455 		error = md_get_mem(mdp, dp->ioc_rdata,
456 		    len, MB_MUSER);
457 		if (error) {
458 			goto bad;
459 		}
460 	} else
461 		dp->ioc_rdatacnt = 0;
462 bad:
463 	smb_t2_done(t2p);
464 	return (error);
465 }
466 
467 /*
468  * Helper for nsmb_ioctl cases
469  * SMBIOC_READ, SMBIOC_WRITE
470  */
471 int
472 smb_usr_rw(struct smb_share *ssp, smbioc_rw_t *rwrq,
473     int cmd, struct smb_cred *scred)
474 {
475 	struct iovec aiov[1];
476 	struct uio  auio;
477 	u_int16_t fh;
478 	int error;
479 	uio_rw_t rw;
480 
481 	switch (cmd) {
482 	case SMBIOC_READ:
483 		rw = UIO_READ;
484 		break;
485 	case SMBIOC_WRITE:
486 		rw = UIO_WRITE;
487 		break;
488 	default:
489 		return (ENODEV);
490 	}
491 
492 	fh = htoles(rwrq->ioc_fh);
493 
494 	aiov[0].iov_base = rwrq->ioc_base;
495 	aiov[0].iov_len = (size_t)rwrq->ioc_cnt;
496 
497 	auio.uio_iov = aiov;
498 	auio.uio_iovcnt = 1;
499 	auio.uio_loffset = rwrq->ioc_offset;
500 	auio.uio_segflg = UIO_USERSPACE;
501 	auio.uio_fmode = 0;
502 	auio.uio_resid = (size_t)rwrq->ioc_cnt;
503 
504 	error = smb_rwuio(ssp, fh, rw, &auio, scred, 0);
505 
506 	/*
507 	 * On return ioc_cnt holds the
508 	 * number of bytes transferred.
509 	 */
510 	rwrq->ioc_cnt -= auio.uio_resid;
511 
512 	return (error);
513 }
514