1/* 2 * Copyright (c) 2000, Boris Popov 3 * All rights reserved. 4 * 5 * Portions Copyright (C) 2001 - 2010 Apple Inc. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Boris Popov. 18 * 4. Neither the name of the author nor the names of any co-contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 */ 35#include <sys/param.h> 36#include <sys/ioctl.h> 37#include <sys/errno.h> 38#include <sys/stat.h> 39#include <ctype.h> 40#include <err.h> 41#include <stdio.h> 42#include <unistd.h> 43#include <strings.h> 44#include <stdlib.h> 45#include <sysexits.h> 46#include <sys/types.h> 47 48#include <netsmb/upi_mbuf.h> 49#include <sys/smb_byte_order.h> 50#include <sys/mchain.h> 51#include <netsmb/smb_lib.h> 52#include <netsmb/rq.h> 53#include <netsmb/smb_conn.h> 54#include <smbclient/ntstatus.h> 55 56struct smb_usr_rq { 57 u_char rq_cmd; 58 struct mbchain rq_rq; 59 struct mdchain rq_rp; 60 struct smb_ctx *rq_ctx; 61 uint8_t *wcount; 62 uint16_t *bcount; 63 uint8_t flags; 64 uint16_t flags2; 65 uint32_t nt_error; 66}; 67 68/* 69 * Takes a Window style UTF-16 string and converts it to a Mac style UTF-8 string 70 * that is not null terminated. 71 */ 72int smb_ntwrkpath_to_localpath(struct smb_ctx *ctx, 73 const char *ntwrkstr, size_t ntwrk_len, 74 char *utf8str, size_t *utf8_len, 75 uint32_t flags) 76{ 77 struct smbioc_path_convert rq; 78 79 bzero(&rq, sizeof(rq)); 80 rq.ioc_version = SMB_IOC_STRUCT_VERSION; 81 rq.ioc_direction = NETWORK_TO_LOCAL; 82 rq.ioc_flags = flags; 83 rq.ioc_src_len = (uint32_t)ntwrk_len; 84 rq.ioc_src = ntwrkstr; 85 rq.ioc_dest_len = *utf8_len; 86 rq.ioc_dest = utf8str; 87 if (smb_ioctl_call(ctx->ct_fd, SMBIOC_CONVERT_PATH, &rq) == 0) { 88 *utf8_len = (size_t)rq.ioc_dest_len; 89 /* 90 * Currently the kernel doesn't null terminate, we may want to change 91 * this later. Since utf8_len has to be less than the utf8str buffer 92 * size this is safe. 93 */ 94 utf8str[*utf8_len] = 0; 95 return 0; 96 } 97 return -1; 98} 99 100/* 101 * Takes a Mac style UTF-8 path string and converts it to a Window style UTF-16 102 * that is not null terminated. 103 */ 104int smb_localpath_to_ntwrkpath(struct smb_ctx *ctx, 105 const char *utf8str, size_t utf8_len, 106 char *ntwrkstr, size_t *ntwrk_len, 107 uint32_t flags) 108{ 109 struct smbioc_path_convert rq; 110 111 bzero(&rq, sizeof(rq)); 112 rq.ioc_version = SMB_IOC_STRUCT_VERSION; 113 rq.ioc_direction = LOCAL_TO_NETWORK; 114 rq.ioc_flags = flags; 115 rq.ioc_src_len = (uint32_t)utf8_len; 116 rq.ioc_src = utf8str; 117 rq.ioc_dest_len = *ntwrk_len; 118 rq.ioc_dest = ntwrkstr; 119 if (smb_ioctl_call(ctx->ct_fd, SMBIOC_CONVERT_PATH, &rq) == 0) { 120 *ntwrk_len = (size_t)rq.ioc_dest_len; 121 return 0; 122 } 123 return -1; 124} 125 126int smb_usr_rq_init(struct smb_ctx *ctx, u_char cmd, uint16_t flags2, struct smb_usr_rq **rqpp) 127{ 128 struct smb_usr_rq *rqp; 129 130 rqp = malloc(sizeof(*rqp)); 131 if (rqp == NULL) 132 return ENOMEM; 133 bzero(rqp, sizeof(*rqp)); 134 rqp->rq_cmd = cmd; 135 rqp->rq_ctx = ctx; 136 smb_usr_rq_setflags2(rqp, flags2); 137 mb_init(&rqp->rq_rq); 138 md_init(&rqp->rq_rp); 139 *rqpp = rqp; 140 return 0; 141} 142 143int smb_usr_rq_init_rcvsize(struct smb_ctx *ctx, u_char cmd, uint16_t flags2, 144 size_t rpbufsz, struct smb_usr_rq **rqpp) 145{ 146 struct smb_usr_rq *rqp; 147 148 rqp = malloc(sizeof(*rqp)); 149 if (rqp == NULL) 150 return ENOMEM; 151 bzero(rqp, sizeof(*rqp)); 152 rqp->rq_cmd = cmd; 153 rqp->rq_ctx = ctx; 154 smb_usr_rq_setflags2(rqp, flags2); 155 mb_init(&rqp->rq_rq); 156 md_init_rcvsize(&rqp->rq_rp, rpbufsz); 157 *rqpp = rqp; 158 return 0; 159} 160 161void 162smb_usr_rq_done(struct smb_usr_rq *rqp) 163{ 164 mb_done((mbchain_t)&rqp->rq_rp); 165 md_done((mdchain_t)&rqp->rq_rq); 166 free(rqp); 167} 168 169mbchain_t smb_usr_rq_getrequest(struct smb_usr_rq *rqp) 170{ 171 return &rqp->rq_rq; 172} 173 174 175mdchain_t smb_usr_rq_getreply(struct smb_usr_rq *rqp) 176{ 177 return &rqp->rq_rp; 178} 179 180uint32_t smb_usr_rq_get_error(struct smb_usr_rq *rqp) 181{ 182 return rqp->nt_error; 183} 184 185uint32_t smb_usr_rq_flags2(struct smb_usr_rq *rqp) 186{ 187 return rqp->flags2; 188} 189 190uint32_t smb_usr_rq_nt_error(struct smb_usr_rq *rqp) 191{ 192 return rqp->nt_error; 193} 194 195void smb_usr_rq_setflags2(struct smb_usr_rq *rqp, uint32_t flags2) 196{ 197 /* We only support SMB_FLAGS2_DFS, log the failure reset the flags to zero */ 198 if (flags2 && (flags2 != SMB_FLAGS2_DFS)) { 199 smb_log_info("%s to 0x%x, syserr = %s", ASL_LEVEL_DEBUG, __FUNCTION__, 200 flags2, strerror(ENOTSUP)); 201 flags2 = 0; 202 } 203 rqp->flags2 = flags2; 204} 205 206void smb_usr_rq_wstart(struct smb_usr_rq *rqp) 207{ 208 rqp->wcount = (uint8_t *)mb_reserve(&rqp->rq_rq, sizeof(uint8_t)); 209 rqp->rq_rq.mb_count = 0; 210} 211 212void smb_usr_rq_wend(struct smb_usr_rq *rqp) 213{ 214 if (rqp->wcount == NULL) { 215 smb_log_info("%s: no word count pointer?", ASL_LEVEL_ERR, __FUNCTION__); 216 return; 217 } 218 if (rqp->rq_rq.mb_count & 1) 219 smb_log_info("%s: odd word count", ASL_LEVEL_ERR, __FUNCTION__); 220 *rqp->wcount = rqp->rq_rq.mb_count / 2; 221} 222 223void smb_usr_rq_bstart(struct smb_usr_rq *rqp) 224{ 225 rqp->bcount = (uint16_t *)mb_reserve(&rqp->rq_rq, sizeof(uint16_t)); 226 rqp->rq_rq.mb_count = 0; 227} 228 229void smb_usr_rq_bend(struct smb_usr_rq *rqp) 230{ 231 uint16_t bcnt; 232 233 if (rqp->bcount == NULL) { 234 smb_log_info("%s: no byte count pointer?", ASL_LEVEL_ERR, __FUNCTION__); 235 return; 236 } 237 /* 238 * Byte Count field should be ignored when dealing with SMB_CAP_LARGE_WRITEX 239 * or SMB_CAP_LARGE_READX messages. So if byte cound becomes to big we set 240 * it to zero and hope the calling process knows what it is doing. 241 */ 242 if (rqp->rq_rq.mb_count > 0x0ffff) 243 bcnt = 0; /* Set the byte count to zero here */ 244 else 245 bcnt = (uint16_t)rqp->rq_rq.mb_count; 246 247 *rqp->bcount = htoles(bcnt); 248} 249 250/* 251 * Given a UTF8 string, convert it to a network string and place it in 252 * the buffer. 253 */ 254int smb_usr_put_dmem(struct smb_ctx *ctx, mbchain_t mbp, const char *src, 255 size_t src_size, int flags, size_t *lenp) 256{ 257 size_t dest_size = (src_size * 2) + 2 + 2; 258 void *dst = NULL; 259 int error = 0; 260 261 /* Nothing to do here */ 262 if (src_size == 0) 263 goto done; 264 265 mb_put_padbyte(mbp); 266 267 dst = mb_getbuffer(mbp, dest_size); 268 if (dst == NULL) { 269 smb_log_info("%s: mb_getbuffer failed, syserr = %s", ASL_LEVEL_DEBUG, 270 __FUNCTION__, strerror(ENOMEM)); 271 error = ENOMEM; 272 goto done; 273 } 274 memset(dst, 0, dest_size); 275 276 if (smb_localpath_to_ntwrkpath(ctx, src, src_size, dst, &dest_size, flags)) { 277 smb_log_info("%s: local to ntwrk path failed, syserr = %s", 278 ASL_LEVEL_DEBUG, __FUNCTION__, strerror(errno)); 279 error = errno; 280 goto done; 281 } 282 mb_consume(mbp, dest_size); 283 if (lenp) 284 *lenp += dest_size; 285done: 286 return error; 287} 288 289/* 290 * Given a UTF8 string, convert it to a network string and place it in 291 * the buffer. Now add the NULL byte/bytes. 292 */ 293int smb_usr_rq_put_dstring(struct smb_ctx *ctx, mbchain_t mbp, const char *src, 294 size_t maxlen, int flags, size_t *lenp) 295{ 296 int error; 297 298 error = smb_usr_put_dmem(ctx, mbp, src, maxlen, flags, lenp); 299 if (error) 300 return error; 301 302 if (lenp ) 303 *lenp += 2; 304 return mb_put_uint16le(mbp, 0); 305} 306 307int 308smb_usr_rq_simple(struct smb_usr_rq *rqp) 309{ 310 struct smbioc_rq krq; 311 mbchain_t mbp; 312 mdchain_t mdp; 313 314 mbp = smb_usr_rq_getrequest(rqp); 315 mb_pullup(mbp); 316 bzero(&krq, sizeof(krq)); 317 krq.ioc_version = SMB_IOC_STRUCT_VERSION; 318 krq.ioc_cmd = rqp->rq_cmd; 319 krq.ioc_twc = (rqp->wcount) ? *(rqp->wcount) : 0; 320 krq.ioc_twords = (rqp->wcount) ? rqp->wcount+sizeof(*rqp->wcount) : NULL; 321 krq.ioc_tbc = (rqp->bcount) ? letohs(*(rqp->bcount)) : 0; 322 krq.ioc_tbytes = (rqp->bcount) ? (uint8_t *)rqp->bcount+sizeof(*rqp->bcount) : NULL; 323 krq.ioc_flags2 = rqp->flags2; 324 325 mdp = smb_usr_rq_getreply(rqp); 326 krq.ioc_rpbufsz = (int32_t)mbuf_maxlen(mdp->md_top); 327 krq.ioc_rpbuf = mbuf_data(mdp->md_top); 328 329 if (smb_ioctl_call(rqp->rq_ctx->ct_fd, SMBIOC_REQUEST, &krq) == -1) { 330 smb_log_info("%s: smb_ioctl_call, syserr = %s", ASL_LEVEL_DEBUG, 331 __FUNCTION__, strerror(errno)); 332 return errno; 333 } 334 mbuf_setlen(mdp->md_top, krq.ioc_rpbufsz); 335 rqp->flags = krq.ioc_flags; 336 rqp->flags2 = krq.ioc_flags2; 337 rqp->nt_error = krq.ioc_ntstatus; 338 /* 339 * Returning an error to the IOCTL code means no other information in the 340 * structure will be updated. The nsmb_dev_ioctl routine should only return 341 * unexpected internal errors. We should return all errors in the ioctl 342 * structure and not through the ioctl call. This will be corrected by the 343 * following: 344 * <rdar://problem/7082077> "SMB error handling is just confusing and needs to be rewritten" 345 */ 346 return krq.ioc_errno; 347} 348 349int 350smb_usr_t2_request(struct smb_ctx *ctx, int setupcount, uint16_t *setup, 351 const char *name, 352 uint16_t tparamcnt, const void *tparam, 353 uint16_t tdatacnt, const void *tdata, 354 uint16_t *rparamcnt, void *rparam, 355 uint16_t *rdatacnt, void *rdata, 356 uint32_t *buffer_oflow) 357{ 358 struct smbioc_t2rq krq; 359 int i; 360 361 if (setupcount < 0 || setupcount > SMB_MAXSETUPWORDS) { 362 /* Bogus setup count, or too many setup words */ 363 return EINVAL; 364 } 365 bzero(&krq, sizeof(krq)); 366 krq.ioc_version = SMB_IOC_STRUCT_VERSION; 367 for (i = 0; i < setupcount; i++) 368 krq.ioc_setup[i] = setup[i]; 369 krq.ioc_setupcnt = setupcount; 370 krq.ioc_name = name; 371 /* 372 * Now when passing the name into the kernel we also send the length 373 * of the name. The ioc_name_len needs to contain the name length and 374 * the null byte. 375 */ 376 if (name) 377 krq.ioc_name_len = (uint32_t)strlen(name) + 1; 378 else 379 krq.ioc_name_len = 0; 380 krq.ioc_tparamcnt = tparamcnt; 381 krq.ioc_tparam = (void *)tparam; 382 krq.ioc_tdatacnt = tdatacnt; 383 krq.ioc_tdata = (void *)tdata; 384 krq.ioc_rparamcnt = *rparamcnt; 385 krq.ioc_rparam = rparam; 386 krq.ioc_rdatacnt = *rdatacnt; 387 krq.ioc_rdata = rdata; 388 389 if (smb_ioctl_call(ctx->ct_fd, SMBIOC_T2RQ, &krq) == -1) { 390 return errno; 391 } 392 *rparamcnt = krq.ioc_rparamcnt; 393 *rdatacnt = krq.ioc_rdatacnt; 394 *buffer_oflow = (krq.ioc_flags2 & SMB_FLAGS2_ERR_STATUS) && 395 (krq.ioc_ntstatus == STATUS_BUFFER_OVERFLOW); 396 return 0; 397} 398