diff options
Diffstat (limited to 'source/smbd/reply.c')
-rw-r--r-- | source/smbd/reply.c | 1483 |
1 files changed, 1088 insertions, 395 deletions
diff --git a/source/smbd/reply.c b/source/smbd/reply.c index b7b51775bb8..2c646d99f5b 100644 --- a/source/smbd/reply.c +++ b/source/smbd/reply.c @@ -2,7 +2,7 @@ Unix SMB/Netbios implementation. Version 1.9. Main SMB reply routines - Copyright (C) Andrew Tridgell 1992-1995 + Copyright (C) Andrew Tridgell 1992-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 @@ -25,21 +25,25 @@ #include "includes.h" -#include "loadparm.h" #include "trans2.h" +#include "nterr.h" /* look in server.c for some explanation of these variables */ extern int Protocol; extern int DEBUGLEVEL; -extern int chain_size; -extern int maxxmit; +extern int max_send; +extern int max_recv; extern int chain_fnum; extern char magic_char; extern connection_struct Connections[]; extern files_struct Files[]; extern BOOL case_sensitive; +extern BOOL case_preserve; +extern BOOL short_case_preserve; extern pstring sesssetup_user; +extern fstring myworkgroup; extern int Client; +extern int global_oplock_break; /* this macro should always be used to extract an fnum (smb_fid) from a packet to ensure chaining works correctly */ @@ -47,62 +51,102 @@ a packet to ensure chaining works correctly */ /**************************************************************************** - reply to an special message +report a possible attack via the password buffer overflow bug ****************************************************************************/ -int reply_special(char *inbuf,char *outbuf) +static void overflow_attack(int len) { - int outsize = 4; - int msg_type = CVAL(inbuf,0); - int msg_flags = CVAL(inbuf,1); - pstring name1,name2; - extern fstring remote_machine; - extern fstring local_machine; - char *p; + DEBUG(0,("ERROR: Invalid password length %d\n", len)); + DEBUG(0,("your machine may be under attack by a user exploiting an old bug\n")); + DEBUG(0,("Attack was from IP=%s\n", client_addr())); + exit_server("possible attack"); +} - *name1 = *name2 = 0; - smb_setlen(outbuf,0); +/**************************************************************************** + reply to an special message +****************************************************************************/ +int reply_special(char *inbuf,char *outbuf) +{ + int outsize = 4; + int msg_type = CVAL(inbuf,0); + int msg_flags = CVAL(inbuf,1); + pstring name1,name2; + extern fstring remote_machine; + extern fstring local_machine; + char *p; + int len; + char name_type = 0; + + *name1 = *name2 = 0; + + smb_setlen(outbuf,0); + + switch (msg_type) { + case 0x81: /* session request */ + CVAL(outbuf,0) = 0x82; + CVAL(outbuf,3) = 0; + if (name_len(inbuf+4) > 50 || + name_len(inbuf+4 + name_len(inbuf + 4)) > 50) { + DEBUG(0,("Invalid name length in session request\n")); + return(0); + } + name_extract(inbuf,4,name1); + name_extract(inbuf,4 + name_len(inbuf + 4),name2); + DEBUG(2,("netbios connect: name1=%s name2=%s\n", + name1,name2)); + + fstrcpy(remote_machine,name2); + trim_string(remote_machine," "," "); + p = strchr(remote_machine,' '); + strlower(remote_machine); + if (p) *p = 0; + + fstrcpy(local_machine,name1); + trim_string(local_machine," "," "); + len = strlen(local_machine); + if (len == 16) { + name_type = local_machine[15]; + local_machine[15] = 0; + } + p = strchr(local_machine,' '); + strlower(local_machine); + if (p) *p = 0; + + if (name_type == 'R') { + /* We are being asked for a pathworks session --- + no thanks! */ + CVAL(outbuf, 0) = 0x83; + break; + } - switch (msg_type) - { - case 0x81: /* session request */ - CVAL(outbuf,0) = 0x82; - CVAL(outbuf,3) = 0; - if (name_len(inbuf+4) > 50) - { - DEBUG(0,("Invalid name length in session request\n")); - return(0); + add_session_user(remote_machine); + + reload_services(True); + reopen_logs(); + + break; + + case 0x89: /* session keepalive request + (some old clients produce this?) */ + CVAL(outbuf,0) = 0x85; + CVAL(outbuf,3) = 0; + break; + + case 0x82: /* positive session response */ + case 0x83: /* negative session response */ + case 0x84: /* retarget session response */ + DEBUG(0,("Unexpected session response\n")); + break; + + case 0x85: /* session keepalive */ + default: + return(0); } - name_extract(inbuf,4,name1); - name_extract(inbuf,4 + name_len(inbuf + 4),name2); - DEBUG(2,("netbios connect: name1=%s name2=%s\n",name1,name2)); - - strcpy(remote_machine,name2); - trim_string(remote_machine," "," "); - p = strchr(remote_machine,' '); - strlower(remote_machine); - if (p) *p = 0; - - strcpy(local_machine,name1); - trim_string(local_machine," "," "); - p = strchr(local_machine,' '); - strlower(local_machine); - if (p) *p = 0; - - add_session_user(remote_machine); - - reload_services(True); - reopen_logs(); - - break; - case 0x85: /* session keepalive */ - default: - return(0); - } - - DEBUG(5,("%s init msg_type=0x%x msg_flags=0x%x\n",timestring(),msg_type,msg_flags)); - - return(outsize); + + DEBUG(5,("%s init msg_type=0x%x msg_flags=0x%x\n", + timestring(),msg_type,msg_flags)); + + return(outsize); } @@ -132,6 +176,44 @@ static int connection_error(char *inbuf,char *outbuf,int connection_num) } + +/**************************************************************************** + parse a share descriptor string +****************************************************************************/ +static void parse_connect(char *p,char *service,char *user, + char *password,int *pwlen,char *dev) +{ + char *p2; + + DEBUG(4,("parsing connect string %s\n",p)); + + p2 = strrchr(p,'\\'); + if (p2 == NULL) + fstrcpy(service,p); + else + fstrcpy(service,p2+1); + + p += strlen(p) + 2; + + fstrcpy(password,p); + *pwlen = strlen(password); + + p += strlen(p) + 2; + + fstrcpy(dev,p); + + *user = 0; + p = strchr(service,'%'); + if (p != NULL) + { + *p = 0; + fstrcpy(user,p+1); + } +} + + + + /**************************************************************************** reply to a tcon ****************************************************************************/ @@ -143,15 +225,12 @@ int reply_tcon(char *inbuf,char *outbuf) pstring dev; int connection_num; int outsize = 0; - int uid = SVAL(inbuf,smb_uid); - int vuid; - int pwlen; + uint16 vuid = SVAL(inbuf,smb_uid); + int pwlen=0; *service = *user = *password = *dev = 0; - vuid = valid_uid(uid); - - parse_connect(inbuf,service,user,password,&pwlen,dev); + parse_connect(smb_buf(inbuf)+1,service,user,password,&pwlen,dev); connection_num = make_connection(service,user,password,pwlen,dev,vuid); @@ -159,7 +238,7 @@ int reply_tcon(char *inbuf,char *outbuf) return(connection_error(inbuf,outbuf,connection_num)); outsize = set_message(outbuf,2,0,True); - SSVAL(outbuf,smb_vwv0,maxxmit); + SSVAL(outbuf,smb_vwv0,max_recv); SSVAL(outbuf,smb_vwv1,connection_num); SSVAL(outbuf,smb_tid,connection_num); @@ -179,39 +258,44 @@ int reply_tcon_and_X(char *inbuf,char *outbuf,int length,int bufsize) pstring password; pstring devicename; int connection_num; - int outsize = 0; - int uid = SVAL(inbuf,smb_uid); - int vuid; - int smb_com2 = SVAL(inbuf,smb_vwv0); - int smb_off2 = SVAL(inbuf,smb_vwv1); + uint16 vuid = SVAL(inbuf,smb_uid); int passlen = SVAL(inbuf,smb_vwv3); + BOOL doencrypt = SMBENCRYPT(); *service = *user = *password = *devicename = 0; /* we might have to close an old one */ if ((SVAL(inbuf,smb_vwv2) & 0x1) != 0) - close_cnum(SVAL(inbuf,smb_tid),uid); - - vuid = valid_uid(uid); + close_cnum(SVAL(inbuf,smb_tid),vuid); + + if (passlen > MAX_PASS_LEN) { + overflow_attack(passlen); + } { char *path; char *p; memcpy(password,smb_buf(inbuf),passlen); - password[passlen]=0; + password[passlen]=0; path = smb_buf(inbuf) + passlen; - DEBUG(4,("parsing net-path %s, passlen=%d\n",path,passlen)); - strcpy(service,path+2); + + if (!doencrypt || passlen != 24) { + if (strequal(password," ")) + *password = 0; + passlen = strlen(password); + } + + fstrcpy(service,path+2); p = strchr(service,'\\'); if (!p) return(ERROR(ERRSRV,ERRinvnetname)); *p = 0; - strcpy(service,p+1); + fstrcpy(service,p+1); p = strchr(service,'%'); if (p) { *p++ = 0; - strcpy(user,p); + fstrcpy(user,p); } StrnCpy(devicename,path + strlen(path) + 1,6); DEBUG(4,("Got device type %s\n",devicename)); @@ -222,7 +306,26 @@ int reply_tcon_and_X(char *inbuf,char *outbuf,int length,int bufsize) if (connection_num < 0) return(connection_error(inbuf,outbuf,connection_num)); - outsize = set_message(outbuf,2,strlen(devicename)+1,True); + if (Protocol < PROTOCOL_NT1) + { + set_message(outbuf,2,strlen(devicename)+1,True); + strcpy(smb_buf(outbuf),devicename); + } + else + { + char *fsname = "SAMBA"; + char *p; + + set_message(outbuf,3,3,True); + + p = smb_buf(outbuf); + strcpy(p,devicename); p = skip_string(p,1); /* device name */ + strcpy(p,fsname); p = skip_string(p,1); /* filesystem type e.g NTFS */ + + set_message(outbuf,3,PTR_DIFF(p,smb_buf(outbuf)),False); + + SSVAL(outbuf, smb_vwv2, 0x0); /* optional support */ + } DEBUG(3,("%s tconX service=%s user=%s cnum=%d\n",timestring(),service,user,connection_num)); @@ -230,17 +333,7 @@ int reply_tcon_and_X(char *inbuf,char *outbuf,int length,int bufsize) SSVAL(inbuf,smb_tid,connection_num); SSVAL(outbuf,smb_tid,connection_num); - CVAL(outbuf,smb_vwv0) = smb_com2; - SSVAL(outbuf,smb_vwv1,(chain_size + outsize)-4); - - strcpy(smb_buf(outbuf),devicename); - - if (smb_com2 != 0xFF) - outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, - outbuf,outbuf+outsize, - length,bufsize); - - return(outsize); + return chain_reply(inbuf,outbuf,length,bufsize); } @@ -269,8 +362,13 @@ int reply_unknown(char *inbuf,char *outbuf) int reply_ioctl(char *inbuf,char *outbuf) { DEBUG(3,("ignoring ioctl\n")); - +#if 0 + /* we just say it succeeds and hope its all OK. + some day it would be nice to interpret them individually */ + return set_message(outbuf,1,0,True); +#else return(ERROR(ERRSRV,ERRnosupport)); +#endif } @@ -279,28 +377,28 @@ reply to a session setup command ****************************************************************************/ int reply_sesssetup_and_X(char *inbuf,char *outbuf,int length,int bufsize) { - int outsize = 0; - int sess_uid; + uint16 sess_vuid; int gid; - int smb_com2; - int smb_off2; + int uid; int smb_bufsize; int smb_mpxmax; int smb_vc_num; uint32 smb_sesskey; - int smb_apasslen; + int smb_apasslen = 0; pstring smb_apasswd; int smb_ntpasslen = 0; pstring smb_ntpasswd; BOOL valid_nt_password = False; pstring user; BOOL guest=False; + BOOL computer_id=False; + static BOOL done_sesssetup = False; + BOOL doencrypt = SMBENCRYPT(); + char *domain = ""; *smb_apasswd = 0; + *smb_ntpasswd = 0; - sess_uid = SVAL(inbuf,smb_uid); - smb_com2 = CVAL(inbuf,smb_vwv0); - smb_off2 = SVAL(inbuf,smb_vwv1); smb_bufsize = SVAL(inbuf,smb_vwv2); smb_mpxmax = SVAL(inbuf,smb_vwv3); smb_vc_num = SVAL(inbuf,smb_vwv4); @@ -308,16 +406,48 @@ int reply_sesssetup_and_X(char *inbuf,char *outbuf,int length,int bufsize) if (Protocol < PROTOCOL_NT1) { smb_apasslen = SVAL(inbuf,smb_vwv7); + if (smb_apasslen > MAX_PASS_LEN) + { + overflow_attack(smb_apasslen); + } + memcpy(smb_apasswd,smb_buf(inbuf),smb_apasslen); - StrnCpy(user,smb_buf(inbuf)+smb_apasslen,sizeof(user)-1); + pstrcpy(user,smb_buf(inbuf)+smb_apasslen); + + if (lp_security() != SEC_SERVER && !doencrypt) { + smb_apasslen = strlen(smb_apasswd); + } } else { uint16 passlen1 = SVAL(inbuf,smb_vwv7); uint16 passlen2 = SVAL(inbuf,smb_vwv8); - BOOL doencrypt = SMBENCRYPT(); - char *p = smb_buf(inbuf); - if (passlen1 > 256) passlen1 = 0; - if (passlen2 > 256) passlen2 = 0; /* I don't know why NT gives weird - lengths sometimes */ + uint32 client_caps = IVAL(inbuf,smb_vwv11); + enum remote_arch_types ra_type = get_remote_arch(); + + char *p = smb_buf(inbuf); + + /* client_caps is used as final determination if client is NT or Win95. + This is needed to return the correct error codes in some + circumstances. + */ + + if(ra_type == RA_WINNT || ra_type == RA_WIN95) + { + if(client_caps & (CAP_NT_SMBS | CAP_STATUS32)) + set_remote_arch( RA_WINNT); + else + set_remote_arch( RA_WIN95); + } + + if (passlen1 != 24 && passlen2 != 24) + doencrypt = False; + + if (passlen1 > MAX_PASS_LEN) { + overflow_attack(passlen1); + } + + passlen1 = MIN(passlen1, MAX_PASS_LEN); + passlen2 = MIN(passlen2, MAX_PASS_LEN); + if(doencrypt) { /* Save the lanman2 password and the NT md4 password. */ smb_apasslen = passlen1; @@ -325,31 +455,99 @@ int reply_sesssetup_and_X(char *inbuf,char *outbuf,int length,int bufsize) smb_ntpasslen = passlen2; memcpy(smb_ntpasswd,p+passlen1,smb_ntpasslen); } else { - /* for Win95 */ - if (passlen1 > passlen2) { - smb_apasslen = passlen1; - StrnCpy(smb_apasswd,p,smb_apasslen); - } else { - smb_apasslen = passlen2; - StrnCpy(smb_apasswd,p + passlen1,smb_apasslen); + /* both Win95 and WinNT stuff up the password lengths for + non-encrypting systems. Uggh. + + if passlen1==24 its a win95 system, and its setting the + password length incorrectly. Luckily it still works with the + default code because Win95 will null terminate the password + anyway + + if passlen1>0 and passlen2>0 then maybe its a NT box and its + setting passlen2 to some random value which really stuffs + things up. we need to fix that one. */ + if (passlen1 > 0 && passlen2 > 0 && passlen2 != 24 && + passlen2 != 1) { + passlen2 = 0; + } + /* we use the first password that they gave */ + smb_apasslen = passlen1; + StrnCpy(smb_apasswd,p,smb_apasslen); + + /* trim the password */ + smb_apasslen = strlen(smb_apasswd); + + /* wfwg sometimes uses a space instead of a null */ + if (strequal(smb_apasswd," ")) { + smb_apasslen = 0; + *smb_apasswd = 0; } } - if (passlen2 == 1) { - /* apparently NT sometimes sets passlen2 to 1 when it means 0. This - tries to work around that problem */ - passlen2 = 0; - } + p += passlen1 + passlen2; - strcpy(user,p); p = skip_string(p,1); + fstrcpy(user,p); p = skip_string(p,1); + domain = p; + DEBUG(3,("Domain=[%s] NativeOS=[%s] NativeLanMan=[%s]\n", - p,skip_string(p,1),skip_string(p,2))); + domain,skip_string(p,1),skip_string(p,2))); } DEBUG(3,("sesssetupX:name=[%s]\n",user)); + /* If name ends in $ then I think it's asking about whether a */ + /* computer with that name (minus the $) has access. For now */ + /* say yes to everything ending in $. */ + if (user[strlen(user) - 1] == '$') + { +#ifdef NTDOMAIN + struct smb_passwd *smb_pass; /* To check if machine account exists */ +/* + PAXX: Ack. We don't want to do this. The workstation trust account + with a $ on the end should exist in the local password database + or be mapped to something generic, but not modified. For NT + domain support we must reject this used in certain circumstances + with a code to indicate to the client that it is an invalid use + of a workstation trust account. NTWKS needs this error to join + a domain. This may be the source of future bugs if we cannot + be sure whether to reject this or not. +*/ + /* non-null user name indicates search by username not by smb userid */ + smb_pass = get_smbpwd_entry(user, 0); + + if (!smb_pass) + { + /* lkclXXXX: if workstation entry doesn't exist, indicate logon failure */ + DEBUG(4,("Workstation trust account %s doesn't exist.",user)); + SSVAL(outbuf, smb_flg2, 0xc003); /* PAXX: Someone please unhack this */ + CVAL(outbuf, smb_reh) = 1; /* PAXX: Someone please unhack this */ + return(ERROR(NT_STATUS_LOGON_FAILURE, 0xc000)); /* decimal 109 NT error, 0xc000 */ + } + else + { + /* PAXX: This is the NO LOGON workstation trust account stuff */ + /* lkclXXXX: if the workstation *does* exist, indicate failure differently! */ + DEBUG(4,("No Workstation trust account %s",user)); + SSVAL(outbuf, smb_flg2, 0xc003); /* PAXX: Someone please unhack this */ + CVAL(outbuf, smb_reh) = 1; /* PAXX: Someone please unhack this */ + return(ERROR(NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT, 0xc000)); /* decimal 409 NT error, 0xc000 */ + } + + computer_id = True; +#else /* not NTDOMAIN, leave this in. PAXX: Someone get rid of this */ + user[strlen(user) - 1] = '\0'; +#endif + } + + + /* If no username is sent use the guest account */ if (!*user) - strcpy(user,lp_guestaccount(-1)); + { + strcpy(user,lp_guestaccount(-1)); + /* If no user and no password then set guest flag. */ + if( *smb_apasswd == 0) + guest = True; + } strlower(user); @@ -359,27 +557,35 @@ int reply_sesssetup_and_X(char *inbuf,char *outbuf,int length,int bufsize) add_session_user(user); + /* Check if the given username was the guest user with no password. + We need to do this check after add_session_user() as that + call can potentially change the username (via map_user). + */ - if (!(lp_security() == SEC_SERVER && server_validate(inbuf)) && + if(!guest && strequal(user,lp_guestaccount(-1)) && (*smb_apasswd == 0)) + guest = True; + + if (!guest && !(lp_security() == SEC_SERVER && + server_validate(user, domain, + smb_apasswd, smb_apasslen, + smb_ntpasswd, smb_ntpasslen)) && !check_hosts_equiv(user)) { - if (strequal(user,lp_guestaccount(-1)) && (*smb_apasswd == 0)) - guest = True; - /* now check if it's a valid username/password */ /* If an NT password was supplied try and validate with that - first. This is superior as the passwords are mixed case 128 length unicode */ - if(smb_ntpasslen && !guest) + first. This is superior as the passwords are mixed case + 128 length unicode */ + if(smb_ntpasslen) { - if(!password_ok(user,smb_ntpasswd,smb_ntpasslen,NULL,True)) + if(!password_ok(user,smb_ntpasswd,smb_ntpasslen,NULL)) DEBUG(0,("NT Password did not match ! Defaulting to Lanman\n")); else valid_nt_password = True; } - if (!valid_nt_password && !guest && !password_ok(user,smb_apasswd,smb_apasslen,NULL,False)) + if (!valid_nt_password && !password_ok(user,smb_apasswd,smb_apasslen,NULL)) { - if (lp_security() >= SEC_USER) { + if (!computer_id && lp_security() >= SEC_USER) { #if (GUEST_SESSSETUP == 0) return(ERROR(ERRSRV,ERRbadpw)); #endif @@ -413,15 +619,15 @@ int reply_sesssetup_and_X(char *inbuf,char *outbuf,int length,int bufsize) /* it's ok - setup a reply */ if (Protocol < PROTOCOL_NT1) { - outsize = set_message(outbuf,3,0,True); + set_message(outbuf,3,0,True); } else { char *p; - outsize = set_message(outbuf,3,3,True); + set_message(outbuf,3,3,True); p = smb_buf(outbuf); strcpy(p,"Unix"); p = skip_string(p,1); strcpy(p,"Samba "); strcat(p,VERSION); p = skip_string(p,1); - strcpy(p,my_workgroup()); p = skip_string(p,1); - outsize = set_message(outbuf,3,PTR_DIFF(p,smb_buf(outbuf)),False); + strcpy(p,myworkgroup); p = skip_string(p,1); + set_message(outbuf,3,PTR_DIFF(p,smb_buf(outbuf)),False); /* perhaps grab OS version here?? */ } @@ -436,28 +642,27 @@ int reply_sesssetup_and_X(char *inbuf,char *outbuf,int length,int bufsize) return(ERROR(ERRSRV,ERRbadpw)); } gid = pw->pw_gid; - SSVAL(outbuf,smb_uid,(uint16)pw->pw_uid); - SSVAL(inbuf,smb_uid,(uint16)pw->pw_uid); + uid = pw->pw_uid; } - CVAL(outbuf,smb_vwv0) = smb_com2; - SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4); - - if (guest) + if (guest && !computer_id) SSVAL(outbuf,smb_vwv2,1); /* register the name and uid as being validated, so further connections to a uid can get through without a password, on the same VC */ - register_uid(SVAL(inbuf,smb_uid),gid,user,guest); + sess_vuid = register_vuid(uid,gid,user,guest); - maxxmit = MIN(maxxmit,smb_bufsize); + SSVAL(outbuf,smb_uid,sess_vuid); + SSVAL(inbuf,smb_uid,sess_vuid); - if (smb_com2 != 0xFF) - outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, - outbuf,outbuf+outsize, - length,bufsize); + if (!done_sesssetup) + max_send = MIN(max_send,smb_bufsize); - return(outsize); + DEBUG(6,("Client requested max send size of %d\n", max_send)); + + done_sesssetup = True; + + return chain_reply(inbuf,outbuf,length,bufsize); } @@ -470,11 +675,12 @@ int reply_chkpth(char *inbuf,char *outbuf) int cnum,mode; pstring name; BOOL ok = False; - + BOOL bad_path = False; + cnum = SVAL(inbuf,smb_tid); - strcpy(name,smb_buf(inbuf) + 1); - unix_convert(name,cnum); + pstrcpy(name,smb_buf(inbuf) + 1); + unix_convert(name,cnum,0,&bad_path); mode = SVAL(inbuf,smb_vwv0); @@ -482,8 +688,31 @@ int reply_chkpth(char *inbuf,char *outbuf) ok = directory_exist(name,NULL); if (!ok) - return(ERROR(ERRDOS,ERRbadpath)); - + { + /* We special case this - as when a Windows machine + is parsing a path is steps through the components + one at a time - if a component fails it expects + ERRbadpath, not ERRbadfile. + */ + if(errno == ENOENT) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + +#if 0 + /* Ugly - NT specific hack - maybe not needed ? (JRA) */ + if((errno == ENOTDIR) && (Protocol >= PROTOCOL_NT1) && + (get_remote_arch() == RA_WINNT)) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbaddirectory; + } +#endif + + return(UNIXERROR(ERRDOS,ERRbadpath)); + } + outsize = set_message(outbuf,0,0,True); DEBUG(3,("%s chkpth %s cnum=%d mode=%d\n",timestring(),name,cnum,mode)); @@ -505,11 +734,12 @@ int reply_getatr(char *inbuf,char *outbuf) int mode=0; uint32 size=0; time_t mtime=0; - + BOOL bad_path = False; + cnum = SVAL(inbuf,smb_tid); - strcpy(fname,smb_buf(inbuf) + 1); - unix_convert(fname,cnum); + pstrcpy(fname,smb_buf(inbuf) + 1); + unix_convert(fname,cnum,0,&bad_path); /* dos smetimes asks for a stat of "" - it returns a "hidden directory" under WfWg - weird! */ @@ -523,23 +753,31 @@ int reply_getatr(char *inbuf,char *outbuf) } else if (check_name(fname,cnum)) + { + if (sys_stat(fname,&sbuf) == 0) { - if (sys_stat(fname,&sbuf) == 0) - { - mode = dos_mode(cnum,fname,&sbuf); - size = sbuf.st_size; - mtime = sbuf.st_mtime; - if (mode & aDIR) - size = 0; - ok = True; - } - else - DEBUG(3,("stat of %s failed (%s)\n",fname,strerror(errno))); + mode = dos_mode(cnum,fname,&sbuf); + size = sbuf.st_size; + mtime = sbuf.st_mtime; + if (mode & aDIR) + size = 0; + ok = True; + } + else + DEBUG(3,("stat of %s failed (%s)\n",fname,strerror(errno))); } if (!ok) + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + return(UNIXERROR(ERRDOS,ERRbadfile)); - + } + outsize = set_message(outbuf,10,0,True); SSVAL(outbuf,smb_vwv0,mode); @@ -550,7 +788,7 @@ int reply_getatr(char *inbuf,char *outbuf) char *p = strrchr(fname,'/'); uint16 flg2 = SVAL(outbuf,smb_flg2); if (!p) p = fname; - if (!is_8_3(fname)) + if (!is_8_3(fname, True)) SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */ } @@ -571,11 +809,12 @@ int reply_setatr(char *inbuf,char *outbuf) BOOL ok=False; int mode; time_t mtime; - + BOOL bad_path = False; + cnum = SVAL(inbuf,smb_tid); - strcpy(fname,smb_buf(inbuf) + 1); - unix_convert(fname,cnum); + pstrcpy(fname,smb_buf(inbuf) + 1); + unix_convert(fname,cnum,0,&bad_path); mode = SVAL(inbuf,smb_vwv0); mtime = make_unix_date3(inbuf+smb_vwv1); @@ -585,11 +824,19 @@ int reply_setatr(char *inbuf,char *outbuf) if (check_name(fname,cnum)) ok = (dos_chmod(cnum,fname,mode,NULL) == 0); if (ok) - ok = set_filetime(fname,mtime); + ok = set_filetime(cnum,fname,mtime); if (!ok) + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + return(UNIXERROR(ERRDOS,ERRnoaccess)); - + } + outsize = set_message(outbuf,0,0,True); DEBUG(3,("%s setatr name=%s mode=%d\n",timestring(),fname,mode)); @@ -651,6 +898,7 @@ int reply_search(char *inbuf,char *outbuf) BOOL check_descend = False; BOOL expect_close = False; BOOL can_open = True; + BOOL bad_path = False; *mask = *directory = *fname = 0; @@ -676,28 +924,34 @@ int reply_search(char *inbuf,char *outbuf) { pstring dir2; - strcpy(directory,smb_buf(inbuf)+1); - strcpy(dir2,smb_buf(inbuf)+1); - unix_convert(directory,cnum); + pstrcpy(directory,smb_buf(inbuf)+1); + pstrcpy(dir2,smb_buf(inbuf)+1); + unix_convert(directory,cnum,0,&bad_path); unix_format(dir2); if (!check_name(directory,cnum)) - can_open = False; + can_open = False; p = strrchr(dir2,'/'); if (p == NULL) - {strcpy(mask,dir2);*dir2 = 0;} + { + strcpy(mask,dir2); + *dir2 = 0; + } else - {*p = 0;strcpy(mask,p+1);} + { + *p = 0; + pstrcpy(mask,p+1); + } p = strrchr(directory,'/'); if (!p) - *directory = 0; + *directory = 0; else - *p = 0; + *p = 0; if (strlen(directory) == 0) - strcpy(directory,"./"); + strcpy(directory,"./"); bzero(status,21); CVAL(status,0) = dirtype; } @@ -721,7 +975,7 @@ int reply_search(char *inbuf,char *outbuf) if ((p = strrchr(mask,' '))) { fstring ext; - strcpy(ext,p+1); + fstrcpy(ext,p+1); *p = 0; trim_string(mask,NULL," "); strcat(mask,"."); @@ -743,7 +997,7 @@ int reply_search(char *inbuf,char *outbuf) if (!strchr(mask,'.') && strlen(mask)>8) { fstring tmp; - strcpy(tmp,&mask[8]); + fstrcpy(tmp,&mask[8]); mask[8] = '.'; mask[9] = 0; strcat(mask,tmp); @@ -761,7 +1015,18 @@ int reply_search(char *inbuf,char *outbuf) { dptr_num = dptr_create(cnum,directory,expect_close,SVAL(inbuf,smb_pid)); if (dptr_num < 0) - return(ERROR(ERRDOS,ERRnofids)); + { + if(dptr_num == -2) + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + return (UNIXERROR(ERRDOS,ERRnofids)); + } + return(ERROR(ERRDOS,ERRnofids)); + } } DEBUG(4,("dptr_num is %d\n",dptr_num)); @@ -906,30 +1171,50 @@ int reply_open(char *inbuf,char *outbuf) int unixmode; int rmode=0; struct stat sbuf; - + BOOL bad_path = False; + files_struct *fsp; + int oplock_request = CORE_OPLOCK_REQUEST(inbuf); + cnum = SVAL(inbuf,smb_tid); share_mode = SVAL(inbuf,smb_vwv0); - strcpy(fname,smb_buf(inbuf)+1); - unix_convert(fname,cnum); + pstrcpy(fname,smb_buf(inbuf)+1); + unix_convert(fname,cnum,0,&bad_path); fnum = find_free_file(); if (fnum < 0) return(ERROR(ERRSRV,ERRnofids)); if (!check_name(fname,cnum)) + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } return(UNIXERROR(ERRDOS,ERRnoaccess)); - + } + unixmode = unix_mode(cnum,aARCH); - open_file_shared(fnum,cnum,fname,share_mode,3,unixmode,&rmode,NULL); + open_file_shared(fnum,cnum,fname,share_mode,3,unixmode, + oplock_request,&rmode,NULL); - if (!Files[fnum].open) + fsp = &Files[fnum]; + + if (!fsp->open) + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } return(UNIXERROR(ERRDOS,ERRnoaccess)); + } - if (fstat(Files[fnum].fd,&sbuf) != 0) { - close_file(fnum); + if (fstat(fsp->fd_ptr->fd,&sbuf) != 0) { + close_file(fnum,False); return(ERROR(ERRDOS,ERRnoaccess)); } @@ -939,7 +1224,7 @@ int reply_open(char *inbuf,char *outbuf) if (fmode & aDIR) { DEBUG(3,("attempt to open a directory %s\n",fname)); - close_file(fnum); + close_file(fnum,False); return(ERROR(ERRDOS,ERRnoaccess)); } @@ -949,7 +1234,13 @@ int reply_open(char *inbuf,char *outbuf) put_dos_date3(outbuf,smb_vwv2,mtime); SIVAL(outbuf,smb_vwv4,size); SSVAL(outbuf,smb_vwv6,rmode); + + if (oplock_request && lp_fake_oplocks(SNUM(cnum))) { + CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED; + } + if(fsp->granted_oplock) + CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED; return(outsize); } @@ -962,12 +1253,9 @@ int reply_open_and_X(char *inbuf,char *outbuf,int length,int bufsize) pstring fname; int cnum = SVAL(inbuf,smb_tid); int fnum = -1; - int outsize = 0; - int openmode = 0; - int smb_com2 = CVAL(inbuf,smb_vwv0); - int smb_off2 = SVAL(inbuf,smb_vwv1); int smb_mode = SVAL(inbuf,smb_vwv3); int smb_attr = SVAL(inbuf,smb_vwv5); + BOOL oplock_request = EXTENDED_OPLOCK_REQUEST(inbuf); #if 0 int open_flags = SVAL(inbuf,smb_vwv2); int smb_sattr = SVAL(inbuf,smb_vwv4); @@ -978,35 +1266,51 @@ int reply_open_and_X(char *inbuf,char *outbuf,int length,int bufsize) int size=0,fmode=0,mtime=0,rmode=0; struct stat sbuf; int smb_action = 0; + BOOL bad_path = False; + files_struct *fsp; + + /* If it's an IPC, pass off the pipe handler. */ + if (IS_IPC(cnum)) + return reply_open_pipe_and_X(inbuf,outbuf,length,bufsize); /* XXXX we need to handle passed times, sattr and flags */ - strcpy(fname,smb_buf(inbuf)); - unix_convert(fname,cnum); + pstrcpy(fname,smb_buf(inbuf)); + unix_convert(fname,cnum,0,&bad_path); - /* now add create and trunc bits */ - if (smb_ofun & 0x10) - openmode |= O_CREAT; - if ((smb_ofun & 0x3) == 2) - openmode |= O_TRUNC; - fnum = find_free_file(); if (fnum < 0) return(ERROR(ERRSRV,ERRnofids)); if (!check_name(fname,cnum)) + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } return(UNIXERROR(ERRDOS,ERRnoaccess)); + } unixmode = unix_mode(cnum,smb_attr | aARCH); open_file_shared(fnum,cnum,fname,smb_mode,smb_ofun,unixmode, - &rmode,&smb_action); + oplock_request, &rmode,&smb_action); - if (!Files[fnum].open) + fsp = &Files[fnum]; + + if (!fsp->open) + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } return(UNIXERROR(ERRDOS,ERRnoaccess)); + } - if (fstat(Files[fnum].fd,&sbuf) != 0) { - close_file(fnum); + if (fstat(fsp->fd_ptr->fd,&sbuf) != 0) { + close_file(fnum,False); return(ERROR(ERRDOS,ERRnoaccess)); } @@ -1014,13 +1318,21 @@ int reply_open_and_X(char *inbuf,char *outbuf,int length,int bufsize) fmode = dos_mode(cnum,fname,&sbuf); mtime = sbuf.st_mtime; if (fmode & aDIR) { - close_file(fnum); + close_file(fnum,False); return(ERROR(ERRDOS,ERRnoaccess)); } - outsize = set_message(outbuf,15,0,True); - CVAL(outbuf,smb_vwv0) = smb_com2; - SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4); + if (oplock_request && lp_fake_oplocks(SNUM(cnum))) { + smb_action |= EXTENDED_OPLOCK_GRANTED; + CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED; + } + + if(fsp->granted_oplock) { + smb_action |= EXTENDED_OPLOCK_GRANTED; + CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED; + } + + set_message(outbuf,15,0,True); SSVAL(outbuf,smb_vwv2,fnum); SSVAL(outbuf,smb_vwv3,fmode); put_dos_date3(outbuf,smb_vwv4,mtime); @@ -1030,14 +1342,7 @@ int reply_open_and_X(char *inbuf,char *outbuf,int length,int bufsize) chain_fnum = fnum; - if (smb_com2 != 0xFF) - outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, - outbuf,outbuf+outsize, - length,bufsize); - - chain_fnum = -1; - - return(outsize); + return chain_reply(inbuf,outbuf,length,bufsize); } @@ -1046,31 +1351,35 @@ int reply_open_and_X(char *inbuf,char *outbuf,int length,int bufsize) ****************************************************************************/ int reply_ulogoffX(char *inbuf,char *outbuf,int length,int bufsize) { - int outsize = 0; - int smb_com2 = CVAL(inbuf,smb_vwv0); - int smb_off2 = SVAL(inbuf,smb_vwv1); - int uid = SVAL(inbuf,smb_uid); + uint16 vuid = SVAL(inbuf,smb_uid); + user_struct *vuser = get_valid_user_struct(vuid); - invalidate_uid(uid); + if(vuser == 0) { + DEBUG(3,("ulogoff, vuser id %d does not map to user.\n", vuid)); + } - outsize = set_message(outbuf,2,0,True); - CVAL(outbuf,smb_vwv0) = smb_com2; - SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4); + /* in user level security we are supposed to close any files + open by this user */ + if ((vuser != 0) && (lp_security() != SEC_SHARE)) { + int i; + for (i=0;i<MAX_OPEN_FILES;i++) + if (Files[i].uid == vuser->uid && Files[i].open) { + close_file(i,False); + } + } - DEBUG(3,("%s ulogoffX uid=%d\n",timestring(),uid)); + invalidate_vuid(vuid); - if (smb_com2 != 0xFF) - outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, - outbuf,outbuf+outsize, - length,bufsize); + set_message(outbuf,2,0,True); - - return(outsize); + DEBUG(3,("%s ulogoffX vuid=%d\n",timestring(),vuid)); + + return chain_reply(inbuf,outbuf,length,bufsize); } /**************************************************************************** - reply to a mknew + reply to a mknew or a create ****************************************************************************/ int reply_mknew(char *inbuf,char *outbuf) { @@ -1080,13 +1389,17 @@ int reply_mknew(char *inbuf,char *outbuf) int outsize = 0; int createmode; mode_t unixmode; - + int ofun = 0; + BOOL bad_path = False; + files_struct *fsp; + int oplock_request = CORE_OPLOCK_REQUEST(inbuf); + com = SVAL(inbuf,smb_com); cnum = SVAL(inbuf,smb_tid); createmode = SVAL(inbuf,smb_vwv0); - strcpy(fname,smb_buf(inbuf)+1); - unix_convert(fname,cnum); + pstrcpy(fname,smb_buf(inbuf)+1); + unix_convert(fname,cnum,0,&bad_path); if (createmode & aVOLID) { @@ -1095,26 +1408,59 @@ int reply_mknew(char *inbuf,char *outbuf) unixmode = unix_mode(cnum,createmode); - if (com == SMBmknew && file_exist(fname,NULL)) - return(ERROR(ERRDOS,ERRfilexists)); - fnum = find_free_file(); if (fnum < 0) return(ERROR(ERRSRV,ERRnofids)); if (!check_name(fname,cnum)) + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + if(com == SMBmknew) + { + /* We should fail if file exists. */ + ofun = 0x10; + } + else + { + /* SMBcreate - Create if file doesn't exist, truncate if it does. */ + ofun = 0x12; + } - open_file(fnum,cnum,fname,O_RDWR | O_CREAT | O_TRUNC,unixmode); + /* Open file in dos compatibility share mode. */ + open_file_shared(fnum,cnum,fname,(DENY_FCB<<4)|0xF, ofun, unixmode, + oplock_request, NULL, NULL); - if (!Files[fnum].open) + fsp = &Files[fnum]; + + if (!fsp->open) + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } return(UNIXERROR(ERRDOS,ERRnoaccess)); - + } + outsize = set_message(outbuf,1,0,True); SSVAL(outbuf,smb_vwv0,fnum); - + + if (oplock_request && lp_fake_oplocks(SNUM(cnum))) { + CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED; + } + + if(fsp->granted_oplock) + CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED; + DEBUG(2,("new file %s\n",fname)); - DEBUG(3,("%s mknew %s fd=%d fnum=%d cnum=%d dmode=%d umode=%o\n",timestring(),fname,Files[fnum].fd,fnum,cnum,createmode,unixmode)); + DEBUG(3,("%s mknew %s fd=%d fnum=%d cnum=%d dmode=%d umode=%o\n",timestring(),fname,Files[fnum].fd_ptr->fd,fnum,cnum,createmode,unixmode)); return(outsize); } @@ -1132,11 +1478,15 @@ int reply_ctemp(char *inbuf,char *outbuf) int outsize = 0; int createmode; mode_t unixmode; - + BOOL bad_path = False; + files_struct *fsp; + int oplock_request = CORE_OPLOCK_REQUEST(inbuf); + cnum = SVAL(inbuf,smb_tid); createmode = SVAL(inbuf,smb_vwv0); - sprintf(fname,"%s/TMXXXXXX",smb_buf(inbuf)+1); - unix_convert(fname,cnum); + pstrcpy(fname,smb_buf(inbuf)+1); + strcat(fname,"/TMXXXXXX"); + unix_convert(fname,cnum,0,&bad_path); unixmode = unix_mode(cnum,createmode); @@ -1145,22 +1495,48 @@ int reply_ctemp(char *inbuf,char *outbuf) return(ERROR(ERRSRV,ERRnofids)); if (!check_name(fname,cnum)) + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } return(UNIXERROR(ERRDOS,ERRnoaccess)); + } strcpy(fname2,(char *)mktemp(fname)); - open_file(fnum,cnum,fname2,O_RDWR | O_CREAT | O_TRUNC,unixmode); + /* Open file in dos compatibility share mode. */ + /* We should fail if file exists. */ + open_file_shared(fnum,cnum,fname2,(DENY_FCB<<4)|0xF, 0x10, unixmode, + oplock_request, NULL, NULL); - if (!Files[fnum].open) + fsp = &Files[fnum]; + + if (!fsp->open) + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } return(UNIXERROR(ERRDOS,ERRnoaccess)); + } outsize = set_message(outbuf,1,2 + strlen(fname2),True); SSVAL(outbuf,smb_vwv0,fnum); CVAL(smb_buf(outbuf),0) = 4; strcpy(smb_buf(outbuf) + 1,fname2); + + if (oplock_request && lp_fake_oplocks(SNUM(cnum))) { + CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED; + } + if(fsp->granted_oplock) + CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED; + DEBUG(2,("created temp file %s\n",fname2)); - DEBUG(3,("%s ctemp %s fd=%d fnum=%d cnum=%d dmode=%d umode=%o\n",timestring(),fname2,Files[fnum].fd,fnum,cnum,createmode,unixmode)); + DEBUG(3,("%s ctemp %s fd=%d fnum=%d cnum=%d dmode=%d umode=%o\n",timestring(),fname2,Files[fnum].fd_ptr->fd,fnum,cnum,createmode,unixmode)); return(outsize); } @@ -1179,7 +1555,9 @@ static BOOL can_delete(char *fname,int cnum,int dirtype) if (sys_lstat(fname,&sbuf) != 0) return(False); fmode = dos_mode(cnum,fname,&sbuf); if (fmode & aDIR) return(False); - if (fmode & aRONLY) return(False); + if (!lp_delete_readonly(SNUM(cnum))) { + if (fmode & aRONLY) return(False); + } if ((fmode & ~dirtype) & (aHIDDEN | aSYSTEM)) return(False); if (!check_file_sharing(cnum,fname)) return(False); @@ -1202,17 +1580,18 @@ int reply_unlink(char *inbuf,char *outbuf) int error = ERRnoaccess; BOOL has_wild; BOOL exists=False; + BOOL bad_path = False; *directory = *mask = 0; cnum = SVAL(inbuf,smb_tid); dirtype = SVAL(inbuf,smb_vwv0); - strcpy(name,smb_buf(inbuf) + 1); + pstrcpy(name,smb_buf(inbuf) + 1); DEBUG(3,("reply_unlink : %s\n",name)); - unix_convert(name,cnum); + unix_convert(name,cnum,0,&bad_path); p = strrchr(name,'/'); if (!p) { @@ -1239,7 +1618,12 @@ int reply_unlink(char *inbuf,char *outbuf) char *dname; if (check_name(directory,cnum)) - dirptr = OpenDir(directory); + dirptr = OpenDir(cnum, directory, True); + + /* XXXX the CIFS spec says that if bit0 of the flags2 field is set then + the pattern matches against the long name, otherwise the short name + We don't implement this yet XXXX + */ if (dirptr) { @@ -1251,7 +1635,7 @@ int reply_unlink(char *inbuf,char *outbuf) while ((dname = ReadDirName(dirptr))) { pstring fname; - strcpy(fname,dname); + pstrcpy(fname,dname); if(!mask_match(fname, mask, case_sensitive, False)) continue; @@ -1269,7 +1653,14 @@ int reply_unlink(char *inbuf,char *outbuf) if (exists) return(ERROR(ERRDOS,error)); else + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } return(UNIXERROR(ERRDOS,error)); + } } outsize = set_message(outbuf,0,0,True); @@ -1285,12 +1676,26 @@ int reply_readbraw(char *inbuf, char *outbuf) { int cnum,maxcount,mincount,fnum; int nread = 0; - int startpos; + uint32 startpos; char *header = outbuf; int ret=0; int fd; char *fname; + /* + * Special check if an oplock break has been issued + * and the readraw request croses on the wire, we must + * return a zero length response here. + */ + + if(global_oplock_break) + { + _smb_setlen(header,0); + transfer_file(0,Client,0,header,4,0); + DEBUG(5,("readbraw - oplock break finished\n")); + return -1; + } + cnum = SVAL(inbuf,smb_tid); fnum = GETFNUM(inbuf,smb_vwv0); @@ -1311,7 +1716,7 @@ int reply_readbraw(char *inbuf, char *outbuf) } else { - fd = Files[fnum].fd; + fd = Files[fnum].fd_ptr->fd; fname = Files[fnum].name; } @@ -1323,13 +1728,13 @@ int reply_readbraw(char *inbuf, char *outbuf) if (size < sizeneeded) { struct stat st; - if (fstat(Files[fnum].fd,&st) == 0) + if (fstat(Files[fnum].fd_ptr->fd,&st) == 0) size = st.st_size; if (!Files[fnum].can_write) Files[fnum].size = size; } - nread = MIN(maxcount,size - startpos); + nread = MIN(maxcount,(int)(size - startpos)); } if (nread < mincount) @@ -1345,8 +1750,10 @@ int reply_readbraw(char *inbuf, char *outbuf) int predict=0; _smb_setlen(header,nread); +#if USE_READ_PREDICTION if (!Files[fnum].can_write) predict = read_predict(fd,startpos,header+4,NULL,nread); +#endif if ((nread-predict) > 0) seek_file(fnum,startpos + predict); @@ -1360,7 +1767,7 @@ int reply_readbraw(char *inbuf, char *outbuf) fname,startpos,nread,ret)); #else - ret = read_file(fnum,header+4,startpos,nread,nread,-1,False); + ret = read_file(fnum,header+4,startpos,nread); if (ret < mincount) ret = 0; _smb_setlen(header,ret); @@ -1402,7 +1809,7 @@ int reply_lockread(char *inbuf,char *outbuf) if(!do_lock( fnum, cnum, numtoread, startpos, &eclass, &ecode)) return (ERROR(eclass,ecode)); - nread = read_file(fnum,data,startpos,numtoread,numtoread,-1,False); + nread = read_file(fnum,data,startpos,numtoread); if (nread < 0) return(UNIXERROR(ERRDOS,ERRnoaccess)); @@ -1426,7 +1833,7 @@ int reply_read(char *inbuf,char *outbuf) int cnum,numtoread,fnum; int nread = 0; char *data; - int startpos; + uint32 startpos; int outsize = 0; cnum = SVAL(inbuf,smb_tid); @@ -1447,7 +1854,7 @@ int reply_read(char *inbuf,char *outbuf) return(ERROR(ERRDOS,ERRlock)); if (numtoread > 0) - nread = read_file(fnum,data,startpos,numtoread,numtoread,-1,False); + nread = read_file(fnum,data,startpos,numtoread); if (nread < 0) return(UNIXERROR(ERRDOS,ERRnoaccess)); @@ -1469,8 +1876,6 @@ int reply_read(char *inbuf,char *outbuf) ****************************************************************************/ int reply_read_and_X(char *inbuf,char *outbuf,int length,int bufsize) { - int smb_com2 = CVAL(inbuf,smb_vwv0); - int smb_off2 = SVAL(inbuf,smb_vwv1); int fnum = GETFNUM(inbuf,smb_vwv2); uint32 smb_offs = IVAL(inbuf,smb_vwv3); int smb_maxcnt = SVAL(inbuf,smb_vwv5); @@ -1478,7 +1883,6 @@ int reply_read_and_X(char *inbuf,char *outbuf,int length,int bufsize) int cnum; int nread = -1; char *data; - int outsize = 0; BOOL ok = False; cnum = SVAL(inbuf,smb_tid); @@ -1487,38 +1891,28 @@ int reply_read_and_X(char *inbuf,char *outbuf,int length,int bufsize) CHECK_READ(fnum); CHECK_ERROR(fnum); - outsize = set_message(outbuf,12,0,True); + set_message(outbuf,12,0,True); data = smb_buf(outbuf); if (is_locked(fnum,cnum,smb_maxcnt,smb_offs)) return(ERROR(ERRDOS,ERRlock)); - nread = read_file(fnum,data,smb_offs,smb_maxcnt,smb_maxcnt,-1,False); + nread = read_file(fnum,data,smb_offs,smb_maxcnt); ok = True; if (nread < 0) return(UNIXERROR(ERRDOS,ERRnoaccess)); - outsize += nread; - CVAL(outbuf,smb_vwv0) = smb_com2; - SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4); SSVAL(outbuf,smb_vwv5,nread); - SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf) + chain_size); + SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf)); SSVAL(smb_buf(outbuf),-2,nread); - DEBUG(3,("%s readX fnum=%d cnum=%d min=%d max=%d nread=%d com2=%d off2=%d\n", + DEBUG(3,("%s readX fnum=%d cnum=%d min=%d max=%d nread=%d\n", timestring(),fnum,cnum, - smb_mincnt,smb_maxcnt,nread,smb_com2,smb_off2)); + smb_mincnt,smb_maxcnt,nread)); chain_fnum = fnum; - if (smb_com2 != 0xFF) - outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, - outbuf,outbuf+outsize, - length,bufsize); - - chain_fnum = -1; - - return(outsize); + return chain_reply(inbuf,outbuf,length,bufsize); } @@ -1587,7 +1981,7 @@ int reply_writebraw(char *inbuf,char *outbuf) send_smb(Client,outbuf); /* Now read the raw data into the buffer and write it */ - if(read_smb_length(Client,inbuf,0) == -1) { + if (read_smb_length(Client,inbuf,SMB_SECONDARY_WAIT) == -1) { exit_server("secondary writebraw failed"); } @@ -1600,7 +1994,7 @@ int reply_writebraw(char *inbuf,char *outbuf) tcount,nwritten,numtowrite)); } - nwritten = transfer_file(Client,Files[fnum].fd,numtowrite,NULL,0, + nwritten = transfer_file(Client,Files[fnum].fd_ptr->fd,numtowrite,NULL,0, startpos+nwritten); total_written += nwritten; @@ -1720,7 +2114,7 @@ int reply_write(char *inbuf,char *outbuf,int dum1,int dum2) zero then the file size should be extended or truncated to the size given in smb_vwv[2-3] */ if(numtowrite == 0) - nwritten = set_filelen(Files[fnum].fd, startpos); + nwritten = set_filelen(Files[fnum].fd_ptr->fd, startpos); else nwritten = write_file(fnum,data,numtowrite); @@ -1750,8 +2144,6 @@ int reply_write(char *inbuf,char *outbuf,int dum1,int dum2) ****************************************************************************/ int reply_write_and_X(char *inbuf,char *outbuf,int length,int bufsize) { - int smb_com2 = CVAL(inbuf,smb_vwv0); - int smb_off2 = SVAL(inbuf,smb_vwv1); int fnum = GETFNUM(inbuf,smb_vwv2); uint32 smb_offs = IVAL(inbuf,smb_vwv3); int smb_dsize = SVAL(inbuf,smb_vwv10); @@ -1759,7 +2151,6 @@ int reply_write_and_X(char *inbuf,char *outbuf,int length,int bufsize) BOOL write_through = BITSETW(inbuf+smb_vwv7,0); int cnum; int nwritten = -1; - int outsize = 0; char *data; cnum = SVAL(inbuf,smb_tid); @@ -1787,10 +2178,8 @@ int reply_write_and_X(char *inbuf,char *outbuf,int length,int bufsize) if(((nwritten == 0) && (smb_dsize != 0))||(nwritten < 0)) return(UNIXERROR(ERRDOS,ERRnoaccess)); - outsize = set_message(outbuf,6,0,True); + set_message(outbuf,6,0,True); - CVAL(outbuf,smb_vwv0) = smb_com2; - SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4); SSVAL(outbuf,smb_vwv2,nwritten); if (nwritten < smb_dsize) { @@ -1805,14 +2194,7 @@ int reply_write_and_X(char *inbuf,char *outbuf,int length,int bufsize) if (lp_syncalways(SNUM(cnum)) || write_through) sync_file(fnum); - if (smb_com2 != 0xFF) - outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, - outbuf,outbuf+outsize, - length,bufsize); - - chain_fnum = -1; - - return(outsize); + return chain_reply(inbuf,outbuf,length,bufsize); } @@ -1845,7 +2227,7 @@ int reply_lseek(char *inbuf,char *outbuf) umode = SEEK_SET; break; } - res = lseek(Files[fnum].fd,startpos,umode); + res = lseek(Files[fnum].fd_ptr->fd,startpos,umode); Files[fnum].pos = res; outsize = set_message(outbuf,2,0,True); @@ -1914,7 +2296,12 @@ int reply_close(char *inbuf,char *outbuf) cnum = SVAL(inbuf,smb_tid); + /* If it's an IPC, pass off to the pipe handler. */ + if (IS_IPC(cnum)) + return reply_pipe_close(inbuf,outbuf); + fnum = GETFNUM(inbuf,smb_vwv0); + CHECK_FNUM(fnum,cnum); if(HAS_CACHED_ERROR(fnum)) { @@ -1924,17 +2311,17 @@ int reply_close(char *inbuf,char *outbuf) mtime = make_unix_date3(inbuf+smb_vwv1); - close_file(fnum); - /* try and set the date */ - set_filetime(Files[fnum].name,mtime); + set_filetime(cnum, Files[fnum].name,mtime); + + close_file(fnum,True); /* We have a cached error */ if(eclass || err) return(ERROR(eclass,err)); DEBUG(3,("%s close fd=%d fnum=%d cnum=%d (numopen=%d)\n", - timestring(),Files[fnum].fd,fnum,cnum, + timestring(),Files[fnum].fd_ptr->fd,fnum,cnum, Connections[cnum].num_files_open)); return(outsize); @@ -1972,10 +2359,10 @@ int reply_writeclose(char *inbuf,char *outbuf) nwritten = write_file(fnum,data,numtowrite); - close_file(fnum); - - set_filetime(Files[fnum].name,mtime); + set_filetime(cnum, Files[fnum].name,mtime); + close_file(fnum,True); + DEBUG(3,("%s writeclose fnum=%d cnum=%d num=%d wrote=%d (numopen=%d)\n", timestring(),fnum,cnum,numtowrite,nwritten, Connections[cnum].num_files_open)); @@ -2010,7 +2397,7 @@ int reply_lock(char *inbuf,char *outbuf) count = IVAL(inbuf,smb_vwv1); offset = IVAL(inbuf,smb_vwv3); - DEBUG(3,("%s lock fd=%d fnum=%d cnum=%d ofs=%d cnt=%d\n",timestring(),Files[fnum].fd,fnum,cnum,offset,count)); + DEBUG(3,("%s lock fd=%d fnum=%d cnum=%d ofs=%d cnt=%d\n",timestring(),Files[fnum].fd_ptr->fd,fnum,cnum,offset,count)); if(!do_lock( fnum, cnum, count, offset, &eclass, &ecode)) return (ERROR(eclass,ecode)); @@ -2042,7 +2429,7 @@ int reply_unlock(char *inbuf,char *outbuf) if(!do_unlock(fnum, cnum, count, offset, &eclass, &ecode)) return (ERROR(eclass,ecode)); - DEBUG(3,("%s unlock fd=%d fnum=%d cnum=%d ofs=%d cnt=%d\n",timestring(),Files[fnum].fd,fnum,cnum,offset,count)); + DEBUG(3,("%s unlock fd=%d fnum=%d cnum=%d ofs=%d cnt=%d\n",timestring(),Files[fnum].fd_ptr->fd,fnum,cnum,offset,count)); return(outsize); } @@ -2053,15 +2440,21 @@ int reply_unlock(char *inbuf,char *outbuf) ****************************************************************************/ int reply_tdis(char *inbuf,char *outbuf) { - int cnum, uid; + int cnum; int outsize = set_message(outbuf,0,0,True); - + uint16 vuid; + cnum = SVAL(inbuf,smb_tid); - uid = SVAL(inbuf,smb_uid); + vuid = SVAL(inbuf,smb_uid); + + if (!OPEN_CNUM(cnum)) { + DEBUG(4,("Invalid cnum in tdis (%d)\n",cnum)); + return(ERROR(ERRSRV,ERRinvnid)); + } Connections[cnum].used = False; - close_cnum(cnum,uid); + close_cnum(cnum,vuid); DEBUG(3,("%s tdis cnum=%d\n",timestring(),cnum)); @@ -2083,11 +2476,17 @@ int reply_echo(char *inbuf,char *outbuf) cnum = SVAL(inbuf,smb_tid); + /* According to the latest CIFS spec we shouldn't + care what the TID is. + */ + +#if 0 if (cnum != 0xFFFF && !OPEN_CNUM(cnum)) { DEBUG(4,("Invalid cnum in echo (%d)\n",cnum)); return(ERROR(ERRSRV,ERRinvnid)); } +#endif /* copy any incoming data back out */ if (data_len > 0) @@ -2135,7 +2534,7 @@ int reply_printopen(char *inbuf,char *outbuf) { pstring s; char *p; - StrnCpy(s,smb_buf(inbuf)+1,sizeof(pstring)-1); + pstrcpy(s,smb_buf(inbuf)+1); p = s; while (*p) { @@ -2158,8 +2557,9 @@ int reply_printopen(char *inbuf,char *outbuf) if (!check_name(fname2,cnum)) return(ERROR(ERRDOS,ERRnoaccess)); - open_file(fnum,cnum,fname2,O_WRONLY | O_CREAT | O_TRUNC, - unix_mode(cnum,0)); + /* Open for exclusive use, write only. */ + open_file_shared(fnum,cnum,fname2,(DENY_ALL<<4)|1, 0x12, unix_mode(cnum,0), + 0, NULL, NULL); if (!Files[fnum].open) return(UNIXERROR(ERRDOS,ERRnoaccess)); @@ -2170,7 +2570,7 @@ int reply_printopen(char *inbuf,char *outbuf) outsize = set_message(outbuf,1,0,True); SSVAL(outbuf,smb_vwv0,fnum); - DEBUG(3,("%s openprint %s fd=%d fnum=%d cnum=%d\n",timestring(),fname2,Files[fnum].fd,fnum,cnum)); + DEBUG(3,("%s openprint %s fd=%d fnum=%d cnum=%d\n",timestring(),fname2,Files[fnum].fd_ptr->fd,fnum,cnum)); return(outsize); } @@ -2193,9 +2593,9 @@ int reply_printclose(char *inbuf,char *outbuf) if (!CAN_PRINT(cnum)) return(ERROR(ERRDOS,ERRnoaccess)); - close_file(fnum); + close_file(fnum,True); - DEBUG(3,("%s printclose fd=%d fnum=%d cnum=%d\n",timestring(),Files[fnum].fd,fnum,cnum)); + DEBUG(3,("%s printclose fd=%d fnum=%d cnum=%d\n",timestring(),Files[fnum].fd_ptr->fd,fnum,cnum)); return(outsize); } @@ -2206,13 +2606,14 @@ int reply_printclose(char *inbuf,char *outbuf) ****************************************************************************/ int reply_printqueue(char *inbuf,char *outbuf) { - int cnum, uid; + int cnum; int outsize = set_message(outbuf,2,3,True); int max_count = SVAL(inbuf,smb_vwv0); int start_index = SVAL(inbuf,smb_vwv1); + uint16 vuid; cnum = SVAL(inbuf,smb_tid); - uid = SVAL(inbuf,smb_uid); + vuid = SVAL(inbuf,smb_uid); /* allow checking the queue for anyone */ #if 0 @@ -2248,7 +2649,7 @@ int reply_printqueue(char *inbuf,char *outbuf) DEBUG(5,("connection not open or not a printer, using cnum %d\n",cnum)); } - if (!become_user(cnum,uid)) + if (!become_user(&Connections[cnum], cnum, vuid)) return(ERROR(ERRSRV,ERRinvnid)); { @@ -2269,7 +2670,7 @@ int reply_printqueue(char *inbuf,char *outbuf) { put_dos_date2(p,0,queue[i].time); CVAL(p,4) = (queue[i].status==LPQ_PRINTING?2:3); - SSVAL(p,5,queue[i].job); + SSVAL(p,5,printjob_encode(SNUM(cnum), queue[i].job)); SIVAL(p,7,queue[i].size); CVAL(p,11) = 0; StrnCpy(p+12,queue[i].user,16); @@ -2334,17 +2735,25 @@ int reply_mkdir(char *inbuf,char *outbuf) pstring directory; int cnum; int outsize,ret= -1; - - strcpy(directory,smb_buf(inbuf) + 1); + BOOL bad_path = False; + + pstrcpy(directory,smb_buf(inbuf) + 1); cnum = SVAL(inbuf,smb_tid); - unix_convert(directory,cnum); + unix_convert(directory,cnum,0,&bad_path); if (check_name(directory,cnum)) ret = sys_mkdir(directory,unix_mode(cnum,aDIR)); if (ret < 0) + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } return(UNIXERROR(ERRDOS,ERRnoaccess)); - + } + outsize = set_message(outbuf,0,0,True); DEBUG(3,("%s mkdir %s cnum=%d ret=%d\n",timestring(),directory,cnum,ret)); @@ -2352,6 +2761,66 @@ int reply_mkdir(char *inbuf,char *outbuf) return(outsize); } +/**************************************************************************** +Static function used by reply_rmdir to delete an entire directory +tree recursively. +****************************************************************************/ +static BOOL recursive_rmdir(char *directory) +{ + char *dname = NULL; + BOOL ret = False; + void *dirptr = OpenDir(-1, directory, False); + + if(dirptr == NULL) + return True; + + while((dname = ReadDirName(dirptr))) + { + pstring fullname; + struct stat st; + + if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) + continue; + + /* Construct the full name. */ + if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname)) + { + errno = ENOMEM; + ret = True; + break; + } + strcpy(fullname, directory); + strcat(fullname, "/"); + strcat(fullname, dname); + + if(sys_lstat(fullname, &st) != 0) + { + ret = True; + break; + } + + if(st.st_mode & S_IFDIR) + { + if(recursive_rmdir(fullname)!=0) + { + ret = True; + break; + } + if(sys_rmdir(fullname) != 0) + { + ret = True; + break; + } + } + else if(sys_unlink(fullname) != 0) + { + ret = True; + break; + } + } + CloseDir(dirptr); + return ret; +} /**************************************************************************** reply to a rmdir @@ -2362,23 +2831,102 @@ int reply_rmdir(char *inbuf,char *outbuf) int cnum; int outsize = 0; BOOL ok = False; - + BOOL bad_path = False; + cnum = SVAL(inbuf,smb_tid); - strcpy(directory,smb_buf(inbuf) + 1); - unix_convert(directory,cnum); + pstrcpy(directory,smb_buf(inbuf) + 1); + unix_convert(directory,cnum,0,&bad_path); if (check_name(directory,cnum)) { + dptr_closepath(directory,SVAL(inbuf,smb_pid)); ok = (sys_rmdir(directory) == 0); + if(!ok && (errno == ENOTEMPTY) && lp_veto_files(SNUM(cnum))) + { + /* Check to see if the only thing in this directory are + vetoed files/directories. If so then delete them and + retry. If we fail to delete any of them (and we *don't* + do a recursive delete) then fail the rmdir. */ + BOOL all_veto_files = True; + char *dname; + void *dirptr = OpenDir(cnum, directory, False); + + if(dirptr != NULL) + { + int dirpos = TellDir(dirptr); + while ((dname = ReadDirName(dirptr))) + { + if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) + continue; + if(!IS_VETO_PATH(cnum, dname)) + { + all_veto_files = False; + break; + } + } + if(all_veto_files) + { + SeekDir(dirptr,dirpos); + while ((dname = ReadDirName(dirptr))) + { + pstring fullname; + struct stat st; + + if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) + continue; + + /* Construct the full name. */ + if(strlen(directory) + strlen(dname) + 1 >= sizeof(fullname)) + { + errno = ENOMEM; + break; + } + pstrcpy(fullname, directory); + strcat(fullname, "/"); + strcat(fullname, dname); + + if(sys_lstat(fullname, &st) != 0) + break; + if(st.st_mode & S_IFDIR) + { + if(lp_recursive_veto_delete(SNUM(cnum))) + { + if(recursive_rmdir(fullname) != 0) + break; + } + if(sys_rmdir(fullname) != 0) + break; + } + else if(sys_unlink(fullname) != 0) + break; + } + CloseDir(dirptr); + /* Retry the rmdir */ + ok = (sys_rmdir(directory) == 0); + } + else + CloseDir(dirptr); + } + else + errno = ENOTEMPTY; + } + if (!ok) - DEBUG(3,("couldn't remove directory %s : %s\n", + DEBUG(3,("couldn't remove directory %s : %s\n", directory,strerror(errno))); } if (!ok) + { + if((errno == ENOENT) && bad_path) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } return(UNIXERROR(ERRDOS,ERRbadpath)); - + } + outsize = set_message(outbuf,0,0,True); DEBUG(3,("%s rmdir %s\n",timestring(),directory)); @@ -2401,21 +2949,21 @@ static BOOL resolve_wildcards(char *name1,char *name2) if (!name1 || !name2) return(False); - strcpy(root1,name1); - strcpy(root2,name2); + fstrcpy(root1,name1); + fstrcpy(root2,name2); p = strrchr(root1,'.'); if (p) { *p = 0; - strcpy(ext1,p+1); + fstrcpy(ext1,p+1); } else { - strcpy(ext1,""); + fstrcpy(ext1,""); } p = strrchr(root2,'.'); if (p) { *p = 0; - strcpy(ext2,p+1); + fstrcpy(ext2,p+1); } else { - strcpy(ext2,""); + fstrcpy(ext2,""); } p = root1; @@ -2476,32 +3024,45 @@ int reply_mv(char *inbuf,char *outbuf) int cnum; pstring directory; pstring mask,newname; + pstring newname_last_component; char *p; int count=0; int error = ERRnoaccess; BOOL has_wild; BOOL exists=False; + BOOL bad_path1 = False; + BOOL bad_path2 = False; *directory = *mask = 0; cnum = SVAL(inbuf,smb_tid); - strcpy(name,smb_buf(inbuf) + 1); - strcpy(newname,smb_buf(inbuf) + 3 + strlen(name)); + pstrcpy(name,smb_buf(inbuf) + 1); + pstrcpy(newname,smb_buf(inbuf) + 3 + strlen(name)); DEBUG(3,("reply_mv : %s -> %s\n",name,newname)); - unix_convert(name,cnum); - unix_convert(newname,cnum); + unix_convert(name,cnum,0,&bad_path1); + unix_convert(newname,cnum,newname_last_component,&bad_path2); + + /* + * Split the old name into directory and last component + * strings. Note that unix_convert may have stripped off a + * leading ./ from both name and newname if the rename is + * at the root of the share. We need to make sure either both + * name and newname contain a / character or neither of them do + * as this is checked in resolve_wildcards(). + */ p = strrchr(name,'/'); if (!p) { - strcpy(directory,"./"); + strcpy(directory,"."); strcpy(mask,name); } else { *p = 0; strcpy(directory,name); strcpy(mask,p+1); + *p = '/'; /* Replace needed for exceptional test below. */ } if (is_mangled(mask)) @@ -2510,12 +3071,64 @@ int reply_mv(char *inbuf,char *outbuf) has_wild = strchr(mask,'*') || strchr(mask,'?'); if (!has_wild) { + BOOL is_short_name = is_8_3(name, True); + + /* Add a terminating '/' to the directory name. */ strcat(directory,"/"); strcat(directory,mask); + + /* Ensure newname contains a '/' also */ + if(strrchr(newname,'/') == 0) { + pstring tmpstr; + + strcpy(tmpstr, "./"); + strcat(tmpstr, newname); + strcpy(newname, tmpstr); + } + + DEBUG(3,("reply_mv : case_sensitive = %d, case_preserve = %d, short case preserve = %d, directory = %s, newname = %s, newname_last_component = %s, is_8_3 = %d\n", + case_sensitive, case_preserve, short_case_preserve, directory, + newname, newname_last_component, is_short_name)); + + /* + * Check for special case with case preserving and not + * case sensitive, if directory and newname are identical, + * and the old last component differs from the original + * last component only by case, then we should allow + * the rename (user is trying to change the case of the + * filename). + */ + if((case_sensitive == False) && ( ((case_preserve == True) && (is_short_name == False)) || + ((short_case_preserve == True) && (is_short_name == True))) && + strcsequal(directory, newname)) { + pstring newname_modified_last_component; + + /* + * Get the last component of the modified name. + * Note that we guarantee that newname contains a '/' + * character above. + */ + p = strrchr(newname,'/'); + strcpy(newname_modified_last_component,p+1); + + if(strcsequal(newname_modified_last_component, + newname_last_component) == False) { + /* + * Replace the modified last component with + * the original. + */ + strcpy(p+1, newname_last_component); + } + } + if (resolve_wildcards(directory,newname) && can_rename(directory,cnum) && !file_exist(newname,NULL) && !sys_rename(directory,newname)) count++; + + DEBUG(3,("reply_mv : %s doing rename on %s -> %s\n",(count != 0) ? "succeeded" : "failed", + directory,newname)); + if (!count) exists = file_exist(directory,NULL); if (!count && exists && file_exist(newname,NULL)) { exists = True; @@ -2527,7 +3140,7 @@ int reply_mv(char *inbuf,char *outbuf) pstring destname; if (check_name(directory,cnum)) - dirptr = OpenDir(directory); + dirptr = OpenDir(cnum, directory, True); if (dirptr) { @@ -2539,20 +3152,29 @@ int reply_mv(char *inbuf,char *outbuf) while ((dname = ReadDirName(dirptr))) { pstring fname; - strcpy(fname,dname); + pstrcpy(fname,dname); if(!mask_match(fname, mask, case_sensitive, False)) continue; error = ERRnoaccess; sprintf(fname,"%s/%s",directory,dname); - if (!can_rename(fname,cnum)) continue; - strcpy(destname,newname); + if (!can_rename(fname,cnum)) { + DEBUG(6,("rename %s refused\n", fname)); + continue; + } + pstrcpy(destname,newname); - if (!resolve_wildcards(fname,destname)) continue; + if (!resolve_wildcards(fname,destname)) { + DEBUG(6,("resolve_wildcards %s %s failed\n", + fname, destname)); + continue; + } if (file_exist(destname,NULL)) { - error = 183; - continue; + DEBUG(6,("file_exist %s\n", + destname)); + error = 183; + continue; } if (!sys_rename(fname,destname)) count++; DEBUG(3,("reply_mv : doing rename on %s -> %s\n",fname,destname)); @@ -2565,7 +3187,14 @@ int reply_mv(char *inbuf,char *outbuf) if (exists) return(ERROR(ERRDOS,error)); else + { + if((errno == ENOENT) && (bad_path1 || bad_path2)) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } return(UNIXERROR(ERRDOS,error)); + } } outsize = set_message(outbuf,0,0,True); @@ -2585,7 +3214,7 @@ static BOOL copy_file(char *src,char *dest1,int cnum,int ofun, int fnum1,fnum2; pstring dest; - strcpy(dest,dest1); + pstrcpy(dest,dest1); if (target_is_directory) { char *p = strrchr(src,'/'); if (p) @@ -2601,7 +3230,7 @@ static BOOL copy_file(char *src,char *dest1,int cnum,int ofun, fnum1 = find_free_file(); if (fnum1<0) return(False); open_file_shared(fnum1,cnum,src,(DENY_NONE<<4), - 1,0,&Access,&action); + 1,0,0,&Access,&action); if (!Files[fnum1].open) return(False); @@ -2610,26 +3239,26 @@ static BOOL copy_file(char *src,char *dest1,int cnum,int ofun, fnum2 = find_free_file(); if (fnum2<0) { - close_file(fnum1); + close_file(fnum1,False); return(False); } open_file_shared(fnum2,cnum,dest,(DENY_NONE<<4)|1, - ofun,st.st_mode,&Access,&action); + ofun,st.st_mode,0,&Access,&action); if (!Files[fnum2].open) { - close_file(fnum1); + close_file(fnum1,False); return(False); } if ((ofun&3) == 1) { - lseek(Files[fnum2].fd,0,SEEK_END); + lseek(Files[fnum2].fd_ptr->fd,0,SEEK_END); } if (st.st_size) - ret = transfer_file(Files[fnum1].fd,Files[fnum2].fd,st.st_size,NULL,0,0); + ret = transfer_file(Files[fnum1].fd_ptr->fd,Files[fnum2].fd_ptr->fd,st.st_size,NULL,0,0); - close_file(fnum1); - close_file(fnum2); + close_file(fnum1,False); + close_file(fnum2,False); return(ret == st.st_size); } @@ -2655,13 +3284,15 @@ int reply_copy(char *inbuf,char *outbuf) int ofun = SVAL(inbuf,smb_vwv1); int flags = SVAL(inbuf,smb_vwv2); BOOL target_is_directory=False; + BOOL bad_path1 = False; + BOOL bad_path2 = False; *directory = *mask = 0; cnum = SVAL(inbuf,smb_tid); - strcpy(name,smb_buf(inbuf)); - strcpy(newname,smb_buf(inbuf) + 1 + strlen(name)); + pstrcpy(name,smb_buf(inbuf)); + pstrcpy(newname,smb_buf(inbuf) + 1 + strlen(name)); DEBUG(3,("reply_copy : %s -> %s\n",name,newname)); @@ -2671,8 +3302,8 @@ int reply_copy(char *inbuf,char *outbuf) return(ERROR(ERRSRV,ERRinvdevice)); } - unix_convert(name,cnum); - unix_convert(newname,cnum); + unix_convert(name,cnum,0,&bad_path1); + unix_convert(newname,cnum,0,&bad_path2); target_is_directory = directory_exist(newname,NULL); @@ -2718,7 +3349,7 @@ int reply_copy(char *inbuf,char *outbuf) pstring destname; if (check_name(directory,cnum)) - dirptr = OpenDir(directory); + dirptr = OpenDir(cnum, directory, True); if (dirptr) { @@ -2730,7 +3361,7 @@ int reply_copy(char *inbuf,char *outbuf) while ((dname = ReadDirName(dirptr))) { pstring fname; - strcpy(fname,dname); + pstrcpy(fname,dname); if(!mask_match(fname, mask, case_sensitive, False)) continue; @@ -2750,7 +3381,14 @@ int reply_copy(char *inbuf,char *outbuf) if (exists) return(ERROR(ERRDOS,error)); else + { + if((errno == ENOENT) && (bad_path1 || bad_path2)) + { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } return(UNIXERROR(ERRDOS,error)); + } } outsize = set_message(outbuf,1,0,True); @@ -2777,7 +3415,7 @@ int reply_setdir(char *inbuf,char *outbuf) if (!CAN_SETDIR(snum)) return(ERROR(ERRDOS,ERRnoaccess)); - strcpy(newdir,smb_buf(inbuf) + 1); + pstrcpy(newdir,smb_buf(inbuf) + 1); strlower(newdir); if (strlen(newdir) == 0) @@ -2806,10 +3444,11 @@ int reply_setdir(char *inbuf,char *outbuf) ****************************************************************************/ int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize) { - int smb_com2 = CVAL(inbuf,smb_vwv0); - int smb_off2 = SVAL(inbuf,smb_vwv1); int fnum = GETFNUM(inbuf,smb_vwv2); - uint16 locktype = SVAL(inbuf,smb_vwv3); + unsigned char locktype = CVAL(inbuf,smb_vwv3); +#if 0 + unsigned char oplocklevel = CVAL(inbuf,smb_vwv3+1); +#endif uint16 num_ulocks = SVAL(inbuf,smb_vwv6); uint16 num_locks = SVAL(inbuf,smb_vwv7); uint32 count, offset; @@ -2818,7 +3457,7 @@ int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize) int i; char *data; uint32 ecode=0, dummy2; - int outsize, eclass=0, dummy1; + int eclass=0, dummy1; cnum = SVAL(inbuf,smb_tid); @@ -2826,6 +3465,55 @@ int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize) CHECK_ERROR(fnum); data = smb_buf(inbuf); + + /* Check if this is an oplock break on a file + we have granted an oplock on. + */ + if ((locktype & LOCKING_ANDX_OPLOCK_RELEASE)) + { + int token; + files_struct *fsp = &Files[fnum]; + uint32 dev = fsp->fd_ptr->dev; + uint32 inode = fsp->fd_ptr->inode; + + DEBUG(5,("reply_lockingX: oplock break reply from client for fnum = %d\n", + fnum)); + /* + * Make sure we have granted an oplock on this file. + */ + if(!fsp->granted_oplock) + { + DEBUG(0,("reply_lockingX: Error : oplock break from client for fnum = %d and \ +no oplock granted on this file.\n", fnum)); + return ERROR(ERRDOS,ERRlock); + } + + /* Remove the oplock flag from the sharemode. */ + lock_share_entry(fsp->cnum, dev, inode, &token); + if(remove_share_oplock( fnum, token)==False) { + DEBUG(0,("reply_lockingX: failed to remove share oplock for fnum %d, \ +dev = %x, inode = %x\n", + fnum, dev, inode)); + unlock_share_entry(fsp->cnum, dev, inode, token); + } else { + unlock_share_entry(fsp->cnum, dev, inode, token); + + /* Clear the granted flag and return. */ + fsp->granted_oplock = False; + } + + /* if this is a pure oplock break request then don't send a reply */ + if (num_locks == 0 && num_ulocks == 0) + { + /* Sanity check - ensure a pure oplock break is not a + chained request. */ + if(CVAL(inbuf,smb_vwv0) != 0xff) + DEBUG(0,("reply_lockingX: Error : pure oplock break is a chained %d request !\n", + (unsigned int)CVAL(inbuf,smb_vwv0) )); + return -1; + } + } + /* Data now points at the beginning of the list of smb_unlkrng structs */ for(i = 0; i < (int)num_ulocks; i++) { @@ -2857,24 +3545,14 @@ int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize) return ERROR(eclass,ecode); } - outsize = set_message(outbuf,2,0,True); - - CVAL(outbuf,smb_vwv0) = smb_com2; - SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4); + set_message(outbuf,2,0,True); DEBUG(3,("%s lockingX fnum=%d cnum=%d type=%d num_locks=%d num_ulocks=%d\n", - timestring(),fnum,cnum,locktype,num_locks,num_ulocks)); + timestring(),fnum,cnum,(unsigned int)locktype,num_locks,num_ulocks)); chain_fnum = fnum; - if (smb_com2 != 0xFF) - outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, - outbuf,outbuf+outsize, - length,bufsize); - - chain_fnum = -1; - - return(outsize); + return chain_reply(inbuf,outbuf,length,bufsize); } @@ -2887,7 +3565,7 @@ int reply_readbmpx(char *inbuf,char *outbuf,int length,int bufsize) int nread = -1; int total_read; char *data; - int32 startpos; + uint32 startpos; int outsize, mincount, maxcount; int max_per_packet; int tcount; @@ -2911,7 +3589,7 @@ int reply_readbmpx(char *inbuf,char *outbuf,int length,int bufsize) mincount = SVAL(inbuf,smb_vwv4); data = smb_buf(outbuf); - pad = ((int)data)%4; + pad = ((long)data)%4; if (pad) pad = 4 - pad; data += pad; @@ -2926,7 +3604,7 @@ int reply_readbmpx(char *inbuf,char *outbuf,int length,int bufsize) { int N = MIN(max_per_packet,tcount-total_read); - nread = read_file(fnum,data,startpos,N,N,-1,False); + nread = read_file(fnum,data,startpos,N); if (nread <= 0) nread = 0; @@ -2958,7 +3636,7 @@ int reply_writebmpx(char *inbuf,char *outbuf) int cnum,numtowrite,fnum; int nwritten = -1; int outsize = 0; - int32 startpos; + uint32 startpos; int tcount, write_through, smb_doff; char *data; @@ -3147,11 +3825,31 @@ int reply_setattrE(char *inbuf,char *outbuf) unix_times.actime = make_unix_date2(inbuf+smb_vwv3); unix_times.modtime = make_unix_date2(inbuf+smb_vwv5); + /* + * Patch from Ray Frush <frush@engr.colostate.edu> + * Sometimes times are sent as zero - ignore them. + */ + + if ((unix_times.actime == 0) && (unix_times.modtime == 0)) + { + /* Ignore request */ + DEBUG(3,("%s reply_setattrE fnum=%d cnum=%d ignoring zero request - \ +not setting timestamps of 0\n", + timestring(), fnum,cnum,unix_times.actime,unix_times.modtime)); + return(outsize); + } + else if ((unix_times.actime != 0) && (unix_times.modtime == 0)) + { + /* set modify time = to access time if modify time was 0 */ + unix_times.modtime = unix_times.actime; + } + /* Set the date on this file */ - if(sys_utime(Files[fnum].name, &unix_times)) + if(file_utime(cnum, Files[fnum].name, &unix_times)) return(ERROR(ERRDOS,ERRnoaccess)); - DEBUG(3,("%s reply_setattrE fnum=%d cnum=%d\n",timestring(),fnum,cnum)); + DEBUG(3,("%s reply_setattrE fnum=%d cnum=%d actime=%d modtime=%d\n", + timestring(), fnum,cnum,unix_times.actime,unix_times.modtime)); return(outsize); } @@ -3176,7 +3874,7 @@ int reply_getattrE(char *inbuf,char *outbuf) CHECK_ERROR(fnum); /* Do an fstat on this file */ - if(fstat(Files[fnum].fd, &sbuf)) + if(fstat(Files[fnum].fd_ptr->fd, &sbuf)) return(UNIXERROR(ERRDOS,ERRnoaccess)); mode = dos_mode(cnum,Files[fnum].name,&sbuf); @@ -3203,8 +3901,3 @@ int reply_getattrE(char *inbuf,char *outbuf) return(outsize); } - - - - - |