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