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