ncp_mod.c revision 111815
1/*
2 * Copyright (c) 2003 Tim J. Robbins.
3 * Copyright (c) 1999, 2000, 2001 Boris Popov
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 *    must display the following acknowledgement:
16 *    This product includes software developed by Boris Popov.
17 * 4. Neither the name of the author nor the names of any co-contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * $FreeBSD: head/sys/netncp/ncp_mod.c 111815 2003-03-03 12:15:54Z phk $
34 */
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/conf.h>
38#include <sys/proc.h>
39#include <sys/kernel.h>
40#include <sys/sysctl.h>
41#include <sys/malloc.h>
42#include <sys/uio.h>
43#include <sys/ioccom.h>
44
45#include <netncp/ncp.h>
46#include <netncp/ncp_conn.h>
47#include <netncp/ncp_subr.h>
48#include <netncp/ncp_ncp.h>
49#include <netncp/ncp_user.h>
50#include <netncp/ncp_rq.h>
51#include <netncp/ncp_nls.h>
52#include <netncp/ncpio.h>
53
54int ncp_version = NCP_VERSION;
55
56SYSCTL_NODE(_net, OID_AUTO, ncp, CTLFLAG_RW, NULL, "NetWare requester");
57SYSCTL_INT(_net_ncp, OID_AUTO, version, CTLFLAG_RD, &ncp_version, 0, "");
58
59MODULE_VERSION(ncp, 1);
60MODULE_DEPEND(ncp, libmchain, 1, 1, 1);
61
62static dev_t		ncp_dev;
63
64static d_ioctl_t	ncp_ioctl;
65
66static struct cdevsw ncp_cdevsw = {
67	.d_open =	nullopen,
68	.d_close =	nullclose,
69	.d_ioctl =	ncp_ioctl,
70	.d_name =	"ncp",
71	.d_maj =	MAJOR_AUTO,
72};
73
74static int ncp_conn_frag_rq(struct ncp_conn *, struct thread *,
75    struct ncp_conn_frag *);
76static int ncp_conn_handler(struct thread *, struct ncpioc_request *,
77    struct ncp_conn *, struct ncp_handle *);
78static int sncp_conn_scan(struct thread *, struct ncpioc_connscan *);
79static int sncp_connect(struct thread *, struct ncpioc_connect *);
80static int sncp_request(struct thread *, struct ncpioc_request *);
81
82static int
83ncp_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
84{
85
86	switch (cmd) {
87	case NCPIOC_CONNECT:
88		return (sncp_connect(td, (struct ncpioc_connect *)data));
89	case NCPIOC_CONNSCAN:
90		return (sncp_conn_scan(td, (struct ncpioc_connscan *)data));
91	case NCPIOC_REQUEST:
92		return (sncp_request(td, (struct ncpioc_request *)data));
93	}
94	return (EINVAL);
95}
96
97/*
98 * Attach to NCP server
99 */
100
101static int
102sncp_connect(struct thread *td, struct ncpioc_connect *args)
103{
104	int connHandle = 0, error;
105	struct ncp_conn *conn;
106	struct ncp_handle *handle;
107	struct ncp_conn_args li;
108
109	checkbad(copyin(args->ioc_li,&li,sizeof(li)));
110	/* XXX Should be useracc() */
111	checkbad(copyout(&connHandle,args->ioc_connhandle,
112	    sizeof(connHandle)));
113	li.password = li.user = NULL;
114	error = ncp_conn_getattached(&li, td, td->td_ucred, NCPM_WRITE | NCPM_EXECUTE, &conn);
115	if (error) {
116		error = ncp_conn_alloc(&li, td, td->td_ucred, &conn);
117		if (error)
118			goto bad;
119		error = ncp_conn_reconnect(conn);
120		if (error)
121			ncp_conn_free(conn);
122	}
123	if (!error) {
124		error = ncp_conn_gethandle(conn, td, &handle);
125		copyout(&handle->nh_id, args->ioc_connhandle,
126		    sizeof(args->ioc_connhandle));
127		ncp_conn_unlock(conn,td);
128	}
129bad:
130	return error;
131}
132
133static int
134sncp_request(struct thread *td, struct ncpioc_request *args)
135{
136	struct ncp_rq *rqp;
137	struct ncp_conn *conn;
138	struct ncp_handle *handle;
139	int error = 0, rqsize;
140
141	error = ncp_conn_findhandle(args->ioc_connhandle, td, &handle);
142	if (error)
143		return error;
144	conn = handle->nh_conn;
145	if (args->ioc_fn == NCP_CONN)
146		return ncp_conn_handler(td, args, conn, handle);
147	error = copyin(&args->ioc_ncpbuf->rqsize, &rqsize, sizeof(int));
148	if (error)
149		return(error);
150	error = ncp_rq_alloc(args->ioc_fn, conn, td, td->td_ucred, &rqp);
151	if (error)
152		return error;
153	if (rqsize) {
154		error = mb_put_mem(&rqp->rq, (caddr_t)args->ioc_ncpbuf->packet,
155		    rqsize, MB_MUSER);
156		if (error)
157			goto bad;
158	}
159	rqp->nr_flags |= NCPR_DONTFREEONERR;
160	error = ncp_request(rqp);
161	if (error == 0 && rqp->nr_rpsize)
162		error = md_get_mem(&rqp->rp, (caddr_t)args->ioc_ncpbuf->packet,
163				rqp->nr_rpsize, MB_MUSER);
164	copyout(&rqp->nr_cs, &args->ioc_ncpbuf->cs, sizeof(rqp->nr_cs));
165	copyout(&rqp->nr_cc, &args->ioc_ncpbuf->cc, sizeof(rqp->nr_cc));
166	copyout(&rqp->nr_rpsize, &args->ioc_ncpbuf->rpsize, sizeof(rqp->nr_rpsize));
167bad:
168	ncp_rq_done(rqp);
169	return error;
170}
171
172static int
173ncp_mod_login(struct ncp_conn *conn, char *user, int objtype, char *password,
174	struct thread *td, struct ucred *cred)
175{
176	int error;
177
178	if (ncp_suser(cred) != 0 && cred->cr_uid != conn->nc_owner->cr_uid)
179		return EACCES;
180	conn->li.user = ncp_str_dup(user);
181	if (conn->li.user == NULL)
182		return ENOMEM;
183	conn->li.password = ncp_str_dup(password);
184	if (conn->li.password == NULL) {
185		error = ENOMEM;
186		goto bad;
187	}
188	ncp_str_upper(conn->li.user);
189	if ((conn->li.opt & NCP_OPT_NOUPCASEPASS) == 0)
190		ncp_str_upper(conn->li.password);
191	conn->li.objtype = objtype;
192	error = ncp_conn_login(conn, td, cred);
193	return error;
194bad:
195	if (conn->li.user) {
196		free(conn->li.user, M_NCPDATA);
197		conn->li.user = NULL;
198	}
199	if (conn->li.password) {
200		free(conn->li.password, M_NCPDATA);
201		conn->li.password = NULL;
202	}
203	return error;
204}
205
206static int
207ncp_conn_handler(struct thread *td, struct ncpioc_request *args,
208	struct ncp_conn *conn, struct ncp_handle *hp)
209{
210	int error = 0, rqsize, subfn;
211	struct ucred *cred;
212
213	char *pdata;
214
215	cred = td->td_ucred;
216	error = copyin(&args->ioc_ncpbuf->rqsize, &rqsize, sizeof(int));
217	if (error)
218		return(error);
219	error = 0;
220	pdata = args->ioc_ncpbuf->packet;
221	subfn = *(pdata++) & 0xff;
222	rqsize--;
223	switch (subfn) {
224	    case NCP_CONN_READ: case NCP_CONN_WRITE: {
225		struct ncp_rw rwrq;
226		struct uio auio;
227		struct iovec iov;
228
229		if (rqsize != sizeof(rwrq))
230			return (EBADRPC);
231		error = copyin(pdata,&rwrq,rqsize);
232		if (error)
233			return (error);
234		iov.iov_base = rwrq.nrw_base;
235		iov.iov_len = rwrq.nrw_cnt;
236		auio.uio_iov = &iov;
237		auio.uio_iovcnt = 1;
238		auio.uio_offset = rwrq.nrw_offset;
239		auio.uio_resid = rwrq.nrw_cnt;
240		auio.uio_segflg = UIO_USERSPACE;
241		auio.uio_rw = (subfn == NCP_CONN_READ) ? UIO_READ : UIO_WRITE;
242		auio.uio_td = td;
243		if (subfn == NCP_CONN_READ)
244			error = ncp_read(conn, &rwrq.nrw_fh, &auio, cred);
245		else
246			error = ncp_write(conn, &rwrq.nrw_fh, &auio, cred);
247		rwrq.nrw_cnt -= auio.uio_resid;
248		/*td->td_retval[0] = rwrq.nrw_cnt;*/
249		break;
250	    } /* case int_read/write */
251	    case NCP_CONN_SETFLAGS: {
252		u_int16_t mask, flags;
253
254		error = copyin(pdata,&mask, sizeof(mask));
255		if (error)
256			return error;
257		pdata += sizeof(mask);
258		error = copyin(pdata,&flags,sizeof(flags));
259		if (error)
260			return error;
261		error = ncp_conn_lock(conn, td, cred, NCPM_WRITE);
262		if (error)
263			return error;
264		if (mask & NCPFL_PERMANENT) {
265			conn->flags &= ~NCPFL_PERMANENT;
266			conn->flags |= (flags & NCPFL_PERMANENT);
267		}
268		if (mask & NCPFL_PRIMARY) {
269			error = ncp_conn_setprimary(conn, flags & NCPFL_PRIMARY);
270			if (error) {
271				ncp_conn_unlock(conn, td);
272				break;
273			}
274		}
275		ncp_conn_unlock(conn, td);
276		break;
277	    }
278	    case NCP_CONN_LOGIN: {
279		struct ncp_conn_login la;
280
281		if (rqsize != sizeof(la))
282			return EBADRPC;
283		if (conn->flags & NCPFL_LOGGED)
284			return EALREADY;
285		if ((error = copyin(pdata,&la,rqsize)) != 0)
286			break;
287		error = ncp_conn_lock(conn, td, cred, NCPM_EXECUTE | NCPM_WRITE);
288		if (error)
289			return error;
290		error = ncp_mod_login(conn, la.username, la.objtype,
291		    la.password, td, td->td_ucred);
292		ncp_conn_unlock(conn, td);
293		break;
294	    }
295	    case NCP_CONN_GETINFO: {
296		struct ncp_conn_stat ncs;
297		int len = sizeof(ncs);
298
299		error = ncp_conn_lock(conn, td, td->td_ucred, NCPM_READ);
300		if (error)
301			return error;
302		ncp_conn_getinfo(conn, &ncs);
303		copyout(&len, &args->ioc_ncpbuf->rpsize, sizeof(int));
304		error = copyout(&ncs, &args->ioc_ncpbuf->packet, len);
305		ncp_conn_unlock(conn, td);
306		break;
307	    }
308	    case NCP_CONN_GETUSER: {
309		int len;
310
311		error = ncp_conn_lock(conn, td, td->td_ucred, NCPM_READ);
312		if (error)
313			return error;
314		len = (conn->li.user) ? strlen(conn->li.user) + 1 : 0;
315		copyout(&len, &args->ioc_ncpbuf->rpsize, sizeof(int));
316		if (len) {
317			error = copyout(conn->li.user,
318			    &args->ioc_ncpbuf->packet, len);
319		}
320		ncp_conn_unlock(conn, td);
321		break;
322	    }
323	    case NCP_CONN_CONN2REF: {
324		int len = sizeof(int);
325
326		error = ncp_conn_lock(conn, td, td->td_ucred, NCPM_READ);
327		if (error)
328			return error;
329		copyout(&len, &args->ioc_ncpbuf->rpsize, sizeof(int));
330		if (len) {
331			error = copyout(&conn->nc_id,
332			    &args->ioc_ncpbuf->packet, len);
333		}
334		ncp_conn_unlock(conn, td);
335		break;
336	    }
337	    case NCP_CONN_FRAG: {
338		struct ncp_conn_frag nf;
339
340		if (rqsize != sizeof(nf))
341			return (EBADRPC);
342		if ((error = copyin(pdata, &nf, rqsize)) != 0) break;
343		error = ncp_conn_lock(conn, td, cred, NCPM_EXECUTE);
344		if (error)
345			return error;
346		error = ncp_conn_frag_rq(conn, td, &nf);
347		ncp_conn_unlock(conn, td);
348		copyout(&nf, &pdata, sizeof(nf));
349		td->td_retval[0] = error;
350		break;
351	    }
352	    case NCP_CONN_DUP: {
353		struct ncp_handle *newhp;
354		int len = sizeof(NWCONN_HANDLE);
355
356		error = ncp_conn_lock(conn, td, cred, NCPM_READ);
357		if (error) break;
358		copyout(&len, &args->ioc_ncpbuf->rpsize, len);
359		error = ncp_conn_gethandle(conn, td, &newhp);
360		if (!error)
361			error = copyout(&newhp->nh_id,
362			    args->ioc_ncpbuf->packet, len);
363		ncp_conn_unlock(conn, td);
364		break;
365	    }
366	    case NCP_CONN_CONNCLOSE: {
367		error = ncp_conn_lock(conn, td, cred, NCPM_EXECUTE);
368		if (error) break;
369		ncp_conn_puthandle(hp, td, 0);
370		error = ncp_conn_free(conn);
371		if (error)
372			ncp_conn_unlock(conn, td);
373		break;
374	    }
375	    default:
376		    error = EOPNOTSUPP;
377	}
378	return error;
379}
380
381static int
382sncp_conn_scan(struct thread *td, struct ncpioc_connscan *args)
383{
384	int connHandle = 0, error;
385	struct ncp_conn_args li, *lip;
386	struct ncp_conn *conn;
387	struct ncp_handle *hp;
388	char *user = NULL, *password = NULL;
389
390	if (args->ioc_li) {
391		if (copyin(args->ioc_li, &li, sizeof(li)))
392			return EFAULT;
393		lip = &li;
394	} else {
395		lip = NULL;
396	}
397
398	if (lip != NULL) {
399		lip->server[sizeof(lip->server)-1]=0; /* just to make sure */
400		ncp_str_upper(lip->server);
401		if (lip->user) {
402			user = ncp_str_dup(lip->user);
403			if (user == NULL)
404				return EINVAL;
405			ncp_str_upper(user);
406		}
407		if (lip->password) {
408			password = ncp_str_dup(lip->password);
409			if (password == NULL) {
410				if (user)
411					free(user, M_NCPDATA);
412				return EINVAL;
413			}
414			ncp_str_upper(password);
415		}
416		lip->user = user;
417		lip->password = password;
418	}
419	error = ncp_conn_getbyli(lip, td, td->td_ucred, NCPM_EXECUTE, &conn);
420	if (!error) {		/* already have this login */
421		ncp_conn_gethandle(conn, td, &hp);
422		connHandle = hp->nh_id;
423		ncp_conn_unlock(conn, td);
424		copyout(&connHandle, args->ioc_connhandle, sizeof(connHandle));
425	}
426	if (user)
427		free(user, M_NCPDATA);
428	if (password)
429		free(password, M_NCPDATA);
430	return error;
431
432}
433
434int
435ncp_conn_frag_rq(struct ncp_conn *conn, struct thread *td,
436		 struct ncp_conn_frag *nfp)
437{
438	NW_FRAGMENT *fp;
439	struct ncp_rq *rqp;
440	u_int32_t fsize;
441	int error, i, rpsize;
442
443	error = ncp_rq_alloc(nfp->fn, conn, td, td->td_ucred, &rqp);
444	if (error)
445		return error;
446	for(fp = nfp->rqf, i = 0; i < nfp->rqfcnt; i++, fp++) {
447		error = mb_put_mem(&rqp->rq, (caddr_t)fp->fragAddress, fp->fragSize, MB_MUSER);
448		if (error)
449			goto bad;
450	}
451	rqp->nr_flags |= NCPR_DONTFREEONERR;
452	error = ncp_request(rqp);
453	if (error)
454		goto bad;
455	rpsize = rqp->nr_rpsize;
456	if (rpsize && nfp->rpfcnt) {
457		for(fp = nfp->rpf, i = 0; i < nfp->rpfcnt; i++, fp++) {
458			error = copyin(&fp->fragSize, &fsize, sizeof (fsize));
459			if (error)
460				break;
461			fsize = min(fsize, rpsize);
462			error = md_get_mem(&rqp->rp, (caddr_t)fp->fragAddress, fsize, MB_MUSER);
463			if (error)
464				break;
465			rpsize -= fsize;
466			error = copyout(&fsize, &fp->fragSize, sizeof (fsize));
467			if (error)
468				break;
469		}
470	}
471	nfp->cs = rqp->nr_cs;
472	nfp->cc = rqp->nr_cc;
473bad:
474	ncp_rq_done(rqp);
475	return error;
476}
477
478static int
479ncp_load(void)
480{
481	int error;
482
483	if ((error = ncp_init()) != 0)
484		return (error);
485	ncp_dev = make_dev(&ncp_cdevsw, 0, 0, 0, 0666, "ncp");
486	printf("ncp_load: loaded\n");
487	return (0);
488}
489
490static int
491ncp_unload(void)
492{
493	int error;
494
495	error = ncp_done();
496	if (error)
497		return (error);
498	destroy_dev(ncp_dev);
499	printf("ncp_unload: unloaded\n");
500	return (0);
501}
502
503static int
504ncp_mod_handler(module_t mod, int type, void *data)
505{
506	int error;
507
508	switch (type) {
509	case MOD_LOAD:
510		error = ncp_load();
511		break;
512	case MOD_UNLOAD:
513		error = ncp_unload();
514		break;
515	default:
516		error = EINVAL;
517	}
518	return error;
519}
520
521static moduledata_t ncp_mod = {
522	"ncp",
523	ncp_mod_handler,
524	NULL
525};
526DECLARE_MODULE(ncp, ncp_mod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY);
527