xref: /onnv-gate/usr/src/lib/libsmbfs/smb/netshareenum.c (revision 10023:71bf38dba3d6)
16007Sthurlow /*
26007Sthurlow  * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
36007Sthurlow  *
46007Sthurlow  * @APPLE_LICENSE_HEADER_START@
56007Sthurlow  *
66007Sthurlow  * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
76007Sthurlow  * Reserved.  This file contains Original Code and/or Modifications of
86007Sthurlow  * Original Code as defined in and that are subject to the Apple Public
96007Sthurlow  * Source License Version 1.0 (the 'License').  You may not use this file
106007Sthurlow  * except in compliance with the License.  Please obtain a copy of the
116007Sthurlow  * License at http://www.apple.com/publicsource and read it before using
126007Sthurlow  * this file.
136007Sthurlow  *
146007Sthurlow  * The Original Code and all software distributed under the License are
156007Sthurlow  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
166007Sthurlow  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
176007Sthurlow  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
186007Sthurlow  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
196007Sthurlow  * License for the specific language governing rights and limitations
206007Sthurlow  * under the License."
216007Sthurlow  *
226007Sthurlow  * @APPLE_LICENSE_HEADER_END@
236007Sthurlow  */
246007Sthurlow 
256007Sthurlow /* BEGIN CSTYLED */
266007Sthurlow /*
276007Sthurlow  *      @(#)ui.c      *
286007Sthurlow  *      (c) 2004   Apple Computer, Inc.  All Rights Reserved
296007Sthurlow  *
306007Sthurlow  *
316007Sthurlow  *      netshareenum.c -- Routines for getting a list of share information
326007Sthurlow  *			  from a server.
336007Sthurlow  *
346007Sthurlow  *      MODIFICATION HISTORY:
356007Sthurlow  *       27-Nov-2004     Guy Harris	New today
366007Sthurlow  */
376007Sthurlow /* END CSTYLED */
386007Sthurlow 
398271SGordon.Ross@Sun.COM /*
40*10023SGordon.Ross@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
418271SGordon.Ross@Sun.COM  * Use is subject to license terms.
428271SGordon.Ross@Sun.COM  */
436007Sthurlow 
446007Sthurlow #include <stdlib.h>
456007Sthurlow #include <string.h>
466007Sthurlow #include <stdio.h>
476007Sthurlow #include <errno.h>
486007Sthurlow 
496007Sthurlow #include <netsmb/mchain.h>
50*10023SGordon.Ross@Sun.COM #include <netsmb/smb.h>
516007Sthurlow #include <netsmb/smb_lib.h>
526007Sthurlow #include <netsmb/smb_rap.h>
536007Sthurlow #include <netsmb/smb_netshareenum.h>
548271SGordon.Ross@Sun.COM #include <smb/charsets.h>
556007Sthurlow 
566007Sthurlow #if 0 /* XXX see below */
576007Sthurlow #include <dce/exc_handling.h>
588271SGordon.Ross@Sun.COM #include <rpc/attrb.h>
596007Sthurlow #include "srvsvc.h"
606007Sthurlow #endif
616007Sthurlow 
626007Sthurlow /*
636007Sthurlow  * Don't want RPC client-side code in here.
646007Sthurlow  * It's good code; just doesn't belong here.
656007Sthurlow  *
666007Sthurlow  * The API provided by this library should be
676007Sthurlow  * just files and pipes (and not much more).
686007Sthurlow  * It MAY be useful to provide some of the
696007Sthurlow  * RAP (remote API) functions functions like
706007Sthurlow  * rap_netshareenum below...
716007Sthurlow  *
726007Sthurlow  * XXX: Not sure this file belongs here at all.
736007Sthurlow  * smb_rap.h looks like a reasonable API
746007Sthurlow  * for this library to export.
756007Sthurlow  */
766007Sthurlow #if 0 /* XXX */
776007Sthurlow 
786007Sthurlow static int
796007Sthurlow rpc_netshareenum(struct smb_ctx *ctx, int *entriesp, int *totalp,
806007Sthurlow     struct share_info **entries_listp)
816007Sthurlow {
826007Sthurlow 	char ctx_string[2+16+1];	/* enough for 64-bit pointer, in hex */
836007Sthurlow 	unsigned_char_p_t binding;
846007Sthurlow 	unsigned32 binding_status;
856007Sthurlow 	rpc_binding_handle_t binding_h;
866007Sthurlow 	int error, i, entries;
876007Sthurlow 	char *addrstr, *srvnamestr;
886007Sthurlow 	unsigned short *usrvnamestr;
896007Sthurlow 	unsigned32 level;
906007Sthurlow 	SHARE_ENUM_STRUCT share_info;
916007Sthurlow 	SHARE_INFO_1_CONTAINER share_info_1_container;
926007Sthurlow 	SHARE_INFO_1 *shares, *share;
936007Sthurlow 	unsigned32 total_entries;
946007Sthurlow 	unsigned32 status, free_status;
956007Sthurlow 	struct share_info *entry_list, *elp;
966007Sthurlow 	static EXCEPTION rpc_x_connect_rejected;
976007Sthurlow 	static int exceptions_initialized;
986007Sthurlow 
996007Sthurlow 	sprintf(ctx_string, "%p", ctx);
1006007Sthurlow 	rpc_string_binding_compose(NULL, "ncacn_np", ctx_string,
1016007Sthurlow 	    "srvsvc", NULL, &binding, &binding_status);
1026007Sthurlow 	if (binding_status != rpc_s_ok) {
1036007Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
1046007Sthurlow 		    "rpc_string_binding_compose failed with %d"),
1056007Sthurlow 		    0, binding_status);
1066007Sthurlow 		return (EINVAL);
1076007Sthurlow 	}
1086007Sthurlow 	rpc_binding_from_string_binding(binding, &binding_h, &status);
1098271SGordon.Ross@Sun.COM 	rpc_string_free(&binding, (unsigned32 *)&free_status);
1106007Sthurlow 	if (binding_status != rpc_s_ok) {
1116007Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
1126007Sthurlow 		    "rpc_binding_from_string_binding failed with %d"), 0,
1136007Sthurlow 		    binding_status);
1146007Sthurlow 		return (EINVAL);
1156007Sthurlow 	}
1166007Sthurlow 	level = 1;
1176007Sthurlow 	share_info.share_union.level = 1;
1186007Sthurlow 	share_info.share_union.tagged_union.share1 = &share_info_1_container;
1196007Sthurlow 	share_info_1_container.share_count = 0;
1206007Sthurlow 	share_info_1_container.shares = NULL;
1216007Sthurlow 	/*
1226007Sthurlow 	 * Convert the server IP address to a string, and send that as
1236007Sthurlow 	 * the "server name" - that's what Windows appears to do, and
1246007Sthurlow 	 * that avoids problems with NetBIOS names containing
1256007Sthurlow 	 * non-ASCII characters.
1266007Sthurlow 	 */
1276007Sthurlow 	addrstr = inet_ntoa(ctx->ct_srvinaddr.sin_addr);
1286007Sthurlow 	srvnamestr = malloc(strlen(addrstr) + 3);
1296007Sthurlow 	if (srvnamestr == NULL) {
1306007Sthurlow 		status = errno;
1316007Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
1326007Sthurlow 		    "can't allocate string for server address"), status);
1336007Sthurlow 		rpc_binding_free(&binding_h, &free_status);
1346007Sthurlow 		return (status);
1356007Sthurlow 	}
1366007Sthurlow 	strcpy(srvnamestr, "\\\\");
1376007Sthurlow 	strcat(srvnamestr, addrstr);
1386007Sthurlow 	usrvnamestr = convert_utf8_to_leunicode(srvnamestr);
1396007Sthurlow 	if (usrvnamestr == NULL) {
1406007Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
1416007Sthurlow 		    "can't convert string for server address to Unicode"), 0);
1426007Sthurlow 		rpc_binding_free(&binding_h, &free_status);
1438271SGordon.Ross@Sun.COM 		free(srvnamestr);
1446007Sthurlow 		return (EINVAL);
1456007Sthurlow 	}
1466007Sthurlow 	if (!exceptions_initialized) {
1476007Sthurlow 		EXCEPTION_INIT(rpc_x_connect_rejected);
1486007Sthurlow 		exc_set_status(&rpc_x_connect_rejected, rpc_s_connect_rejected);
1496007Sthurlow 		exceptions_initialized = 1;
1506007Sthurlow 	}
1516007Sthurlow 	/* printf("Calling NetrShareEnum.."); XXX */
1526007Sthurlow 	TRY
1536007Sthurlow 		status = NetrShareEnum(binding_h, usrvnamestr, &level,
1546007Sthurlow 		    &share_info, 4294967295U, &total_entries, NULL);
1556007Sthurlow 		if (status != 0)
1566007Sthurlow 			smb_error(dgettext(TEXT_DOMAIN,
1576007Sthurlow 			    "error from NetrShareEnum call: status = 0x%08x"),
1586007Sthurlow 			    0, status);
1596007Sthurlow 	/*CSTYLED*/
1606007Sthurlow 	CATCH (rpc_x_connect_rejected)
1616007Sthurlow 		/*
1626007Sthurlow 		 * This is what we get if we can't open the pipe.
1636007Sthurlow 		 * That's a normal occurrence when we're talking
1646007Sthurlow 		 * to a system that (presumably) doesn't support
1656007Sthurlow 		 * DCE RPC on the server side, such as Windows 95/98/Me,
1666007Sthurlow 		 * so we don't log an error.
1676007Sthurlow 		 */
1686007Sthurlow 		/*CSTYLED*/
1696007Sthurlow 		status = ENOTSUP;
1706007Sthurlow 	CATCH_ALL
1716007Sthurlow 		/*
1726007Sthurlow 		 * XXX - should we handle some exceptions differently,
1736007Sthurlow 		 * returning different errors, and try RAP only for
1746007Sthurlow 		 * ENOTSUP?
1756007Sthurlow 		 */
1766007Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
1776007Sthurlow 		    "error from NetrShareEnum call: exception = %u"),
1786007Sthurlow 		    0, THIS_CATCH->match.value);
1796007Sthurlow 		status = ENOTSUP;
1806007Sthurlow 	ENDTRY
1816007Sthurlow 	rpc_binding_free(&binding_h, &free_status);
1826007Sthurlow 	free(srvnamestr);
1836007Sthurlow 	free(usrvnamestr);
1846007Sthurlow 	if (status != 0)
1856007Sthurlow 		return (ENOTSUP);
1866007Sthurlow 
1876007Sthurlow 	/*
1886007Sthurlow 	 * XXX - if the IDL is correct, it's not clear whether the
1896007Sthurlow 	 * unmarshalling code will properly handle the case where
1906007Sthurlow 	 * a packet where "share_count" and the max count for the
1916007Sthurlow 	 * array of shares don't match; a valid DCE RPC implementation
1926007Sthurlow 	 * won't marshal something like that, but there's no guarantee
1936007Sthurlow 	 * that the server we're talking to has a valid implementation
1946007Sthurlow 	 * (which could be a *malicious* implementation!).
1956007Sthurlow 	 */
1966007Sthurlow 	entries = share_info.share_union.tagged_union.share1->share_count;
1976007Sthurlow 	shares = share_info.share_union.tagged_union.share1->shares;
1986007Sthurlow 	entry_list = calloc(entries, sizeof (struct share_info));
1996007Sthurlow 	if (entry_list == NULL) {
2006007Sthurlow 		error = errno;
2016007Sthurlow 		goto cleanup_and_return;
2026007Sthurlow 	}
2036007Sthurlow 	for (share = shares, elp = entry_list, i = 0; i < entries;
2046007Sthurlow 	    i++, share++) {
2056007Sthurlow 		elp->type = share->shi1_type;
2066007Sthurlow 		elp->netname = convert_unicode_to_utf8(share->shi1_share);
2076007Sthurlow 		if (elp->netname == NULL)
2086007Sthurlow 			goto fail;
2096007Sthurlow 		elp->remark = convert_unicode_to_utf8(share->shi1_remark);
2106007Sthurlow 		if (elp->remark == NULL)
2116007Sthurlow 			goto fail;
2126007Sthurlow 		elp++;
2136007Sthurlow 	}
2146007Sthurlow 	*entriesp = entries;
2156007Sthurlow 	*totalp = total_entries;
2166007Sthurlow 	*entries_listp = entry_list;
2176007Sthurlow 	error = 0;
2186007Sthurlow 	goto cleanup_and_return;
2196007Sthurlow 
2206007Sthurlow fail:
2216007Sthurlow 	error = errno;
2226007Sthurlow 	for (elp = entry_list, i = 0; i < entries; i++, elp++) {
2236007Sthurlow 		/*
2246007Sthurlow 		 * elp->netname is set before elp->remark, so if
2256007Sthurlow 		 * elp->netname is null, elp->remark is also null.
2266007Sthurlow 		 * If either of them is null, we haven't done anything
2276007Sthurlow 		 * to any entries after this one.
2286007Sthurlow 		 */
2296007Sthurlow 		if (elp->netname == NULL)
2306007Sthurlow 			break;
2316007Sthurlow 		free(elp->netname);
2326007Sthurlow 		if (elp->remark == NULL)
2336007Sthurlow 			break;
2346007Sthurlow 		free(elp->remark);
2356007Sthurlow 	}
2366007Sthurlow 	free(entry_list);
2376007Sthurlow 
2386007Sthurlow cleanup_and_return:
2396007Sthurlow 	for (share = shares, i = 0; i < entries; i++, share++) {
2406007Sthurlow 		free(share->shi1_share);
2416007Sthurlow 		free(share->shi1_remark);
2426007Sthurlow 	}
2436007Sthurlow 	free(shares);
2446007Sthurlow 	/*
2456007Sthurlow 	 * XXX - "share1" should be a unique pointer, but we haven't
2466007Sthurlow 	 * changed the marshalling code to support non-full pointers
2476007Sthurlow 	 * in unions, so we leave it as a full pointer.
2486007Sthurlow 	 *
2496007Sthurlow 	 * That means that this might, or might not, be changed from
2506007Sthurlow 	 * pointing to "share_info_1_container" to pointing to a
2516007Sthurlow 	 * mallocated structure, according to the DCE RPC 1.1 IDL spec;
2526007Sthurlow 	 * we free it only if it's changed.
2536007Sthurlow 	 */
2546007Sthurlow 	if (share_info.share_union.tagged_union.share1 !=
2556007Sthurlow 	    &share_info_1_container)
2566007Sthurlow 		free(share_info.share_union.tagged_union.share1);
2576007Sthurlow 	return (error);
2586007Sthurlow }
2596007Sthurlow #endif /* XXX */
2606007Sthurlow 
2618271SGordon.Ross@Sun.COM /*
2628271SGordon.Ross@Sun.COM  * Enumerate shares using RAP
2638271SGordon.Ross@Sun.COM  */
2648271SGordon.Ross@Sun.COM 
2658271SGordon.Ross@Sun.COM struct smb_share_info_1 {
2668271SGordon.Ross@Sun.COM 	char		shi1_netname[13];
2678271SGordon.Ross@Sun.COM 	char		shi1_pad;
2688271SGordon.Ross@Sun.COM 	uint16_t	shi1_type;
2698271SGordon.Ross@Sun.COM 	uint32_t	shi1_remark;		/* char * */
2708271SGordon.Ross@Sun.COM };
2718271SGordon.Ross@Sun.COM 
2728271SGordon.Ross@Sun.COM static int
2738271SGordon.Ross@Sun.COM smb_rap_NetShareEnum(struct smb_ctx *ctx, int sLevel, void *pbBuffer,
2748271SGordon.Ross@Sun.COM 	int *cbBuffer, int *pcEntriesRead, int *pcTotalAvail)
2758271SGordon.Ross@Sun.COM {
2768271SGordon.Ross@Sun.COM 	struct smb_rap *rap;
2778271SGordon.Ross@Sun.COM 	long lval = -1;
2788271SGordon.Ross@Sun.COM 	int error;
2798271SGordon.Ross@Sun.COM 
2808271SGordon.Ross@Sun.COM 	error = smb_rap_create(0, "WrLeh", "B13BWz", &rap);
2818271SGordon.Ross@Sun.COM 	if (error)
2828271SGordon.Ross@Sun.COM 		return (error);
2838271SGordon.Ross@Sun.COM 	smb_rap_setNparam(rap, sLevel);		/* W - sLevel */
2848271SGordon.Ross@Sun.COM 	smb_rap_setPparam(rap, pbBuffer);	/* r - pbBuffer */
2858271SGordon.Ross@Sun.COM 	smb_rap_setNparam(rap, *cbBuffer);	/* L - cbBuffer */
2868271SGordon.Ross@Sun.COM 	error = smb_rap_request(rap, ctx);
2878271SGordon.Ross@Sun.COM 	if (error == 0) {
2888271SGordon.Ross@Sun.COM 		*pcEntriesRead = rap->r_entries;
2898271SGordon.Ross@Sun.COM 		error = smb_rap_getNparam(rap, &lval);
2908271SGordon.Ross@Sun.COM 		*pcTotalAvail = lval;
2918271SGordon.Ross@Sun.COM 		/* Copy the data length into the IN/OUT variable. */
2928271SGordon.Ross@Sun.COM 		*cbBuffer = rap->r_rcvbuflen;
2938271SGordon.Ross@Sun.COM 	}
2948271SGordon.Ross@Sun.COM 	error = smb_rap_error(rap, error);
2958271SGordon.Ross@Sun.COM 	smb_rap_done(rap);
2968271SGordon.Ross@Sun.COM 	return (error);
2978271SGordon.Ross@Sun.COM }
2988271SGordon.Ross@Sun.COM 
2996007Sthurlow static int
3006007Sthurlow rap_netshareenum(struct smb_ctx *ctx, int *entriesp, int *totalp,
3016007Sthurlow     struct share_info **entries_listp)
3026007Sthurlow {
3036007Sthurlow 	int error, bufsize, i, entries, total, nreturned;
3046007Sthurlow 	struct smb_share_info_1 *rpbuf, *ep;
3056007Sthurlow 	struct share_info *entry_list, *elp;
3066007Sthurlow 	char *cp;
3076007Sthurlow 	int lbound, rbound;
3086007Sthurlow 
3096007Sthurlow 	bufsize = 0xffe0;	/* samba notes win2k bug for 65535 */
3106007Sthurlow 	rpbuf = malloc(bufsize);
3116007Sthurlow 	if (rpbuf == NULL)
3126007Sthurlow 		return (errno);
3136007Sthurlow 
3146007Sthurlow 	error = smb_rap_NetShareEnum(ctx, 1, rpbuf, &bufsize, &entries, &total);
3156007Sthurlow 	if (error &&
3166007Sthurlow 	    error != (SMB_ERROR_MORE_DATA | SMB_RAP_ERROR)) {
3176007Sthurlow 		free(rpbuf);
3186007Sthurlow 		return (error);
3196007Sthurlow 	}
3206007Sthurlow 	entry_list = malloc(entries * sizeof (struct share_info));
3216007Sthurlow 	if (entry_list == NULL) {
3226007Sthurlow 		error = errno;
3236007Sthurlow 		free(rpbuf);
3246007Sthurlow 		return (error);
3256007Sthurlow 	}
3266007Sthurlow 	lbound = entries * (sizeof (struct smb_share_info_1));
3276007Sthurlow 	rbound = bufsize;
3286007Sthurlow 	for (ep = rpbuf, elp = entry_list, i = 0, nreturned = 0; i < entries;
3296007Sthurlow 	    i++, ep++) {
3306007Sthurlow 		elp->type = letohs(ep->shi1_type);
3316007Sthurlow 		ep->shi1_pad = '\0'; /* ensure null termination */
3326007Sthurlow 		elp->netname = convert_wincs_to_utf8(ep->shi1_netname);
3336007Sthurlow 		if (elp->netname == NULL)
3346007Sthurlow 			continue;	/* punt on this entry */
3356007Sthurlow 		/*
3366007Sthurlow 		 * Check for validity of offset.
3376007Sthurlow 		 */
3386007Sthurlow 		if (ep->shi1_remark >= lbound && ep->shi1_remark < rbound) {
3396007Sthurlow 			cp = (char *)rpbuf + ep->shi1_remark;
3408271SGordon.Ross@Sun.COM 			elp->remark = convert_wincs_to_utf8(cp);
3416007Sthurlow 		} else
3426007Sthurlow 			elp->remark = NULL;
3436007Sthurlow 		elp++;
3446007Sthurlow 		nreturned++;
3456007Sthurlow 	}
3466007Sthurlow 	*entriesp = nreturned;
3476007Sthurlow 	*totalp = total;
3486007Sthurlow 	*entries_listp = entry_list;
3496007Sthurlow 	free(rpbuf);
3506007Sthurlow 	return (0);
3516007Sthurlow }
3526007Sthurlow 
3536007Sthurlow /*
3546007Sthurlow  * First we try the RPC-based NetrShareEnum, and, if that fails, we fall
3556007Sthurlow  * back on the RAP-based NetShareEnum.
3566007Sthurlow  */
3576007Sthurlow int
3586007Sthurlow smb_netshareenum(struct smb_ctx *ctx, int *entriesp, int *totalp,
3596007Sthurlow     struct share_info **entry_listp)
3606007Sthurlow {
3616007Sthurlow 	int error;
3626007Sthurlow 
3636007Sthurlow #ifdef NOTYETDEFINED
3646007Sthurlow 	/*
3656007Sthurlow 	 * Try getting a list of shares with the SRVSVC RPC service.
3666007Sthurlow 	 */
3676007Sthurlow 	error = rpc_netshareenum(ctx, entriesp, totalp, entry_listp);
3686007Sthurlow 	if (error == 0)
3696007Sthurlow 		return (0);
3706007Sthurlow #endif
3716007Sthurlow 
3726007Sthurlow 	/*
3736007Sthurlow 	 * OK, that didn't work - try RAP.
3746007Sthurlow 	 * XXX - do so only if it failed because we couldn't open
3756007Sthurlow 	 * the pipe?
3766007Sthurlow 	 */
377*10023SGordon.Ross@Sun.COM 	error = rap_netshareenum(ctx, entriesp, totalp, entry_listp);
378*10023SGordon.Ross@Sun.COM 	return (error);
3796007Sthurlow }
380