1/*
2   Unix SMB/CIFS implementation.
3   SMB filter/socket plugin
4   Copyright (C) Andrew Tridgell 1999
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 3 of the License, or
9   (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program.  If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#include "includes.h"
21
22#define SECURITY_MASK 0
23#define SECURITY_SET  0
24
25/* this forces non-unicode */
26#define CAPABILITY_MASK 0
27#define CAPABILITY_SET  0
28
29/* and non-unicode for the client too */
30#define CLI_CAPABILITY_MASK 0
31#define CLI_CAPABILITY_SET  0
32
33static char *netbiosname;
34static char packet[BUFFER_SIZE];
35
36static void save_file(const char *fname, void *ppacket, size_t length)
37{
38	int fd;
39	fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0644);
40	if (fd == -1) {
41		perror(fname);
42		return;
43	}
44	if (write(fd, ppacket, length) != length) {
45		fprintf(stderr,"Failed to write %s\n", fname);
46		return;
47	}
48	close(fd);
49	printf("Wrote %ld bytes to %s\n", (unsigned long)length, fname);
50}
51
52static void filter_reply(char *buf)
53{
54	int msg_type = CVAL(buf,0);
55	int type = CVAL(buf,smb_com);
56	unsigned x;
57
58	if (msg_type) return;
59
60	switch (type) {
61
62	case SMBnegprot:
63		/* force the security bits */
64		x = CVAL(buf, smb_vwv1);
65		x = (x | SECURITY_SET) & ~SECURITY_MASK;
66		SCVAL(buf, smb_vwv1, x);
67
68		/* force the capabilities */
69		x = IVAL(buf,smb_vwv9+1);
70		x = (x | CAPABILITY_SET) & ~CAPABILITY_MASK;
71		SIVAL(buf, smb_vwv9+1, x);
72		break;
73
74	}
75}
76
77static void filter_request(char *buf, size_t buf_len)
78{
79	int msg_type = CVAL(buf,0);
80	int type = CVAL(buf,smb_com);
81	unsigned x;
82	fstring name1,name2;
83	int name_len1, name_len2;
84	int name_type1, name_type2;
85
86	if (msg_type) {
87		/* it's a netbios special */
88		switch (msg_type)
89		case 0x81:
90			/* session request */
91			/* inbuf_size is guaranteed to be at least 4. */
92			name_len1 = name_len((unsigned char *)(buf+4),
93					buf_len - 4);
94			if (name_len1 <= 0 || name_len1 > buf_len - 4) {
95				DEBUG(0,("Invalid name length in session request\n"));
96				return;
97			}
98			name_len2 = name_len((unsigned char *)(buf+4+name_len1),
99					buf_len - 4 - name_len1);
100			if (name_len2 <= 0 || name_len2 > buf_len - 4 - name_len1) {
101				DEBUG(0,("Invalid name length in session request\n"));
102				return;
103			}
104
105			name_type1 = name_extract((unsigned char *)buf,
106					buf_len,(unsigned int)4,name1);
107			name_type2 = name_extract((unsigned char *)buf,
108					buf_len,(unsigned int)(4 + name_len1),name2);
109
110			if (name_type1 == -1 || name_type2 == -1) {
111				DEBUG(0,("Invalid name type in session request\n"));
112				return;
113			}
114
115			d_printf("sesion_request: %s -> %s\n",
116				 name1, name2);
117			if (netbiosname) {
118				char *mangled = name_mangle(
119					talloc_tos(), netbiosname, 0x20);
120				if (mangled != NULL) {
121					/* replace the destination netbios
122					 * name */
123					memcpy(buf+4, mangled,
124					       name_len((unsigned char *)mangled,
125							talloc_get_size(mangled)));
126					TALLOC_FREE(mangled);
127				}
128			}
129		return;
130	}
131
132	/* it's an ordinary SMB request */
133	switch (type) {
134	case SMBsesssetupX:
135		/* force the client capabilities */
136		x = IVAL(buf,smb_vwv11);
137		d_printf("SMBsesssetupX cap=0x%08x\n", x);
138		d_printf("pwlen=%d/%d\n", SVAL(buf, smb_vwv7), SVAL(buf, smb_vwv8));
139		system("mv sessionsetup.dat sessionsetup1.dat");
140		save_file("sessionsetup.dat", smb_buf(buf), SVAL(buf, smb_vwv7));
141		x = (x | CLI_CAPABILITY_SET) & ~CLI_CAPABILITY_MASK;
142		SIVAL(buf, smb_vwv11, x);
143		break;
144	}
145}
146
147/****************************************************************************
148 Send an smb to a fd.
149****************************************************************************/
150
151static bool send_smb(int fd, char *buffer)
152{
153	size_t len;
154	size_t nwritten=0;
155	ssize_t ret;
156
157        len = smb_len(buffer) + 4;
158
159	while (nwritten < len) {
160		ret = write_data(fd,buffer+nwritten,len - nwritten);
161		if (ret <= 0) {
162			DEBUG(0,("Error writing %d bytes to client. %d. (%s)\n",
163				(int)len,(int)ret, strerror(errno) ));
164			return false;
165		}
166		nwritten += ret;
167	}
168
169	return true;
170}
171
172static void filter_child(int c, struct sockaddr_storage *dest_ss)
173{
174	NTSTATUS status;
175	int s = -1;
176
177	/* we have a connection from a new client, now connect to the server */
178	status = open_socket_out(dest_ss, 445, LONG_CONNECT_TIMEOUT, &s);
179
180	if (s == -1) {
181		char addr[INET6_ADDRSTRLEN];
182		if (dest_ss) {
183			print_sockaddr(addr, sizeof(addr), dest_ss);
184		}
185
186		d_printf("Unable to connect to %s (%s)\n",
187			 dest_ss?addr:"NULL",strerror(errno));
188		exit(1);
189	}
190
191	while (c != -1 || s != -1) {
192		fd_set fds;
193		int num;
194
195		FD_ZERO(&fds);
196		if (s >= 0 && s < FD_SETSIZE) FD_SET(s, &fds);
197		if (c >= 0 && c < FD_SETSIZE) FD_SET(c, &fds);
198
199		num = sys_select_intr(MAX(s+1, c+1),&fds,NULL,NULL,NULL);
200		if (num <= 0) continue;
201
202		if (c != -1 && FD_ISSET(c, &fds)) {
203			size_t len;
204			if (!NT_STATUS_IS_OK(receive_smb_raw(
205							c, packet, sizeof(packet),
206							0, 0, &len))) {
207				d_printf("client closed connection\n");
208				exit(0);
209			}
210			filter_request(packet, len);
211			if (!send_smb(s, packet)) {
212				d_printf("server is dead\n");
213				exit(1);
214			}
215		}
216		if (s != -1 && FD_ISSET(s, &fds)) {
217			size_t len;
218			if (!NT_STATUS_IS_OK(receive_smb_raw(
219							s, packet, sizeof(packet),
220							0, 0, &len))) {
221				d_printf("server closed connection\n");
222				exit(0);
223			}
224			filter_reply(packet);
225			if (!send_smb(c, packet)) {
226				d_printf("client is dead\n");
227				exit(1);
228			}
229		}
230	}
231	d_printf("Connection closed\n");
232	exit(0);
233}
234
235
236static void start_filter(char *desthost)
237{
238	int s, c;
239	struct sockaddr_storage dest_ss;
240	struct sockaddr_storage my_ss;
241
242	CatchChild();
243
244	/* start listening on port 445 locally */
245
246	zero_sockaddr(&my_ss);
247	s = open_socket_in(SOCK_STREAM, 445, 0, &my_ss, True);
248
249	if (s == -1) {
250		d_printf("bind failed\n");
251		exit(1);
252	}
253
254	if (listen(s, 5) == -1) {
255		d_printf("listen failed\n");
256	}
257
258	if (!resolve_name(desthost, &dest_ss, 0x20, false)) {
259		d_printf("Unable to resolve host %s\n", desthost);
260		exit(1);
261	}
262
263	while (1) {
264		fd_set fds;
265		int num;
266		struct sockaddr_storage ss;
267		socklen_t in_addrlen = sizeof(ss);
268
269		FD_ZERO(&fds);
270		if (s < 0 || s >= FD_SETSIZE) {
271			break;
272		}
273		FD_SET(s, &fds);
274
275		num = sys_select_intr(s+1,&fds,NULL,NULL,NULL);
276		if (num > 0) {
277			c = accept(s, (struct sockaddr *)&ss, &in_addrlen);
278			if (c != -1) {
279				if (fork() == 0) {
280					close(s);
281					filter_child(c, &dest_ss);
282					exit(0);
283				} else {
284					close(c);
285				}
286			}
287		}
288	}
289}
290
291
292int main(int argc, char *argv[])
293{
294	char *desthost;
295	const char *configfile;
296	TALLOC_CTX *frame = talloc_stackframe();
297
298	load_case_tables();
299
300	setup_logging(argv[0],True);
301
302	configfile = get_dyn_CONFIGFILE();
303
304	if (argc < 2) {
305		fprintf(stderr,"smbfilter <desthost> <netbiosname>\n");
306		exit(1);
307	}
308
309	desthost = argv[1];
310	if (argc > 2) {
311		netbiosname = argv[2];
312	}
313
314	if (!lp_load(configfile,True,False,False,True)) {
315		d_printf("Unable to load config file\n");
316	}
317
318	start_filter(desthost);
319	TALLOC_FREE(frame);
320	return 0;
321}
322