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
 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
 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
 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 }