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 34#include <sys/cdefs.h> 35__FBSDID("$FreeBSD$"); 36 37#include <sys/param.h> 38#include <sys/systm.h> 39#include <sys/conf.h> 40#include <sys/priv.h> 41#include <sys/proc.h> 42#include <sys/kernel.h> 43#include <sys/module.h> 44#include <sys/sysctl.h> 45#include <sys/malloc.h> 46#include <sys/uio.h> 47#include <sys/ioccom.h> 48 49#include <netncp/ncp.h> 50#include <netncp/ncp_conn.h> 51#include <netncp/ncp_subr.h> 52#include <netncp/ncp_ncp.h> 53#include <netncp/ncp_user.h> 54#include <netncp/ncp_rq.h> 55#include <netncp/ncp_nls.h> 56#include <netncp/ncpio.h> 57 58int ncp_version = NCP_VERSION; 59 60SYSCTL_NODE(_net, OID_AUTO, ncp, CTLFLAG_RW, NULL, "NetWare requester"); 61SYSCTL_INT(_net_ncp, OID_AUTO, version, CTLFLAG_RD, &ncp_version, 0, ""); 62 63MODULE_VERSION(ncp, 1); 64MODULE_DEPEND(ncp, libmchain, 1, 1, 1); 65 66static struct cdev *ncp_dev; 67 68static d_ioctl_t ncp_ioctl; 69 70static struct cdevsw ncp_cdevsw = { 71 .d_version = D_VERSION, 72 .d_flags = D_NEEDGIANT, 73 .d_ioctl = ncp_ioctl, 74 .d_name = "ncp", 75}; 76 77static int ncp_conn_frag_rq(struct ncp_conn *, struct thread *, 78 struct ncp_conn_frag *); 79static int ncp_conn_handler(struct thread *, struct ncpioc_request *, 80 struct ncp_conn *, struct ncp_handle *); 81static int sncp_conn_scan(struct thread *, struct ncpioc_connscan *); 82static int sncp_connect(struct thread *, struct ncpioc_connect *); 83static int sncp_request(struct thread *, struct ncpioc_request *); 84 85static int 86ncp_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 87{ 88 89 switch (cmd) { 90 case NCPIOC_CONNECT: 91 return (sncp_connect(td, (struct ncpioc_connect *)data)); 92 case NCPIOC_CONNSCAN: 93 return (sncp_conn_scan(td, (struct ncpioc_connscan *)data)); 94 case NCPIOC_REQUEST: 95 return (sncp_request(td, (struct ncpioc_request *)data)); 96 } 97 return (EINVAL); 98} 99 100/* 101 * Attach to NCP server 102 */ 103 104static int 105sncp_connect(struct thread *td, struct ncpioc_connect *args) 106{ 107 int connHandle = 0, error; 108 struct ncp_conn *conn; 109 struct ncp_handle *handle; 110 struct ncp_conn_args li; 111 112 checkbad(copyin(args->ioc_li,&li,sizeof(li))); 113 /* XXX Should be useracc() */ 114 checkbad(copyout(&connHandle,args->ioc_connhandle, 115 sizeof(connHandle))); 116 li.password = li.user = NULL; 117 error = ncp_conn_getattached(&li, td, td->td_ucred, NCPM_WRITE | NCPM_EXECUTE, &conn); 118 if (error) { 119 error = ncp_conn_alloc(&li, td, td->td_ucred, &conn); 120 if (error) 121 goto bad; 122 error = ncp_conn_reconnect(conn); 123 if (error) 124 ncp_conn_free(conn); 125 } 126 if (!error) { 127 error = ncp_conn_gethandle(conn, td, &handle); 128 copyout(&handle->nh_id, args->ioc_connhandle, 129 sizeof(args->ioc_connhandle)); 130 ncp_conn_unlock(conn,td); 131 } 132bad: 133 return error; 134} 135 136static int 137sncp_request(struct thread *td, struct ncpioc_request *args) 138{ 139 struct ncp_rq *rqp; 140 struct ncp_conn *conn; 141 struct ncp_handle *handle; 142 int error = 0, rqsize; 143 144 error = ncp_conn_findhandle(args->ioc_connhandle, td, &handle); 145 if (error) 146 return error; 147 conn = handle->nh_conn; 148 if (args->ioc_fn == NCP_CONN) 149 return ncp_conn_handler(td, args, conn, handle); 150 error = copyin(&args->ioc_ncpbuf->rqsize, &rqsize, sizeof(int)); 151 if (error) 152 return(error); 153 error = ncp_rq_alloc(args->ioc_fn, conn, td, td->td_ucred, &rqp); 154 if (error) 155 return error; 156 if (rqsize) { 157 error = mb_put_mem(&rqp->rq, (caddr_t)args->ioc_ncpbuf->packet, 158 rqsize, MB_MUSER); 159 if (error) 160 goto bad; 161 } 162 rqp->nr_flags |= NCPR_DONTFREEONERR; 163 error = ncp_request(rqp); 164 if (error == 0 && rqp->nr_rpsize) 165 error = md_get_mem(&rqp->rp, (caddr_t)args->ioc_ncpbuf->packet, 166 rqp->nr_rpsize, MB_MUSER); 167 copyout(&rqp->nr_cs, &args->ioc_ncpbuf->cs, sizeof(rqp->nr_cs)); 168 copyout(&rqp->nr_cc, &args->ioc_ncpbuf->cc, sizeof(rqp->nr_cc)); 169 copyout(&rqp->nr_rpsize, &args->ioc_ncpbuf->rpsize, sizeof(rqp->nr_rpsize)); 170bad: 171 ncp_rq_done(rqp); 172 return error; 173} 174 175static int 176ncp_mod_login(struct ncp_conn *conn, char *user, int objtype, char *password, 177 struct thread *td, struct ucred *cred) 178{ 179 int error; 180 181 if (ncp_suser(cred) != 0 && cred->cr_uid != conn->nc_owner->cr_uid) 182 return EACCES; 183 conn->li.user = ncp_str_dup(user); 184 if (conn->li.user == NULL) 185 return ENOMEM; 186 conn->li.password = ncp_str_dup(password); 187 if (conn->li.password == NULL) { 188 error = ENOMEM; 189 goto bad; 190 } 191 ncp_str_upper(conn->li.user); 192 if ((conn->li.opt & NCP_OPT_NOUPCASEPASS) == 0) 193 ncp_str_upper(conn->li.password); 194 conn->li.objtype = objtype; 195 error = ncp_conn_login(conn, td, cred); 196 return error; 197bad: 198 if (conn->li.user) { 199 free(conn->li.user, M_NCPDATA); 200 conn->li.user = NULL; 201 } 202 if (conn->li.password) { 203 free(conn->li.password, M_NCPDATA); 204 conn->li.password = NULL; 205 } 206 return error; 207} 208 209static int 210ncp_conn_handler(struct thread *td, struct ncpioc_request *args, 211 struct ncp_conn *conn, struct ncp_handle *hp) 212{ 213 int error = 0, rqsize, subfn; 214 struct ucred *cred; 215 216 char *pdata; 217 218 cred = td->td_ucred; 219 error = copyin(&args->ioc_ncpbuf->rqsize, &rqsize, sizeof(int)); 220 if (error) 221 return(error); 222 error = 0; 223 pdata = args->ioc_ncpbuf->packet; 224 subfn = *(pdata++) & 0xff; 225 rqsize--; 226 switch (subfn) { 227 case NCP_CONN_READ: case NCP_CONN_WRITE: { 228 struct ncp_rw rwrq; 229 struct uio auio; 230 struct iovec iov; 231 232 if (rqsize != sizeof(rwrq)) 233 return (EBADRPC); 234 error = copyin(pdata,&rwrq,rqsize); 235 if (error) 236 return (error); 237 iov.iov_base = rwrq.nrw_base; 238 iov.iov_len = rwrq.nrw_cnt; 239 auio.uio_iov = &iov; 240 auio.uio_iovcnt = 1; 241 auio.uio_offset = rwrq.nrw_offset; 242 auio.uio_resid = rwrq.nrw_cnt; 243 auio.uio_segflg = UIO_USERSPACE; 244 auio.uio_rw = (subfn == NCP_CONN_READ) ? UIO_READ : UIO_WRITE; 245 auio.uio_td = td; 246 if (subfn == NCP_CONN_READ) 247 error = ncp_read(conn, &rwrq.nrw_fh, &auio, cred); 248 else 249 error = ncp_write(conn, &rwrq.nrw_fh, &auio, cred); 250 rwrq.nrw_cnt -= auio.uio_resid; 251 /*td->td_retval[0] = rwrq.nrw_cnt;*/ 252 break; 253 } /* case int_read/write */ 254 case NCP_CONN_SETFLAGS: { 255 u_int16_t mask, flags; 256 257 error = copyin(pdata,&mask, sizeof(mask)); 258 if (error) 259 return error; 260 pdata += sizeof(mask); 261 error = copyin(pdata,&flags,sizeof(flags)); 262 if (error) 263 return error; 264 error = ncp_conn_lock(conn, td, cred, NCPM_WRITE); 265 if (error) 266 return error; 267 if (mask & NCPFL_PERMANENT) { 268 conn->flags &= ~NCPFL_PERMANENT; 269 conn->flags |= (flags & NCPFL_PERMANENT); 270 } 271 if (mask & NCPFL_PRIMARY) { 272 error = ncp_conn_setprimary(conn, flags & NCPFL_PRIMARY); 273 if (error) { 274 ncp_conn_unlock(conn, td); 275 break; 276 } 277 } 278 ncp_conn_unlock(conn, td); 279 break; 280 } 281 case NCP_CONN_LOGIN: { 282 struct ncp_conn_login la; 283 284 if (rqsize != sizeof(la)) 285 return EBADRPC; 286 if (conn->flags & NCPFL_LOGGED) 287 return EALREADY; 288 if ((error = copyin(pdata,&la,rqsize)) != 0) 289 break; 290 error = ncp_conn_lock(conn, td, cred, NCPM_EXECUTE | NCPM_WRITE); 291 if (error) 292 return error; 293 error = ncp_mod_login(conn, la.username, la.objtype, 294 la.password, td, td->td_ucred); 295 ncp_conn_unlock(conn, td); 296 break; 297 } 298 case NCP_CONN_GETINFO: { 299 struct ncp_conn_stat ncs; 300 int len = sizeof(ncs); 301 302 error = ncp_conn_lock(conn, td, td->td_ucred, NCPM_READ); 303 if (error) 304 return error; 305 ncp_conn_getinfo(conn, &ncs); 306 copyout(&len, &args->ioc_ncpbuf->rpsize, sizeof(int)); 307 error = copyout(&ncs, &args->ioc_ncpbuf->packet, len); 308 ncp_conn_unlock(conn, td); 309 break; 310 } 311 case NCP_CONN_GETUSER: { 312 int len; 313 314 error = ncp_conn_lock(conn, td, td->td_ucred, NCPM_READ); 315 if (error) 316 return error; 317 len = (conn->li.user) ? strlen(conn->li.user) + 1 : 0; 318 copyout(&len, &args->ioc_ncpbuf->rpsize, sizeof(int)); 319 if (len) { 320 error = copyout(conn->li.user, 321 &args->ioc_ncpbuf->packet, len); 322 } 323 ncp_conn_unlock(conn, td); 324 break; 325 } 326 case NCP_CONN_CONN2REF: { 327 int len = sizeof(int); 328 329 error = ncp_conn_lock(conn, td, td->td_ucred, NCPM_READ); 330 if (error) 331 return error; 332 copyout(&len, &args->ioc_ncpbuf->rpsize, sizeof(int)); 333 if (len) { 334 error = copyout(&conn->nc_id, 335 &args->ioc_ncpbuf->packet, len); 336 } 337 ncp_conn_unlock(conn, td); 338 break; 339 } 340 case NCP_CONN_FRAG: { 341 struct ncp_conn_frag nf; 342 343 if (rqsize != sizeof(nf)) 344 return (EBADRPC); 345 if ((error = copyin(pdata, &nf, rqsize)) != 0) break; 346 error = ncp_conn_lock(conn, td, cred, NCPM_EXECUTE); 347 if (error) 348 return error; 349 error = ncp_conn_frag_rq(conn, td, &nf); 350 ncp_conn_unlock(conn, td); 351 copyout(&nf, &pdata, sizeof(nf)); 352 td->td_retval[0] = error; 353 break; 354 } 355 case NCP_CONN_DUP: { 356 struct ncp_handle *newhp; 357 int len = sizeof(NWCONN_HANDLE); 358 359 error = ncp_conn_lock(conn, td, cred, NCPM_READ); 360 if (error) break; 361 copyout(&len, &args->ioc_ncpbuf->rpsize, len); 362 error = ncp_conn_gethandle(conn, td, &newhp); 363 if (!error) 364 error = copyout(&newhp->nh_id, 365 args->ioc_ncpbuf->packet, len); 366 ncp_conn_unlock(conn, td); 367 break; 368 } 369 case NCP_CONN_CONNCLOSE: { 370 error = ncp_conn_lock(conn, td, cred, NCPM_EXECUTE); 371 if (error) break; 372 ncp_conn_puthandle(hp, td, 0); 373 error = ncp_conn_free(conn); 374 if (error) 375 ncp_conn_unlock(conn, td); 376 break; 377 } 378 default: 379 error = EOPNOTSUPP; 380 } 381 return error; 382} 383 384static int 385sncp_conn_scan(struct thread *td, struct ncpioc_connscan *args) 386{ 387 int connHandle = 0, error; 388 struct ncp_conn_args li, *lip; 389 struct ncp_conn *conn; 390 struct ncp_handle *hp; 391 char *user = NULL, *password = NULL; 392 393 if (args->ioc_li) { 394 if (copyin(args->ioc_li, &li, sizeof(li))) 395 return EFAULT; 396 lip = &li; 397 } else { 398 lip = NULL; 399 } 400 401 if (lip != NULL) { 402 lip->server[sizeof(lip->server)-1]=0; /* just to make sure */ 403 ncp_str_upper(lip->server); 404 if (lip->user) { 405 user = ncp_str_dup(lip->user); 406 if (user == NULL) 407 return EINVAL; 408 ncp_str_upper(user); 409 } 410 if (lip->password) { 411 password = ncp_str_dup(lip->password); 412 if (password == NULL) { 413 if (user) 414 free(user, M_NCPDATA); 415 return EINVAL; 416 } 417 ncp_str_upper(password); 418 } 419 lip->user = user; 420 lip->password = password; 421 } 422 error = ncp_conn_getbyli(lip, td, td->td_ucred, NCPM_EXECUTE, &conn); 423 if (!error) { /* already have this login */ 424 ncp_conn_gethandle(conn, td, &hp); 425 connHandle = hp->nh_id; 426 ncp_conn_unlock(conn, td); 427 copyout(&connHandle, args->ioc_connhandle, sizeof(connHandle)); 428 } 429 if (user) 430 free(user, M_NCPDATA); 431 if (password) 432 free(password, M_NCPDATA); 433 return error; 434 435} 436 437int 438ncp_conn_frag_rq(struct ncp_conn *conn, struct thread *td, 439 struct ncp_conn_frag *nfp) 440{ 441 NW_FRAGMENT *fp; 442 struct ncp_rq *rqp; 443 u_int32_t fsize; 444 int error, i, rpsize; 445 446 error = ncp_rq_alloc(nfp->fn, conn, td, td->td_ucred, &rqp); 447 if (error) 448 return error; 449 for(fp = nfp->rqf, i = 0; i < nfp->rqfcnt; i++, fp++) { 450 error = mb_put_mem(&rqp->rq, (caddr_t)fp->fragAddress, fp->fragSize, MB_MUSER); 451 if (error) 452 goto bad; 453 } 454 rqp->nr_flags |= NCPR_DONTFREEONERR; 455 error = ncp_request(rqp); 456 if (error) 457 goto bad; 458 rpsize = rqp->nr_rpsize; 459 if (rpsize && nfp->rpfcnt) { 460 for(fp = nfp->rpf, i = 0; i < nfp->rpfcnt; i++, fp++) { 461 error = copyin(&fp->fragSize, &fsize, sizeof (fsize)); 462 if (error) 463 break; 464 fsize = min(fsize, rpsize); 465 error = md_get_mem(&rqp->rp, (caddr_t)fp->fragAddress, fsize, MB_MUSER); 466 if (error) 467 break; 468 rpsize -= fsize; 469 error = copyout(&fsize, &fp->fragSize, sizeof (fsize)); 470 if (error) 471 break; 472 } 473 } 474 nfp->cs = rqp->nr_cs; 475 nfp->cc = rqp->nr_cc; 476bad: 477 ncp_rq_done(rqp); 478 return error; 479} 480 481static int 482ncp_load(void) 483{ 484 int error; 485 486 if ((error = ncp_init()) != 0) 487 return (error); 488 ncp_dev = make_dev(&ncp_cdevsw, 0, 0, 0, 0666, "ncp"); 489 printf("ncp_load: loaded\n"); 490 return (0); 491} 492 493static int 494ncp_unload(void) 495{ 496 int error; 497 498 error = ncp_done(); 499 if (error) 500 return (error); 501 destroy_dev(ncp_dev); 502 printf("ncp_unload: unloaded\n"); 503 return (0); 504} 505 506static int 507ncp_mod_handler(module_t mod, int type, void *data) 508{ 509 int error; 510 511 switch (type) { 512 case MOD_LOAD: 513 error = ncp_load(); 514 break; 515 case MOD_UNLOAD: 516 error = ncp_unload(); 517 break; 518 default: 519 error = EINVAL; 520 } 521 return error; 522} 523 524static moduledata_t ncp_mod = { 525 "ncp", 526 ncp_mod_handler, 527 NULL 528}; 529DECLARE_MODULE(ncp, ncp_mod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY); 530