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 /*
36  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
37  * Use is subject to license terms.
38  */
39 
40 #include <sys/param.h>
41 #include <sys/kmem.h>
42 #include <sys/systm.h>
43 #include <sys/policy.h>
44 #include <sys/conf.h>
45 #include <sys/proc.h>
46 #include <sys/fcntl.h>
47 #include <sys/socket.h>
48 #include <sys/cmn_err.h>
49 
50 #include <netsmb/smb_osdep.h>
51 
52 #include <netsmb/smb.h>
53 #include <netsmb/smb_conn.h>
54 #include <netsmb/smb_rq.h>
55 #include <netsmb/smb_subr.h>
56 #include <netsmb/smb_dev.h>
57 
58 /*
59  * helpers for nsmb device. Can be moved to the smb_dev.c file.
60  */
61 static void smb_usr_vcspec_free(struct smb_vcspec *spec);
62 
63 /*
64  * Moved the access checks here, just becuase
65  * this was a more convenient place to do it
66  * than in every function calling this.
67  */
68 static int
69 smb_usr_ioc2vcspec(struct smbioc_ossn *dp, struct smb_vcspec *spec)
70 {
71 	cred_t *cr = CRED();
72 	uid_t realuid;
73 
74 	/*
75 	 * Only superuser can specify a UID or GID.
76 	 */
77 	realuid = crgetruid(cr);
78 	if (dp->ioc_owner == SMBM_ANY_OWNER)
79 		spec->owner = realuid;
80 	else {
81 		/*
82 		 * Do we have the privilege to create with the
83 		 * specified uid?  (does uid == cr->cr_uid, etc.)
84 		 * MacOS would want suser(), or similar here.
85 		 */
86 		if (secpolicy_vnode_owner(cr, dp->ioc_owner))
87 			return (EPERM);
88 		spec->owner = dp->ioc_owner;
89 	}
90 	if (dp->ioc_group == SMBM_ANY_GROUP)
91 		spec->group = crgetgid(cr);
92 	else {
93 		/*
94 		 * Do we have the privilege to create with the
95 		 * specified gid?  (one of our groups?)
96 		 */
97 		if (groupmember(dp->ioc_group, cr) ||
98 		    secpolicy_vnode_create_gid(cr) == 0)
99 			spec->group = dp->ioc_group;
100 		else
101 			return (EPERM);
102 	}
103 
104 	/*
105 	 * Valid codesets?  XXX
106 	 */
107 	if (dp->ioc_localcs[0] == 0) {
108 		spec->localcs = "ISO8859-1";
109 #ifdef NOTYETRESOLVED
110 		SMBERROR("no local charset ? dp->ioc_localcs[0]: %d\n",
111 		    dp->ioc_localcs[0]);
112 		return (EINVAL);
113 #endif
114 	} else
115 		spec->localcs = spec->localcs;
116 
117 	/*
118 	 * Check for valid sa_family.
119 	 * XXX: Just NetBIOS for now.
120 	 */
121 	if (dp->ioc_server.sa.sa_family != AF_NETBIOS)
122 		return (EINVAL);
123 	spec->sap = &dp->ioc_server.sa;
124 
125 	if (dp->ioc_local.sa.sa_family) {
126 		/* If specified, local AF must be the same. */
127 		if (dp->ioc_local.sa.sa_family !=
128 		    dp->ioc_server.sa.sa_family)
129 			return (EINVAL);
130 		spec->lap = &dp->ioc_local.sa;
131 	}
132 
133 	if (dp->ioc_intok) {
134 		spec->tok = smb_memdupin(dp->ioc_intok, dp->ioc_intoklen);
135 		if (spec->tok == NULL)
136 			return (EFAULT);
137 		spec->toklen = dp->ioc_intoklen;
138 	}
139 
140 	spec->srvname = dp->ioc_srvname;
141 	spec->pass = dp->ioc_password;
142 	spec->domain = dp->ioc_workgroup;
143 	spec->username = dp->ioc_user;
144 	spec->mode = dp->ioc_mode;
145 	spec->rights = dp->ioc_rights;
146 	spec->servercs = dp->ioc_servercs;
147 	spec->optflags = dp->ioc_opt;
148 
149 	return (0);
150 }
151 
152 static void
153 smb_usr_shspec_free(struct smb_sharespec *sspec)
154 {
155 	kmem_free(sspec, sizeof (struct smb_sharespec));
156 }
157 
158 static void
159 smb_usr_vcspec_free(struct smb_vcspec *spec)
160 {
161 
162 	if (spec->tok) {
163 		kmem_free(spec->tok, spec->toklen);
164 	}
165 	kmem_free(spec, sizeof (*spec));
166 }
167 
168 static int
169 smb_usr_ioc2sharespec(struct smbioc_oshare *dp, struct smb_sharespec *spec)
170 {
171 	bzero(spec, sizeof (*spec));
172 	spec->name = dp->ioc_share;
173 	spec->pass = dp->ioc_password;
174 	spec->mode = dp->ioc_mode;
175 	spec->rights = dp->ioc_rights;
176 	spec->owner = dp->ioc_owner;
177 	spec->group = dp->ioc_group;
178 	spec->stype = dp->ioc_stype;
179 	spec->optflags = dp->ioc_opt;
180 	return (0);
181 }
182 
183 int
184 smb_usr_findvc(struct smbioc_lookup *dp, struct smb_cred *scred,
185 	struct smb_vc **vcpp)
186 {
187 	struct smb_vc *vcp = NULL;
188 	struct smb_vcspec *vspec = NULL;
189 	int error = 0;
190 
191 	if (dp->ioc_flags & SMBLK_CREATE)
192 		return (EINVAL);
193 	if (dp->ioc_level != SMBL_VC)
194 		return (EINVAL);
195 	vspec = kmem_zalloc(sizeof (struct smb_vcspec), KM_SLEEP);
196 	error = smb_usr_ioc2vcspec(&dp->ioc_ssn, vspec);
197 	if (error)
198 		goto out;
199 	error = smb_sm_findvc(vspec, scred, &vcp);
200 	if (error == 0)
201 		*vcpp =  vcp;
202 out:
203 	smb_usr_vcspec_free(vspec);
204 	return (error);
205 }
206 
207 int
208 smb_usr_negotiate(struct smbioc_lookup *dp, struct smb_cred *scred,
209 	struct smb_vc **vcpp)
210 {
211 	struct smb_vc *vcp = NULL;
212 	struct smb_vcspec *vspec = NULL;
213 	struct smb_sharespec *sspecp = NULL;
214 	int error = 0;
215 
216 	if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE)
217 		return (EINVAL);
218 	vspec = kmem_zalloc(sizeof (struct smb_vcspec), KM_SLEEP);
219 	error = smb_usr_ioc2vcspec(&dp->ioc_ssn, vspec);
220 	if (error)
221 		return (error);
222 	if (dp->ioc_flags & SMBLK_CREATE)
223 		vspec->optflags |= SMBVOPT_CREATE;
224 	if (dp->ioc_level >= SMBL_SHARE) {
225 		sspecp = kmem_alloc(sizeof (*sspecp), KM_SLEEP);
226 		error = smb_usr_ioc2sharespec(&dp->ioc_sh, sspecp);
227 		if (error)
228 			goto out;
229 	}
230 	error = smb_sm_negotiate(vspec, scred, &vcp);
231 	if (error == 0) {
232 		*vcpp =  vcp;
233 		/*
234 		 * Used to copyout ioc_outtok, outtoklen here,
235 		 * but that's now in smb_dev. (our caller)
236 		 *
237 		 * If this call asked for extended security and
238 		 * the server does not support it, clear the
239 		 * flag so the caller knows this.
240 		 *
241 		 * XXX: Should just add sv_caps to ioc_ssn,
242 		 * set the new sv_caps field here, and let
243 		 * let the copyout of ioc_ssn handle it.
244 		 */
245 		if (!(vcp->vc_sopt.sv_caps & SMB_CAP_EXT_SECURITY) &&
246 		    (dp->ioc_ssn.ioc_opt & SMBVOPT_EXT_SEC)) {
247 			dp->ioc_ssn.ioc_opt &= ~SMBVOPT_EXT_SEC;
248 			SMBSDEBUG("turned off extended security");
249 		}
250 	}
251 out:
252 	smb_usr_vcspec_free(vspec);
253 	smb_usr_shspec_free(sspecp);
254 	return (error);
255 }
256 
257 int
258 smb_usr_ssnsetup(struct smbioc_lookup *dp, struct smb_cred *scred,
259 	struct smb_vc *vcp)
260 {
261 	struct smb_vcspec *vspec = NULL;
262 	int error;
263 
264 	if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE)
265 		return (EINVAL);
266 
267 	vspec = kmem_zalloc(sizeof (struct smb_vcspec), KM_SLEEP);
268 	error = smb_usr_ioc2vcspec(&dp->ioc_ssn, vspec);
269 	if (error)
270 		goto out;
271 
272 	error = smb_sm_ssnsetup(vspec, scred, vcp);
273 	/*
274 	 * Moved the copyout of ioc_outtok to
275 	 * smb_dev.c (our caller)
276 	 */
277 
278 out:
279 	smb_usr_vcspec_free(vspec);
280 	return (error);
281 }
282 
283 
284 int
285 smb_usr_tcon(struct smbioc_lookup *dp, struct smb_cred *scred,
286 	struct smb_vc *vcp, struct smb_share **sspp)
287 {
288 	struct smb_sharespec *sspecp = NULL;
289 	int error;
290 
291 	if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE)
292 		return (EINVAL);
293 
294 	if (dp->ioc_level >= SMBL_SHARE) {
295 		sspecp = kmem_alloc(sizeof (*sspecp), KM_SLEEP);
296 		error = smb_usr_ioc2sharespec(&dp->ioc_sh, sspecp);
297 		if (error)
298 			goto out;
299 	}
300 	error = smb_sm_tcon(sspecp, scred, vcp, sspp);
301 
302 out:
303 	if (sspecp)
304 		smb_usr_shspec_free(sspecp);
305 
306 	return (error);
307 }
308 
309 /*
310  * Connect to the resource specified by smbioc_ossn structure.
311  * It may either find an existing connection or try to establish a new one.
312  * If no errors occured smb_vc returned locked and referenced.
313  */
314 
315 int
316 smb_usr_simplerequest(struct smb_share *ssp, struct smbioc_rq *dp,
317 	struct smb_cred *scred)
318 {
319 	struct smb_rq rq, *rqp = &rq;
320 	struct mbchain *mbp;
321 	struct mdchain *mdp;
322 	char *p;
323 	size_t wc2;
324 	u_int8_t wc;
325 	u_int16_t bc;
326 	int error;
327 
328 	switch (dp->ioc_cmd) {
329 	case SMB_COM_TRANSACTION2:
330 	case SMB_COM_TRANSACTION2_SECONDARY:
331 	case SMB_COM_CLOSE_AND_TREE_DISC:
332 	case SMB_COM_TREE_CONNECT:
333 	case SMB_COM_TREE_DISCONNECT:
334 	case SMB_COM_NEGOTIATE:
335 	case SMB_COM_SESSION_SETUP_ANDX:
336 	case SMB_COM_LOGOFF_ANDX:
337 	case SMB_COM_TREE_CONNECT_ANDX:
338 		return (EPERM);
339 	}
340 	error = smb_rq_init(rqp, SSTOCP(ssp), dp->ioc_cmd, scred);
341 	if (error)
342 		return (error);
343 	mbp = &rqp->sr_rq;
344 	smb_rq_wstart(rqp);
345 	error = mb_put_mem(mbp, dp->ioc_twords,
346 	    dp->ioc_twc * 2, MB_MUSER);
347 	if (error)
348 		goto bad;
349 	smb_rq_wend(rqp);
350 	smb_rq_bstart(rqp);
351 	error = mb_put_mem(mbp, dp->ioc_tbytes,
352 	    dp->ioc_tbc, MB_MUSER);
353 	if (error)
354 		goto bad;
355 	smb_rq_bend(rqp);
356 	error = smb_rq_simple(rqp);
357 	if (error)
358 		goto bad;
359 	mdp = &rqp->sr_rp;
360 	md_get_uint8(mdp, &wc);
361 	dp->ioc_rwc = wc;
362 	wc2 = wc * 2;
363 	if (wc2 > dp->ioc_rpbufsz) {
364 		error = EBADRPC;
365 		goto bad;
366 	}
367 	error = md_get_mem(mdp, dp->ioc_rpbuf, wc2, MB_MUSER);
368 	if (error)
369 		goto bad;
370 	md_get_uint16le(mdp, &bc);
371 	if ((wc2 + bc) > dp->ioc_rpbufsz) {
372 		error = EBADRPC;
373 		goto bad;
374 	}
375 	dp->ioc_rbc = bc;
376 	p = dp->ioc_rpbuf;
377 	error = md_get_mem(mdp, p + wc2, bc, MB_MUSER);
378 bad:
379 	dp->ioc_errclass = rqp->sr_errclass;
380 	dp->ioc_serror = rqp->sr_serror;
381 	dp->ioc_error = rqp->sr_error;
382 	smb_rq_done(rqp);
383 	return (error);
384 
385 }
386 
387 static int
388 smb_cpdatain(struct mbchain *mbp, int len, char *data)
389 {
390 	int error;
391 
392 	if (len == 0)
393 		return (0);
394 	error = mb_init(mbp);
395 	if (error)
396 		return (error);
397 	return (mb_put_mem(mbp, data, len, MB_MUSER));
398 }
399 
400 int
401 smb_usr_t2request(struct smb_share *ssp, smbioc_t2rq_t *dp,
402 	struct smb_cred *scred)
403 {
404 	struct smb_t2rq t2, *t2p = &t2;
405 	struct mdchain *mdp;
406 	int error, len;
407 
408 	if (dp->ioc_setupcnt > SMB_MAXSETUPWORDS)
409 		return (EINVAL);
410 	error = smb_t2_init(t2p, SSTOCP(ssp), dp->ioc_setup, dp->ioc_setupcnt,
411 	    scred);
412 	if (error)
413 		return (error);
414 	len = t2p->t2_setupcount = dp->ioc_setupcnt;
415 	if (len > 1)
416 		t2p->t2_setupdata = dp->ioc_setup;
417 	/* This ioc member is a fixed-size array. */
418 	if (dp->ioc_name[0]) {
419 		t2p->t_name_maxlen = SMBIOC_T2RQ_MAXNAME;
420 		t2p->t_name = kmem_alloc(t2p->t_name_maxlen, KM_SLEEP);
421 		bcopy(dp->ioc_name, t2p->t_name, t2p->t_name_maxlen);
422 		/* Get the string length - carefully! */
423 		t2p->t_name[t2p->t_name_maxlen - 1] = '\0';
424 		t2p->t_name_len = strlen(t2p->t_name);
425 	}
426 	t2p->t2_maxscount = 0;
427 	t2p->t2_maxpcount = dp->ioc_rparamcnt;
428 	t2p->t2_maxdcount = dp->ioc_rdatacnt;
429 	error = smb_cpdatain(&t2p->t2_tparam, dp->ioc_tparamcnt,
430 	    dp->ioc_tparam);
431 	if (error)
432 		goto bad;
433 	error = smb_cpdatain(&t2p->t2_tdata,
434 	    dp->ioc_tdatacnt, dp->ioc_tdata);
435 	if (error)
436 		goto bad;
437 	error = smb_t2_request(t2p);
438 	dp->ioc_errclass = t2p->t2_sr_errclass;
439 	dp->ioc_serror = t2p->t2_sr_serror;
440 	dp->ioc_error = t2p->t2_sr_error;
441 	dp->ioc_rpflags2 = t2p->t2_sr_rpflags2;
442 	if (error)
443 		goto bad;
444 	mdp = &t2p->t2_rparam;
445 	if (mdp->md_top) {
446 		mblk_t *m = mdp->md_top;
447 #ifdef lint
448 		m = m;
449 #endif
450 		len = m_fixhdr(mdp->md_top);
451 		if (len > dp->ioc_rparamcnt) {
452 			error = EMSGSIZE;
453 			goto bad;
454 		}
455 		dp->ioc_rparamcnt = (ushort_t)len;
456 		error = md_get_mem(mdp, dp->ioc_rparam,
457 		    len, MB_MUSER);
458 		if (error) {
459 			goto bad;
460 		}
461 	} else
462 		dp->ioc_rparamcnt = 0;
463 	mdp = &t2p->t2_rdata;
464 	if (mdp->md_top) {
465 		mblk_t *m = mdp->md_top;
466 #ifdef lint
467 		m = m;
468 #endif
469 		len = m_fixhdr(mdp->md_top);
470 		if (len > dp->ioc_rdatacnt) {
471 			error = EMSGSIZE;
472 			goto bad;
473 		}
474 		dp->ioc_rdatacnt = (ushort_t)len;
475 		error = md_get_mem(mdp, dp->ioc_rdata,
476 		    len, MB_MUSER);
477 		if (error) {
478 			goto bad;
479 		}
480 	} else
481 		dp->ioc_rdatacnt = 0;
482 bad:
483 	if (t2p->t_name) {
484 		kmem_free(t2p->t_name, t2p->t_name_maxlen);
485 		t2p->t_name = NULL;
486 	}
487 	smb_t2_done(t2p);
488 	return (error);
489 }
490 
491 /*
492  * Helper for nsmb_ioctl cases
493  * SMBIOC_READ, SMBIOC_WRITE
494  */
495 int
496 smb_usr_rw(struct smb_share *ssp, smbioc_rw_t *rwrq,
497     int cmd, struct smb_cred *scred)
498 {
499 	struct iovec aiov[1];
500 	struct uio  auio;
501 	u_int16_t fh;
502 	int error;
503 	uio_rw_t rw;
504 
505 	switch (cmd) {
506 	case SMBIOC_READ:
507 		rw = UIO_READ;
508 		break;
509 	case SMBIOC_WRITE:
510 		rw = UIO_WRITE;
511 		break;
512 	default:
513 		return (ENODEV);
514 	}
515 
516 	fh = htoles(rwrq->ioc_fh);
517 
518 	aiov[0].iov_base = rwrq->ioc_base;
519 	aiov[0].iov_len = (size_t)rwrq->ioc_cnt;
520 
521 	auio.uio_iov = aiov;
522 	auio.uio_iovcnt = 1;
523 	auio.uio_loffset = rwrq->ioc_offset;
524 	auio.uio_segflg = UIO_USERSPACE;
525 	auio.uio_fmode = 0;
526 	auio.uio_resid = (size_t)rwrq->ioc_cnt;
527 
528 	error = smb_rwuio(ssp, fh, rw, &auio, scred, 0);
529 
530 	/*
531 	 * On return ioc_cnt holds the
532 	 * number of bytes transferred.
533 	 */
534 	rwrq->ioc_cnt -= auio.uio_resid;
535 
536 	return (error);
537 }
538