1 /* 2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights 7 * Reserved. This file contains Original Code and/or Modifications of 8 * Original Code as defined in and that are subject to the Apple Public 9 * Source License Version 1.0 (the 'License'). You may not use this file 10 * except in compliance with the License. Please obtain a copy of the 11 * License at http://www.apple.com/publicsource and read it before using 12 * this file. 13 * 14 * The Original Code and all software distributed under the License are 15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the 19 * License for the specific language governing rights and limitations 20 * under the License." 21 * 22 * @APPLE_LICENSE_HEADER_END@ 23 */ 24 25 /* BEGIN CSTYLED */ 26 /* 27 * @(#)ui.c * 28 * (c) 2004 Apple Computer, Inc. All Rights Reserved 29 * 30 * 31 * netshareenum.c -- Routines for getting a list of share information 32 * from a server. 33 * 34 * MODIFICATION HISTORY: 35 * 27-Nov-2004 Guy Harris New today 36 */ 37 /* END CSTYLED */ 38 39 /* 40 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 41 */ 42 43 #include <stdlib.h> 44 #include <string.h> 45 #include <stdio.h> 46 #include <errno.h> 47 48 #include <netsmb/mchain.h> 49 #include <netsmb/smb.h> 50 #include <netsmb/smb_lib.h> 51 #include <netsmb/smb_rap.h> 52 #include <netsmb/smb_netshareenum.h> 53 #include <smb/charsets.h> 54 55 #if 0 /* XXX see below */ 56 #include <dce/exc_handling.h> 57 #include <rpc/attrb.h> 58 #include "srvsvc.h" 59 #endif 60 61 /* 62 * Don't want RPC client-side code in here. 63 * It's good code; just doesn't belong here. 64 * 65 * The API provided by this library should be 66 * just files and pipes (and not much more). 67 * It MAY be useful to provide some of the 68 * RAP (remote API) functions functions like 69 * rap_netshareenum below... 70 * 71 * XXX: Not sure this file belongs here at all. 72 * smb_rap.h looks like a reasonable API 73 * for this library to export. 74 */ 75 #if 0 /* XXX */ 76 77 static int 78 rpc_netshareenum(struct smb_ctx *ctx, int *entriesp, int *totalp, 79 struct share_info **entries_listp) 80 { 81 char ctx_string[2+16+1]; /* enough for 64-bit pointer, in hex */ 82 unsigned_char_p_t binding; 83 unsigned32 binding_status; 84 rpc_binding_handle_t binding_h; 85 int error, i, entries; 86 char *addrstr, *srvnamestr; 87 unsigned short *usrvnamestr; 88 unsigned32 level; 89 SHARE_ENUM_STRUCT share_info; 90 SHARE_INFO_1_CONTAINER share_info_1_container; 91 SHARE_INFO_1 *shares, *share; 92 unsigned32 total_entries; 93 unsigned32 status, free_status; 94 struct share_info *entry_list, *elp; 95 static EXCEPTION rpc_x_connect_rejected; 96 static int exceptions_initialized; 97 98 sprintf(ctx_string, "%p", ctx); 99 rpc_string_binding_compose(NULL, "ncacn_np", ctx_string, 100 "srvsvc", NULL, &binding, &binding_status); 101 if (binding_status != rpc_s_ok) { 102 smb_error(dgettext(TEXT_DOMAIN, 103 "rpc_string_binding_compose failed with %d"), 104 0, binding_status); 105 return (EINVAL); 106 } 107 rpc_binding_from_string_binding(binding, &binding_h, &status); 108 rpc_string_free(&binding, (unsigned32 *)&free_status); 109 if (binding_status != rpc_s_ok) { 110 smb_error(dgettext(TEXT_DOMAIN, 111 "rpc_binding_from_string_binding failed with %d"), 0, 112 binding_status); 113 return (EINVAL); 114 } 115 level = 1; 116 share_info.share_union.level = 1; 117 share_info.share_union.tagged_union.share1 = &share_info_1_container; 118 share_info_1_container.share_count = 0; 119 share_info_1_container.shares = NULL; 120 /* 121 * Convert the server IP address to a string, and send that as 122 * the "server name" - that's what Windows appears to do, and 123 * that avoids problems with NetBIOS names containing 124 * non-ASCII characters. 125 */ 126 addrstr = inet_ntoa(ctx->ct_srvinaddr.sin_addr); 127 srvnamestr = malloc(strlen(addrstr) + 3); 128 if (srvnamestr == NULL) { 129 status = errno; 130 smb_error(dgettext(TEXT_DOMAIN, 131 "can't allocate string for server address"), status); 132 rpc_binding_free(&binding_h, &free_status); 133 return (status); 134 } 135 strcpy(srvnamestr, "\\\\"); 136 strcat(srvnamestr, addrstr); 137 usrvnamestr = convert_utf8_to_leunicode(srvnamestr); 138 if (usrvnamestr == NULL) { 139 smb_error(dgettext(TEXT_DOMAIN, 140 "can't convert string for server address to Unicode"), 0); 141 rpc_binding_free(&binding_h, &free_status); 142 free(srvnamestr); 143 return (EINVAL); 144 } 145 if (!exceptions_initialized) { 146 EXCEPTION_INIT(rpc_x_connect_rejected); 147 exc_set_status(&rpc_x_connect_rejected, rpc_s_connect_rejected); 148 exceptions_initialized = 1; 149 } 150 /* printf("Calling NetrShareEnum.."); XXX */ 151 TRY 152 status = NetrShareEnum(binding_h, usrvnamestr, &level, 153 &share_info, 4294967295U, &total_entries, NULL); 154 if (status != 0) 155 smb_error(dgettext(TEXT_DOMAIN, 156 "error from NetrShareEnum call: status = 0x%08x"), 157 0, status); 158 /*CSTYLED*/ 159 CATCH (rpc_x_connect_rejected) 160 /* 161 * This is what we get if we can't open the pipe. 162 * That's a normal occurrence when we're talking 163 * to a system that (presumably) doesn't support 164 * DCE RPC on the server side, such as Windows 95/98/Me, 165 * so we don't log an error. 166 */ 167 /*CSTYLED*/ 168 status = ENOTSUP; 169 CATCH_ALL 170 /* 171 * XXX - should we handle some exceptions differently, 172 * returning different errors, and try RAP only for 173 * ENOTSUP? 174 */ 175 smb_error(dgettext(TEXT_DOMAIN, 176 "error from NetrShareEnum call: exception = %u"), 177 0, THIS_CATCH->match.value); 178 status = ENOTSUP; 179 ENDTRY 180 rpc_binding_free(&binding_h, &free_status); 181 free(srvnamestr); 182 free(usrvnamestr); 183 if (status != 0) 184 return (ENOTSUP); 185 186 /* 187 * XXX - if the IDL is correct, it's not clear whether the 188 * unmarshalling code will properly handle the case where 189 * a packet where "share_count" and the max count for the 190 * array of shares don't match; a valid DCE RPC implementation 191 * won't marshal something like that, but there's no guarantee 192 * that the server we're talking to has a valid implementation 193 * (which could be a *malicious* implementation!). 194 */ 195 entries = share_info.share_union.tagged_union.share1->share_count; 196 shares = share_info.share_union.tagged_union.share1->shares; 197 entry_list = calloc(entries, sizeof (struct share_info)); 198 if (entry_list == NULL) { 199 error = errno; 200 goto cleanup_and_return; 201 } 202 for (share = shares, elp = entry_list, i = 0; i < entries; 203 i++, share++) { 204 elp->type = share->shi1_type; 205 elp->netname = convert_unicode_to_utf8(share->shi1_share); 206 if (elp->netname == NULL) 207 goto fail; 208 elp->remark = convert_unicode_to_utf8(share->shi1_remark); 209 if (elp->remark == NULL) 210 goto fail; 211 elp++; 212 } 213 *entriesp = entries; 214 *totalp = total_entries; 215 *entries_listp = entry_list; 216 error = 0; 217 goto cleanup_and_return; 218 219 fail: 220 error = errno; 221 for (elp = entry_list, i = 0; i < entries; i++, elp++) { 222 /* 223 * elp->netname is set before elp->remark, so if 224 * elp->netname is null, elp->remark is also null. 225 * If either of them is null, we haven't done anything 226 * to any entries after this one. 227 */ 228 if (elp->netname == NULL) 229 break; 230 free(elp->netname); 231 if (elp->remark == NULL) 232 break; 233 free(elp->remark); 234 } 235 free(entry_list); 236 237 cleanup_and_return: 238 for (share = shares, i = 0; i < entries; i++, share++) { 239 free(share->shi1_share); 240 free(share->shi1_remark); 241 } 242 free(shares); 243 /* 244 * XXX - "share1" should be a unique pointer, but we haven't 245 * changed the marshalling code to support non-full pointers 246 * in unions, so we leave it as a full pointer. 247 * 248 * That means that this might, or might not, be changed from 249 * pointing to "share_info_1_container" to pointing to a 250 * mallocated structure, according to the DCE RPC 1.1 IDL spec; 251 * we free it only if it's changed. 252 */ 253 if (share_info.share_union.tagged_union.share1 != 254 &share_info_1_container) 255 free(share_info.share_union.tagged_union.share1); 256 return (error); 257 } 258 #endif /* XXX */ 259 260 /* 261 * Enumerate shares using RAP 262 */ 263 264 struct smb_share_info_1 { 265 char shi1_netname[13]; 266 char shi1_pad; 267 uint16_t shi1_type; 268 uint32_t shi1_remark; /* char * */ 269 }; 270 271 static int 272 smb_rap_NetShareEnum(struct smb_ctx *ctx, int sLevel, void *pbBuffer, 273 int *cbBuffer, int *pcEntriesRead, int *pcTotalAvail) 274 { 275 struct smb_rap *rap; 276 long lval = -1; 277 int error; 278 279 error = smb_rap_create(0, "WrLeh", "B13BWz", &rap); 280 if (error) 281 return (error); 282 (void) smb_rap_setNparam(rap, sLevel); /* W - sLevel */ 283 (void) smb_rap_setPparam(rap, pbBuffer); /* r - pbBuffer */ 284 (void) smb_rap_setNparam(rap, *cbBuffer); /* L - cbBuffer */ 285 error = smb_rap_request(rap, ctx); 286 if (error == 0) { 287 *pcEntriesRead = rap->r_entries; 288 error = smb_rap_getNparam(rap, &lval); 289 *pcTotalAvail = lval; 290 /* Copy the data length into the IN/OUT variable. */ 291 *cbBuffer = rap->r_rcvbuflen; 292 } 293 error = smb_rap_error(rap, error); 294 smb_rap_done(rap); 295 return (error); 296 } 297 298 static int 299 rap_netshareenum(struct smb_ctx *ctx, int *entriesp, int *totalp, 300 struct share_info **entries_listp) 301 { 302 int error, bufsize, i, entries, total, nreturned; 303 struct smb_share_info_1 *rpbuf, *ep; 304 struct share_info *entry_list, *elp; 305 char *cp; 306 int lbound, rbound; 307 308 bufsize = 0xffe0; /* samba notes win2k bug for 65535 */ 309 rpbuf = malloc(bufsize); 310 if (rpbuf == NULL) 311 return (errno); 312 313 error = smb_rap_NetShareEnum(ctx, 1, rpbuf, &bufsize, &entries, &total); 314 if (error && 315 error != (ERROR_MORE_DATA | SMB_RAP_ERROR)) { 316 free(rpbuf); 317 return (error); 318 } 319 entry_list = malloc(entries * sizeof (struct share_info)); 320 if (entry_list == NULL) { 321 error = errno; 322 free(rpbuf); 323 return (error); 324 } 325 lbound = entries * (sizeof (struct smb_share_info_1)); 326 rbound = bufsize; 327 for (ep = rpbuf, elp = entry_list, i = 0, nreturned = 0; i < entries; 328 i++, ep++) { 329 elp->type = letohs(ep->shi1_type); 330 ep->shi1_pad = '\0'; /* ensure null termination */ 331 elp->netname = convert_wincs_to_utf8(ep->shi1_netname); 332 if (elp->netname == NULL) 333 continue; /* punt on this entry */ 334 /* 335 * Check for validity of offset. 336 */ 337 if (ep->shi1_remark >= lbound && ep->shi1_remark < rbound) { 338 cp = (char *)rpbuf + ep->shi1_remark; 339 elp->remark = convert_wincs_to_utf8(cp); 340 } else 341 elp->remark = NULL; 342 elp++; 343 nreturned++; 344 } 345 *entriesp = nreturned; 346 *totalp = total; 347 *entries_listp = entry_list; 348 free(rpbuf); 349 return (0); 350 } 351 352 /* 353 * First we try the RPC-based NetrShareEnum, and, if that fails, we fall 354 * back on the RAP-based NetShareEnum. 355 */ 356 int 357 smb_netshareenum(struct smb_ctx *ctx, int *entriesp, int *totalp, 358 struct share_info **entry_listp) 359 { 360 int error; 361 362 #ifdef NOTYETDEFINED 363 /* 364 * Try getting a list of shares with the SRVSVC RPC service. 365 */ 366 error = rpc_netshareenum(ctx, entriesp, totalp, entry_listp); 367 if (error == 0) 368 return (0); 369 #endif 370 371 /* 372 * OK, that didn't work - try RAP. 373 * XXX - do so only if it failed because we couldn't open 374 * the pipe? 375 */ 376 error = rap_netshareenum(ctx, entriesp, totalp, entry_listp); 377 return (error); 378 }