xref: /titanic_50/usr/src/lib/libsmbfs/smb/netshareenum.c (revision ff3124eff995e6cd8ebd8c6543648e0670920034)
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