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