1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  25  */
  26 
  27 /*
  28  * Test program for the smbfs named pipe API.
  29  */
  30 
  31 #include <sys/types.h>
  32 #include <errno.h>
  33 #include <fcntl.h>
  34 #include <stdio.h>
  35 #include <stdlib.h>
  36 #include <string.h>
  37 #include <unistd.h>
  38 #include <libintl.h>
  39 #include <ctype.h>
  40 
  41 #include <netsmb/smb_lib.h>
  42 
  43 /*
  44  * This is a quick hack for testing client-side named pipes.
  45  * Its purpose is to test SMB named-pipe interface separately
  46  * from the RPC implementation.  It's a "hack" because it uses
  47  * hand-crafted RPC messages (extracted from network traffic).
  48  */
  49 
  50 /* This is a DCE/RPC bind call for "srvsvc". */
  51 static const uchar_t
  52 srvsvc_bind[] = {
  53         0x05, 0x00, 0x0b, 0x03, 0x10, 0x00, 0x00, 0x00,
  54         0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  55         0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
  56         0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
  57         0xc8, 0x4f, 0x32, 0x4b, 0x70, 0x16, 0xd3, 0x01,
  58         0x12, 0x78, 0x5a, 0x47, 0xbf, 0x6e, 0xe1, 0x88,
  59         0x03, 0x00, 0x00, 0x00, 0x04, 0x5d, 0x88, 0x8a,
  60         0xeb, 0x1c, 0xc9, 0x11, 0x9f, 0xe8, 0x08, 0x00,
  61         0x2b, 0x10, 0x48, 0x60, 0x02, 0x00, 0x00, 0x00 };
  62 
  63 /* This is a srvsvc "enum servers" call, in two parts */
  64 static const uchar_t
  65 srvsvc_enum1[] = {
  66         0x05, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00,
  67 #define ENUM_RPCLEN_OFF 8
  68         /* V - RPC frag length */
  69         0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  70         /* ... and the operation number is: VVVV */
  71         0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0f, 0x00,
  72 #define ENUM_SLEN1_OFF  28
  73 #define ENUM_SLEN2_OFF  36
  74         /* server name, length 14 vv ... */
  75         0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
  76         0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00 };
  77         /* UNC server here, i.e.: "\\192.168.1.6" */
  78 
  79 static const uchar_t
  80 srvsvc_enum2[] = {
  81         0x01, 0x00, 0x00, 0x00,
  82         0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  83         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  84         0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 };
  85 
  86 static uchar_t sendbuf[1024];
  87 static uchar_t recvbuf[4096];
  88 
  89 /*
  90  * Print strings found in the buffer.
  91  */
  92 static void
  93 pstrings(const uchar_t *buf, int len)
  94 {
  95         const uchar_t *p = buf;
  96         uint16_t u2;
  97         boolean_t instr = B_FALSE;
  98 
  99         while (len > 2) {
 100                 u2 = *p++;
 101                 u2 |= (*p++) << 8;
 102                 len -= 2;
 103 
 104                 if ((u2 & 0xFF80) == 0 && isprint(u2)) {
 105                         /* printable */
 106                         instr = B_TRUE;
 107                         putchar(u2);
 108                 } else {
 109                         /* not printalbe */
 110                         if (instr)
 111                                 putchar('\n');
 112                         instr = B_FALSE;
 113                 }
 114         }
 115         if (instr)
 116                 putchar('\n');
 117 }
 118 
 119 /*
 120  * Put a unicode UNC server name, including the null.
 121  * Quick-n-dirty, just for this test...
 122  */
 123 static int
 124 put_uncserver(const char *s, uchar_t *buf)
 125 {
 126         uchar_t *p = buf;
 127         char c;
 128 
 129         *p++ = '\\'; *p++ = '\0';
 130         *p++ = '\\'; *p++ = '\0';
 131 
 132         do {
 133                 c = *s++;
 134                 if (c == '/')
 135                         c = '\\';
 136                 *p++ = c;
 137                 *p++ = '\0';
 138 
 139         } while (c != 0);
 140 
 141         return (p - buf);
 142 }
 143 
 144 /*
 145  * Send the bind and read the ack.
 146  * This tests smb_fh_xactnp.
 147  */
 148 static int
 149 do_bind(int fid)
 150 {
 151         int err, len, more;
 152 
 153         more = 0;
 154         len = sizeof (recvbuf);
 155         err = smb_fh_xactnp(fid,
 156             sizeof (srvsvc_bind), (char *)srvsvc_bind,
 157             &len, (char *)recvbuf, &more);
 158         if (err) {
 159                 printf("xact bind, err=%d\n", err);
 160                 return (err);
 161         }
 162         if (more > 0) {
 163                 if (more > sizeof (recvbuf)) {
 164                         printf("bogus more=%d\n", more);
 165                         more = sizeof (recvbuf);
 166                 }
 167                 len = smb_fh_read(fid, 0,
 168                     more, (char *)recvbuf);
 169                 if (len == -1) {
 170                         err = EIO;
 171                         printf("read enum resp, err=%d\n", err);
 172                         return (err);
 173                 }
 174         }
 175 
 176         return (0);
 177 }
 178 
 179 static int
 180 do_enum(char *server, int fid)
 181 {
 182         int err, len, rlen, wlen;
 183         uchar_t *p;
 184 
 185         /*
 186          * Build the enum request - three parts.
 187          * See above: srvsvc_enum1, srvsvc_enum2
 188          *
 189          * First part: RPC header, etc.
 190          */
 191         p = sendbuf;
 192         len = sizeof (srvsvc_enum1); /* 40 */
 193         memcpy(p, srvsvc_enum1, len);
 194         p += len;
 195 
 196         /* Second part: UNC server name */
 197         len = put_uncserver(server, p);
 198         p += len;
 199         sendbuf[ENUM_SLEN1_OFF] = len / 2;
 200         sendbuf[ENUM_SLEN2_OFF] = len / 2;
 201 
 202         /* Third part: level, etc. (align4) */
 203         for (len = (p - sendbuf) & 3; len; len--)
 204                 *p++ = '\0';
 205         len = sizeof (srvsvc_enum2); /* 28 */
 206         memcpy(p, srvsvc_enum2, len);
 207         p += len;
 208 
 209         /*
 210          * Compute total length, and fixup RPC header.
 211          */
 212         len = p - sendbuf;
 213         sendbuf[ENUM_RPCLEN_OFF] = len;
 214 
 215         /*
 216          * Send the enum request, read the response.
 217          * This tests smb_fh_write, smb_fh_read.
 218          */
 219         wlen = smb_fh_write(fid, 0, len, (char *)sendbuf);
 220         if (wlen == -1) {
 221                 err = errno;
 222                 printf("write enum req, err=%d\n", err);
 223                 return (err);
 224         }
 225         if (wlen != len) {
 226                 printf("write enum req, short write %d\n", wlen);
 227                 return (EIO);
 228         }
 229 
 230         rlen = smb_fh_read(fid, 0,
 231             sizeof (recvbuf), (char *)recvbuf);
 232         if (rlen == -1) {
 233                 err = errno;
 234                 printf("read enum resp, err=%d\n", err);
 235                 return (err);
 236         }
 237 
 238         /*
 239          * Just dump strings found in the response data.
 240          * Skip the irst 0x90 (RPC wrappers).
 241          */
 242         printf("enum strings\n");
 243         pstrings(recvbuf + 0x90, rlen - 0x90);
 244 
 245         return (0);
 246 }
 247 
 248 int
 249 list_shares(struct smb_ctx *ctx)
 250 {
 251         static char path[] = "/srvsvc";
 252         static uchar_t key[16];
 253         char *server = ctx->ct_srvname;
 254         int err, fd;
 255 
 256         printf("open pipe: %s\n", path);
 257         fd = smb_fh_open(ctx, path, O_RDWR);
 258         if (fd < 0) {
 259                 perror(path);
 260                 return (errno);
 261         }
 262 
 263         /* Test this too. */
 264         err = smb_fh_getssnkey(fd, key, sizeof (key));
 265         if (err) {
 266                 printf("getssnkey: %d\n", err);
 267                 goto out;
 268         }
 269 
 270         err = do_bind(fd);
 271         if (err) {
 272                 printf("do_bind: %d\n", err);
 273                 goto out;
 274         }
 275         err = do_enum(server, fd);
 276         if (err)
 277                 printf("do_enum: %d\n", err);
 278 
 279 out:
 280         smb_fh_close(fd);
 281         return (0);
 282 }