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
smb_rap_NetShareEnum(struct smb_ctx * ctx,int sLevel,void * pbBuffer,int * cbBuffer,int * pcEntriesRead,int * pcTotalAvail)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
rap_netshareenum(struct smb_ctx * ctx,int * entriesp,int * totalp,struct share_info ** entries_listp)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
smb_netshareenum(struct smb_ctx * ctx,int * entriesp,int * totalp,struct share_info ** entry_listp)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 }
379