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 #pragma ident "%Z%%M% %I% %E% SMI" 40 41 #include <stdlib.h> 42 #include <string.h> 43 #include <stdio.h> 44 #include <errno.h> 45 46 #include <netsmb/mchain.h> 47 #include <netsmb/smb_lib.h> 48 #include <netsmb/smb_rap.h> 49 #include <netsmb/smb_netshareenum.h> 50 #include "charsets.h" 51 52 #if 0 /* XXX see below */ 53 #include <dce/exc_handling.h> 54 #include <attrb.h> 55 #include "srvsvc.h" 56 #endif 57 58 /* 59 * Don't want RPC client-side code in here. 60 * It's good code; just doesn't belong here. 61 * 62 * The API provided by this library should be 63 * just files and pipes (and not much more). 64 * It MAY be useful to provide some of the 65 * RAP (remote API) functions functions like 66 * rap_netshareenum below... 67 * 68 * XXX: Not sure this file belongs here at all. 69 * smb_rap.h looks like a reasonable API 70 * for this library to export. 71 */ 72 #if 0 /* XXX */ 73 74 static int 75 rpc_netshareenum(struct smb_ctx *ctx, int *entriesp, int *totalp, 76 struct share_info **entries_listp) 77 { 78 char ctx_string[2+16+1]; /* enough for 64-bit pointer, in hex */ 79 unsigned_char_p_t binding; 80 unsigned32 binding_status; 81 rpc_binding_handle_t binding_h; 82 int error, i, entries; 83 char *addrstr, *srvnamestr; 84 unsigned short *usrvnamestr; 85 unsigned32 level; 86 SHARE_ENUM_STRUCT share_info; 87 SHARE_INFO_1_CONTAINER share_info_1_container; 88 SHARE_INFO_1 *shares, *share; 89 unsigned32 total_entries; 90 unsigned32 status, free_status; 91 struct share_info *entry_list, *elp; 92 static EXCEPTION rpc_x_connect_rejected; 93 static int exceptions_initialized; 94 95 sprintf(ctx_string, "%p", ctx); 96 rpc_string_binding_compose(NULL, "ncacn_np", ctx_string, 97 "srvsvc", NULL, &binding, &binding_status); 98 if (binding_status != rpc_s_ok) { 99 smb_error(dgettext(TEXT_DOMAIN, 100 "rpc_string_binding_compose failed with %d"), 101 0, binding_status); 102 return (EINVAL); 103 } 104 rpc_binding_from_string_binding(binding, &binding_h, &status); 105 if (binding_status != rpc_s_ok) { 106 smb_error(dgettext(TEXT_DOMAIN, 107 "rpc_binding_from_string_binding failed with %d"), 0, 108 binding_status); 109 return (EINVAL); 110 } 111 level = 1; 112 share_info.share_union.level = 1; 113 share_info.share_union.tagged_union.share1 = &share_info_1_container; 114 share_info_1_container.share_count = 0; 115 share_info_1_container.shares = NULL; 116 /* 117 * Convert the server IP address to a string, and send that as 118 * the "server name" - that's what Windows appears to do, and 119 * that avoids problems with NetBIOS names containing 120 * non-ASCII characters. 121 */ 122 addrstr = inet_ntoa(ctx->ct_srvinaddr.sin_addr); 123 srvnamestr = malloc(strlen(addrstr) + 3); 124 if (srvnamestr == NULL) { 125 status = errno; 126 smb_error(dgettext(TEXT_DOMAIN, 127 "can't allocate string for server address"), status); 128 rpc_binding_free(&binding_h, &free_status); 129 return (status); 130 } 131 strcpy(srvnamestr, "\\\\"); 132 strcat(srvnamestr, addrstr); 133 #ifdef NOTYETDEFINED 134 usrvnamestr = convert_utf8_to_leunicode(srvnamestr); 135 #endif 136 usrvnamestr = srvnamestr; 137 if (usrvnamestr == NULL) { 138 smb_error(dgettext(TEXT_DOMAIN, 139 "can't convert string for server address to Unicode"), 0); 140 rpc_binding_free(&binding_h, &free_status); 141 return (EINVAL); 142 } 143 if (!exceptions_initialized) { 144 EXCEPTION_INIT(rpc_x_connect_rejected); 145 exc_set_status(&rpc_x_connect_rejected, rpc_s_connect_rejected); 146 exceptions_initialized = 1; 147 } 148 /* printf("Calling NetrShareEnum.."); XXX */ 149 TRY 150 status = NetrShareEnum(binding_h, usrvnamestr, &level, 151 &share_info, 4294967295U, &total_entries, NULL); 152 if (status != 0) 153 smb_error(dgettext(TEXT_DOMAIN, 154 "error from NetrShareEnum call: status = 0x%08x"), 155 0, status); 156 /*CSTYLED*/ 157 CATCH (rpc_x_connect_rejected) 158 /* 159 * This is what we get if we can't open the pipe. 160 * That's a normal occurrence when we're talking 161 * to a system that (presumably) doesn't support 162 * DCE RPC on the server side, such as Windows 95/98/Me, 163 * so we don't log an error. 164 */ 165 /*CSTYLED*/ 166 status = ENOTSUP; 167 CATCH_ALL 168 /* 169 * XXX - should we handle some exceptions differently, 170 * returning different errors, and try RAP only for 171 * ENOTSUP? 172 */ 173 smb_error(dgettext(TEXT_DOMAIN, 174 "error from NetrShareEnum call: exception = %u"), 175 0, THIS_CATCH->match.value); 176 status = ENOTSUP; 177 ENDTRY 178 rpc_binding_free(&binding_h, &free_status); 179 free(srvnamestr); 180 free(usrvnamestr); 181 if (status != 0) 182 return (ENOTSUP); 183 184 /* 185 * XXX - if the IDL is correct, it's not clear whether the 186 * unmarshalling code will properly handle the case where 187 * a packet where "share_count" and the max count for the 188 * array of shares don't match; a valid DCE RPC implementation 189 * won't marshal something like that, but there's no guarantee 190 * that the server we're talking to has a valid implementation 191 * (which could be a *malicious* implementation!). 192 */ 193 entries = share_info.share_union.tagged_union.share1->share_count; 194 shares = share_info.share_union.tagged_union.share1->shares; 195 entry_list = calloc(entries, sizeof (struct share_info)); 196 if (entry_list == NULL) { 197 error = errno; 198 goto cleanup_and_return; 199 } 200 for (share = shares, elp = entry_list, i = 0; i < entries; 201 i++, share++) { 202 elp->type = share->shi1_type; 203 #ifdef NOTYETDEFINED 204 elp->netname = convert_unicode_to_utf8(share->shi1_share); 205 #endif 206 elp->netname = share->shi1_share; 207 if (elp->netname == NULL) 208 goto fail; 209 #ifdef NOTYETDEFINED 210 elp->remark = convert_unicode_to_utf8(share->shi1_remark); 211 #endif 212 elp->remark = share->shi1_remark; 213 if (elp->remark == NULL) 214 goto fail; 215 elp++; 216 } 217 *entriesp = entries; 218 *totalp = total_entries; 219 *entries_listp = entry_list; 220 error = 0; 221 goto cleanup_and_return; 222 223 fail: 224 error = errno; 225 for (elp = entry_list, i = 0; i < entries; i++, elp++) { 226 /* 227 * elp->netname is set before elp->remark, so if 228 * elp->netname is null, elp->remark is also null. 229 * If either of them is null, we haven't done anything 230 * to any entries after this one. 231 */ 232 if (elp->netname == NULL) 233 break; 234 free(elp->netname); 235 if (elp->remark == NULL) 236 break; 237 free(elp->remark); 238 } 239 free(entry_list); 240 241 cleanup_and_return: 242 for (share = shares, i = 0; i < entries; i++, share++) { 243 free(share->shi1_share); 244 free(share->shi1_remark); 245 } 246 free(shares); 247 /* 248 * XXX - "share1" should be a unique pointer, but we haven't 249 * changed the marshalling code to support non-full pointers 250 * in unions, so we leave it as a full pointer. 251 * 252 * That means that this might, or might not, be changed from 253 * pointing to "share_info_1_container" to pointing to a 254 * mallocated structure, according to the DCE RPC 1.1 IDL spec; 255 * we free it only if it's changed. 256 */ 257 if (share_info.share_union.tagged_union.share1 != 258 &share_info_1_container) 259 free(share_info.share_union.tagged_union.share1); 260 return (error); 261 } 262 #endif /* XXX */ 263 264 static int 265 rap_netshareenum(struct smb_ctx *ctx, int *entriesp, int *totalp, 266 struct share_info **entries_listp) 267 { 268 int error, bufsize, i, entries, total, nreturned; 269 struct smb_share_info_1 *rpbuf, *ep; 270 struct share_info *entry_list, *elp; 271 char *cp; 272 int lbound, rbound; 273 274 bufsize = 0xffe0; /* samba notes win2k bug for 65535 */ 275 rpbuf = malloc(bufsize); 276 if (rpbuf == NULL) 277 return (errno); 278 279 error = smb_rap_NetShareEnum(ctx, 1, rpbuf, &bufsize, &entries, &total); 280 if (error && 281 error != (SMB_ERROR_MORE_DATA | SMB_RAP_ERROR)) { 282 free(rpbuf); 283 return (error); 284 } 285 entry_list = malloc(entries * sizeof (struct share_info)); 286 if (entry_list == NULL) { 287 error = errno; 288 free(rpbuf); 289 return (error); 290 } 291 lbound = entries * (sizeof (struct smb_share_info_1)); 292 rbound = bufsize; 293 for (ep = rpbuf, elp = entry_list, i = 0, nreturned = 0; i < entries; 294 i++, ep++) { 295 elp->type = letohs(ep->shi1_type); 296 ep->shi1_pad = '\0'; /* ensure null termination */ 297 elp->netname = strdup(ep->shi1_netname); 298 #ifdef NOTYETDEFINED 299 elp->netname = convert_wincs_to_utf8(ep->shi1_netname); 300 #endif 301 if (elp->netname == NULL) 302 continue; /* punt on this entry */ 303 /* 304 * Check for validity of offset. 305 */ 306 if (ep->shi1_remark >= lbound && ep->shi1_remark < rbound) { 307 cp = (char *)rpbuf + ep->shi1_remark; 308 elp->remark = cp; 309 #ifdef NOTYETDEFINED 310 elp->remark = nls_str_toloc(cp, cp); 311 #endif 312 } else 313 elp->remark = NULL; 314 elp++; 315 nreturned++; 316 } 317 *entriesp = nreturned; 318 *totalp = total; 319 *entries_listp = entry_list; 320 free(rpbuf); 321 return (0); 322 } 323 324 /* 325 * First we try the RPC-based NetrShareEnum, and, if that fails, we fall 326 * back on the RAP-based NetShareEnum. 327 */ 328 int 329 smb_netshareenum(struct smb_ctx *ctx, int *entriesp, int *totalp, 330 struct share_info **entry_listp) 331 { 332 int error; 333 334 #ifdef NOTYETDEFINED 335 /* 336 * Try getting a list of shares with the SRVSVC RPC service. 337 */ 338 error = rpc_netshareenum(ctx, entriesp, totalp, entry_listp); 339 if (error == 0) 340 return (0); 341 #endif 342 343 /* 344 * OK, that didn't work - try RAP. 345 * XXX - do so only if it failed because we couldn't open 346 * the pipe? 347 */ 348 return (rap_netshareenum(ctx, entriesp, totalp, entry_listp)); 349 } 350