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