xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c (revision 628e3cbed6489fa1db545d8524a06cd6535af456)
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_findvc(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 	int error = 0;
192 
193 	if (dp->ioc_flags & SMBLK_CREATE)
194 		return (EINVAL);
195 	if (dp->ioc_level != SMBL_VC)
196 		return (EINVAL);
197 	vspec = kmem_zalloc(sizeof (struct smb_vcspec), KM_SLEEP);
198 	error = smb_usr_ioc2vcspec(&dp->ioc_ssn, vspec);
199 	if (error)
200 		goto out;
201 	error = smb_sm_findvc(vspec, scred, &vcp);
202 	if (error == 0)
203 		*vcpp =  vcp;
204 out:
205 	smb_usr_vcspec_free(vspec);
206 	return (error);
207 }
208 
209 int
210 smb_usr_negotiate(struct smbioc_lookup *dp, struct smb_cred *scred,
211 	struct smb_vc **vcpp)
212 {
213 	struct smb_vc *vcp = NULL;
214 	struct smb_vcspec *vspec = NULL;
215 	struct smb_sharespec *sspecp = NULL;
216 	int error = 0;
217 
218 	if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE)
219 		return (EINVAL);
220 	vspec = kmem_zalloc(sizeof (struct smb_vcspec), KM_SLEEP);
221 	error = smb_usr_ioc2vcspec(&dp->ioc_ssn, vspec);
222 	if (error)
223 		return (error);
224 	if (dp->ioc_flags & SMBLK_CREATE)
225 		vspec->optflags |= SMBVOPT_CREATE;
226 	if (dp->ioc_level >= SMBL_SHARE) {
227 		sspecp = kmem_alloc(sizeof (*sspecp), KM_SLEEP);
228 		error = smb_usr_ioc2sharespec(&dp->ioc_sh, sspecp);
229 		if (error)
230 			goto out;
231 	}
232 	error = smb_sm_negotiate(vspec, scred, &vcp);
233 	if (error == 0) {
234 		*vcpp =  vcp;
235 		/*
236 		 * Used to copyout ioc_outtok, outtoklen here,
237 		 * but that's now in smb_dev. (our caller)
238 		 *
239 		 * If this call asked for extended security and
240 		 * the server does not support it, clear the
241 		 * flag so the caller knows this.
242 		 *
243 		 * XXX: Should just add sv_caps to ioc_ssn,
244 		 * set the new sv_caps field here, and let
245 		 * let the copyout of ioc_ssn handle it.
246 		 */
247 		if (!(vcp->vc_sopt.sv_caps & SMB_CAP_EXT_SECURITY) &&
248 		    (dp->ioc_ssn.ioc_opt & SMBVOPT_EXT_SEC)) {
249 			dp->ioc_ssn.ioc_opt &= ~SMBVOPT_EXT_SEC;
250 			SMBSDEBUG("turned off extended security");
251 		}
252 	}
253 out:
254 	smb_usr_vcspec_free(vspec);
255 	smb_usr_shspec_free(sspecp);
256 	return (error);
257 }
258 
259 int
260 smb_usr_ssnsetup(struct smbioc_lookup *dp, struct smb_cred *scred,
261 	struct smb_vc *vcp)
262 {
263 	struct smb_vcspec *vspec = NULL;
264 	int error;
265 
266 	if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE)
267 		return (EINVAL);
268 
269 	vspec = kmem_zalloc(sizeof (struct smb_vcspec), KM_SLEEP);
270 	error = smb_usr_ioc2vcspec(&dp->ioc_ssn, vspec);
271 	if (error)
272 		goto out;
273 
274 	error = smb_sm_ssnsetup(vspec, scred, vcp);
275 	/*
276 	 * Moved the copyout of ioc_outtok to
277 	 * smb_dev.c (our caller)
278 	 */
279 
280 out:
281 	smb_usr_vcspec_free(vspec);
282 	return (error);
283 }
284 
285 
286 int
287 smb_usr_tcon(struct smbioc_lookup *dp, struct smb_cred *scred,
288 	struct smb_vc *vcp, struct smb_share **sspp)
289 {
290 	struct smb_sharespec *sspecp = NULL;
291 	int error;
292 
293 	if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE)
294 		return (EINVAL);
295 
296 	if (dp->ioc_level >= SMBL_SHARE) {
297 		sspecp = kmem_alloc(sizeof (*sspecp), KM_SLEEP);
298 		error = smb_usr_ioc2sharespec(&dp->ioc_sh, sspecp);
299 		if (error)
300 			goto out;
301 	}
302 	error = smb_sm_tcon(sspecp, scred, vcp, sspp);
303 
304 out:
305 	if (sspecp)
306 		smb_usr_shspec_free(sspecp);
307 
308 	return (error);
309 }
310 
311 /*
312  * Connect to the resource specified by smbioc_ossn structure.
313  * It may either find an existing connection or try to establish a new one.
314  * If no errors occured smb_vc returned locked and referenced.
315  */
316 
317 int
318 smb_usr_simplerequest(struct smb_share *ssp, struct smbioc_rq *dp,
319 	struct smb_cred *scred)
320 {
321 	struct smb_rq rq, *rqp = &rq;
322 	struct mbchain *mbp;
323 	struct mdchain *mdp;
324 	char *p;
325 	size_t wc2;
326 	u_int8_t wc;
327 	u_int16_t bc;
328 	int error;
329 
330 	switch (dp->ioc_cmd) {
331 	case SMB_COM_TRANSACTION2:
332 	case SMB_COM_TRANSACTION2_SECONDARY:
333 	case SMB_COM_CLOSE_AND_TREE_DISC:
334 	case SMB_COM_TREE_CONNECT:
335 	case SMB_COM_TREE_DISCONNECT:
336 	case SMB_COM_NEGOTIATE:
337 	case SMB_COM_SESSION_SETUP_ANDX:
338 	case SMB_COM_LOGOFF_ANDX:
339 	case SMB_COM_TREE_CONNECT_ANDX:
340 		return (EPERM);
341 	}
342 	error = smb_rq_init(rqp, SSTOCP(ssp), dp->ioc_cmd, scred);
343 	if (error)
344 		return (error);
345 	mbp = &rqp->sr_rq;
346 	smb_rq_wstart(rqp);
347 	error = mb_put_mem(mbp, dp->ioc_twords,
348 	    dp->ioc_twc * 2, MB_MUSER);
349 	if (error)
350 		goto bad;
351 	smb_rq_wend(rqp);
352 	smb_rq_bstart(rqp);
353 	error = mb_put_mem(mbp, dp->ioc_tbytes,
354 	    dp->ioc_tbc, MB_MUSER);
355 	if (error)
356 		goto bad;
357 	smb_rq_bend(rqp);
358 	error = smb_rq_simple(rqp);
359 	if (error)
360 		goto bad;
361 	mdp = &rqp->sr_rp;
362 	md_get_uint8(mdp, &wc);
363 	dp->ioc_rwc = wc;
364 	wc2 = wc * 2;
365 	if (wc2 > dp->ioc_rpbufsz) {
366 		error = EBADRPC;
367 		goto bad;
368 	}
369 	error = md_get_mem(mdp, dp->ioc_rpbuf, wc2, MB_MUSER);
370 	if (error)
371 		goto bad;
372 	md_get_uint16le(mdp, &bc);
373 	if ((wc2 + bc) > dp->ioc_rpbufsz) {
374 		error = EBADRPC;
375 		goto bad;
376 	}
377 	dp->ioc_rbc = bc;
378 	p = dp->ioc_rpbuf;
379 	error = md_get_mem(mdp, p + wc2, bc, MB_MUSER);
380 bad:
381 	dp->ioc_errclass = rqp->sr_errclass;
382 	dp->ioc_serror = rqp->sr_serror;
383 	dp->ioc_error = rqp->sr_error;
384 	smb_rq_done(rqp);
385 	return (error);
386 
387 }
388 
389 static int
390 smb_cpdatain(struct mbchain *mbp, int len, char *data)
391 {
392 	int error;
393 
394 	if (len == 0)
395 		return (0);
396 	error = mb_init(mbp);
397 	if (error)
398 		return (error);
399 	return (mb_put_mem(mbp, data, len, MB_MUSER));
400 }
401 
402 int
403 smb_usr_t2request(struct smb_share *ssp, smbioc_t2rq_t *dp,
404 	struct smb_cred *scred)
405 {
406 	struct smb_t2rq t2, *t2p = &t2;
407 	struct mdchain *mdp;
408 	int error, len;
409 
410 	if (dp->ioc_setupcnt > SMB_MAXSETUPWORDS)
411 		return (EINVAL);
412 
413 	t2p = (struct smb_t2rq *)kmem_alloc(sizeof (struct smb_t2rq), KM_SLEEP);
414 	if (t2p == NULL)
415 		return (ENOMEM);
416 	error = smb_t2_init(t2p, SSTOCP(ssp), dp->ioc_setup, dp->ioc_setupcnt,
417 	    scred);
418 	if (error)
419 		return (error);
420 	len = t2p->t2_setupcount = dp->ioc_setupcnt;
421 	if (len > 1)
422 		t2p->t2_setupdata = dp->ioc_setup;
423 	if (dp->ioc_name) {
424 		bcopy(dp->ioc_name, t2p->t_name, 128);
425 		if (t2p->t_name == NULL) {
426 			error = ENOMEM;
427 			goto bad;
428 		}
429 	}
430 	t2p->t2_maxscount = 0;
431 	t2p->t2_maxpcount = dp->ioc_rparamcnt;
432 	t2p->t2_maxdcount = dp->ioc_rdatacnt;
433 	error = smb_cpdatain(&t2p->t2_tparam, dp->ioc_tparamcnt,
434 	    dp->ioc_tparam);
435 	if (error)
436 		goto bad;
437 	error = smb_cpdatain(&t2p->t2_tdata,
438 	    dp->ioc_tdatacnt, dp->ioc_tdata);
439 	if (error)
440 		goto bad;
441 	error = smb_t2_request(t2p);
442 	dp->ioc_errclass = t2p->t2_sr_errclass;
443 	dp->ioc_serror = t2p->t2_sr_serror;
444 	dp->ioc_error = t2p->t2_sr_error;
445 	dp->ioc_rpflags2 = t2p->t2_sr_rpflags2;
446 	if (error)
447 		goto bad;
448 	mdp = &t2p->t2_rparam;
449 	if (mdp->md_top) {
450 		mblk_t *m = mdp->md_top;
451 #ifdef lint
452 		m = m;
453 #endif
454 		len = m_fixhdr(mdp->md_top);
455 		if (len > dp->ioc_rparamcnt) {
456 			error = EMSGSIZE;
457 			goto bad;
458 		}
459 		dp->ioc_rparamcnt = (ushort_t)len;
460 		error = md_get_mem(mdp, dp->ioc_rparam,
461 		    len, MB_MUSER);
462 		if (error) {
463 			goto bad;
464 		}
465 	} else
466 		dp->ioc_rparamcnt = 0;
467 	mdp = &t2p->t2_rdata;
468 	if (mdp->md_top) {
469 		mblk_t *m = mdp->md_top;
470 #ifdef lint
471 		m = m;
472 #endif
473 		len = m_fixhdr(mdp->md_top);
474 		if (len > dp->ioc_rdatacnt) {
475 			error = EMSGSIZE;
476 			goto bad;
477 		}
478 		dp->ioc_rdatacnt = (ushort_t)len;
479 		error = md_get_mem(mdp, dp->ioc_rdata,
480 		    len, MB_MUSER);
481 		if (error) {
482 			goto bad;
483 		}
484 	} else
485 		dp->ioc_rdatacnt = 0;
486 bad:
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