/* Unix SMB/Netbios implementation. Version 1.9. SMB client Copyright (C) Andrew Tridgell 1994-1998 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 no_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); } opt.protocol = Protocol = prots[SVAL(inbuf,smb_vwv0)].prot; if (Protocol < PROTOCOL_LANMAN1) { /* no extra params */ } else if (Protocol < PROTOCOL_NT1) { opt.sec_mode = SVAL(inbuf,smb_vwv1); opt.max_xmit = max_xmit = SVAL(inbuf,smb_vwv2); opt.sesskey = IVAL(inbuf,smb_vwv6); opt.serverzone = 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) { opt.rawmode = SVAL(inbuf,smb_vwv5); 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))); opt.max_vcs = SVAL(inbuf,smb_vwv4); DEBUG(3,("max vcs %d\n",opt.max_vcs)); DEBUG(3,("max blk %d\n",SVAL(inbuf,smb_vwv5))); } else { /* NT protocol */ opt.sec_mode = CVAL(inbuf,smb_vwv1); opt.max_xmit = max_xmit = IVAL(inbuf,smb_vwv3+1); opt.sesskey = IVAL(inbuf,smb_vwv7+1); opt.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))); opt.max_vcs = SVAL(inbuf,smb_vwv2+1); DEBUG(3,("max vcs %d\n",opt.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 = ((opt.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: pass = 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; fstrcpy(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 (!(opt.sec_mode & 1)) {fstrcpy(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,opt.max_vcs-1); SIVAL(outbuf,smb_vwv5,opt.sesskey); SSVAL(outbuf,smb_vwv7,passlen); p = smb_buf(outbuf); memcpy(p,pword,passlen); p += passlen; pstrcpy(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,opt.sesskey); SSVAL(outbuf,smb_vwv7,passlen); SSVAL(outbuf,smb_vwv8,0); p = smb_buf(outbuf); memcpy(p,pword,passlen); p += SVAL(outbuf,smb_vwv7); pstrcpy(p,username);p = skip_string(p,1); pstrcpy(p,workgroup);p = skip_string(p,1); pstrcpy(p,"Unix");p = skip_string(p,1); pstrcpy(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")); if (! no_pass) goto get_pass; } /* DEBUG(0,("Session setup failed for username=%s myname=%s destname=%s %s\n", username,myname,desthost,smb_errstr(inbuf))); */ 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)); opt.server_uid = uid = SVAL(inbuf,smb_uid); } /* if (opt.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; fstrcpy(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 ((opt.sec_mode & 1)) { fstrcpy(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; pstrcpy(p, service); p = skip_string(p,1); *p++ = 0x04; memcpy(p,pword,passlen); p += passlen; *p++ = 0x04; pstrcpy(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; pstrcpy(p,service); p = skip_string(p,1); pstrcpy(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))); pstrcpy(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); } opt.max_xmit = max_xmit; opt.tid = cnum; DEBUG(3,("Connected with cnum=%d max_xmit=%d\n",cnum,max_xmit)); if (was_null) { free(inbuf); free(outbuf); } if (options != NULL) { *options = opt; } return True; } /**************************************************************************** send a logout command ****************************************************************************/ void cli_send_logout(char *dum_in, char *dum_out) { 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 } /**************************************************************************** open the client sockets ****************************************************************************/ BOOL cli_open_sockets(int port ) { static int last_port; char *host; pstring service2; extern int Client; if (port == 0) port=last_port; last_port=port; strupper(service); if (*desthost) { host = desthost; } else { pstrcpy(service2,service); host = strtok(service2,"\\/"); if (!host) { DEBUG(0,("Badly formed host name\n")); return(False); } pstrcpy(desthost,host); } if (!(*myname)) { get_myname(myname,NULL); } strupper(myname); DEBUG(3,("Opening sockets\n")); if (!have_ip) { if(!resolve_name( host, &dest_ip)) { DEBUG(0,("cli_open_sockets: 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,NULL)); }