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: rap.c,v 1.5 2004/12/13 00:25:23 lindak Exp $ 33 * 34 * This is very simple implementation of RAP protocol. 35 */ 36 37 #pragma ident "%Z%%M% %I% %E% SMI" 38 39 #include <sys/param.h> 40 #include <sys/errno.h> 41 #include <sys/stat.h> 42 #include <sys/isa_defs.h> 43 44 #include <ctype.h> 45 #include <stdio.h> 46 #include <unistd.h> 47 #include <strings.h> 48 #include <stdlib.h> 49 #include <libintl.h> 50 #include <sysexits.h> 51 52 #include <netsmb/mchain.h> 53 #include <netsmb/smb_lib.h> 54 #include <netsmb/smb_rap.h> 55 56 static int 57 smb_rap_parserqparam(const char *s, char **next, int *rlen) 58 { 59 char *np; 60 int len; 61 62 switch (*s++) { 63 case 'L': 64 case 'T': 65 case 'W': 66 len = 2; 67 break; 68 case 'D': 69 case 'O': 70 len = 4; 71 break; 72 case 'b': 73 case 'F': 74 len = 1; 75 break; 76 case 'r': 77 case 's': 78 len = 0; 79 break; 80 default: 81 return (EINVAL); 82 } 83 if (isdigit(*s)) { 84 len *= strtoul(s, &np, 10); 85 s = np; 86 } 87 *rlen = len; 88 *(const char **)next = s; 89 return (0); 90 } 91 92 static int 93 smb_rap_parserpparam(const char *s, char **next, int *rlen) 94 { 95 char *np; 96 int len = 0; 97 98 switch (*s++) { 99 case 'e': 100 case 'h': 101 len = 2; 102 break; 103 case 'i': 104 len = 4; 105 break; 106 case 'g': 107 len = 1; 108 break; 109 default: 110 return (EINVAL); 111 } 112 if (isdigit(*s)) { 113 len *= strtoul(s, &np, 10); 114 s = np; 115 } 116 *rlen = len; 117 *(const char **)next = s; 118 return (0); 119 } 120 121 static int 122 smb_rap_parserpdata(const char *s, char **next, int *rlen) 123 { 124 char *np; 125 int len; 126 127 switch (*s++) { 128 case 'B': 129 len = 1; 130 break; 131 case 'W': 132 len = 2; 133 break; 134 case 'D': 135 case 'O': 136 case 'z': 137 len = 4; 138 break; 139 default: 140 return (EINVAL); 141 } 142 if (isdigit(*s)) { 143 len *= strtoul(s, &np, 10); 144 s = np; 145 } 146 *rlen = len; 147 *(const char **)next = s; 148 return (0); 149 } 150 151 static int 152 smb_rap_rqparam_z(struct smb_rap *rap, const char *value) 153 { 154 int len = strlen(value) + 1; 155 156 bcopy(value, rap->r_npbuf, len); 157 rap->r_npbuf += len; 158 rap->r_plen += len; 159 return (0); 160 } 161 162 /* 163 * Marshal RAP request parameters. 164 * Note: value is in host order. 165 */ 166 static int 167 smb_rap_rqparam(struct smb_rap *rap, char ptype, char plen, int value) 168 { 169 char *p = rap->r_npbuf; 170 int len = 0; 171 uint_t uv = (uint_t)value; 172 173 switch (ptype) { 174 case 'L': 175 case 'W': 176 /* LINTED */ 177 setwle(p, 0, uv); 178 len = 2; 179 break; 180 case 'D': 181 /* LINTED */ 182 setdle(p, 0, uv); 183 len = 4; 184 break; 185 case 'b': 186 memset(p, uv, plen); 187 len = plen; 188 default: 189 return (EINVAL); 190 } 191 rap->r_npbuf += len; 192 rap->r_plen += len; 193 return (0); 194 } 195 196 int 197 smb_rap_create(int fn, const char *param, const char *data, 198 struct smb_rap **rapp) 199 { 200 struct smb_rap *rap; 201 char *p; 202 int plen = 0, len = 0; 203 int i; 204 205 rap = malloc(sizeof (*rap)); 206 if (rap == NULL) 207 return (ENOMEM); 208 bzero(rap, sizeof (*rap)); 209 p = rap->r_sparam = rap->r_nparam = strdup(param); 210 rap->r_sdata = rap->r_ndata = strdup(data); 211 212 /* 213 * Calculate length of request parameter block 214 */ 215 len = 2 + strlen(param) + 1 + strlen(data) + 1; 216 while (*p) { 217 if (smb_rap_parserqparam(p, &p, &plen) != 0) 218 break; 219 len += plen; 220 } 221 rap->r_pbuf = rap->r_npbuf = malloc(len); 222 smb_rap_rqparam(rap, 'W', 1, fn); 223 smb_rap_rqparam_z(rap, rap->r_sparam); 224 smb_rap_rqparam_z(rap, rap->r_sdata); 225 *rapp = rap; 226 return (0); 227 } 228 229 void 230 smb_rap_done(struct smb_rap *rap) 231 { 232 if (rap->r_sparam) 233 free(rap->r_sparam); 234 if (rap->r_sdata) 235 free(rap->r_sdata); 236 if (rap->r_pbuf) 237 free(rap->r_pbuf); 238 #ifdef NOTYETDEFINED 239 if (rap->r_npbuf) 240 free(rap->r_npbuf); 241 if (rap->r_dbuf) 242 free(rap->r_dbuf); 243 if (rap->r_rcvbuf) 244 free(rap->r_rcvbuf); 245 #endif 246 free(rap); 247 } 248 249 int 250 smb_rap_setNparam(struct smb_rap *rap, int value) 251 { 252 char *p = rap->r_nparam; 253 char ptype = *p; 254 int error, plen; 255 256 error = smb_rap_parserqparam(p, &p, &plen); 257 if (error) 258 return (error); 259 switch (ptype) { 260 case 'L': 261 rap->r_rcvbuflen = value; 262 /* FALLTHROUGH */ 263 case 'W': 264 case 'D': 265 case 'b': 266 error = smb_rap_rqparam(rap, ptype, plen, value); 267 break; 268 default: 269 return (EINVAL); 270 } 271 rap->r_nparam = p; 272 return (0); 273 } 274 275 int 276 smb_rap_setPparam(struct smb_rap *rap, void *value) 277 { 278 char *p = rap->r_nparam; 279 char ptype = *p; 280 int error, plen; 281 282 error = smb_rap_parserqparam(p, &p, &plen); 283 if (error) 284 return (error); 285 switch (ptype) { 286 case 'r': 287 rap->r_rcvbuf = value; 288 break; 289 default: 290 return (EINVAL); 291 } 292 rap->r_nparam = p; 293 return (0); 294 } 295 296 static int 297 smb_rap_getNparam(struct smb_rap *rap, long *value) 298 { 299 char *p = rap->r_nparam; 300 char ptype = *p; 301 int error, plen; 302 uint16_t *te; 303 304 error = smb_rap_parserpparam(p, &p, &plen); 305 if (error) 306 return (error); 307 switch (ptype) { 308 case 'h': 309 /* LINTED */ 310 te = (uint16_t *)rap->r_npbuf; 311 *value = letohs(*te); 312 break; 313 default: 314 return (EINVAL); 315 } 316 rap->r_npbuf += plen; 317 rap->r_nparam = p; 318 return (0); 319 } 320 321 int 322 smb_rap_request(struct smb_rap *rap, struct smb_ctx *ctx) 323 { 324 uint16_t *rp, conv, *tmp; 325 uint32_t *p32, ps1; 326 char *dp, *p = rap->r_nparam; 327 char ptype; 328 int error, rdatacnt, rparamcnt, entries, done, dlen, buffer_oflow, i; 329 330 rdatacnt = rap->r_rcvbuflen; 331 rparamcnt = rap->r_plen; 332 error = smb_t2_request(ctx, 0, NULL, "\\PIPE\\LANMAN", 333 rap->r_plen, rap->r_pbuf, /* int tparamcnt,void *tparam */ 334 0, NULL, /* int tdatacnt, void *tdata */ 335 &rparamcnt, rap->r_pbuf, /* rparamcnt, void *rparam */ 336 &rdatacnt, rap->r_rcvbuf, /* int *rdatacnt, void *rdata */ 337 &buffer_oflow); 338 if (error) 339 return (error); 340 341 /* LINTED */ 342 rp = (uint16_t *)rap->r_pbuf; 343 344 /* 345 * Note: First is a "LanMan API" error code. 346 * See: usr/src/uts/common/smbsrv/lmerr.h 347 */ 348 if (rparamcnt < 2) 349 return (EBADRPC); 350 rap->r_result = letohs(*rp); 351 rp++; rparamcnt -= 2; 352 353 if (rap->r_result != 0) { 354 /* 355 * Could also return zero and let the caller 356 * come get r_result via smb_rap_error(), 357 * but in case they dont... 358 */ 359 return (rap->r_result | SMB_RAP_ERROR); 360 } 361 362 if (rparamcnt < 2) 363 return (EBADRPC); 364 conv = letohs(*rp); 365 rp++; rparamcnt -= 2; 366 367 rap->r_npbuf = (char *)rp; 368 rap->r_entries = entries = 0; 369 /* Save the returned data length */ 370 rap->r_rcvbuflen = rdatacnt; 371 done = 0; 372 373 while (!done && *p) { 374 ptype = *p; 375 switch (ptype) { 376 case 'e': 377 if (rparamcnt < 2) 378 return (EBADRPC); 379 /* LINTED */ 380 tmp = (uint16_t *)rap->r_npbuf; 381 rap->r_entries = entries = letohs(*tmp); 382 rap->r_npbuf += 2; 383 rparamcnt -= 2; 384 p++; 385 break; 386 default: 387 done = 1; 388 } 389 #if 0 /* commented out in Darwin. Why? */ 390 error = smb_rap_parserpparam(p, &p, &plen); 391 if (error) { 392 smb_error(dgettext(TEXT_DOMAIN, 393 "reply parameter mismatch %s"), 0, p); 394 return (EBADRPC); 395 } 396 #endif 397 } 398 rap->r_nparam = p; 399 /* 400 * In general, unpacking entries we may need to relocate 401 * entries for proper aligning. For now use them as is. 402 */ 403 dp = rap->r_rcvbuf; 404 while (entries--) { 405 p = rap->r_sdata; 406 while (*p) { 407 ptype = *p; 408 error = smb_rap_parserpdata(p, &p, &dlen); 409 if (error) { 410 smb_error(dgettext(TEXT_DOMAIN, 411 "reply data mismatch %s"), 0, p); 412 return (EBADRPC); 413 } 414 if (rdatacnt < dlen) 415 return (EBADRPC); 416 switch (ptype) { 417 case 'z': 418 /* LINTED */ 419 p32 = (uint32_t *)dp; 420 *p32 = (letohl(*p32) & 0xffff) - conv; 421 break; 422 } 423 dp += dlen; 424 rdatacnt -= dlen; 425 } 426 } 427 return (error); 428 } 429 430 int 431 smb_rap_error(struct smb_rap *rap, int error) 432 { 433 if (error) 434 return (error); 435 if (rap->r_result == 0) 436 return (0); 437 return (rap->r_result | SMB_RAP_ERROR); 438 } 439 440 /* todo: move this function to libnetapi */ 441 int 442 smb_rap_NetShareEnum(struct smb_ctx *ctx, int sLevel, void *pbBuffer, 443 int *cbBuffer, int *pcEntriesRead, int *pcTotalAvail) 444 { 445 struct smb_rap *rap; 446 long lval = -1; 447 int error; 448 char *pass; 449 int i; 450 451 error = smb_rap_create(0, "WrLeh", "B13BWz", &rap); 452 if (error) 453 return (error); 454 smb_rap_setNparam(rap, sLevel); /* W - sLevel */ 455 smb_rap_setPparam(rap, pbBuffer); /* r - pbBuffer */ 456 smb_rap_setNparam(rap, *cbBuffer); /* L - cbBuffer */ 457 error = smb_rap_request(rap, ctx); 458 if (error == 0) { 459 *pcEntriesRead = rap->r_entries; 460 error = smb_rap_getNparam(rap, &lval); 461 *pcTotalAvail = lval; 462 /* Copy the data length into the IN/OUT variable. */ 463 *cbBuffer = rap->r_rcvbuflen; 464 } 465 error = smb_rap_error(rap, error); 466 smb_rap_done(rap); 467 return (error); 468 } 469