1 /* 2 * Copyright (c) 2000, 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: rq.c,v 1.4 2004/12/13 00:25:23 lindak Exp $ 33 */ 34 35 #include <sys/types.h> 36 #include <sys/param.h> 37 #include <sys/ioctl.h> 38 #include <sys/errno.h> 39 #include <sys/stat.h> 40 41 #include <ctype.h> 42 #include <errno.h> 43 #include <stdio.h> 44 #include <unistd.h> 45 #include <strings.h> 46 #include <stdlib.h> 47 #include <sysexits.h> 48 #include <libintl.h> 49 50 #include <netsmb/smb.h> 51 #include <netsmb/smb_lib.h> 52 #include "private.h" 53 54 static uint32_t smb_map_doserr(uint8_t, uint16_t); 55 56 /* 57 * Create and initialize a request structure, for either an 58 * "internal" request (one that does not use the driver) or 59 * a regular "driver" request, that uses driver ioctls. 60 * 61 * The two kinds are built a little differently: 62 * Driver requests are composed starting with the 63 * first word of the "variable word vector" section. 64 * The driver prepends the SMB header and word count. 65 * The driver also needs an output buffer to receive 66 * the response, filled in via copyout in the ioctl. 67 * 68 * Internal requests are composed entirely in this library. 69 * Space for the SMB header is reserved here, and later 70 * filled in by smb_rq_internal before the send/receive. 71 */ 72 int 73 smb_rq_init(struct smb_ctx *ctx, uchar_t cmd, struct smb_rq **rqpp) 74 { 75 struct smb_rq *rqp; 76 77 rqp = malloc(sizeof (*rqp)); 78 if (rqp == NULL) 79 goto errout; 80 bzero(rqp, sizeof (*rqp)); 81 rqp->rq_cmd = cmd; 82 rqp->rq_ctx = ctx; 83 84 /* 85 * Setup the request buffer. 86 * Do the reply buffer later. 87 */ 88 if (mb_init(&rqp->rq_rq, M_MINSIZE)) 89 goto errout; 90 91 /* Space for the SMB header. (filled in later) */ 92 mb_put_mem(&rqp->rq_rq, NULL, SMB_HDRLEN); 93 94 /* 95 * Copy the ctx flags here, so the caller can 96 * update the req flags before the OTW call. 97 */ 98 rqp->rq_hflags = ctx->ct_hflags; 99 rqp->rq_hflags2 = ctx->ct_hflags2; 100 101 *rqpp = rqp; 102 return (0); 103 104 errout: 105 if (rqp) { 106 smb_rq_done(rqp); 107 free(rqp); 108 } 109 return (ENOMEM); 110 } 111 112 void 113 smb_rq_done(struct smb_rq *rqp) 114 { 115 mb_done(&rqp->rq_rp); 116 mb_done(&rqp->rq_rq); 117 free(rqp); 118 } 119 120 /* 121 * Reserve space for the word count, which is filled in later by 122 * smb_rq_wend(). Also initialize the counter that it uses 123 * to figure out what value to fill in. 124 * 125 * Note that the word count happens to be 8-bits, 126 * which can lead to confusion. 127 */ 128 void 129 smb_rq_wstart(struct smb_rq *rqp) 130 { 131 struct mbdata *mbp = &rqp->rq_rq; 132 133 mb_fit(mbp, 1, &rqp->rq_wcntp); 134 rqp->rq_wcbase = mbp->mb_count; 135 } 136 137 /* 138 * Fill in the word count, in the space reserved by 139 * smb_rq_wstart(). 140 */ 141 void 142 smb_rq_wend(struct smb_rq *rqp) 143 { 144 struct mbdata *mbp = &rqp->rq_rq; 145 int wcnt; 146 147 if (rqp->rq_wcntp == NULL) { 148 DPRINT("no wcount ptr\n"); 149 return; 150 } 151 wcnt = mbp->mb_count - rqp->rq_wcbase; 152 if (wcnt > 0x1ff) 153 DPRINT("word count too large (%d)\n", wcnt); 154 if (wcnt & 1) 155 DPRINT("odd word count\n"); 156 wcnt >>= 1; 157 158 /* 159 * Fill in the word count (8-bits). 160 * Also store it in the rq, in case 161 * we're using the ioctl path. 162 */ 163 *rqp->rq_wcntp = (char)wcnt; 164 } 165 166 /* 167 * Reserve space for the byte count, which is filled in later by 168 * smb_rq_bend(). Also initialize the counter that it uses 169 * to figure out what value to fill in. 170 * 171 * Note that the byte count happens to be 16-bits, 172 * which can lead to confusion. 173 */ 174 void 175 smb_rq_bstart(struct smb_rq *rqp) 176 { 177 struct mbdata *mbp = &rqp->rq_rq; 178 179 mb_fit(mbp, 2, &rqp->rq_bcntp); 180 rqp->rq_bcbase = mbp->mb_count; 181 } 182 183 /* 184 * Fill in the byte count, in the space reserved by 185 * smb_rq_bstart(). 186 */ 187 void 188 smb_rq_bend(struct smb_rq *rqp) 189 { 190 struct mbdata *mbp = &rqp->rq_rq; 191 int bcnt; 192 193 if (rqp->rq_bcntp == NULL) { 194 DPRINT("no bcount ptr\n"); 195 return; 196 } 197 bcnt = mbp->mb_count - rqp->rq_bcbase; 198 if (bcnt > 0xffff) 199 DPRINT("byte count too large (%d)\n", bcnt); 200 /* 201 * Fill in the byte count (16-bits). 202 * Also store it in the rq, in case 203 * we're using the ioctl path. 204 * 205 * The pointer is char * type due to 206 * typical off-by-one alignment. 207 */ 208 rqp->rq_bcntp[0] = bcnt & 0xFF; 209 rqp->rq_bcntp[1] = (bcnt >> 8); 210 } 211 212 /* 213 * Removed: smb_rq_dmem 214 * which was mostly like: mb_put_mem 215 */ 216 217 int 218 smb_rq_simple(struct smb_rq *rqp) 219 { 220 struct smbioc_rq krq; 221 struct mbdata *mbp; 222 char *data; 223 uint32_t len; 224 size_t rpbufsz; 225 226 bzero(&krq, sizeof (krq)); 227 krq.ioc_cmd = rqp->rq_cmd; 228 229 /* 230 * Make the SMB request body contiguous, 231 * and fill in the ioctl request. 232 */ 233 mbp = smb_rq_getrequest(rqp); 234 m_lineup(mbp->mb_top, &mbp->mb_top); 235 data = mtod(mbp->mb_top, char *); 236 len = m_totlen(mbp->mb_top); 237 238 /* 239 * _rq_init left space for the SMB header, 240 * which makes mb_count the offset from 241 * the beginning of the header (useful). 242 * However, in this code path the driver 243 * prepends the header, so we skip it. 244 */ 245 krq.ioc_tbufsz = len - SMB_HDRLEN; 246 krq.ioc_tbuf = data + SMB_HDRLEN; 247 248 /* 249 * Setup a buffer to hold the reply. 250 * 251 * Default size is M_MINSIZE, but the 252 * caller may increase rq_rpbufsz 253 * before calling this. 254 */ 255 mbp = smb_rq_getreply(rqp); 256 rpbufsz = rqp->rq_rpbufsz; 257 if (rpbufsz < M_MINSIZE) 258 rpbufsz = M_MINSIZE; 259 if (mb_init(mbp, rpbufsz)) 260 return (ENOMEM); 261 krq.ioc_rbufsz = rpbufsz; 262 krq.ioc_rbuf = mtod(mbp->mb_top, char *); 263 264 /* 265 * Call the driver 266 */ 267 if (ioctl(rqp->rq_ctx->ct_dev_fd, SMBIOC_REQUEST, &krq) == -1) 268 return (errno); 269 270 /* 271 * Initialize returned mbdata. 272 * SMB header already parsed. 273 */ 274 mbp->mb_top->m_len = krq.ioc_rbufsz; 275 276 return (0); 277 } 278 279 280 int 281 smb_t2_request(struct smb_ctx *ctx, int setupcount, uint16_t *setup, 282 const char *name, 283 int tparamcnt, void *tparam, 284 int tdatacnt, void *tdata, 285 int *rparamcnt, void *rparam, 286 int *rdatacnt, void *rdata, 287 int *buffer_oflow) 288 { 289 smbioc_t2rq_t *krq; 290 int i; 291 292 krq = (smbioc_t2rq_t *)malloc(sizeof (smbioc_t2rq_t)); 293 bzero(krq, sizeof (*krq)); 294 295 if (setupcount < 0 || setupcount >= SMBIOC_T2RQ_MAXSETUP) { 296 /* Bogus setup count, or too many setup words */ 297 return (EINVAL); 298 } 299 for (i = 0; i < setupcount; i++) 300 krq->ioc_setup[i] = setup[i]; 301 krq->ioc_setupcnt = setupcount; 302 strcpy(krq->ioc_name, name); 303 krq->ioc_tparamcnt = tparamcnt; 304 krq->ioc_tparam = tparam; 305 krq->ioc_tdatacnt = tdatacnt; 306 krq->ioc_tdata = tdata; 307 308 krq->ioc_rparamcnt = *rparamcnt; 309 krq->ioc_rdatacnt = *rdatacnt; 310 krq->ioc_rparam = rparam; 311 krq->ioc_rdata = rdata; 312 313 if (ioctl(ctx->ct_dev_fd, SMBIOC_T2RQ, krq) == -1) { 314 return (errno); 315 } 316 317 *rparamcnt = krq->ioc_rparamcnt; 318 *rdatacnt = krq->ioc_rdatacnt; 319 *buffer_oflow = (krq->ioc_rpflags2 & SMB_FLAGS2_ERR_STATUS) && 320 (krq->ioc_error == NT_STATUS_BUFFER_OVERFLOW); 321 free(krq); 322 323 return (0); 324 } 325 326 327 /* 328 * Do an over-the-wire call without using the nsmb driver. 329 * This is all "internal" to this library, and used only 330 * for connection setup (negotiate protocol, etc.) 331 */ 332 int 333 smb_rq_internal(struct smb_ctx *ctx, struct smb_rq *rqp) 334 { 335 static const uint8_t ffsmb[4] = SMB_SIGNATURE; 336 struct smb_iods *is = &ctx->ct_iods; 337 uint32_t sigbuf[2]; 338 struct mbdata mbtmp, *mbp; 339 int err, save_mlen; 340 uint8_t ctmp; 341 342 rqp->rq_uid = is->is_smbuid; 343 rqp->rq_tid = SMB_TID_UNKNOWN; 344 rqp->rq_mid = is->is_next_mid++; 345 346 /* 347 * Fill in the NBT and SMB headers 348 * Using mbtmp so we can rewind without 349 * affecting the passed request mbdata. 350 */ 351 bcopy(&rqp->rq_rq, &mbtmp, sizeof (mbtmp)); 352 mbp = &mbtmp; 353 mbp->mb_cur = mbp->mb_top; 354 mbp->mb_pos = mbp->mb_cur->m_data; 355 mbp->mb_count = 0; 356 /* Have to save and restore m_len */ 357 save_mlen = mbp->mb_cur->m_len; 358 mbp->mb_cur->m_len = 0; 359 360 /* 361 * rewind done; fill it in 362 */ 363 mb_put_mem(mbp, (char *)SMB_SIGNATURE, SMB_SIGLEN); 364 mb_put_uint8(mbp, rqp->rq_cmd); 365 mb_put_mem(mbp, NULL, 4); /* status */ 366 mb_put_uint8(mbp, rqp->rq_hflags); 367 mb_put_uint16le(mbp, rqp->rq_hflags2); 368 mb_put_uint16le(mbp, 0); /* pid_hi */ 369 mb_put_mem(mbp, NULL, 8); /* signature */ 370 mb_put_uint16le(mbp, 0); /* reserved */ 371 mb_put_uint16le(mbp, rqp->rq_tid); 372 mb_put_uint16le(mbp, 0); /* pid_lo */ 373 mb_put_uint16le(mbp, rqp->rq_uid); 374 mb_put_uint16le(mbp, rqp->rq_mid); 375 376 /* Restore original m_len */ 377 mbp->mb_cur->m_len = save_mlen; 378 379 /* 380 * Sign the message, if flags2 indicates. 381 */ 382 if (rqp->rq_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) { 383 smb_rq_sign(rqp); 384 } 385 386 /* 387 * Send it, wait for the reply. 388 */ 389 if ((err = smb_ssn_send(ctx, &rqp->rq_rq)) != 0) 390 return (err); 391 392 if ((err = smb_ssn_recv(ctx, &rqp->rq_rp)) != 0) 393 return (err); 394 395 /* 396 * Should have an SMB header, at least. 397 */ 398 mbp = &rqp->rq_rp; 399 if (mbp->mb_cur->m_len < SMB_HDRLEN) { 400 DPRINT("len < 32"); 401 return (EBADRPC); 402 } 403 404 /* 405 * If the request was signed, validate the 406 * signature on the response. 407 */ 408 if (rqp->rq_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) { 409 err = smb_rq_verify(rqp); 410 if (err) { 411 DPRINT("bad signature"); 412 return (err); 413 } 414 } 415 416 /* 417 * Decode the SMB header. 418 */ 419 mb_get_mem(mbp, (char *)sigbuf, 4); 420 if (0 != bcmp(sigbuf, ffsmb, 4)) { 421 DPRINT("not SMB"); 422 return (EBADRPC); 423 } 424 mb_get_uint8(mbp, &ctmp); /* SMB cmd */ 425 mb_get_uint32le(mbp, &rqp->rq_status); 426 mb_get_uint8(mbp, &rqp->rq_hflags); 427 mb_get_uint16le(mbp, &rqp->rq_hflags2); 428 mb_get_uint16le(mbp, NULL); /* pid_hi */ 429 mb_get_mem(mbp, NULL, 8); /* signature */ 430 mb_get_uint16le(mbp, NULL); /* reserved */ 431 mb_get_uint16le(mbp, &rqp->rq_tid); 432 mb_get_uint16le(mbp, NULL); /* pid_lo */ 433 mb_get_uint16le(mbp, &rqp->rq_uid); 434 mb_get_uint16le(mbp, &rqp->rq_mid); 435 436 /* 437 * Figure out the status return. 438 * Caller looks at rq_status. 439 */ 440 if ((rqp->rq_hflags2 & SMB_FLAGS2_ERR_STATUS) == 0) { 441 uint16_t serr; 442 uint8_t class; 443 444 class = rqp->rq_status & 0xff; 445 serr = rqp->rq_status >> 16; 446 rqp->rq_status = smb_map_doserr(class, serr); 447 } 448 449 return (0); 450 } 451 452 /* 453 * Map old DOS errors (etc.) to NT status codes. 454 * We probably don't need this anymore, since 455 * the oldest server we talk to is NT. But if 456 * later find we do need this, add support here 457 * for the DOS errors we care about. 458 */ 459 static uint32_t 460 smb_map_doserr(uint8_t class, uint16_t serr) 461 { 462 if (class == 0 && serr == 0) 463 return (0); 464 465 DPRINT("class 0x%x serr 0x%x", (int)class, (int)serr); 466 return (NT_STATUS_UNSUCCESSFUL); 467 } 468