/* Unix SMB/Netbios implementation. Version 1.9. SMB client Copyright (C) Andrew Tridgell 1994-1997 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef SYSLOG #undef SYSLOG #endif #include "includes.h" #ifndef REGISTER #define REGISTER 0 #endif pstring service=""; pstring desthost=""; extern pstring myname; pstring password = ""; pstring smb_login_passwd = ""; pstring username=""; pstring workgroup=WORKGROUP; BOOL got_pass = False; BOOL connect_as_printer = False; BOOL connect_as_ipc = False; char cryptkey[8]; BOOL doencrypt=False; extern pstring user_socket_options; /* 30 second timeout on most commands */ #define CLIENT_TIMEOUT (30*1000) #define SHORT_TIMEOUT (5*1000) int name_type = 0x20; int max_protocol = PROTOCOL_NT1; BOOL readbraw_supported = False; BOOL writebraw_supported = False; extern int DEBUGLEVEL; int cnum = 0; int pid = 0; int gid = 0; int uid = 0; int mid = 0; int max_xmit = BUFFER_SIZE; BOOL have_ip = False; extern struct in_addr dest_ip; extern int Protocol; extern int Client; /**************************************************************************** setup basics in a outgoing packet ****************************************************************************/ void cli_setup_pkt(char *outbuf) { SSVAL(outbuf,smb_pid,pid); SSVAL(outbuf,smb_uid,uid); SSVAL(outbuf,smb_mid,mid); if (Protocol > PROTOCOL_COREPLUS) { SCVAL(outbuf,smb_flg,0x8); SSVAL(outbuf,smb_flg2,0x1); } } /**************************************************************************** call a remote api ****************************************************************************/ BOOL cli_call_api(char *pipe_name, int pipe_name_len, int prcnt,int drcnt, int srcnt, int mprcnt,int mdrcnt, int *rprcnt,int *rdrcnt, char *param,char *data, uint16 *setup, char **rparam,char **rdata) { static char *inbuf=NULL; static char *outbuf=NULL; if (!inbuf) inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); if (!outbuf) outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); if (pipe_name_len == 0) pipe_name_len = strlen(pipe_name); cli_send_trans_request(outbuf,SMBtrans,pipe_name, pipe_name_len, 0,0, data, param, setup, drcnt, prcnt, srcnt, mdrcnt, mprcnt, 0); return (cli_receive_trans_response(inbuf,SMBtrans, rdrcnt,rprcnt, rdata,rparam)); } /**************************************************************************** receive a SMB trans or trans2 response allocating the necessary memory ****************************************************************************/ BOOL cli_receive_trans_response(char *inbuf,int trans, int *data_len,int *param_len, char **data,char **param) { int total_data=0; int total_param=0; int this_data,this_param; *data_len = *param_len = 0; client_receive_smb(Client,inbuf,CLIENT_TIMEOUT); show_msg(inbuf); /* sanity check */ if (CVAL(inbuf,smb_com) != trans) { DEBUG(0,("Expected %s response, got command 0x%02x\n", trans==SMBtrans?"SMBtrans":"SMBtrans2", CVAL(inbuf,smb_com))); return(False); } if (CVAL(inbuf,smb_rcls) != 0) return(False); /* parse out the lengths */ total_data = SVAL(inbuf,smb_tdrcnt); total_param = SVAL(inbuf,smb_tprcnt); /* allocate it */ *data = Realloc(*data,total_data); *param = Realloc(*param,total_param); while (1) { this_data = SVAL(inbuf,smb_drcnt); this_param = SVAL(inbuf,smb_prcnt); if (this_data) memcpy(*data + SVAL(inbuf,smb_drdisp), smb_base(inbuf) + SVAL(inbuf,smb_droff), this_data); if (this_param) memcpy(*param + SVAL(inbuf,smb_prdisp), smb_base(inbuf) + SVAL(inbuf,smb_proff), this_param); *data_len += this_data; *param_len += this_param; /* parse out the total lengths again - they can shrink! */ total_data = SVAL(inbuf,smb_tdrcnt); total_param = SVAL(inbuf,smb_tprcnt); if (total_data <= *data_len && total_param <= *param_len) break; client_receive_smb(Client,inbuf,CLIENT_TIMEOUT); show_msg(inbuf); /* sanity check */ if (CVAL(inbuf,smb_com) != trans) { DEBUG(0,("Expected %s response, got command 0x%02x\n", trans==SMBtrans?"SMBtrans":"SMBtrans2", CVAL(inbuf,smb_com))); return(False); } if (CVAL(inbuf,smb_rcls) != 0) return(False); } return(True); } /**************************************************************************** send a SMB trans or trans2 request ****************************************************************************/ BOOL cli_send_trans_request(char *outbuf,int trans, char *name,int namelen, int fid,int flags, char *data,char *param,uint16 *setup, int ldata,int lparam,int lsetup, int mdata,int mparam,int msetup) { int i; int this_ldata,this_lparam; int tot_data=0,tot_param=0; char *outdata,*outparam; pstring inbuf; char *p; this_lparam = MIN(lparam,max_xmit - (500+lsetup*SIZEOFWORD)); /* hack */ this_ldata = MIN(ldata,max_xmit - (500+lsetup*SIZEOFWORD+this_lparam)); bzero(outbuf,smb_size); set_message(outbuf,14+lsetup,0,True); CVAL(outbuf,smb_com) = trans; SSVAL(outbuf,smb_tid,cnum); cli_setup_pkt(outbuf); outparam = smb_buf(outbuf)+(trans==SMBtrans ? namelen+1 : 3); outdata = outparam+this_lparam; /* primary request */ SSVAL(outbuf,smb_tpscnt,lparam); /* tpscnt */ SSVAL(outbuf,smb_tdscnt,ldata); /* tdscnt */ SSVAL(outbuf,smb_mprcnt,mparam); /* mprcnt */ SSVAL(outbuf,smb_mdrcnt,mdata); /* mdrcnt */ SCVAL(outbuf,smb_msrcnt,msetup); /* msrcnt */ SSVAL(outbuf,smb_flags,flags); /* flags */ SIVAL(outbuf,smb_timeout,0); /* timeout */ SSVAL(outbuf,smb_pscnt,this_lparam); /* pscnt */ SSVAL(outbuf,smb_psoff,smb_offset(outparam,outbuf)); /* psoff */ SSVAL(outbuf,smb_dscnt,this_ldata); /* dscnt */ SSVAL(outbuf,smb_dsoff,smb_offset(outdata,outbuf)); /* dsoff */ SCVAL(outbuf,smb_suwcnt,lsetup); /* suwcnt */ for (i=0;i= numprots)) { DEBUG(0,("SMBnegprot failed. myname=%s destname=%s - %s \n", myname,desthost,smb_errstr(inbuf))); if (was_null) { free(inbuf); free(outbuf); } return(False); } Protocol = prots[SVAL(inbuf,smb_vwv0)].prot; if (Protocol < PROTOCOL_LANMAN1) { /* no extra params */ } else if (Protocol < PROTOCOL_NT1) { sec_mode = SVAL(inbuf,smb_vwv1); max_xmit = SVAL(inbuf,smb_vwv2); sesskey = IVAL(inbuf,smb_vwv6); serverzone = SVALS(inbuf,smb_vwv10)*60; /* this time is converted to GMT by make_unix_date */ servertime = make_unix_date(inbuf+smb_vwv8); if (Protocol >= PROTOCOL_COREPLUS) { readbraw_supported = ((SVAL(inbuf,smb_vwv5) & 0x1) != 0); writebraw_supported = ((SVAL(inbuf,smb_vwv5) & 0x2) != 0); } crypt_len = smb_buflen(inbuf); memcpy(cryptkey,smb_buf(inbuf),8); DEBUG(3,("max mux %d\n",SVAL(inbuf,smb_vwv3))); max_vcs = SVAL(inbuf,smb_vwv4); DEBUG(3,("max vcs %d\n",max_vcs)); DEBUG(3,("max blk %d\n",SVAL(inbuf,smb_vwv5))); } else { /* NT protocol */ sec_mode = CVAL(inbuf,smb_vwv1); max_xmit = IVAL(inbuf,smb_vwv3+1); sesskey = IVAL(inbuf,smb_vwv7+1); serverzone = SVALS(inbuf,smb_vwv15+1)*60; /* this time arrives in real GMT */ servertime = interpret_long_date(inbuf+smb_vwv11+1); crypt_len = CVAL(inbuf,smb_vwv16+1); memcpy(cryptkey,smb_buf(inbuf),8); if (IVAL(inbuf,smb_vwv9+1) & 1) readbraw_supported = writebraw_supported = True; DEBUG(3,("max mux %d\n",SVAL(inbuf,smb_vwv1+1))); max_vcs = SVAL(inbuf,smb_vwv2+1); DEBUG(3,("max vcs %d\n",max_vcs)); DEBUG(3,("max raw %d\n",IVAL(inbuf,smb_vwv5+1))); DEBUG(3,("capabilities 0x%x\n",IVAL(inbuf,smb_vwv9+1))); } DEBUG(3,("Sec mode %d\n",SVAL(inbuf,smb_vwv1))); DEBUG(3,("max xmt %d\n",max_xmit)); DEBUG(3,("Got %d byte crypt key\n",crypt_len)); DEBUG(3,("Chose protocol [%s]\n",prots[SVAL(inbuf,smb_vwv0)].name)); doencrypt = ((sec_mode & 2) != 0); if (servertime) { static BOOL done_time = False; if (!done_time) { DEBUG(1,("Server time is %sTimezone is UTC%+02.1f\n", asctime(LocalTime(&servertime)), -(double)(serverzone/3600.0))); done_time = True; } } get_pass: if (got_pass) pass = password; else pass = (char *)getpass("Password: "); pstrcpy(smb_login_passwd, pass); /* use a blank username for the 2nd try with a blank password */ if (tries++ && !*pass) *username = 0; if (Protocol >= PROTOCOL_LANMAN1 && use_setup) { fstring pword; int passlen = strlen(pass)+1; strcpy(pword,pass); if (doencrypt && *pass) { DEBUG(3,("Using encrypted passwords\n")); passlen = 24; SMBencrypt((uchar *)pass,(uchar *)cryptkey,(uchar *)pword); } /* if in share level security then don't send a password now */ if (!(sec_mode & 1)) {strcpy(pword, "");passlen=1;} /* send a session setup command */ bzero(outbuf,smb_size); if (Protocol < PROTOCOL_NT1) { set_message(outbuf,10,1 + strlen(username) + passlen,True); CVAL(outbuf,smb_com) = SMBsesssetupX; cli_setup_pkt(outbuf); CVAL(outbuf,smb_vwv0) = 0xFF; SSVAL(outbuf,smb_vwv2,max_xmit); SSVAL(outbuf,smb_vwv3,2); SSVAL(outbuf,smb_vwv4,max_vcs-1); SIVAL(outbuf,smb_vwv5,sesskey); SSVAL(outbuf,smb_vwv7,passlen); p = smb_buf(outbuf); memcpy(p,pword,passlen); p += passlen; strcpy(p,username); } else { if (!doencrypt) passlen--; /* for Win95 */ set_message(outbuf,13,0,True); CVAL(outbuf,smb_com) = SMBsesssetupX; cli_setup_pkt(outbuf); CVAL(outbuf,smb_vwv0) = 0xFF; SSVAL(outbuf,smb_vwv2,BUFFER_SIZE); SSVAL(outbuf,smb_vwv3,2); SSVAL(outbuf,smb_vwv4,getpid()); SIVAL(outbuf,smb_vwv5,sesskey); SSVAL(outbuf,smb_vwv7,passlen); SSVAL(outbuf,smb_vwv8,0); p = smb_buf(outbuf); memcpy(p,pword,passlen); p += SVAL(outbuf,smb_vwv7); strcpy(p,username);p = skip_string(p,1); strcpy(p,workgroup);p = skip_string(p,1); strcpy(p,"Unix");p = skip_string(p,1); strcpy(p,"Samba");p = skip_string(p,1); set_message(outbuf,13,PTR_DIFF(p,smb_buf(outbuf)),False); } send_smb(Client,outbuf); client_receive_smb(Client,inbuf,CLIENT_TIMEOUT); show_msg(inbuf); if (CVAL(inbuf,smb_rcls) != 0) { if (! *pass && ((CVAL(inbuf,smb_rcls) == ERRDOS && SVAL(inbuf,smb_err) == ERRnoaccess) || (CVAL(inbuf,smb_rcls) == ERRSRV && SVAL(inbuf,smb_err) == ERRbadpw))) { got_pass = False; DEBUG(3,("resending login\n")); goto get_pass; } DEBUG(0,("Session setup failed for username=%s myname=%s destname=%s %s\n", username,myname,desthost,smb_errstr(inbuf))); DEBUG(0,("You might find the -U, -W or -n options useful\n")); DEBUG(0,("Sometimes you have to use `-n USERNAME' (particularly with OS/2)\n")); DEBUG(0,("Some servers also insist on uppercase-only passwords\n")); if (was_null) { free(inbuf); free(outbuf); } return(False); } if (Protocol >= PROTOCOL_NT1) { char *domain,*os,*lanman; p = smb_buf(inbuf); os = p; lanman = skip_string(os,1); domain = skip_string(lanman,1); if (*domain || *os || *lanman) DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",domain,os,lanman)); } /* use the returned uid from now on */ if (SVAL(inbuf,smb_uid) != uid) DEBUG(3,("Server gave us a UID of %d. We gave %d\n", SVAL(inbuf,smb_uid),uid)); uid = SVAL(inbuf,smb_uid); } if (sec_mode & 1) { if (SVAL(inbuf, smb_vwv2) & 1) DEBUG(1,("connected as guest ")); DEBUG(1,("security=user\n")); } else { DEBUG(1,("security=share\n")); } /* now we've got a connection - send a tcon message */ bzero(outbuf,smb_size); if (strncmp(service,"\\\\",2) != 0) { DEBUG(0,("\nWarning: Your service name doesn't start with \\\\. This is probably incorrect.\n")); DEBUG(0,("Perhaps try replacing each \\ with \\\\ on the command line?\n\n")); } again2: { int passlen = strlen(pass)+1; fstring pword; strcpy(pword,pass); if (doencrypt && *pass) { passlen=24; SMBencrypt((uchar *)pass,(uchar *)cryptkey,(uchar *)pword); } /* if in user level security then don't send a password now */ if ((sec_mode & 1)) { strcpy(pword, ""); passlen=1; } if (Protocol <= PROTOCOL_COREPLUS) { set_message(outbuf,0,6 + strlen(service) + passlen + strlen(dev),True); CVAL(outbuf,smb_com) = SMBtcon; cli_setup_pkt(outbuf); p = smb_buf(outbuf); *p++ = 0x04; strcpy(p, service); p = skip_string(p,1); *p++ = 0x04; memcpy(p,pword,passlen); p += passlen; *p++ = 0x04; strcpy(p, dev); } else { set_message(outbuf,4,2 + strlen(service) + passlen + strlen(dev),True); CVAL(outbuf,smb_com) = SMBtconX; cli_setup_pkt(outbuf); SSVAL(outbuf,smb_vwv0,0xFF); SSVAL(outbuf,smb_vwv3,passlen); p = smb_buf(outbuf); memcpy(p,pword,passlen); p += passlen; strcpy(p,service); p = skip_string(p,1); strcpy(p,dev); } } send_smb(Client,outbuf); client_receive_smb(Client,inbuf,CLIENT_TIMEOUT); /* trying again with a blank password */ if (CVAL(inbuf,smb_rcls) != 0 && (int)strlen(pass) > 0 && !doencrypt && Protocol >= PROTOCOL_LANMAN1) { DEBUG(2,("first SMBtconX failed, trying again. %s\n",smb_errstr(inbuf))); strcpy(pass,""); goto again2; } if (CVAL(inbuf,smb_rcls) != 0) { DEBUG(0,("SMBtconX failed. %s\n",smb_errstr(inbuf))); DEBUG(0,("Perhaps you are using the wrong sharename, username or password?\n")); DEBUG(0,("Some servers insist that these be in uppercase\n")); if (was_null) { free(inbuf); free(outbuf); } return(False); } if (Protocol <= PROTOCOL_COREPLUS) { max_xmit = SVAL(inbuf,smb_vwv0); cnum = SVAL(inbuf,smb_vwv1); } else { max_xmit = MIN(max_xmit,BUFFER_SIZE-4); if (max_xmit <= 0) max_xmit = BUFFER_SIZE - 4; cnum = SVAL(inbuf,smb_tid); } DEBUG(3,("Connected with cnum=%d max_xmit=%d\n",cnum,max_xmit)); if (was_null) { free(inbuf); free(outbuf); } return True; } /**************************************************************************** send a logout command ****************************************************************************/ void cli_send_logout(void ) { pstring inbuf,outbuf; DEBUG(5,("cli_send_logout\n")); bzero(outbuf,smb_size); set_message(outbuf,0,0,True); CVAL(outbuf,smb_com) = SMBtdis; SSVAL(outbuf,smb_tid,cnum); cli_setup_pkt(outbuf); send_smb(Client,outbuf); client_receive_smb(Client,inbuf,SHORT_TIMEOUT); if (CVAL(inbuf,smb_rcls) != 0) { DEBUG(0,("SMBtdis failed %s\n",smb_errstr(inbuf))); } #ifdef STATS stats_report(); #endif exit(0); } /**************************************************************************** open the client sockets ****************************************************************************/ BOOL cli_open_sockets(int port ) { static int last_port; char *host; pstring service2; extern int Client; BOOL failed = True; if (port == 0) port=last_port; last_port=port; strupper(service); if (*desthost) { host = desthost; } else { strcpy(service2,service); host = strtok(service2,"\\/"); if (!host) { DEBUG(0,("Badly formed host name\n")); return(False); } strcpy(desthost,host); } if (!(*myname)) { get_myname(myname,NULL); } strupper(myname); DEBUG(3,("Opening sockets\n")); if (!have_ip) { struct hostent *hp; if ((hp = Get_Hostbyname(host))) { putip((char *)&dest_ip,(char *)hp->h_addr); failed = False; } else { #ifdef USENMB /* Try and resolve the name with the netbios server */ int bcast, count; struct in_addr *ip_list; if ((bcast = open_socket_in(SOCK_DGRAM, 0, 3, interpret_addr(lp_socket_address()))) != -1) { set_socket_options(bcast, "SO_BROADCAST"); if ((ip_list = name_query(bcast, host, name_type, True, True, *iface_bcast(dest_ip), &count,0)) { dest_ip = ip_list[0]; free(ip_list); failed = False; } close (bcast); } #endif if (failed) { DEBUG(0,("Get_Hostbyname: Unknown host %s.\n",host)); return False; } } } Client = open_socket_out(SOCK_STREAM, &dest_ip, port, LONG_CONNECT_TIMEOUT); if (Client == -1) return False; DEBUG(3,("Connected\n")); set_socket_options(Client,user_socket_options); return True; } /**************************************************************************** close and open the connection again ****************************************************************************/ BOOL cli_reopen_connection(char *inbuf,char *outbuf) { static int open_count=0; open_count++; if (open_count>5) return(False); DEBUG(1,("Trying to re-open connection\n")); set_message(outbuf,0,0,True); SCVAL(outbuf,smb_com,SMBtdis); SSVAL(outbuf,smb_tid,cnum); cli_setup_pkt(outbuf); send_smb(Client,outbuf); client_receive_smb(Client,inbuf,SHORT_TIMEOUT); close_sockets(); if (!cli_open_sockets(0)) return(False); return(cli_send_login(inbuf,outbuf,True,True)); }