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