diff options
author | Andrew Tridgell <tridge@samba.org> | 1999-12-13 13:27:58 +0000 |
---|---|---|
committer | Andrew Tridgell <tridge@samba.org> | 1999-12-13 13:27:58 +0000 |
commit | 3db52feb1f3b2c07ce0b06ad4a7099fa6efe3fc7 (patch) | |
tree | 866dd15416c3d8554bb207709f433a87ad0c012d /source3/smbd | |
parent | f6276724bafdb6145c0c7b565172d80cb04516ea (diff) | |
download | samba-3db52feb1f3b2c07ce0b06ad4a7099fa6efe3fc7.tar.gz samba-3db52feb1f3b2c07ce0b06ad4a7099fa6efe3fc7.tar.bz2 samba-3db52feb1f3b2c07ce0b06ad4a7099fa6efe3fc7.zip |
first pass at updating head branch to be to be the same as the SAMBA_2_0 branch
(This used to be commit 453a822a76780063dff23526c35408866d0c0154)
Diffstat (limited to 'source3/smbd')
29 files changed, 6978 insertions, 3643 deletions
diff --git a/source3/smbd/blocking.c b/source3/smbd/blocking.c index 32b6d010a4..16882be2bb 100644 --- a/source3/smbd/blocking.c +++ b/source3/smbd/blocking.c @@ -211,18 +211,16 @@ static void reply_lockingX_error(blocking_lock_record *blr, int eclass, int32 ec for(i = blr->lock_num; i >= 0; i--) { int dummy1; uint32 dummy2; - if(!large_file_format) { - count = IVAL(data,SMB_LKLEN_OFFSET(i)); - offset = IVAL(data,SMB_LKOFF_OFFSET(i)); - } -#ifdef LARGE_SMB_OFF_T - else { - count = (((SMB_OFF_T) IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(i))) << 32) | - ((SMB_OFF_T) IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(i))); - offset = (((SMB_OFF_T) IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(i))) << 32) | - ((SMB_OFF_T) IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(i))); - } -#endif /* LARGE_SMB_OFF_T */ + BOOL err; + + count = get_lock_count( data, i, large_file_format, &err); + offset = get_lock_offset( data, i, large_file_format, &err); + + /* + * We know err cannot be set as if it was the lock + * request would never have been queued. JRA. + */ + do_unlock(fsp,conn,count,offset,&dummy1,&dummy2); } @@ -313,7 +311,7 @@ static BOOL process_lockread(blocking_lock_record *blr) SSVAL(smb_buf(outbuf),1,nread); DEBUG(3, ( "process_lockread file = %s, fnum=%d num=%d nread=%d\n", - fsp->fsp_name, fsp->fnum, numtoread, nread ) ); + fsp->fsp_name, fsp->fnum, (int)numtoread, (int)nread ) ); send_blocking_reply(outbuf,outsize); return True; @@ -401,18 +399,15 @@ static BOOL process_lockingX(blocking_lock_record *blr) */ for(; blr->lock_num < num_locks; blr->lock_num++) { - if(!large_file_format) { - count = IVAL(data,SMB_LKLEN_OFFSET(blr->lock_num)); - offset = IVAL(data,SMB_LKOFF_OFFSET(blr->lock_num)); - } -#ifdef LARGE_SMB_OFF_T - else { - count = (((SMB_OFF_T) IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(blr->lock_num))) << 32) | - ((SMB_OFF_T) IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(blr->lock_num))); - offset = (((SMB_OFF_T) IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(blr->lock_num))) << 32) | - ((SMB_OFF_T) IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(blr->lock_num))); - } -#endif /* LARGE_SMB_OFF_T */ + BOOL err; + + count = get_lock_count( data, blr->lock_num, large_file_format, &err); + offset = get_lock_offset( data, blr->lock_num, large_file_format, &err); + + /* + * We know err cannot be set as if it was the lock + * request would never have been queued. JRA. + */ errno = 0; if(!do_lock(fsp,conn,count,offset, ((locktype & 1) ? F_RDLCK : F_WRLCK), &eclass, &ecode)) @@ -527,6 +522,16 @@ file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum )); } /**************************************************************************** + Return True if the blocking lock queue has entries. +*****************************************************************************/ + +BOOL blocking_locks_pending(void) +{ + blocking_lock_record *blr = (blocking_lock_record *)ubi_slFirst( &blocking_lock_queue ); + return (blr == NULL ? False : True); +} + +/**************************************************************************** Process the blocking lock queue. Note that this is only called as root. *****************************************************************************/ diff --git a/source3/smbd/chgpasswd.c b/source3/smbd/chgpasswd.c index 3d31db7fb5..b86091e773 100644 --- a/source3/smbd/chgpasswd.c +++ b/source3/smbd/chgpasswd.c @@ -52,24 +52,20 @@ extern int DEBUGLEVEL; #if ALLOW_CHANGE_PASSWORD -#define MINPASSWDLENGTH 5 -#define BUFSIZE 512 static int findpty(char **slave) { int master; -#ifndef HAVE_GRANTPT static fstring line; - DIR *dirp; - struct dirent *dentry; + void *dirp; char *dpname; -#endif /* !HAVE_GRANTPT */ #if defined(HAVE_GRANTPT) - if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 1) { + /* Try to open /dev/ptmx. If that fails, fall through to old method. */ + if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0) { grantpt(master); unlockpt(master); - *slave = ptsname(master); + *slave = (char *)ptsname(master); if (*slave == NULL) { DEBUG(0,("findpty: Unable to create master/slave pty pair.\n")); /* Stop fd leak on error. */ @@ -80,14 +76,14 @@ static int findpty(char **slave) return (master); } } -#else /* HAVE_GRANTPT */ +#endif /* HAVE_GRANTPT */ + fstrcpy( line, "/dev/ptyXX" ); - dirp = opendir("/dev"); + dirp = OpenDir(NULL, "/dev", False); if (!dirp) return(-1); - while ((dentry = readdir(dirp)) != NULL) { - dpname = dentry->d_name; + while ((dpname = ReadDirName(dirp)) != NULL) { if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) { DEBUG(3,("pty: try to open %s, line was %s\n", dpname, line ) ); line[8] = dpname[3]; @@ -96,13 +92,12 @@ static int findpty(char **slave) DEBUG(3,("pty: opened %s\n", line ) ); line[5] = 't'; *slave = line; - closedir(dirp); + CloseDir(dirp); return (master); } } } - closedir(dirp); -#endif /* HAVE_GRANTPT */ + CloseDir(dirp); return (-1); } @@ -110,9 +105,9 @@ static int dochild(int master,char *slavedev, char *name, char *passwordprogram, { int slave; struct termios stermios; - const struct passwd *pass = Get_Pwnam(name,True); - int gid; - int uid; + struct passwd *pass = Get_Pwnam(name,True); + gid_t gid; + uid_t uid; if (pass == NULL) { DEBUG(0,("dochild: user name %s doesn't exist in the UNIX password database.\n", @@ -122,11 +117,8 @@ static int dochild(int master,char *slavedev, char *name, char *passwordprogram, gid = pass->pw_gid; uid = pass->pw_uid; -#ifdef HAVE_SETRESUID - setresuid(0,0,0); -#else - setuid(0); -#endif + + gain_root_privilege(); /* Start new session - gets rid of controlling terminal. */ if (setsid() < 0) { @@ -186,19 +178,7 @@ static int dochild(int master,char *slavedev, char *name, char *passwordprogram, /* make us completely into the right uid */ if (!as_root) { -#ifdef HAVE_SETRESUID - setresgid(0,0,0); - setresuid(0,0,0); - setresgid(gid,gid,gid); - setresuid(uid,uid,uid); -#else - setuid(0); - seteuid(0); - setgid(gid); - setegid(gid); - setuid(uid); - seteuid(uid); -#endif + become_user_permanently(uid, gid); } DEBUG(10, ("Invoking '%s' as password change program.\n", passwordprogram)); @@ -211,92 +191,91 @@ static int dochild(int master,char *slavedev, char *name, char *passwordprogram, return(True); } -static int expect(int master,char *expected,char *buf) +static int expect(int master, char *issue, char *expected) { - int n, m; - - n = 0; - buf[0] = 0; - while (1) { - if (n >= BUFSIZE-1) { - return False; - } + pstring buffer; + int attempts, timeout, nread, len; + BOOL match = False; - /* allow 4 seconds for some output to appear */ - m = read_with_timeout(master, buf+n, 1, BUFSIZE-1-n, 4000); - if (m < 0) - return False; + for (attempts = 0; attempts < 2; attempts++) + { + if (!strequal(issue, ".")) + { + if (lp_passwd_chat_debug()) + DEBUG(100, ("expect: sending [%s]\n", issue)); - n += m; - buf[n] = 0; + write(master, issue, strlen(issue)); + } - { - pstring s1,s2; - pstrcpy(s1,buf); - pstrcpy(s2,expected); - if (do_match(s1, s2, False)) - return(True); - } - } + if (strequal(expected, ".")) + return True; + + timeout = 2000; + nread = 0; + buffer[nread] = 0; + + while ((len = read_with_timeout(master, buffer + nread, 1, + sizeof(buffer) - nread - 1, timeout)) > 0) + { + nread += len; + buffer[nread] = 0; + + if ((match = unix_do_match(buffer, expected, False))) + timeout = 200; + } + + if (lp_passwd_chat_debug()) + DEBUG(100, ("expect: expected [%s] received [%s]\n", + expected, buffer)); + + if (match) + break; + + if (len < 0) + { + DEBUG(2, ("expect: %s\n", strerror(errno))); + return False; + } + } + + return match; } static void pwd_sub(char *buf) { - string_sub(buf,"\\n","\n"); - string_sub(buf,"\\r","\r"); - string_sub(buf,"\\s"," "); - string_sub(buf,"\\t","\t"); + all_string_sub(buf,"\\n","\n",0); + all_string_sub(buf,"\\r","\r",0); + all_string_sub(buf,"\\s"," ",0); + all_string_sub(buf,"\\t","\t",0); } -static void writestring(int fd,char *s) +static int talktochild(int master, char *seq) { - int l; - - l = strlen (s); - write (fd, s, l); -} + int count = 0; + fstring issue, expected; + fstrcpy(issue, "."); -static int talktochild(int master, char *chatsequence) -{ - char buf[BUFSIZE]; - int count=0; - char *ptr=chatsequence; - fstring chatbuf; - - *buf = 0; - sleep(1); - - while (next_token(&ptr,chatbuf,NULL,sizeof(chatbuf))) { - BOOL ok=True; - count++; - pwd_sub(chatbuf); - if (!strequal(chatbuf,".")) - ok = expect(master,chatbuf,buf); - - if (lp_passwd_chat_debug()) - DEBUG(100,("talktochild: chatbuf=[%s] responsebuf=[%s]\n",chatbuf,buf)); - - if (!ok) { - DEBUG(3,("response %d incorrect\n",count)); - return(False); - } + while (next_token(&seq, expected, NULL, sizeof(expected))) + { + pwd_sub(expected); + count++; - if (!next_token(&ptr,chatbuf,NULL,sizeof(chatbuf))) break; - pwd_sub(chatbuf); - if (!strequal(chatbuf,".")) - writestring(master,chatbuf); + if (!expect(master, issue, expected)) + { + DEBUG(3,("Response %d incorrect\n", count)); + return False; + } - if (lp_passwd_chat_debug()) - DEBUG(100,("talktochild: sendbuf=[%s]\n",chatbuf)); - } + if (!next_token(&seq, issue, NULL, sizeof(issue))) + fstrcpy(issue, "."); - if (count<1) return(False); + pwd_sub(issue); + } - return (True); + return (count > 0); } - static BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequence, BOOL as_root) { char *slavedev; @@ -311,9 +290,17 @@ static BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequenc return(False); } + /* + * We need to temporarily stop CatchChild from eating + * SIGCLD signals as it also eats the exit status code. JRA. + */ + + CatchChildLeaveStatus(); + if ((pid = fork()) < 0) { DEBUG(3,("Cannot fork() child for password change: %s\n",name)); close(master); + CatchChild(); return(False); } @@ -324,12 +311,26 @@ static BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequenc kill(pid, SIGKILL); /* be sure to end this process */ } - if ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) { + while((wpid = sys_waitpid(pid, &wstat, 0)) < 0) { + if(errno == EINTR) { + errno = 0; + continue; + } + break; + } + + if (wpid < 0) { DEBUG(3,("The process is no longer waiting!\n\n")); close(master); + CatchChild(); return(False); } + /* + * Go back to ignoring children. + */ + CatchChild(); + close(master); if (pid != wpid) { @@ -362,8 +363,12 @@ static BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequenc DEBUG(3,("Dochild for user %s (uid=%d,gid=%d)\n",name,(int)getuid(),(int)getgid())); chstat = dochild(master, slavedev, name, passwordprogram, as_root); - if (as_root) - unbecome_root(False); + /* + * The child should never return from dochild() .... + */ + + DEBUG(0,("chat_with_program: Error: dochild() returned %d\n", chstat )); + exit(1); } if (chstat) @@ -376,8 +381,8 @@ BOOL chgpasswd(char *name,char *oldpass,char *newpass, BOOL as_root) { pstring passwordprogram; pstring chatsequence; - int i; - int len; + size_t i; + size_t len; strlower(name); DEBUG(3,("Password change for user: %s\n",name)); @@ -388,9 +393,10 @@ BOOL chgpasswd(char *name,char *oldpass,char *newpass, BOOL as_root) /* Take the passed information and test it for minimum criteria */ /* Minimum password length */ - if (strlen(newpass) < MINPASSWDLENGTH) /* too short, must be at least MINPASSWDLENGTH */ + if (strlen(newpass) < lp_min_passwd_length()) /* too short, must be at least MINPASSWDLENGTH */ { - DEBUG(2,("Password Change: %s, New password is shorter than MINPASSWDLENGTH\n",name)); + DEBUG(0,("Password Change: user %s, New password is shorter than minimum password length = %d\n", + name, lp_min_passwd_length())); return (False); /* inform the user */ } @@ -435,13 +441,14 @@ BOOL chgpasswd(char *name,char *oldpass,char *newpass, BOOL as_root) } } - string_sub(passwordprogram,"%u",name); - all_string_sub(passwordprogram,"%o",oldpass); - all_string_sub(passwordprogram,"%n",newpass); + pstring_sub(passwordprogram,"%u",name); + /* note that we do NOT substitute the %o and %n in the password program + as this would open up a security hole where the user could use + a new password containing shell escape characters */ - string_sub(chatsequence,"%u",name); - all_string_sub(chatsequence,"%o",oldpass); - all_string_sub(chatsequence,"%n",newpass); + pstring_sub(chatsequence,"%u",name); + all_string_sub(chatsequence,"%o",oldpass,sizeof(pstring)); + all_string_sub(chatsequence,"%n",newpass,sizeof(pstring)); return(chat_with_program(passwordprogram,name,chatsequence, as_root)); } @@ -531,7 +538,7 @@ BOOL change_lanman_password(struct smb_passwd *smbpw, uchar *pass1, uchar *pass2 if (smbpw->acct_ctrl & ACB_DISABLED) { - DEBUG(0,("change_lanman_password: account %s disabled.\n", smbpw->unix_name)); + DEBUG(0,("change_lanman_password: account %s disabled.\n", smbpw->smb_name)); return False; } @@ -573,14 +580,6 @@ BOOL pass_oem_change(char *user, &sampw, new_passwd, sizeof(new_passwd)); - /* now we check to see if we are actually allowed to change the - password. */ - - if (ret && (sampw->acct_ctrl & ACB_PWLOCK)) - { - ret = False; - } - /* * At this point we have the new case-sensitive plaintext * password in the fstring new_passwd. If we wanted to synchronise @@ -621,12 +620,12 @@ BOOL check_oem_password(char *user, static uchar null_pw[16]; static uchar null_ntpw[16]; struct smb_passwd *smbpw = NULL; + int new_pw_len; uchar new_ntp16[16]; uchar unenc_old_ntpw[16]; uchar new_p16[16]; uchar unenc_old_pw[16]; char no_pw[2]; - uint32 len; BOOL nt_pass_set = (ntdata != NULL && nthash != NULL); @@ -683,11 +682,35 @@ BOOL check_oem_password(char *user, */ SamOEMhash( (uchar *)lmdata, (uchar *)smbpw->smb_passwd, True); - if (!decode_pw_buffer(lmdata, new_passwd, new_passwd_size, &len)) + /* + * The length of the new password is in the last 4 bytes of + * the data buffer. + */ + + new_pw_len = IVAL(lmdata, 512); + if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1) { + DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len)); return False; } + if (nt_pass_set) + { + /* + * nt passwords are in unicode + */ + int uni_pw_len = new_pw_len; + char *pw; + new_pw_len /= 2; + pw = dos_unistrn2((uint16*)(&lmdata[512-uni_pw_len]), new_pw_len); + memcpy(new_passwd, pw, new_pw_len+1); + } + else + { + memcpy(new_passwd, &lmdata[512-new_pw_len], new_pw_len); + new_passwd[new_pw_len] = '\0'; + } + /* * To ensure we got the correct new password, hash it and * use it as a key to test the passed old password. diff --git a/source3/smbd/close.c b/source3/smbd/close.c index c37c2ceab2..d06cb3b5bb 100644 --- a/source3/smbd/close.c +++ b/source3/smbd/close.c @@ -23,9 +23,6 @@ extern int DEBUGLEVEL; -extern int32 global_oplocks_open; - - /**************************************************************************** run a file if it is a magic script ****************************************************************************/ @@ -72,6 +69,8 @@ static void close_filestruct(files_struct *fsp) { connection_struct *conn = fsp->conn; + flush_write_cache(fsp, CLOSE_FLUSH); + fsp->open = False; fsp->is_directory = False; @@ -80,13 +79,6 @@ static void close_filestruct(files_struct *fsp) free((char *)fsp->wbmpx_ptr); fsp->wbmpx_ptr = NULL; } - -#if WITH_MMAP - if(fsp->mmap_ptr) { - munmap(fsp->mmap_ptr,fsp->mmap_size); - fsp->mmap_ptr = NULL; - } -#endif } /**************************************************************************** @@ -97,14 +89,16 @@ static void close_filestruct(files_struct *fsp) the closing of the connection. In the latter case printing and magic scripts are not run. ****************************************************************************/ -void close_file(files_struct *fsp, BOOL normal_close) + +static int close_normal_file(files_struct *fsp, BOOL normal_close) { SMB_DEV_T dev = fsp->fd_ptr->dev; SMB_INO_T inode = fsp->fd_ptr->inode; int token; - BOOL last_reference = False; - BOOL delete_on_close = fsp->fd_ptr->delete_on_close; + BOOL last_reference = False; + BOOL delete_on_close = fsp->fd_ptr->delete_on_close; connection_struct *conn = fsp->conn; + int err = 0; remove_pending_lock_requests_by_fid(fsp); @@ -119,7 +113,10 @@ void close_file(files_struct *fsp, BOOL normal_close) del_share_mode(token, fsp); } - if(fd_attempt_close(fsp) == 0) + if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) + release_file_oplock(fsp); + + if(fd_attempt_close(fsp->fd_ptr,&err) == 0) last_reference = True; fsp->fd_ptr = NULL; @@ -129,7 +126,7 @@ void close_file(files_struct *fsp, BOOL normal_close) /* NT uses smbclose to start a print - weird */ if (normal_close && fsp->print_file) - print_file(conn, SNUM(conn), fsp); + print_file(conn, fsp); /* check for magic scripts */ if (normal_close) { @@ -144,7 +141,8 @@ void close_file(files_struct *fsp, BOOL normal_close) if (normal_close && last_reference && delete_on_close) { DEBUG(5,("close_file: file %s. Delete on close was set - deleting file.\n", fsp->fsp_name)); - if(fsp->conn->vfs_ops.unlink(dos_to_unix(fsp->fsp_name, False)) != 0) { + if(dos_unlink(fsp->fsp_name) != 0) { + /* * This call can potentially fail as another smbd may have * had the file open with delete on close set and deleted @@ -157,31 +155,47 @@ with error %s\n", fsp->fsp_name, strerror(errno) )); } } - if(fsp->granted_oplock == True) - global_oplocks_open--; - - fsp->sent_oplock_break = False; - - DEBUG(2,("%s closed file %s (numopen=%d)\n", + DEBUG(2,("%s closed file %s (numopen=%d) %s\n", conn->user,fsp->fsp_name, - conn->num_files_open)); + conn->num_files_open, err ? strerror(err) : "")); if (fsp->fsp_name) { string_free(&fsp->fsp_name); } file_free(fsp); + + return err; } /**************************************************************************** Close a directory opened by an NT SMB call. ****************************************************************************/ -void close_directory(files_struct *fsp) +static int close_directory(files_struct *fsp, BOOL normal_close) { remove_pending_change_notify_requests_by_fid(fsp); /* + * NT can set delete_on_close of the last open + * reference to a directory also. + */ + + if (normal_close && fsp->directory_delete_on_close) { + BOOL ok = rmdir_internals(fsp->conn, fsp->fsp_name); + DEBUG(5,("close_directory: %s. Delete on close was set - deleting directory %s.\n", + fsp->fsp_name, ok ? "succeeded" : "failed" )); + + /* + * Ensure we remove any change notify requests that would + * now fail as the directory has been deleted. + */ + + if(ok) + remove_pending_change_notify_requests_by_filename(fsp); + } + + /* * Do the code common to files and directories. */ close_filestruct(fsp); @@ -190,5 +204,35 @@ void close_directory(files_struct *fsp) string_free(&fsp->fsp_name); file_free(fsp); + + return 0; +} + +/**************************************************************************** + Close a file opened with null permissions in order to read permissions. +****************************************************************************/ + +static int close_statfile(files_struct *fsp, BOOL normal_close) +{ + close_filestruct(fsp); + + if (fsp->fsp_name) + string_free(&fsp->fsp_name); + + file_free(fsp); + + return 0; } +/**************************************************************************** + Close a directory opened by an NT SMB call. +****************************************************************************/ + +int close_file(files_struct *fsp, BOOL normal_close) +{ + if(fsp->is_directory) + return close_directory(fsp, normal_close); + else if(fsp->stat_open) + return close_statfile(fsp, normal_close); + return close_normal_file(fsp, normal_close); +} diff --git a/source3/smbd/connection.c b/source3/smbd/connection.c index db6c66f1d5..393a3e7372 100644 --- a/source3/smbd/connection.c +++ b/source3/smbd/connection.c @@ -34,7 +34,7 @@ BOOL yield_connection(connection_struct *conn,char *name,int max_connections) struct connect_record crec; pstring fname; int fd; - int mypid = getpid(); + pid_t mypid = getpid(); int i; DEBUG(3,("Yielding connection to %s\n",name)); @@ -42,7 +42,7 @@ BOOL yield_connection(connection_struct *conn,char *name,int max_connections) if (max_connections <= 0) return(True); - bzero(&crec,sizeof(crec)); + memset((char *)&crec,'\0',sizeof(crec)); pstrcpy(fname,lp_lockdir()); trim_string(fname,"","/"); @@ -85,7 +85,7 @@ BOOL yield_connection(connection_struct *conn,char *name,int max_connections) return(False); } - bzero((void *)&crec,sizeof(crec)); + memset((void *)&crec,'\0',sizeof(crec)); /* remove our mark */ if (sys_lseek(fd,i*sizeof(crec),SEEK_SET) != i*sizeof(crec) || @@ -154,7 +154,7 @@ BOOL claim_connection(connection_struct *conn,char *name,int max_connections,BOO return False; } - total_recs = file_size(fname) / sizeof(crec); + total_recs = get_file_size(fname) / sizeof(crec); /* find a free spot */ for (i=0;i<max_connections;i++) { @@ -168,10 +168,10 @@ BOOL claim_connection(connection_struct *conn,char *name,int max_connections,BOO if (Clear && crec.pid && !process_exists(crec.pid)) { if(sys_lseek(fd,i*sizeof(crec),SEEK_SET) != i*sizeof(crec)) { DEBUG(0,("claim_connection: ERROR: sys_lseek failed to seek \ -to %d\n", i*sizeof(crec) )); +to %d\n", (int)(i*sizeof(crec)) )); continue; } - bzero((void *)&crec,sizeof(crec)); + memset((void *)&crec,'\0',sizeof(crec)); write(fd, &crec,sizeof(crec)); if (foundi < 0) foundi = i; continue; @@ -192,7 +192,7 @@ to %d\n", i*sizeof(crec) )); } /* fill in the crec */ - bzero((void *)&crec,sizeof(crec)); + memset((void *)&crec,'\0',sizeof(crec)); crec.magic = 0x280267; crec.pid = getpid(); if (conn) { diff --git a/source3/smbd/dfree.c b/source3/smbd/dfree.c index 8cba8d0644..86a155f526 100644 --- a/source3/smbd/dfree.c +++ b/source3/smbd/dfree.c @@ -27,7 +27,7 @@ extern int DEBUGLEVEL; /**************************************************************************** normalise for DOS usage ****************************************************************************/ -static void disk_norm(SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize) +static void disk_norm(BOOL small_query, SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize) { /* check if the disk is beyond the max disk size */ SMB_BIG_UINT maxdisksize = lp_maxdisksize(); @@ -39,18 +39,23 @@ static void disk_norm(SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsiz /* the -1 should stop applications getting div by 0 errors */ } - + while (*dfree > WORDMAX || *dsize > WORDMAX || *bsize < 512) { *dfree /= 2; *dsize /= 2; *bsize *= 2; - if (*bsize > WORDMAX) { - *bsize = WORDMAX; - if (*dsize > WORDMAX) - *dsize = WORDMAX; - if (*dfree > WORDMAX) - *dfree = WORDMAX; - break; + if(small_query) { + /* + * Force max to fit in 16 bit fields. + */ + if (*bsize > (WORDMAX*512)) { + *bsize = (WORDMAX*512); + if (*dsize > WORDMAX) + *dsize = WORDMAX; + if (*dfree > WORDMAX) + *dfree = WORDMAX; + break; + } } } } @@ -152,7 +157,7 @@ static int fsusage(const char *path, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) #endif /* STAT_STATFS4 */ -#ifdef STAT_STATVFS /* SVR4 */ +#if defined(STAT_STATVFS) || defined(STAT_STATVFS64) /* SVR4 */ # define CONVERT_BLOCKS(B) \ adjust_blocks ((SMB_BIG_UINT)(B), fsd.f_frsize ? (SMB_BIG_UINT)fsd.f_frsize : (SMB_BIG_UINT)fsd.f_bsize, (SMB_BIG_UINT)512) @@ -185,7 +190,9 @@ static int fsusage(const char *path, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) /**************************************************************************** return number of 1K blocks available on a path and total number ****************************************************************************/ -static SMB_BIG_UINT disk_free(char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize) + +static SMB_BIG_UINT disk_free(char *path, BOOL small_query, + SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize) { int dfree_retval; SMB_BIG_UINT dfree_q = 0; @@ -219,7 +226,7 @@ static SMB_BIG_UINT disk_free(char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree *dfree = MAX(1,*dfree); } - disk_norm(bsize,dfree,dsize); + disk_norm(small_query,bsize,dfree,dsize); if ((*bsize) < 1024) { dfree_retval = (*dfree)/(1024/(*bsize)); @@ -234,7 +241,8 @@ static SMB_BIG_UINT disk_free(char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree /**************************************************************************** wrap it to get filenames right ****************************************************************************/ -SMB_BIG_UINT sys_disk_free(char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize) +SMB_BIG_UINT sys_disk_free(char *path, BOOL small_query, + SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize) { - return(disk_free(dos_to_unix(path,False),bsize,dfree,dsize)); + return(disk_free(dos_to_unix(path,False),small_query, bsize,dfree,dsize)); } diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index b7ae2af47c..28faa9a06b 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -27,257 +27,378 @@ extern int DEBUGLEVEL; This module implements directory related functions for Samba. */ - - -static uint32 dircounter = 0; - - -#define NUMDIRPTRS 256 - - -static struct dptr_struct { - int pid; +typedef struct _dptr_struct { + struct _dptr_struct *next, *prev; + int dnum; + uint16 spid; connection_struct *conn; - uint32 lastused; void *ptr; - BOOL valid; - BOOL finished; BOOL expect_close; char *wcard; /* Field only used for trans2_ searches */ uint16 attr; /* Field only used for trans2_ searches */ char *path; -} -dirptrs[NUMDIRPTRS]; +} dptr_struct; +static struct bitmap *dptr_bmap; +static dptr_struct *dirptrs; static int dptrs_open = 0; +#define INVALID_DPTR_KEY (-3) + /**************************************************************************** -initialise the dir array + Initialise the dir bitmap. ****************************************************************************/ + void init_dptrs(void) { static BOOL dptrs_init=False; - int i; - if (dptrs_init) return; - for (i=0;i<NUMDIRPTRS;i++) - { - dirptrs[i].valid = False; - dirptrs[i].wcard = NULL; - dirptrs[i].ptr = NULL; - string_init(&dirptrs[i].path,""); - } + if (dptrs_init) + return; + + dptr_bmap = bitmap_allocate(MAX_DIRECTORY_HANDLES); + + if (!dptr_bmap) + exit_server("out of memory in init_dptrs\n"); + dptrs_init = True; } /**************************************************************************** -idle a dptr - the directory is closed but the control info is kept + Idle a dptr - the directory is closed but the control info is kept. ****************************************************************************/ -static void dptr_idle(int key) + +static void dptr_idle(dptr_struct *dptr) { - if (dirptrs[key].valid && dirptrs[key].ptr) { - DEBUG(4,("Idling dptr key %d\n",key)); + if (dptr->ptr) { + DEBUG(4,("Idling dptr dnum %d\n",dptr->dnum)); dptrs_open--; - CloseDir(dirptrs[key].ptr); - dirptrs[key].ptr = NULL; - } + CloseDir(dptr->ptr); + dptr->ptr = NULL; + } } /**************************************************************************** -idle the oldest dptr + Idle the oldest dptr. ****************************************************************************/ + static void dptr_idleoldest(void) { - int i; - uint32 old=dircounter+1; - int oldi= -1; - for (i=0;i<NUMDIRPTRS;i++) - if (dirptrs[i].valid && dirptrs[i].ptr && dirptrs[i].lastused < old) { - old = dirptrs[i].lastused; - oldi = i; + dptr_struct *dptr; + + /* + * Go to the end of the list. + */ + for(dptr = dirptrs; dptr && dptr->next; dptr = dptr->next) + ; + + if(!dptr) { + DEBUG(0,("No dptrs available to idle ?\n")); + return; + } + + /* + * Idle the oldest pointer. + */ + + for(; dptr; dptr = dptr->prev) { + if (dptr->ptr) { + dptr_idle(dptr); + return; } - if (oldi != -1) - dptr_idle(oldi); - else - DEBUG(0,("No dptrs available to idle??\n")); + } } /**************************************************************************** -get the dir ptr for a dir index + Get the dptr_struct for a dir index. ****************************************************************************/ -static void *dptr_get(int key,uint32 lastused) + +static dptr_struct *dptr_get(int key, BOOL forclose) { - struct dptr_struct *dp = &dirptrs[key]; - - if (dp->valid) { - if (lastused) dp->lastused = lastused; - if (!dp->ptr) { - if (dptrs_open >= MAX_OPEN_DIRECTORIES) - dptr_idleoldest(); - DEBUG(4,("Reopening dptr key %d\n",key)); - if ((dp->ptr = OpenDir(dp->conn, dp->path, True))) - dptrs_open++; - } - return(dp->ptr); - } - return(NULL); + dptr_struct *dptr; + + for(dptr = dirptrs; dptr; dptr = dptr->next) { + if(dptr->dnum == key) { + if (!forclose && !dptr->ptr) { + if (dptrs_open >= MAX_OPEN_DIRECTORIES) + dptr_idleoldest(); + DEBUG(4,("Reopening dptr key %d\n",key)); + if ((dptr->ptr = OpenDir(dptr->conn, dptr->path, True))) + dptrs_open++; + } + DLIST_PROMOTE(dirptrs,dptr); + return dptr; + } + } + return(NULL); } /**************************************************************************** -get the dir path for a dir index + Get the dptr ptr for a dir index. ****************************************************************************/ + +static void *dptr_ptr(int key) +{ + dptr_struct *dptr = dptr_get(key, False); + + if (dptr) + return(dptr->ptr); + return(NULL); +} + +/**************************************************************************** + Get the dir path for a dir index. +****************************************************************************/ + char *dptr_path(int key) { - if (dirptrs[key].valid) - return(dirptrs[key].path); + dptr_struct *dptr = dptr_get(key, False); + + if (dptr) + return(dptr->path); return(NULL); } /**************************************************************************** -get the dir wcard for a dir index (lanman2 specific) + Get the dir wcard for a dir index (lanman2 specific). ****************************************************************************/ + char *dptr_wcard(int key) { - if (dirptrs[key].valid) - return(dirptrs[key].wcard); + dptr_struct *dptr = dptr_get(key, False); + + if (dptr) + return(dptr->wcard); return(NULL); } /**************************************************************************** -set the dir wcard for a dir index (lanman2 specific) -Returns 0 on ok, 1 on fail. + Set the dir wcard for a dir index (lanman2 specific). + Returns 0 on ok, 1 on fail. ****************************************************************************/ + BOOL dptr_set_wcard(int key, char *wcard) { - if (dirptrs[key].valid) { - dirptrs[key].wcard = wcard; + dptr_struct *dptr = dptr_get(key, False); + + if (dptr) { + dptr->wcard = wcard; return True; } return False; } /**************************************************************************** -set the dir attrib for a dir index (lanman2 specific) -Returns 0 on ok, 1 on fail. + Set the dir attrib for a dir index (lanman2 specific). + Returns 0 on ok, 1 on fail. ****************************************************************************/ + BOOL dptr_set_attr(int key, uint16 attr) { - if (dirptrs[key].valid) { - dirptrs[key].attr = attr; + dptr_struct *dptr = dptr_get(key, False); + + if (dptr) { + dptr->attr = attr; return True; } return False; } /**************************************************************************** -get the dir attrib for a dir index (lanman2 specific) + Get the dir attrib for a dir index (lanman2 specific) ****************************************************************************/ + uint16 dptr_attr(int key) { - if (dirptrs[key].valid) - return(dirptrs[key].attr); + dptr_struct *dptr = dptr_get(key, False); + + if (dptr) + return(dptr->attr); return(0); } /**************************************************************************** -close a dptr + Close a dptr (internal func). +****************************************************************************/ + +static void dptr_close_internal(dptr_struct *dptr) +{ + DEBUG(4,("closing dptr key %d\n",dptr->dnum)); + + DLIST_REMOVE(dirptrs, dptr); + + /* + * Free the dnum in the bitmap. Remember the dnum value is always + * biased by one with respect to the bitmap. + */ + + if(bitmap_query( dptr_bmap, dptr->dnum - 1) != True) { + DEBUG(0,("dptr_close_internal : Error - closing dnum = %d and bitmap not set !\n", + dptr->dnum )); + } + + bitmap_clear(dptr_bmap, dptr->dnum - 1); + + if (dptr->ptr) { + CloseDir(dptr->ptr); + dptrs_open--; + } + + /* Lanman 2 specific code */ + if (dptr->wcard) + free(dptr->wcard); + string_set(&dptr->path,""); + free((char *)dptr); +} + +/**************************************************************************** + Close a dptr given a key. ****************************************************************************/ -void dptr_close(int key) + +void dptr_close(int *key) { + dptr_struct *dptr; + + if(*key == INVALID_DPTR_KEY) + return; + /* OS/2 seems to use -1 to indicate "close all directories" */ - if (key == -1) { - int i; - for (i=0;i<NUMDIRPTRS;i++) - dptr_close(i); + if (*key == -1) { + dptr_struct *next; + for(dptr = dirptrs; dptr; dptr = next) { + next = dptr->next; + dptr_close_internal(dptr); + } + *key = INVALID_DPTR_KEY; return; } - if (key < 0 || key >= NUMDIRPTRS) { - DEBUG(3,("Invalid key %d given to dptr_close\n",key)); + dptr = dptr_get(*key, True); + + if (!dptr) { + DEBUG(0,("Invalid key %d given to dptr_close\n", *key)); return; } - if (dirptrs[key].valid) { - DEBUG(4,("closing dptr key %d\n",key)); - if (dirptrs[key].ptr) { - CloseDir(dirptrs[key].ptr); - dptrs_open--; - } - /* Lanman 2 specific code */ - if (dirptrs[key].wcard) - free(dirptrs[key].wcard); - dirptrs[key].valid = False; - string_set(&dirptrs[key].path,""); - } + dptr_close_internal(dptr); + + *key = INVALID_DPTR_KEY; } /**************************************************************************** -close all dptrs for a cnum + Close all dptrs for a cnum. ****************************************************************************/ + void dptr_closecnum(connection_struct *conn) { - int i; - for (i=0;i<NUMDIRPTRS;i++) - if (dirptrs[i].valid && dirptrs[i].conn == conn) - dptr_close(i); + dptr_struct *dptr, *next; + for(dptr = dirptrs; dptr; dptr = next) { + next = dptr->next; + if (dptr->conn == conn) + dptr_close_internal(dptr); + } } /**************************************************************************** -idle all dptrs for a cnum + Idle all dptrs for a cnum. ****************************************************************************/ + void dptr_idlecnum(connection_struct *conn) { - int i; - for (i=0;i<NUMDIRPTRS;i++) - if (dirptrs[i].valid && dirptrs[i].conn == conn && dirptrs[i].ptr) - dptr_idle(i); + dptr_struct *dptr; + for(dptr = dirptrs; dptr; dptr = dptr->next) { + if (dptr->conn == conn && dptr->ptr) + dptr_idle(dptr); + } } /**************************************************************************** -close a dptr that matches a given path, only if it matches the pid also + Close a dptr that matches a given path, only if it matches the spid also. ****************************************************************************/ -void dptr_closepath(char *path,int pid) + +void dptr_closepath(char *path,uint16 spid) { - int i; - for (i=0;i<NUMDIRPTRS;i++) - if (dirptrs[i].valid && pid == dirptrs[i].pid && - strequal(dirptrs[i].path,path)) - dptr_close(i); + dptr_struct *dptr, *next; + for(dptr = dirptrs; dptr; dptr = next) { + next = dptr->next; + if (spid == dptr->spid && strequal(dptr->path,path)) + dptr_close_internal(dptr); + } } /**************************************************************************** - start a directory listing + Start a directory listing. ****************************************************************************/ + static BOOL start_dir(connection_struct *conn,char *directory) { - DEBUG(5,("start_dir dir=%s\n",directory)); + DEBUG(5,("start_dir dir=%s\n",directory)); - if (!check_name(directory,conn)) - return(False); + if (!check_name(directory,conn)) + return(False); - if (! *directory) - directory = "."; - - conn->dirptr = OpenDir(conn, directory, True); - if (conn->dirptr) { - dptrs_open++; - string_set(&conn->dirpath,directory); - return(True); - } + if (! *directory) + directory = "."; + + conn->dirptr = OpenDir(conn, directory, True); + if (conn->dirptr) { + dptrs_open++; + string_set(&conn->dirpath,directory); + return(True); + } - return(False); + return(False); } +/**************************************************************************** + Try and close the oldest handle not marked for + expect close in the hope that the client has + finished with that one. +****************************************************************************/ + +static void dptr_close_oldest(BOOL old) +{ + dptr_struct *dptr; + + /* + * Go to the end of the list. + */ + for(dptr = dirptrs; dptr && dptr->next; dptr = dptr->next) + ; + + if(!dptr) { + DEBUG(0,("No old dptrs available to close oldest ?\n")); + return; + } + + /* + * If 'old' is true, close the oldest oldhandle dnum (ie. 1 < dnum < 256) that + * does not have expect_close set. If 'old' is false, close + * one of the new dnum handles. + */ + + for(; dptr; dptr = dptr->prev) { + if ((old && (dptr->dnum < 256) && !dptr->expect_close) || + (!old && (dptr->dnum > 255))) { + dptr_close_internal(dptr); + return; + } + } +} /**************************************************************************** -create a new dir ptr + Create a new dir ptr. If the flag old_handle is true then we must allocate + from the bitmap range 0 - 255 as old SMBsearch directory handles are only + one byte long. If old_handle is false we allocate from the range + 256 - MAX_DIRECTORY_HANDLES. We bias the number we return by 1 to ensure + a directory handle is never zero. All the above is folklore taught to + me at Andrew's knee.... :-) :-). JRA. ****************************************************************************/ -int dptr_create(connection_struct *conn,char *path, BOOL expect_close,int pid) + +int dptr_create(connection_struct *conn,char *path, BOOL old_handle, BOOL expect_close,uint16 spid) { - int i; - uint32 old; - int oldi; + dptr_struct *dptr; if (!start_dir(conn,path)) return(-2); /* Code to say use a unix error return code. */ @@ -285,70 +406,103 @@ int dptr_create(connection_struct *conn,char *path, BOOL expect_close,int pid) if (dptrs_open >= MAX_OPEN_DIRECTORIES) dptr_idleoldest(); - for (i=0;i<NUMDIRPTRS;i++) - if (!dirptrs[i].valid) - break; - if (i == NUMDIRPTRS) i = -1; + dptr = (dptr_struct *)malloc(sizeof(dptr_struct)); + if(!dptr) { + DEBUG(0,("malloc fail in dptr_create.\n")); + return -1; + } + + ZERO_STRUCTP(dptr); + + if(old_handle) { + + /* + * This is an old-style SMBsearch request. Ensure the + * value we return will fit in the range 1-255. + */ + + dptr->dnum = bitmap_find(dptr_bmap, 0); + + if(dptr->dnum == -1 || dptr->dnum > 254) { + + /* + * Try and close the oldest handle not marked for + * expect close in the hope that the client has + * finished with that one. + */ + + dptr_close_oldest(True); + /* Now try again... */ + dptr->dnum = bitmap_find(dptr_bmap, 0); - /* as a 2nd option, grab the oldest not marked for expect_close */ - if (i == -1) { - old=dircounter+1; - oldi= -1; - for (i=0;i<NUMDIRPTRS;i++) - if (!dirptrs[i].expect_close && dirptrs[i].lastused < old) { - old = dirptrs[i].lastused; - oldi = i; + if(dptr->dnum == -1 || dptr->dnum > 254) { + DEBUG(0,("dptr_create: returned %d: Error - all old dirptrs in use ?\n", dptr->dnum)); + free((char *)dptr); + return -1; } - i = oldi; - } + } + } else { + + /* + * This is a new-style trans2 request. Allocate from + * a range that will return 256 - MAX_DIRECTORY_HANDLES. + */ + + dptr->dnum = bitmap_find(dptr_bmap, 255); + + if(dptr->dnum == -1 || dptr->dnum < 255) { - /* a 3rd option - grab the oldest one */ - if (i == -1) { - old=dircounter+1; - oldi= -1; - for (i=0;i<NUMDIRPTRS;i++) - if (dirptrs[i].lastused < old) { - old = dirptrs[i].lastused; - oldi = i; + /* + * Try and close the oldest handle close in the hope that + * the client has finished with that one. This will only + * happen in the case of the Win98 client bug where it leaks + * directory handles. + */ + + dptr_close_oldest(False); + + /* Now try again... */ + dptr->dnum = bitmap_find(dptr_bmap, 255); + + if(dptr->dnum == -1 || dptr->dnum < 255) { + DEBUG(0,("dptr_create: returned %d: Error - all new dirptrs in use ?\n", dptr->dnum)); + free((char *)dptr); + return -1; } - i = oldi; + } } - if (i == -1) { - DEBUG(0,("Error - all dirptrs in use??\n")); - return(-1); - } + bitmap_set(dptr_bmap, dptr->dnum); + + dptr->dnum += 1; /* Always bias the dnum by one - no zero dnums allowed. */ - if (dirptrs[i].valid) - dptr_close(i); + dptr->ptr = conn->dirptr; + string_set(&dptr->path,path); + dptr->conn = conn; + dptr->spid = spid; + dptr->expect_close = expect_close; + dptr->wcard = NULL; /* Only used in lanman2 searches */ + dptr->attr = 0; /* Only used in lanman2 searches */ - dirptrs[i].ptr = conn->dirptr; - string_set(&dirptrs[i].path,path); - dirptrs[i].lastused = dircounter++; - dirptrs[i].finished = False; - dirptrs[i].conn = conn; - dirptrs[i].pid = pid; - dirptrs[i].expect_close = expect_close; - dirptrs[i].wcard = NULL; /* Only used in lanman2 searches */ - dirptrs[i].attr = 0; /* Only used in lanman2 searches */ - dirptrs[i].valid = True; + DLIST_ADD(dirptrs, dptr); DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n", - i,path,expect_close)); + dptr->dnum,path,expect_close)); - return(i); + return(dptr->dnum); } #define DPTR_MASK ((uint32)(((uint32)1)<<31)) /**************************************************************************** -fill the 5 byte server reserved dptr field + Fill the 5 byte server reserved dptr field. ****************************************************************************/ + BOOL dptr_fill(char *buf1,unsigned int key) { unsigned char *buf = (unsigned char *)buf1; - void *p = dptr_get(key,0); + void *p = dptr_ptr(key); uint32 offset; if (!p) { DEBUG(1,("filling null dirptr %d\n",key)); @@ -364,20 +518,22 @@ BOOL dptr_fill(char *buf1,unsigned int key) /**************************************************************************** -return True is the offset is at zero + Return True if the offset is at zero. ****************************************************************************/ + BOOL dptr_zero(char *buf) { return((IVAL(buf,1)&~DPTR_MASK) == 0); } /**************************************************************************** -fetch the dir ptr and seek it given the 5 byte server field + Fetch the dir ptr and seek it given the 5 byte server field. ****************************************************************************/ + void *dptr_fetch(char *buf,int *num) { unsigned int key = *(unsigned char *)buf; - void *p = dptr_get(key,dircounter++); + void *p = dptr_ptr(key); uint32 offset; if (!p) { DEBUG(3,("fetched null dirptr %d\n",key)); @@ -392,11 +548,12 @@ void *dptr_fetch(char *buf,int *num) } /**************************************************************************** -fetch the dir ptr. + Fetch the dir ptr. ****************************************************************************/ + void *dptr_fetch_lanman2(int dptr_num) { - void *p = dptr_get(dptr_num,dircounter++); + void *p = dptr_ptr(dptr_num); if (!p) { DEBUG(3,("fetched null dirptr %d\n",dptr_num)); @@ -407,8 +564,9 @@ void *dptr_fetch_lanman2(int dptr_num) } /**************************************************************************** -check a filetype for being valid + Check a filetype for being valid. ****************************************************************************/ + BOOL dir_check_ftype(connection_struct *conn,int mode,SMB_STRUCT_STAT *st,int dirtype) { if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0) @@ -417,8 +575,9 @@ BOOL dir_check_ftype(connection_struct *conn,int mode,SMB_STRUCT_STAT *st,int di } /**************************************************************************** - get a directory entry + Get an 8.3 directory entry. ****************************************************************************/ + BOOL get_dir_entry(connection_struct *conn,char *mask,int dirtype,char *fname, SMB_OFF_T *size,int *mode,time_t *date,BOOL check_descend) { @@ -437,64 +596,71 @@ BOOL get_dir_entry(connection_struct *conn,char *mask,int dirtype,char *fname, strequal(conn->dirpath,".") || strequal(conn->dirpath,"/")); - needslash = - ( conn->dirpath[strlen(conn->dirpath) -1] != '/'); + needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/'); if (!conn->dirptr) return(False); while (!found) - { - dname = ReadDirName(conn->dirptr); + { + BOOL filename_is_mask = False; + dname = ReadDirName(conn->dirptr); - DEBUG(6,("readdir on dirptr 0x%lx now at offset %d\n", - (long)conn->dirptr,TellDir(conn->dirptr))); + DEBUG(6,("readdir on dirptr 0x%lx now at offset %d\n", + (long)conn->dirptr,TellDir(conn->dirptr))); - if (dname == NULL) - return(False); + if (dname == NULL) + return(False); - pstrcpy(filename,dname); - - if ((strcmp(filename,mask) == 0) || - (name_map_mangle(filename,True,SNUM(conn)) && - mask_match(filename,mask,False,False))) - { - if (isrootdir && (strequal(filename,"..") || strequal(filename,"."))) - continue; - - pstrcpy(fname,filename); - *path = 0; - pstrcpy(path,conn->dirpath); - if(needslash) - pstrcat(path,"/"); - pstrcpy(pathreal,path); - pstrcat(path,fname); - pstrcat(pathreal,dname); - if (conn->vfs_ops.stat(dos_to_unix(pathreal, False), &sbuf) != 0) - { - DEBUG(5,("Couldn't stat 1 [%s]\n",path)); - continue; - } - - if (check_descend && - !strequal(fname,".") && !strequal(fname,"..")) - continue; + pstrcpy(filename,dname); + + if ((filename_is_mask = (strcmp(filename,mask) == 0)) || + (name_map_mangle(filename,True,False,SNUM(conn)) && + mask_match(filename,mask,False,False))) + { + if (isrootdir && (strequal(filename,"..") || strequal(filename,"."))) + continue; + + pstrcpy(fname,filename); + *path = 0; + pstrcpy(path,conn->dirpath); + if(needslash) + pstrcat(path,"/"); + pstrcpy(pathreal,path); + pstrcat(path,fname); + pstrcat(pathreal,dname); + if (dos_stat(pathreal,&sbuf) != 0) + { + DEBUG(5,("Couldn't stat 1 [%s]. Error = %s\n",path, strerror(errno) )); + continue; + } + + if (check_descend && !strequal(fname,".") && !strequal(fname,"..")) + continue; - *mode = dos_mode(conn,pathreal,&sbuf); + *mode = dos_mode(conn,pathreal,&sbuf); + + if (!dir_check_ftype(conn,*mode,&sbuf,dirtype)) + { + DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype)); + continue; + } - if (!dir_check_ftype(conn,*mode,&sbuf,dirtype)) { - DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype)); - continue; - } + if (!filename_is_mask) + { + /* Now we can allow the mangled cache to be updated */ + pstrcpy(filename,dname); + name_map_mangle(filename,True,True,SNUM(conn)); + } - *size = sbuf.st_size; - *date = sbuf.st_mtime; + *size = sbuf.st_size; + *date = sbuf.st_mtime; - DEBUG(5,("get_dir_entry found %s fname=%s\n",pathreal,fname)); + DEBUG(5,("get_dir_entry found %s fname=%s\n",pathreal,fname)); - found = True; - } + found = True; } + } return(found); } @@ -512,25 +678,26 @@ typedef struct /******************************************************************* -open a directory + Open a directory. ********************************************************************/ + void *OpenDir(connection_struct *conn, char *name, BOOL use_veto) { Dir *dirp; char *n; - DIR *p = conn->vfs_ops.opendir(name); + DIR *p = dos_opendir(name); int used=0; if (!p) return(NULL); dirp = (Dir *)malloc(sizeof(Dir)); if (!dirp) { - conn->vfs_ops.closedir(p); + closedir(p); return(NULL); } dirp->pos = dirp->numentries = dirp->mallocsize = 0; dirp->data = dirp->current = NULL; - while ((n = vfs_readdirname(conn, p))) + while ((n = dos_readdirname(p))) { int l = strlen(n)+1; @@ -554,14 +721,15 @@ void *OpenDir(connection_struct *conn, char *name, BOOL use_veto) dirp->numentries++; } - conn->vfs_ops.closedir(p); + closedir(p); return((void *)dirp); } /******************************************************************* -close a directory + Close a directory. ********************************************************************/ + void CloseDir(void *p) { Dir *dirp = (Dir *)p; @@ -571,8 +739,9 @@ void CloseDir(void *p) } /******************************************************************* -read from a directory + Read from a directory. ********************************************************************/ + char *ReadDirName(void *p) { char *ret; @@ -589,8 +758,9 @@ char *ReadDirName(void *p) /******************************************************************* -seek a dir + Seek a dir. ********************************************************************/ + BOOL SeekDir(void *p,int pos) { Dir *dirp = (Dir *)p; @@ -608,8 +778,9 @@ BOOL SeekDir(void *p,int pos) } /******************************************************************* -tell a dir position + Tell a dir position. ********************************************************************/ + int TellDir(void *p) { Dir *dirp = (Dir *)p; diff --git a/source3/smbd/dosmode.c b/source3/smbd/dosmode.c index e6f1dc7206..27393fe1c6 100644 --- a/source3/smbd/dosmode.c +++ b/source3/smbd/dosmode.c @@ -50,7 +50,7 @@ mode_t unix_mode(connection_struct *conn,int dosmode) can always create a file in a read-only directory. */ result |= (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH | S_IWUSR); /* Apply directory mask */ - result &= lp_dir_mode(SNUM(conn)); + result &= lp_dir_mask(SNUM(conn)); /* Add in force bits */ result |= lp_force_dir_mode(SNUM(conn)); } else { @@ -64,7 +64,7 @@ mode_t unix_mode(connection_struct *conn,int dosmode) result |= S_IXOTH; /* Apply mode mask */ - result &= lp_create_mode(SNUM(conn)); + result &= lp_create_mask(SNUM(conn)); /* Add in force bits */ result |= lp_force_create_mode(SNUM(conn)); } @@ -148,7 +148,7 @@ int file_chmod(connection_struct *conn,char *fname,int dosmode,SMB_STRUCT_STAT * if (!st) { st = &st1; - if (conn->vfs_ops.stat(dos_to_unix(fname,False),st)) return(-1); + if (dos_stat(fname,st)) return(-1); } if (S_ISDIR(st->st_mode)) dosmode |= aDIR; @@ -179,14 +179,12 @@ int file_chmod(connection_struct *conn,char *fname,int dosmode,SMB_STRUCT_STAT * } /* if we previously had any w bits set then leave them alone - if the new mode is not rdonly */ - if (!IS_DOS_READONLY(dosmode) && - (tmp = st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))) { - unixmode &= ~(S_IWUSR|S_IWGRP|S_IWOTH); - unixmode |= tmp; + whilst adding in the new w bits, if the new mode is not rdonly */ + if (!IS_DOS_READONLY(dosmode)) { + unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)); } - return(conn->vfs_ops.chmod(fname,unixmode)); + return(dos_chmod(fname,unixmode)); } @@ -202,7 +200,7 @@ int file_utime(connection_struct *conn, char *fname, struct utimbuf *times) errno = 0; - if(conn->vfs_ops.utime(dos_to_unix(fname, False), times) == 0) + if(dos_utime(fname, times) == 0) return 0; if((errno != EPERM) && (errno != EACCES)) @@ -217,7 +215,7 @@ int file_utime(connection_struct *conn, char *fname, struct utimbuf *times) (as DOS does). */ - if(conn->vfs_ops.stat(dos_to_unix(fname,False),&sb) != 0) + if(dos_stat(fname,&sb) != 0) return -1; /* Check if we have write access. */ @@ -230,7 +228,7 @@ int file_utime(connection_struct *conn, char *fname, struct utimbuf *times) current_user.ngroups,current_user.groups)))) { /* We are allowed to become root and change the filetime. */ become_root(False); - ret = conn->vfs_ops.utime(dos_to_unix(fname, False), times); + ret = dos_utime(fname, times); unbecome_root(False); } } @@ -251,9 +249,8 @@ BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime) if (file_utime(conn, fname, ×)) { DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno))); + return False; } return(True); } - - diff --git a/source3/smbd/fileio.c b/source3/smbd/fileio.c index 1e16627515..8b48a921fd 100644 --- a/source3/smbd/fileio.c +++ b/source3/smbd/fileio.c @@ -23,6 +23,7 @@ extern int DEBUGLEVEL; +static BOOL setup_write_cache(files_struct *, SMB_OFF_T); /**************************************************************************** seek a file. Try to avoid the seek if possible @@ -36,7 +37,19 @@ SMB_OFF_T seek_file(files_struct *fsp,SMB_OFF_T pos) if (fsp->print_file && lp_postscript(fsp->conn->service)) offset = 3; - seek_ret = fsp->conn->vfs_ops.lseek(fsp->fd_ptr->fd,pos+offset,SEEK_SET); + seek_ret = sys_lseek(fsp->fd_ptr->fd,pos+offset,SEEK_SET); + + /* + * We want to maintain the fiction that we can seek + * on a fifo for file system purposes. This allows + * people to set up UNIX fifo's that feed data to Windows + * applications. JRA. + */ + + if((seek_ret == -1) && (errno == ESPIPE)) { + seek_ret = pos+offset; + errno = 0; + } if((seek_ret == -1) || (seek_ret != pos+offset)) { DEBUG(0,("seek_file: sys_lseek failed. Error was %s\n", strerror(errno) )); @@ -53,6 +66,29 @@ SMB_OFF_T seek_file(files_struct *fsp,SMB_OFF_T pos) } /**************************************************************************** + Read from write cache if we can. +****************************************************************************/ + +static unsigned int cache_read_hits; + +BOOL read_from_write_cache(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n) +{ + write_cache *wcp = fsp->wcp; + + if(!wcp) + return False; + + if(n > wcp->data_size || pos < wcp->offset || pos + n > wcp->offset + wcp->data_size) + return False; + + memcpy(data, wcp->data + (pos - wcp->offset), n); + + cache_read_hits++; + + return True; +} + +/**************************************************************************** read from a file ****************************************************************************/ @@ -62,7 +98,7 @@ ssize_t read_file(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n) #if USE_READ_PREDICTION if (!fsp->can_write) { - ret = read_predict(fsp, fsp->fd_ptr->fd,pos,data,NULL,n); + ret = read_predict(fsp->fd_ptr->fd,pos,data,NULL,n); data += ret; n -= ret; @@ -70,40 +106,64 @@ ssize_t read_file(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n) } #endif -#if WITH_MMAP - if (fsp->mmap_ptr) { - SMB_OFF_T num = (fsp->mmap_size > pos) ? (fsp->mmap_size - pos) : 0; - num = MIN(n,num); - if (num > 0) { - memcpy(data,fsp->mmap_ptr+pos,num); - data += num; - pos += num; - n -= num; - ret += num; - } - } -#endif + /* + * Serve from write cache if we can. + */ + if(read_from_write_cache(fsp, data, pos, n)) + return n; + + flush_write_cache(fsp, READ_FLUSH); if (seek_file(fsp,pos) == -1) { DEBUG(3,("read_file: Failed to seek to %.0f\n",(double)pos)); return(ret); } - + if (n > 0) { - readret = fsp->conn->vfs_ops.read(fsp->fd_ptr->fd,data,n); + readret = read(fsp->fd_ptr->fd,data,n); if (readret > 0) ret += readret; } return(ret); } +/* Write cache static counters. */ + +static unsigned int abutted_writes; +static unsigned int total_writes; +static unsigned int non_oplock_writes; +static unsigned int direct_writes; +static unsigned int init_writes; +static unsigned int flushed_writes; +static unsigned int num_perfect_writes; +static unsigned int flush_reasons[NUM_FLUSH_REASONS]; + +/* how many write cache buffers have been allocated */ +static unsigned int allocated_write_caches; +static unsigned int num_write_caches; + +/**************************************************************************** + *Really* write to a file +****************************************************************************/ + +static ssize_t real_write_file(files_struct *fsp,char *data,SMB_OFF_T pos, size_t n) +{ + if ((pos != -1) && (seek_file(fsp,pos) == -1)) + return -1; + + return write_data(fsp->fd_ptr->fd,data,n); +} /**************************************************************************** write to a file ****************************************************************************/ -ssize_t write_file(files_struct *fsp,char *data,size_t n) +ssize_t write_file(files_struct *fsp, char *data, SMB_OFF_T pos, size_t n) { + write_cache *wcp = fsp->wcp; + ssize_t total_written = 0; + int write_path = -1; + if (!fsp->can_write) { errno = EPERM; return(0); @@ -112,25 +172,488 @@ ssize_t write_file(files_struct *fsp,char *data,size_t n) if (!fsp->modified) { SMB_STRUCT_STAT st; fsp->modified = True; - if (fsp->conn->vfs_ops.fstat(fsp->fd_ptr->fd,&st) == 0) { + + if (sys_fstat(fsp->fd_ptr->fd,&st) == 0) { int dosmode = dos_mode(fsp->conn,fsp->fsp_name,&st); if (MAP_ARCHIVE(fsp->conn) && !IS_DOS_ARCHIVE(dosmode)) { file_chmod(fsp->conn,fsp->fsp_name,dosmode | aARCH,&st); } + + /* + * If this is the first write and we have an exclusive oplock then setup + * the write cache. + */ + + if ((fsp->oplock_type == EXCLUSIVE_OPLOCK) && !wcp) { + setup_write_cache(fsp, st.st_size); + wcp = fsp->wcp; + } } } - return(vfs_write_data(fsp,data,n)); + total_writes++; + if (!fsp->oplock_type) { + non_oplock_writes++; + } + + /* + * If this file is level II oplocked then we need + * to grab the shared memory lock and inform all + * other files with a level II lock that they need + * to flush their read caches. We keep the lock over + * the shared memory area whilst doing this. + */ + + if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) { + SMB_DEV_T dev = fsp->fd_ptr->dev; + SMB_INO_T inode = fsp->fd_ptr->inode; + share_mode_entry *share_list = NULL; + pid_t pid = getpid(); + int token = -1; + int num_share_modes = 0; + int i; + + if (lock_share_entry(fsp->conn, dev, inode, &token) == False) { + DEBUG(0,("write_file: failed to lock share mode entry for file %s.\n", fsp->fsp_name )); + } + + num_share_modes = get_share_modes(fsp->conn, token, dev, inode, &share_list); + + for(i = 0; i < num_share_modes; i++) { + share_mode_entry *share_entry = &share_list[i]; + + /* + * As there could have been multiple writes waiting at the lock_share_entry + * gate we may not be the first to enter. Hence the state of the op_types + * in the share mode entries may be partly NO_OPLOCK and partly LEVEL_II + * oplock. It will do no harm to re-send break messages to those smbd's + * that are still waiting their turn to remove their LEVEL_II state, and + * also no harm to ignore existing NO_OPLOCK states. JRA. + */ + + if (share_entry->op_type == NO_OPLOCK) + continue; + + /* Paranoia .... */ + if (EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) { + DEBUG(0,("write_file: PANIC. share mode entry %d is an exlusive oplock !\n", i )); + abort(); + } + + /* + * Check if this is a file we have open (including the + * file we've been called to do write_file on. If so + * then break it directly without releasing the lock. + */ + + if (pid == share_entry->pid) { + files_struct *new_fsp = file_find_dit(dev, inode, &share_entry->time); + + /* Paranoia check... */ + if(new_fsp == NULL) { + DEBUG(0,("write_file: PANIC. share mode entry %d is not a local file !\n", i )); + abort(); + } + oplock_break_level2(new_fsp, True, token); + + } else { + + /* + * This is a remote file and so we send an asynchronous + * message. + */ + + request_oplock_break(share_entry, dev, inode); + } + } + + free((char *)share_list); + unlock_share_entry(fsp->conn, dev, inode, token); + } + + /* Paranoia check... */ + if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) { + DEBUG(0,("write_file: PANIC. File %s still has a level II oplock.\n", fsp->fsp_name)); + abort(); + } + + if (total_writes % 500 == 0) { + DEBUG(3,("WRITECACHE: initwrites=%u abutted=%u flushes=%u total=%u \ +nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n", + init_writes, abutted_writes, flushed_writes, total_writes, non_oplock_writes, + allocated_write_caches, + num_write_caches, direct_writes, num_perfect_writes, cache_read_hits )); + + DEBUG(3,("WRITECACHE: SEEK=%d, READ=%d, WRITE=%d, READRAW=%d, OPLOCK=%d, CLOSE=%d, SYNC=%d\n", + flush_reasons[SEEK_FLUSH], + flush_reasons[READ_FLUSH], + flush_reasons[WRITE_FLUSH], + flush_reasons[READRAW_FLUSH], + flush_reasons[OPLOCK_RELEASE_FLUSH], + flush_reasons[CLOSE_FLUSH], + flush_reasons[SYNC_FLUSH] )); + } + + if(!wcp) { + direct_writes++; + return real_write_file(fsp, data, pos, n); + } + + DEBUG(9,("write_file(fd=%d pos=%d size=%d) wofs=%d wsize=%d\n", + fsp->fd_ptr->fd, (int)pos, (int)n, (int)wcp->offset, (int)wcp->data_size)); + + /* + * If we have active cache and it isn't contiguous then we flush. + * NOTE: There is a small problem with running out of disk .... + */ + + if (wcp->data_size) { + + BOOL cache_flush_needed = False; + + if ((pos >= wcp->offset) && (pos <= wcp->offset + wcp->data_size)) { + + /* + * Start of write overlaps or abutts the existing data. + */ + + size_t data_used = MIN((wcp->alloc_size - (pos - wcp->offset)), n); + + memcpy(wcp->data + (pos - wcp->offset), data, data_used); + + /* + * Update the current buffer size with the new data. + */ + + if(pos + data_used > wcp->offset + wcp->data_size) + wcp->data_size = pos + data_used - wcp->offset; + + /* + * If we used all the data then + * return here. + */ + + if(n == data_used) + return n; + else + cache_flush_needed = True; + + /* + * Move the start of data forward by the amount used, + * cut down the amount left by the same amount. + */ + + data += data_used; + pos += data_used; + n -= data_used; + + abutted_writes++; + total_written = data_used; + + write_path = 1; + + } else if ((pos < wcp->offset) && (pos + n > wcp->offset) && + (pos + n <= wcp->offset + wcp->alloc_size)) { + + /* + * End of write overlaps the existing data. + */ + + size_t data_used = pos + n - wcp->offset; + + memcpy(wcp->data, data + n - data_used, data_used); + + /* + * Update the current buffer size with the new data. + */ + + if(pos + n > wcp->offset + wcp->data_size) + wcp->data_size = pos + n - wcp->offset; + + /* + * We don't need to move the start of data, but we + * cut down the amount left by the amount used. + */ + + n -= data_used; + + /* + * We cannot have used all the data here. + */ + + cache_flush_needed = True; + + abutted_writes++; + total_written = data_used; + + write_path = 2; + + } else if ( (pos >= wcp->file_size) && + (pos > wcp->offset + wcp->data_size) && + (pos < wcp->offset + wcp->alloc_size) ) { + + /* + * Non-contiguous write part of which fits within + * the cache buffer and is extending the file. + */ + + size_t data_used; + + if(pos + n <= wcp->offset + wcp->alloc_size) + data_used = n; + else + data_used = wcp->offset + wcp->alloc_size - pos; + + /* + * Fill in the non-continuous area with zeros. + */ + + memset(wcp->data + wcp->data_size, '\0', + pos - (wcp->offset + wcp->data_size) ); + + memcpy(wcp->data + (pos - wcp->offset), data, data_used); + + /* + * Update the current buffer size with the new data. + */ + + if(pos + data_used > wcp->offset + wcp->data_size) + wcp->data_size = pos + data_used - wcp->offset; + + /* + * Update the known file length. + */ + + wcp->file_size = wcp->offset + wcp->data_size; + +#if 0 + if (set_filelen(fsp->fd_ptr->fd, wcp->file_size) == -1) { + DEBUG(0,("write_file: error %s in setting file to length %.0f\n", + strerror(errno), (double)wcp->file_size )); + return -1; + } +#endif + + /* + * If we used all the data then + * return here. + */ + + if(n == data_used) + return n; + else + cache_flush_needed = True; + + /* + * Move the start of data forward by the amount used, + * cut down the amount left by the same amount. + */ + + data += data_used; + pos += data_used; + n -= data_used; + + abutted_writes++; + total_written = data_used; + + write_path = 3; + + } else { + + /* + * Write is bigger than buffer, or there is no overlap on the + * low or high ends. + */ + + DEBUG(9,("write_file: non cacheable write : fd = %d, pos = %.0f, len = %u, current cache pos = %.0f \ +len = %u\n",fsp->fd_ptr->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (unsigned int)wcp->data_size )); + + /* + * Update the file size if needed. + */ + + if(pos + n > wcp->file_size) + wcp->file_size = pos + n; + + /* + * If write would fit in the cache, and is larger than + * the data already in the cache, flush the cache and + * preferentially copy the data new data into it. Otherwise + * just write the data directly. + */ + + if ( n <= wcp->alloc_size && n > wcp->data_size) { + cache_flush_needed = True; + } else { + direct_writes++; + return real_write_file(fsp, data, pos, n); + } + + write_path = 4; + + } + + if(wcp->data_size > wcp->file_size) + wcp->file_size = wcp->data_size; + + if (cache_flush_needed) { + flushed_writes++; + + DEBUG(3,("WRITE_FLUSH:%d: due to noncontinuous write: fd = %d, size = %.0f, pos = %.0f, \ +n = %u, wcp->offset=%.0f, wcp->data_size=%u\n", + write_path, fsp->fd_ptr->fd, (double)wcp->file_size, (double)pos, (unsigned int)n, + (double)wcp->offset, (unsigned int)wcp->data_size )); + + flush_write_cache(fsp, WRITE_FLUSH); + } + } + + /* + * If the write request is bigger than the cache + * size, write it all out. + */ + + if (n > wcp->alloc_size ) { + if(real_write_file(fsp, data, pos, n) == -1) + return -1; + direct_writes++; + return total_written + n; + } + + /* + * If there's any data left, cache it. + */ + + if (n) { + if (wcp->data_size) { + abutted_writes++; + DEBUG(9,("abutted write (%u)\n", abutted_writes)); + } else { + init_writes++; + } + memcpy(wcp->data+wcp->data_size, data, n); + if (wcp->data_size == 0) { + wcp->offset = pos; + num_write_caches++; + } + wcp->data_size += n; + DEBUG(9,("cache return %u\n", (unsigned int)n)); + total_written += n; + return total_written; /* .... that's a write :) */ + } + + return total_written; } +/**************************************************************************** + Delete the write cache structure. +****************************************************************************/ +void delete_write_cache(files_struct *fsp) +{ + write_cache *wcp; + + if(!fsp) + return; + + if(!(wcp = fsp->wcp)) + return; + + allocated_write_caches--; + + SMB_ASSERT(wcp->data_size == 0); + + free(wcp->data); + free(wcp); + + fsp->wcp = NULL; +} + +/**************************************************************************** + Setup the write cache structure. +****************************************************************************/ + +static BOOL setup_write_cache(files_struct *fsp, SMB_OFF_T file_size) +{ + ssize_t alloc_size = lp_write_cache_size(SNUM(fsp->conn)); + write_cache *wcp; + + if (allocated_write_caches >= MAX_WRITE_CACHES) return False; + + if(alloc_size == 0 || fsp->wcp) + return False; + + if((wcp = (write_cache *)malloc(sizeof(write_cache))) == NULL) { + DEBUG(0,("setup_write_cache: malloc fail.\n")); + return False; + } + + wcp->file_size = file_size; + wcp->offset = 0; + wcp->alloc_size = alloc_size; + wcp->data_size = 0; + if((wcp->data = malloc(wcp->alloc_size)) == NULL) { + DEBUG(0,("setup_write_cache: malloc fail for buffer size %u.\n", + (unsigned int)wcp->alloc_size )); + free(wcp); + return False; + } + + fsp->wcp = wcp; + allocated_write_caches++; + + return True; +} + +/**************************************************************************** + Cope with a size change. +****************************************************************************/ + +void set_filelen_write_cache(files_struct *fsp, SMB_OFF_T file_size) +{ + if(fsp->wcp) { + flush_write_cache(fsp, SIZECHANGE_FLUSH); + fsp->wcp->file_size = file_size; + } +} + +/******************************************************************* + Flush a write cache struct to disk. +********************************************************************/ + +ssize_t flush_write_cache(files_struct *fsp, enum flush_reason_enum reason) +{ + write_cache *wcp = fsp->wcp; + size_t data_size; + + if(!wcp || !wcp->data_size) + return 0; + + data_size = wcp->data_size; + wcp->data_size = 0; + + num_write_caches--; + + flush_reasons[(int)reason]++; + + DEBUG(9,("flushing write cache: fd = %d, off=%.0f, size=%u\n", + fsp->fd_ptr->fd, (double)wcp->offset, (unsigned int)data_size)); + + if(data_size == wcp->alloc_size) + num_perfect_writes++; + + return real_write_file(fsp, wcp->data, wcp->offset, data_size); +} /******************************************************************* sync a file ********************************************************************/ -void sys_sync_file(int fd) +void sync_file(connection_struct *conn, files_struct *fsp) { #ifdef HAVE_FSYNC - fsync(fd); + if(lp_strict_sync(SNUM(conn)) && fsp->fd_ptr != NULL) { + flush_write_cache(fsp, SYNC_FLUSH); + fsync(fsp->fd_ptr->fd); + } #endif } diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index 1d9c5ef754..64f63805f3 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -26,7 +26,6 @@ extern BOOL case_sensitive; extern BOOL case_preserve; extern BOOL short_case_preserve; extern fstring remote_machine; -extern pstring global_myname; extern BOOL use_mangled_map; static BOOL scan_directory(char *path, char *name,connection_struct *conn,BOOL docache); @@ -97,7 +96,12 @@ static int global_stat_cache_hits; void print_stat_cache_statistics(void) { - double eff = (100.0* (double)global_stat_cache_hits)/(double)global_stat_cache_lookups; + double eff; + + if(global_stat_cache_lookups == 0) + return; + + eff = (100.0* (double)global_stat_cache_hits)/(double)global_stat_cache_lookups; DEBUG(0,("stat cache stats: lookups = %d, hits = %d, misses = %d, \ stat cache was %f%% effective.\n", global_stat_cache_lookups, @@ -230,9 +234,7 @@ static void stat_cache_add( char *full_orig_name, char *orig_translated_path) Return True if we translated (and did a scuccessful stat on) the entire name. *****************************************************************************/ -static BOOL stat_cache_lookup(struct connection_struct *conn, char *name, - char *dirpath, char **start, - SMB_STRUCT_STAT *pst) +static BOOL stat_cache_lookup( char *name, char *dirpath, char **start, SMB_STRUCT_STAT *pst) { stat_cache_entry *scp; stat_cache_entry *longest_hit = NULL; @@ -285,7 +287,7 @@ static BOOL stat_cache_lookup(struct connection_struct *conn, char *name, * and then promote it to the top. */ - if(conn->vfs_ops.stat(longest_hit->translated_name, pst) != 0) { + if(dos_stat( longest_hit->translated_name, pst) != 0) { /* * Discard this entry. */ @@ -310,35 +312,6 @@ static BOOL stat_cache_lookup(struct connection_struct *conn, char *name, } /**************************************************************************** - this routine converts from the dos and dfs namespace to the unix namespace. -****************************************************************************/ -BOOL unix_dfs_convert(char *name,connection_struct *conn, - char *saved_last_component, - BOOL *bad_path, SMB_STRUCT_STAT *pst) -{ - pstring local_path; - - DEBUG(10,("unix_dfs_convert: %s\n", name)); - - if (name != NULL && - under_dfs(conn, name, local_path, sizeof(local_path))) - { - DEBUG(10,("%s is in dfs map.\n", name)); - - /* check for our own name */ - if (StrCaseCmp(global_myname, name+1) > 0) - { - return False; - } - - pstrcpy(name, local_path); - - DEBUG(10,("removed name: %s\n", name)); - } - return unix_convert(name, conn, saved_last_component, bad_path, pst); -} - -/**************************************************************************** This routine is called to convert names from the dos namespace to unix namespace. It needs to handle any case conversions, mangling, format changes etc. @@ -359,15 +332,14 @@ used to pick the correct error code to return between ENOENT and ENOTDIR as Windows applications depend on ERRbadpath being returned if a component of a pathname does not exist. ****************************************************************************/ -BOOL unix_convert(char *name,connection_struct *conn, - char *saved_last_component, - BOOL *bad_path, SMB_STRUCT_STAT *pst) + +BOOL unix_convert(char *name,connection_struct *conn,char *saved_last_component, + BOOL *bad_path, SMB_STRUCT_STAT *pst) { SMB_STRUCT_STAT st; char *start, *end; pstring dirpath; pstring orig_path; - int saved_errno; BOOL component_was_mangled = False; BOOL name_has_wildcard = False; #if 0 @@ -443,7 +415,7 @@ BOOL unix_convert(char *name,connection_struct *conn, for (s=name2 ; *s ; s++) if (!issafe(*s)) *s = '_'; - pstrcpy(name,(char *)mktemp(name2)); + pstrcpy(name,(char *)smbd_mktemp(name2)); } return(True); } @@ -463,7 +435,7 @@ BOOL unix_convert(char *name,connection_struct *conn, pstrcpy(orig_path, name); - if(stat_cache_lookup(conn, name, dirpath, &start, &st)) { + if(stat_cache_lookup( name, dirpath, &start, &st)) { if(pst) *pst = st; return True; @@ -473,7 +445,7 @@ BOOL unix_convert(char *name,connection_struct *conn, * stat the name - if it exists then we are all done! */ - if (conn->vfs_ops.stat(name,&st) == 0) { + if (dos_stat(name,&st) == 0) { stat_cache_add(orig_path, name); DEBUG(5,("conversion finished %s -> %s\n",orig_path, name)); if(pst) @@ -481,8 +453,6 @@ BOOL unix_convert(char *name,connection_struct *conn, return(True); } - saved_errno = errno; - DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n", name, dirpath, start)); @@ -492,7 +462,7 @@ BOOL unix_convert(char *name,connection_struct *conn, */ if (case_sensitive && !is_mangled(name) && - !lp_strip_dot() && !use_mangled_map && (saved_errno != ENOENT)) + !lp_strip_dot() && !use_mangled_map) return(False); if(strchr(start,'?') || strchr(start,'*')) @@ -541,8 +511,7 @@ BOOL unix_convert(char *name,connection_struct *conn, /* * Check if the name exists up to this point. */ - - if (conn->vfs_ops.stat(name, &st) == 0) { + if (dos_stat(name, &st) == 0) { /* * It exists. it must either be a directory or this must be * the last part of the path for it to be OK. @@ -694,7 +663,7 @@ BOOL check_name(char *name,connection_struct *conn) if (!lp_symlinks(SNUM(conn))) { SMB_STRUCT_STAT statbuf; - if ( (conn->vfs_ops.lstat(dos_to_unix(name,False),&statbuf) != -1) && + if ( (dos_lstat(name,&statbuf) != -1) && (S_ISLNK(statbuf.st_mode)) ) { DEBUG(3,("check_name: denied: file path name %s is a symlink\n",name)); @@ -758,7 +727,8 @@ static BOOL scan_directory(char *path, char *name,connection_struct *conn,BOOL d continue; pstrcpy(name2,dname); - if (!name_map_mangle(name2,False,SNUM(conn))) continue; + if (!name_map_mangle(name2,False,True,SNUM(conn))) + continue; if ((mangled && mangled_equal(name,name2)) || fname_equal(name, name2)) diff --git a/source3/smbd/files.c b/source3/smbd/files.c index 7f4533e7ae..d6203580cf 100644 --- a/source3/smbd/files.c +++ b/source3/smbd/files.c @@ -186,10 +186,7 @@ void file_close_conn(connection_struct *conn) for (fsp=Files;fsp;fsp=next) { next = fsp->next; if (fsp->conn == conn && fsp->open) { - if (fsp->is_directory) - close_directory(fsp); - else - close_file(fsp,False); + close_file(fsp,False); } } } @@ -226,7 +223,7 @@ open files, %d are available.\n", request_max_open_files, real_max_open_files)); } /* - * Ensure that pipe_handle_offset is set correctly. + * Ensure that pipe_handle_oppset is set correctly. */ set_pipe_handle_offset(real_max_open_files); } @@ -242,10 +239,7 @@ void file_close_user(int vuid) for (fsp=Files;fsp;fsp=next) { next=fsp->next; if ((fsp->vuid == vuid) && fsp->open) { - if(!fsp->is_directory) - close_file(fsp,False); - else - close_directory(fsp); + close_file(fsp,False); } } } @@ -340,13 +334,13 @@ void file_sync_all(connection_struct *conn) for (fsp=Files;fsp;fsp=next) { next=fsp->next; - if (fsp->open && (conn == fsp->conn) && (fsp->fd_ptr != NULL) - && lp_strict_sync(SNUM(conn))){ - conn->vfs_ops.sync(fsp->fd_ptr->fd); + if (fsp->open && (conn == fsp->conn) && (fsp->fd_ptr != NULL)) { + sync_file(conn,fsp); } } } + /**************************************************************************** free up a fd_ptr ****************************************************************************/ diff --git a/source3/smbd/groupname.c b/source3/smbd/groupname.c index 0be558848a..4dadfaa939 100644 --- a/source3/smbd/groupname.c +++ b/source3/smbd/groupname.c @@ -19,4 +19,225 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* this module is retired, it is moved to lib/domain_namemap.c */ +#ifdef USING_GROUPNAME_MAP + +#include "includes.h" +extern int DEBUGLEVEL; +extern DOM_SID global_sam_sid; + + +/************************************************************************** + Groupname map functionality. The code loads a groupname map file and + (currently) loads it into a linked list. This is slow and memory + hungry, but can be changed into a more efficient storage format + if the demands on it become excessive. +***************************************************************************/ + +typedef struct groupname_map { + ubi_slNode next; + + char *windows_name; + DOM_SID windows_sid; + char *unix_name; + gid_t unix_gid; +} groupname_map_entry; + +static ubi_slList groupname_map_list; + +/************************************************************************** + Delete all the entries in the groupname map list. +***************************************************************************/ + +static void delete_groupname_map_list(void) +{ + groupname_map_entry *gmep; + + while((gmep = (groupname_map_entry *)ubi_slRemHead( &groupname_map_list )) != NULL) { + if(gmep->windows_name) + free(gmep->windows_name); + if(gmep->unix_name) + free(gmep->unix_name); + free((char *)gmep); + } +} + +/************************************************************************** + Load a groupname map file. Sets last accessed timestamp. +***************************************************************************/ + +void load_groupname_map(void) +{ + static time_t groupmap_file_last_modified = (time_t)0; + static BOOL initialized = False; + char *groupname_map_file = lp_groupname_map(); + SMB_STRUCT_STAT st; + FILE *fp; + char *s; + pstring buf; + groupname_map_entry *new_ep; + + if(!initialized) { + ubi_slInitList( &groupname_map_list ); + initialized = True; + } + + if (!*groupname_map_file) + return; + + if(sys_stat(groupname_map_file, &st) != 0) { + DEBUG(0, ("load_groupname_map: Unable to stat file %s. Error was %s\n", + groupname_map_file, strerror(errno) )); + return; + } + + /* + * Check if file has changed. + */ + if( st.st_mtime <= groupmap_file_last_modified) + return; + + groupmap_file_last_modified = st.st_mtime; + + /* + * Load the file. + */ + + fp = sys_fopen(groupname_map_file,"r"); + if (!fp) { + DEBUG(0,("load_groupname_map: can't open groupname map %s. Error was %s\n", + groupname_map_file, strerror(errno))); + return; + } + + /* + * Throw away any previous list. + */ + delete_groupname_map_list(); + + DEBUG(4,("load_groupname_map: Scanning groupname map %s\n",groupname_map_file)); + + while((s=fgets_slash(buf,sizeof(buf),fp))!=NULL) { + pstring unixname; + pstring windows_name; + struct group *gptr; + DOM_SID tmp_sid; + + DEBUG(10,("load_groupname_map: Read line |%s|\n", s)); + + if (!*s || strchr("#;",*s)) + continue; + + if(!next_token(&s,unixname, "\t\n\r=", sizeof(unixname))) + continue; + + if(!next_token(&s,windows_name, "\t\n\r=", sizeof(windows_name))) + continue; + + trim_string(unixname, " ", " "); + trim_string(windows_name, " ", " "); + + if (!*windows_name) + continue; + + if(!*unixname) + continue; + + DEBUG(5,("load_groupname_map: unixname = %s, windowsname = %s.\n", + unixname, windows_name)); + + /* + * Attempt to get the unix gid_t for this name. + */ + + if((gptr = (struct group *)getgrnam(unixname)) == NULL) { + DEBUG(0,("load_groupname_map: getgrnam for group %s failed.\ +Error was %s.\n", unixname, strerror(errno) )); + continue; + } + + /* + * Now map to an NT SID. + */ + + if(!lookup_wellknown_sid_from_name(windows_name, &tmp_sid)) { + /* + * It's not a well known name, convert the UNIX gid_t + * to a rid within this domain SID. + */ + tmp_sid = global_sam_sid; + tmp_sid.sub_auths[tmp_sid.num_auths++] = + pdb_gid_to_group_rid((gid_t)gptr->gr_gid); + } + + /* + * Create the list entry and add it onto the list. + */ + + if((new_ep = (groupname_map_entry *)malloc( sizeof(groupname_map_entry) ))== NULL) { + DEBUG(0,("load_groupname_map: malloc fail for groupname_map_entry.\n")); + fclose(fp); + return; + } + + new_ep->unix_gid = gptr->gr_gid; + new_ep->windows_sid = tmp_sid; + new_ep->windows_name = strdup( windows_name ); + new_ep->unix_name = strdup( unixname ); + + if(new_ep->windows_name == NULL || new_ep->unix_name == NULL) { + DEBUG(0,("load_groupname_map: malloc fail for names in groupname_map_entry.\n")); + fclose(fp); + if(new_ep->windows_name != NULL) + free(new_ep->windows_name); + if(new_ep->unix_name != NULL) + free(new_ep->unix_name); + free((char *)new_ep); + return; + } + memset((char *)&new_ep->next, '\0', sizeof(new_ep->next) ); + + ubi_slAddHead( &groupname_map_list, (ubi_slNode *)new_ep); + } + + DEBUG(10,("load_groupname_map: Added %ld entries to groupname map.\n", + ubi_slCount(&groupname_map_list))); + + fclose(fp); +} + +/*********************************************************** + Lookup a SID entry by gid_t. +************************************************************/ + +void map_gid_to_sid( gid_t gid, DOM_SID *psid) +{ + groupname_map_entry *gmep; + + /* + * Initialize and load if not already loaded. + */ + load_groupname_map(); + + for( gmep = (groupname_map_entry *)ubi_slFirst( &groupname_map_list); + gmep; gmep = (groupname_map_entry *)ubi_slNext( gmep )) { + + if( gmep->unix_gid == gid) { + *psid = gmep->windows_sid; + DEBUG(7,("map_gid_to_sid: Mapping unix group %s to windows group %s.\n", + gmep->unix_name, gmep->windows_name )); + return; + } + } + + /* + * If there's no map, convert the UNIX gid_t + * to a rid within this domain SID. + */ + *psid = global_sam_sid; + psid->sub_auths[psid->num_auths++] = pdb_gid_to_group_rid(gid); + + return; +} +#else /* USING_GROUPNAME_MAP */ + void load_groupname_map(void) {;} +#endif /* USING_GROUPNAME_MAP */ diff --git a/source3/smbd/ipc.c b/source3/smbd/ipc.c index eeb367bcb5..4e9418fa94 100644 --- a/source3/smbd/ipc.c +++ b/source3/smbd/ipc.c @@ -61,7 +61,6 @@ extern fstring global_myworkgroup; extern int Client; extern int smb_read_error; -extern uint32 global_client_caps; static BOOL api_Unsupported(connection_struct *conn,uint16 vuid, char *param,char *data, int mdrcnt,int mprcnt, @@ -82,7 +81,7 @@ static int CopyExpanded(connection_struct *conn, if (!src || !dst || !n || !(*dst)) return(0); StrnCpy(buf,src,sizeof(buf)/2); - string_sub(buf,"%S",lp_servicename(snum)); + pstring_sub(buf,"%S",lp_servicename(snum)); standard_sub(conn,buf); StrnCpy(*dst,buf,*n); l = strlen(*dst) + 1; @@ -95,7 +94,7 @@ static int CopyAndAdvance(char** dst, char* src, int* n) { int l; if (!src || !dst || !n || !(*dst)) return(0); - StrnCpy(*dst,src,*n); + StrnCpy(*dst,src,*n-1); l = strlen(*dst) + 1; (*dst) += l; (*n) -= l; @@ -107,7 +106,7 @@ static int StrlenExpanded(connection_struct *conn, int snum, char* s) pstring buf; if (!s) return(0); StrnCpy(buf,s,sizeof(buf)/2); - string_sub(buf,"%S",lp_servicename(snum)); + pstring_sub(buf,"%S",lp_servicename(snum)); standard_sub(conn,buf); return strlen(buf) + 1; } @@ -117,7 +116,7 @@ static char* Expand(connection_struct *conn, int snum, char* s) static pstring buf; if (!s) return(NULL); StrnCpy(buf,s,sizeof(buf)/2); - string_sub(buf,"%S",lp_servicename(snum)); + pstring_sub(buf,"%S",lp_servicename(snum)); standard_sub(conn,buf); return &buf[0]; } @@ -139,70 +138,69 @@ static BOOL prefix_ok(char *str,char *prefix) cause of some of the ipc problems being experienced. lkcl26dec97 ******************************************************************/ + static void copy_trans_params_and_data(char *outbuf, int align, - struct mem_buf *rparam, struct mem_buf *rdata, - int param_offset, int data_offset, - int param_len, int data_len) + char *rparam, int param_offset, int param_len, + char *rdata, int data_offset, int data_len) { char *copy_into = smb_buf(outbuf)+1; + if(param_len < 0) + param_len = 0; + + if(data_len < 0) + data_len = 0; + DEBUG(5,("copy_trans_params_and_data: params[%d..%d] data[%d..%d]\n", param_offset, param_offset + param_len, data_offset , data_offset + data_len)); - if (param_len) mem_buf_copy(copy_into, rparam, param_offset, param_len); + if (param_len) + memcpy(copy_into, &rparam[param_offset], param_len); + copy_into += param_len + align; - if (data_len ) mem_buf_copy(copy_into, rdata , data_offset , data_len); + + if (data_len ) + memcpy(copy_into, &rdata[data_offset], data_len); } /**************************************************************************** - send a trans reply - ****************************************************************************/ + Send a trans reply. + ****************************************************************************/ + static void send_trans_reply(char *outbuf, - struct mem_buf *rdata, - struct mem_buf *rparam, - uint16 *setup, int lsetup, int max_data_ret) + char *rparam, int rparam_len, + char *rdata, int rdata_len, + BOOL buffer_too_large) { - int i; int this_ldata,this_lparam; - int tot_data=0,tot_param=0; + int tot_data_sent = 0; + int tot_param_sent = 0; int align; - int ldata = rdata ? mem_buf_len(rdata ) : 0; - int lparam = rparam ? mem_buf_len(rparam) : 0; - - BOOL buffer_too_large = max_data_ret ? ldata > max_data_ret : False; + int ldata = rdata ? rdata_len : 0; + int lparam = rparam ? rparam_len : 0; if (buffer_too_large) - { - DEBUG(5,("send_trans_reply: buffer %d too large %d\n", ldata, max_data_ret)); - ldata = max_data_ret; - } + DEBUG(5,("send_trans_reply: buffer %d too large\n", ldata )); - this_lparam = MIN(lparam,max_send - (500+lsetup*SIZEOFWORD)); /* hack */ - this_ldata = MIN(ldata,max_send - (500+lsetup*SIZEOFWORD+this_lparam)); + this_lparam = MIN(lparam,max_send - 500); /* hack */ + this_ldata = MIN(ldata,max_send - (500+this_lparam)); align = ((this_lparam)%4); - set_message(outbuf,10+lsetup,1+align+this_ldata+this_lparam,True); + set_message(outbuf,10,1+align+this_ldata+this_lparam,True); if (buffer_too_large) { - if (global_client_caps & CAP_STATUS32) - { - /* issue a buffer size warning. on a DCE/RPC pipe, expect an SMBreadX... */ - SIVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - SIVAL(outbuf, smb_rcls, 0x80000005); /* STATUS_BUFFER_OVERFLOW */ - } else { - SCVAL(outbuf, smb_rcls, ERRDOS); - SSVAL(outbuf, smb_err, ERRmoredata); - } + /* issue a buffer size warning. on a DCE/RPC pipe, expect an SMBreadX... */ + SIVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); + SIVAL(outbuf, smb_rcls, 0x80000000 | NT_STATUS_ACCESS_VIOLATION); } copy_trans_params_and_data(outbuf, align, - rparam , rdata, - tot_param , tot_data, - this_lparam, this_ldata); + rparam, tot_param_sent, this_lparam, + rdata, tot_data_sent, this_ldata); SSVAL(outbuf,smb_vwv0,lparam); SSVAL(outbuf,smb_vwv1,ldata); @@ -212,46 +210,46 @@ static void send_trans_reply(char *outbuf, SSVAL(outbuf,smb_vwv6,this_ldata); SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+1+this_lparam+align,outbuf)); SSVAL(outbuf,smb_vwv8,0); - SSVAL(outbuf,smb_vwv9,lsetup); - - for (i=0;i<lsetup;i++) - { - SSVAL(outbuf,smb_vwv10+i*SIZEOFWORD,setup[i]); - } + SSVAL(outbuf,smb_vwv9,0); show_msg(outbuf); send_smb(Client,outbuf); - tot_data = this_ldata; - tot_param = this_lparam; + tot_data_sent = this_ldata; + tot_param_sent = this_lparam; - while (tot_data < ldata || tot_param < lparam) + while (tot_data_sent < ldata || tot_param_sent < lparam) { - this_lparam = MIN(lparam-tot_param, max_send - 500); /* hack */ - this_ldata = MIN(ldata -tot_data , max_send - (500+this_lparam)); + this_lparam = MIN(lparam-tot_param_sent, max_send - 500); /* hack */ + this_ldata = MIN(ldata -tot_data_sent, max_send - (500+this_lparam)); + + if(this_lparam < 0) + this_lparam = 0; + + if(this_ldata < 0) + this_ldata = 0; align = (this_lparam%4); set_message(outbuf,10,1+this_ldata+this_lparam+align,False); copy_trans_params_and_data(outbuf, align, - rparam , rdata, - tot_param , tot_data, - this_lparam, this_ldata); + rparam, tot_param_sent, this_lparam, + rdata, tot_data_sent, this_ldata); SSVAL(outbuf,smb_vwv3,this_lparam); SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf)+1,outbuf)); - SSVAL(outbuf,smb_vwv5,tot_param); + SSVAL(outbuf,smb_vwv5,tot_param_sent); SSVAL(outbuf,smb_vwv6,this_ldata); SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+1+this_lparam+align,outbuf)); - SSVAL(outbuf,smb_vwv8,tot_data); + SSVAL(outbuf,smb_vwv8,tot_data_sent); SSVAL(outbuf,smb_vwv9,0); show_msg(outbuf); send_smb(Client,outbuf); - tot_data += this_ldata; - tot_param += this_lparam; + tot_data_sent += this_ldata; + tot_param_sent += this_lparam; } } @@ -378,7 +376,10 @@ va_dcl #endif stringneeded = -1; - if (!p->curpos) return(0); + if (!p->curpos) { + va_end(args); + return(0); + } switch( *p->curpos++ ) { case 'W': /* word (2 byte) */ @@ -400,7 +401,7 @@ va_dcl needed = get_counter(&p->curpos); { char *s = va_arg(args,char*); - if (p->buflen >= needed) StrnCpy(p->structbuf,s?s:"",needed); + if (p->buflen >= needed) StrnCpy(p->structbuf,s?s:"",needed-1); } break; case 'z': /* offset to zero terminated string (4 byte) */ @@ -656,7 +657,7 @@ static void fill_printq_info(connection_struct *conn, int snum, int uLevel, return; } - bzero(p, 8192*sizeof(char)); + memset(p, '\0',8192*sizeof(char)); q=p; /* lookup the long printer driver name in the file description */ @@ -809,8 +810,8 @@ static BOOL api_DosPrintQGetInfo(connection_struct *conn, print_queue_struct *queue=NULL; print_status_struct status; - bzero(&status,sizeof(status)); - bzero(&desc,sizeof(desc)); + memset((char *)&status,'\0',sizeof(status)); + memset((char *)&desc,'\0',sizeof(desc)); p = skip_string(p,1); uLevel = SVAL(p,0); @@ -823,7 +824,20 @@ static BOOL api_DosPrintQGetInfo(connection_struct *conn, /* check it's a supported varient */ if (!prefix_ok(str1,"zWrLh")) return False; - if (!check_printq_info(&desc,uLevel,str2,str3)) return False; + if (!check_printq_info(&desc,uLevel,str2,str3)) { + /* + * Patch from Scott Moomaw <scott@bridgewater.edu> + * to return the 'invalid info level' error if an + * unknown level was requested. + */ + *rdata_len = 0; + *rparam_len = 6; + *rparam = REALLOC(*rparam,*rparam_len); + SSVALS(*rparam,0,ERROR_INVALID_LEVEL); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,0); + return(True); + } snum = lp_servicenumber(QueueName); if (snum < 0 && pcap_printername_ok(QueueName,NULL)) { @@ -908,13 +922,26 @@ static BOOL api_DosPrintQEnum(connection_struct *conn, uint16 vuid, char* param, int* subcntarr = NULL; int queuecnt, subcnt=0, succnt=0; - bzero(&desc,sizeof(desc)); + memset((char *)&desc,'\0',sizeof(desc)); DEBUG(3,("DosPrintQEnum uLevel=%d\n",uLevel)); if (!prefix_ok(param_format,"WrLeh")) return False; - if (!check_printq_info(&desc,uLevel,output_format1,output_format2)) - return False; + if (!check_printq_info(&desc,uLevel,output_format1,output_format2)) { + /* + * Patch from Scott Moomaw <scott@bridgewater.edu> + * to return the 'invalid info level' error if an + * unknown level was requested. + */ + *rdata_len = 0; + *rparam_len = 6; + *rparam = REALLOC(*rparam,*rparam_len); + SSVALS(*rparam,0,ERROR_INVALID_LEVEL); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,0); + return(True); + } + queuecnt = 0; for (i = 0; i < services; i++) if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) @@ -1057,7 +1084,7 @@ static int get_server_info(uint32 servertype, (*servers) = (struct srv_info_struct *) Realloc(*servers,sizeof(**servers)*alloced); if (!(*servers)) return(0); - bzero((char *)((*servers)+count),sizeof(**servers)*(alloced-count)); + memset((char *)((*servers)+count),'\0',sizeof(**servers)*(alloced-count)); } s = &(*servers)[count]; @@ -1297,7 +1324,7 @@ static BOOL api_RNetServerEnum(connection_struct *conn, uint16 vuid, char *param *rdata_len = fixed_len + string_len; *rdata = REALLOC(*rdata,*rdata_len); - bzero(*rdata,*rdata_len); + memset(*rdata,'\0',*rdata_len); p2 = (*rdata) + fixed_len; /* auxilliary data (strings) will go here */ p = *rdata; @@ -1355,7 +1382,6 @@ static BOOL api_RNetGroupGetUsers(connection_struct *conn, uint16 vuid, char *pa if (!prefix_ok(str1,"zWrLeh")) return False; *rdata_len = 0; - *rdata = NULL; *rparam_len = 8; *rparam = REALLOC(*rparam,*rparam_len); @@ -1650,6 +1676,8 @@ static BOOL api_SetUserPassword(connection_struct *conn,uint16 vuid, char *param p = skip_string(p,1); + memset(pass1,'\0',sizeof(pass1)); + memset(pass2,'\0',sizeof(pass2)); memcpy(pass1,p,16); memcpy(pass2,p+16,16); @@ -1680,7 +1708,7 @@ static BOOL api_SetUserPassword(connection_struct *conn,uint16 vuid, char *param * Older versions of Windows seem to do this. */ - if (password_ok(user, pass1,strlen(pass1),NULL, NULL) && + if (password_ok(user, pass1,strlen(pass1),NULL) && chgpasswd(user,pass1,pass2,False)) { SSVAL(*rparam,0,NERR_Success); @@ -1703,8 +1731,8 @@ static BOOL api_SetUserPassword(connection_struct *conn,uint16 vuid, char *param } } - bzero(pass1,sizeof(fstring)); - bzero(pass2,sizeof(fstring)); + memset((char *)pass1,'\0',sizeof(fstring)); + memset((char *)pass2,'\0',sizeof(fstring)); return(True); } @@ -2005,7 +2033,7 @@ static BOOL api_PrintJobInfo(connection_struct *conn,uint16 vuid,char *param,cha !become_service(fconn,True)) break; - if (conn->vfs_ops.rename(dos_to_unix(fsp->fsp_name,False),name) == 0) { + if (dos_rename(fsp->fsp_name,name) == 0) { string_set(&fsp->fsp_name,name); } break; @@ -2091,7 +2119,7 @@ static BOOL api_RNetServerGetInfo(connection_struct *conn,uint16 vuid, char *par pstring comment; uint32 servertype= lp_default_server_announce(); - pstrcpy(comment,lp_serverstring()); + pstrcpy(comment,string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH)); if ((count=get_server_info(SV_TYPE_ALL,&servers,global_myworkgroup))>0) { for (i=0;i<count;i++) @@ -2450,7 +2478,7 @@ static BOOL api_RNetUserGetInfo(connection_struct *conn,uint16 vuid, char *param SIVAL(p,usri11_auth_flags,AF_OP_PRINT); /* auth flags */ SIVALS(p,usri11_password_age,-1); /* password age */ SIVAL(p,usri11_homedir,PTR_DIFF(p2,p)); /* home dir */ - pstrcpy(p2, lp_logon_path()); + pstrcpy(p2, lp_logon_home()); p2 = skip_string(p2,1); SIVAL(p,usri11_parms,PTR_DIFF(p2,p)); /* parms */ pstrcpy(p2,""); @@ -2486,12 +2514,15 @@ static BOOL api_RNetUserGetInfo(connection_struct *conn,uint16 vuid, char *param SSVAL(p,42, conn->admin_user?USER_PRIV_ADMIN:USER_PRIV_USER); SIVAL(p,44,PTR_DIFF(p2,*rdata)); /* home dir */ - pstrcpy(p2,lp_logon_path()); + pstrcpy(p2,lp_logon_home()); p2 = skip_string(p2,1); SIVAL(p,48,PTR_DIFF(p2,*rdata)); /* comment */ *p2++ = 0; SSVAL(p,52,0); /* flags */ - SIVAL(p,54,0); /* script_path */ + SIVAL(p,54,PTR_DIFF(p2,*rdata)); /* script_path */ + pstrcpy(p2,lp_logon_script()); + standard_sub( conn, p2 ); + p2 = skip_string(p2,1); if (uLevel == 2) { SIVAL(p,60,0); /* auth_flags */ @@ -2590,12 +2621,11 @@ static BOOL api_WWkstaUserLogon(connection_struct *conn,uint16 vuid, char *param int uLevel; struct pack_desc desc; char* name; - char* logon_script; uLevel = SVAL(p,0); name = p + 2; - bzero(&desc,sizeof(desc)); + memset((char *)&desc,'\0',sizeof(desc)); DEBUG(3,("WWkstaUserLogon uLevel=%d name=%s\n",uLevel,name)); @@ -2636,9 +2666,12 @@ static BOOL api_WWkstaUserLogon(connection_struct *conn,uint16 vuid, char *param /* JHT - By calling lp_logon_script() and standard_sub() we have */ /* made sure all macros are fully substituted and available */ - logon_script = lp_logon_script(); - standard_sub( conn, logon_script ); - PACKS(&desc,"z", logon_script); /* script path */ + { + pstring logon_script; + pstrcpy(logon_script,lp_logon_script()); + standard_sub( conn, logon_script ); + PACKS(&desc,"z", logon_script); /* script path */ + } /* End of JHT mods */ PACKI(&desc,"D",0x00000000); /* reserved */ @@ -2706,8 +2739,8 @@ static BOOL api_WPrintJobGetInfo(connection_struct *conn,uint16 vuid, char *para uLevel = SVAL(p,2); - bzero(&desc,sizeof(desc)); - bzero(&status,sizeof(status)); + memset((char *)&desc,'\0',sizeof(desc)); + memset((char *)&status,'\0',sizeof(status)); DEBUG(3,("WPrintJobGetInfo uLevel=%d uJobId=0x%X\n",uLevel,SVAL(p,0))); @@ -2767,8 +2800,8 @@ static BOOL api_WPrintJobEnumerate(connection_struct *conn,uint16 vuid, char *pa print_queue_struct *queue=NULL; print_status_struct status; - bzero(&desc,sizeof(desc)); - bzero(&status,sizeof(status)); + memset((char *)&desc,'\0',sizeof(desc)); + memset((char *)&status,'\0',sizeof(status)); p = skip_string(p,1); uLevel = SVAL(p,0); @@ -2880,7 +2913,7 @@ static BOOL api_WPrintDestGetInfo(connection_struct *conn,uint16 vuid, char *par struct pack_desc desc; int snum; - bzero(&desc,sizeof(desc)); + memset((char *)&desc,'\0',sizeof(desc)); p = skip_string(p,1); uLevel = SVAL(p,0); @@ -2939,7 +2972,7 @@ static BOOL api_WPrintDestEnum(connection_struct *conn,uint16 vuid, char *param, struct pack_desc desc; int services = lp_numservices(); - bzero(&desc,sizeof(desc)); + memset((char *)&desc,'\0',sizeof(desc)); uLevel = SVAL(p,0); @@ -2994,7 +3027,7 @@ static BOOL api_WPrintDriverEnum(connection_struct *conn,uint16 vuid, char *para int succnt; struct pack_desc desc; - bzero(&desc,sizeof(desc)); + memset((char *)&desc,'\0',sizeof(desc)); uLevel = SVAL(p,0); @@ -3038,7 +3071,7 @@ static BOOL api_WPrintQProcEnum(connection_struct *conn,uint16 vuid, char *param int succnt; struct pack_desc desc; - bzero(&desc,sizeof(desc)); + memset((char *)&desc,'\0',sizeof(desc)); uLevel = SVAL(p,0); @@ -3083,7 +3116,7 @@ static BOOL api_WPrintPortEnum(connection_struct *conn,uint16 vuid, char *param, int succnt; struct pack_desc desc; - bzero(&desc,sizeof(desc)); + memset((char *)&desc,'\0',sizeof(desc)); uLevel = SVAL(p,0); @@ -3094,7 +3127,7 @@ static BOOL api_WPrintPortEnum(connection_struct *conn,uint16 vuid, char *param, if (uLevel != 0 || strcmp(str2,"B9") != 0) return False; if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt); - bzero(&desc,sizeof(desc)); + memset((char *)&desc,'\0',sizeof(desc)); desc.base = *rdata; desc.buflen = mdrcnt; desc.format = str2; @@ -3117,35 +3150,48 @@ static BOOL api_WPrintPortEnum(connection_struct *conn,uint16 vuid, char *param, return(True); } -static void api_rpc_trans_reply(char *outbuf, - pipes_struct *p) +/**************************************************************************** + Start the first part of an RPC reply which began with an SMBtrans request. +****************************************************************************/ + +static BOOL api_rpc_trans_reply(char *outbuf, pipes_struct *p) { - send_trans_reply(outbuf, p->rsmb_pdu.data, NULL, NULL, 0, p->file_offset); + char *rdata = malloc(p->max_trans_reply); + int data_len; - if (mem_buf_len(p->rsmb_pdu.data) <= p->file_offset) - { - /* all of data was sent: no need to wait for SMBreadX calls */ - mem_free_data(p->rsmb_pdu.data); + if(rdata == NULL) { + DEBUG(0,("api_rpc_trans_reply: malloc fail.\n")); + return False; } + + if((data_len = read_from_pipe( p, rdata, p->max_trans_reply)) < 0) { + free(rdata); + return False; + } + + send_trans_reply(outbuf, NULL, 0, rdata, data_len, (int)prs_offset(&p->rdata) > data_len); + + free(rdata); + return True; } /**************************************************************************** WaitNamedPipeHandleState ****************************************************************************/ -static BOOL api_WNPHS(char *outbuf, pipes_struct *p, char *param) + +static BOOL api_WNPHS(char *outbuf, pipes_struct *p, char *param, int param_len) { uint16 priority; - if (!param) return False; + if (!param || param_len < 2) + return False; - priority = param[0] + (param[1] << 8); + priority = SVAL(param,0); DEBUG(4,("WaitNamedPipeHandleState priority %x\n", priority)); - if (wait_rpc_pipe_hnd_state(p, priority)) - { + if (wait_rpc_pipe_hnd_state(p, priority)) { /* now send the reply */ - send_trans_reply(outbuf, NULL, NULL, NULL, 0, p->file_offset); - + send_trans_reply(outbuf, NULL, 0, NULL, 0, False); return True; } return False; @@ -3155,20 +3201,20 @@ static BOOL api_WNPHS(char *outbuf, pipes_struct *p, char *param) /**************************************************************************** SetNamedPipeHandleState ****************************************************************************/ -static BOOL api_SNPHS(char *outbuf, pipes_struct *p, char *param) + +static BOOL api_SNPHS(char *outbuf, pipes_struct *p, char *param, int param_len) { uint16 id; - if (!param) return False; + if (!param || param_len < 2) + return False; - id = param[0] + (param[1] << 8); + id = SVAL(param,0); DEBUG(4,("SetNamedPipeHandleState to code %x\n", id)); - if (set_rpc_pipe_hnd_state(p, id)) - { + if (set_rpc_pipe_hnd_state(p, id)) { /* now send the reply */ - send_trans_reply(outbuf, NULL, NULL, NULL, 0, p->file_offset); - + send_trans_reply(outbuf, NULL, 0, NULL, 0, False); return True; } return False; @@ -3176,111 +3222,93 @@ static BOOL api_SNPHS(char *outbuf, pipes_struct *p, char *param) /**************************************************************************** - when no reply is generated, indicate unsupported. + When no reply is generated, indicate unsupported. ****************************************************************************/ + static BOOL api_no_reply(char *outbuf, int max_rdata_len) { - struct mem_buf rparam; - - mem_init(&rparam, 0); - mem_alloc_data(&rparam, 4); - - rparam.offset.start = 0; - rparam.offset.end = 4; + char rparam[4]; /* unsupported */ - SSVAL(rparam.data,0,NERR_notsupported); - SSVAL(rparam.data,2,0); /* converter word */ + SSVAL(rparam,0,NERR_notsupported); + SSVAL(rparam,2,0); /* converter word */ DEBUG(3,("Unsupported API fd command\n")); /* now send the reply */ - send_trans_reply(outbuf, NULL, &rparam, NULL, 0, max_rdata_len); - - mem_free_data(&rparam); + send_trans_reply(outbuf, rparam, 4, NULL, 0, False); - return(-1); + return -1; } /**************************************************************************** - handle remote api calls delivered to a named pipe already opened. - ****************************************************************************/ + Handle remote api calls delivered to a named pipe already opened. + ****************************************************************************/ + static int api_fd_reply(connection_struct *conn,uint16 vuid,char *outbuf, uint16 *setup,char *data,char *params, int suwcnt,int tdscnt,int tpscnt,int mdrcnt,int mprcnt) { - BOOL reply = False; - - uint16 pnum; - uint16 subcommand; + BOOL reply = False; pipes_struct *p = NULL; + int pnum; + int subcommand; DEBUG(5,("api_fd_reply\n")); /* First find out the name of this file. */ - if (suwcnt != 2) - { + if (suwcnt != 2) { DEBUG(0,("Unexpected named pipe transaction.\n")); return(-1); } /* Get the file handle and hence the file name. */ - pnum = setup[1]; - subcommand = setup[0]; - p = get_rpc_pipe(pnum); + /* + * NB. The setup array has already been transformed + * via SVAL and so is in gost byte order. + */ + pnum = ((int)setup[1]) & 0xFFFF; + subcommand = ((int)setup[0]) & 0xFFFF; - if (p != NULL) - { - DEBUG(3,("Got API command 0x%x on pipe \"%s\" (pnum %x)", - subcommand, p->name, pnum)); + if(!(p = get_rpc_pipe(pnum))) { + DEBUG(1,("api_fd_reply: INVALID PIPE HANDLE: %x\n", pnum)); + return api_no_reply(outbuf, mdrcnt); + } - /* record maximum data length that can be transmitted in an SMBtrans */ - p->file_offset = mdrcnt; - p->prev_pdu_file_offset = 0; + DEBUG(3,("Got API command 0x%x on pipe \"%s\" (pnum %x)", subcommand, p->name, pnum)); - DEBUG(10,("api_fd_reply: p:%p file_offset: %d\n", - p, p->file_offset)); + /* record maximum data length that can be transmitted in an SMBtrans */ + p->max_trans_reply = mdrcnt; - switch (subcommand) - { - case 0x26: - { - reply = rpc_to_smb(p, data, tdscnt); - if (reply) - { - api_rpc_trans_reply(outbuf, p); - } - break; - } - case 0x53: - { - /* Wait Named Pipe Handle state */ - reply = api_WNPHS(outbuf, p, params); - break; - } - case 0x01: - { - /* Set Named Pipe Handle state */ - reply = api_SNPHS(outbuf, p, params); - break; - } - } - } - else - { - DEBUG(1,("api_fd_reply: INVALID PIPE HANDLE: %x\n", pnum)); + DEBUG(10,("api_fd_reply: p:%p max_trans_reply: %d\n", p, p->max_trans_reply)); + + switch (subcommand) { + case 0x26: + /* dce/rpc command */ + reply = rpc_command(p, data, tdscnt); + if (reply) + reply = api_rpc_trans_reply(outbuf, p); + break; + case 0x53: + /* Wait Named Pipe Handle state */ + reply = api_WNPHS(outbuf, p, params, tpscnt); + break; + case 0x01: + /* Set Named Pipe Handle state */ + reply = api_SNPHS(outbuf, p, params, tpscnt); + break; } if (!reply) - { return api_no_reply(outbuf, mdrcnt); - } + return -1; } /**************************************************************************** - the buffer was too small - ****************************************************************************/ + The buffer was too small + ****************************************************************************/ + static BOOL api_TooSmall(connection_struct *conn,uint16 vuid, char *param,char *data, int mdrcnt,int mprcnt, char **rdata,char **rparam, @@ -3300,8 +3328,9 @@ static BOOL api_TooSmall(connection_struct *conn,uint16 vuid, char *param,char * /**************************************************************************** - the request is not supported - ****************************************************************************/ + The request is not supported + ****************************************************************************/ + static BOOL api_Unsupported(connection_struct *conn,uint16 vuid, char *param,char *data, int mdrcnt,int mprcnt, char **rdata,char **rparam, @@ -3364,14 +3393,13 @@ struct /**************************************************************************** - handle remote api calls - ****************************************************************************/ + Handle remote api calls + ****************************************************************************/ + static int api_reply(connection_struct *conn,uint16 vuid,char *outbuf,char *data,char *params, int tdscnt,int tpscnt,int mdrcnt,int mprcnt) { int api_command; - struct mem_buf rdata_buf; - struct mem_buf rparam_buf; char *rdata = NULL; char *rparam = NULL; int rdata_len = 0; @@ -3379,7 +3407,10 @@ static int api_reply(connection_struct *conn,uint16 vuid,char *outbuf,char *data BOOL reply=False; int i; - SMB_ASSERT(params != 0); + if (!params) { + DEBUG(0,("ERROR: NULL params in api_reply()\n")); + return 0; + } api_command = SVAL(params,0); @@ -3389,15 +3420,20 @@ static int api_reply(connection_struct *conn,uint16 vuid,char *outbuf,char *data skip_string(params+2,1), tdscnt,tpscnt,mdrcnt,mprcnt)); - for (i=0;api_commands[i].name;i++) - if (api_commands[i].id == api_command && api_commands[i].fn) - { + for (i=0;api_commands[i].name;i++) { + if (api_commands[i].id == api_command && api_commands[i].fn) { DEBUG(3,("Doing %s\n",api_commands[i].name)); break; - } + } + } - rdata = (char *)malloc(1024); if (rdata) bzero(rdata,1024); - rparam = (char *)malloc(1024); if (rparam) bzero(rparam,1024); + rdata = (char *)malloc(1024); + if (rdata) + memset(rdata,'\0',1024); + + rparam = (char *)malloc(1024); + if (rparam) + memset(rparam,'\0',1024); if(!rdata || !rparam) { DEBUG(0,("api_reply: malloc fail !\n")); @@ -3409,29 +3445,24 @@ static int api_reply(connection_struct *conn,uint16 vuid,char *outbuf,char *data if (rdata_len > mdrcnt || - rparam_len > mprcnt) - { + rparam_len > mprcnt) { reply = api_TooSmall(conn,vuid,params,data,mdrcnt,mprcnt, &rdata,&rparam,&rdata_len,&rparam_len); - } - + } /* if we get False back then it's actually unsupported */ if (!reply) api_Unsupported(conn,vuid,params,data,mdrcnt,mprcnt, &rdata,&rparam,&rdata_len,&rparam_len); - - mem_create(&rdata_buf , rdata , 0, rdata_len , 0, False); - mem_create(&rparam_buf, rparam, 0, rparam_len, 0, False); - - /* now send the reply */ - send_trans_reply(outbuf, &rdata_buf, &rparam_buf, NULL, 0, 0); + send_trans_reply(outbuf, rparam, rparam_len, rdata, rdata_len, False); - if (rdata ) free(rdata); - if (rparam) free(rparam); + if (rdata ) + free(rdata); + if (rparam) + free(rparam); - return(-1); + return -1; } /**************************************************************************** @@ -3445,9 +3476,7 @@ static int named_pipe(connection_struct *conn,uint16 vuid, char *outbuf,char *na DEBUG(3,("named pipe command on <%s> name\n", name)); if (strequal(name,"LANMAN")) - { return api_reply(conn,vuid,outbuf,data,params,tdscnt,tpscnt,mdrcnt,mprcnt); - } if (strequal(name,"WKSSVC") || strequal(name,"SRVSVC") || @@ -3460,26 +3489,23 @@ static int named_pipe(connection_struct *conn,uint16 vuid, char *outbuf,char *na } if (strlen(name) < 1) - { return api_fd_reply(conn,vuid,outbuf,setup,data,params,suwcnt,tdscnt,tpscnt,mdrcnt,mprcnt); - } if (setup) - { DEBUG(3,("unknown named pipe: setup 0x%X setup1=%d\n", (int)setup[0],(int)setup[1])); - } return 0; } /**************************************************************************** - reply to a SMBtrans - ****************************************************************************/ + Reply to a SMBtrans. + ****************************************************************************/ + int reply_trans(connection_struct *conn, char *inbuf,char *outbuf, int size, int bufsize) { fstring name; - int name_offset = 0; + int name_offset = 0; char *data=NULL,*params=NULL; uint16 *setup=NULL; int outsize = 0; @@ -3497,7 +3523,7 @@ int reply_trans(connection_struct *conn, char *inbuf,char *outbuf, int size, int int dsoff = SVAL(inbuf,smb_vwv12); int suwcnt = CVAL(inbuf,smb_vwv13); - bzero(name, sizeof(name)); + memset(name, '\0',sizeof(name)); fstrcpy(name,smb_buf(inbuf)); if (dscnt > tdscnt || pscnt > tpscnt) { @@ -3523,7 +3549,7 @@ int reply_trans(connection_struct *conn, char *inbuf,char *outbuf, int size, int if (suwcnt) { int i; if((setup = (uint16 *)malloc(suwcnt*sizeof(uint16))) == NULL) { - DEBUG(0,("reply_trans: setup malloc fail for %d bytes !\n", suwcnt * sizeof(uint16))); + DEBUG(0,("reply_trans: setup malloc fail for %d bytes !\n", (int)(suwcnt * sizeof(uint16)))); return(ERROR(ERRDOS,ERRnomem)); } for (i=0;i<suwcnt;i++) @@ -3546,16 +3572,19 @@ int reply_trans(connection_struct *conn, char *inbuf,char *outbuf, int size, int ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT); - if ((ret && (CVAL(inbuf, smb_com) != SMBtrans)) || !ret) { + if ((ret && (CVAL(inbuf, smb_com) != SMBtranss)) || !ret) { if(ret) { DEBUG(0,("reply_trans: Invalid secondary trans packet\n")); } else { DEBUG(0,("reply_trans: %s in getting secondary trans response.\n", (smb_read_error == READ_ERROR) ? "error" : "timeout" )); } - if (params) free(params); - if (data) free(data); - if (setup) free(setup); + if (params) + free(params); + if (data) + free(data); + if (setup) + free(setup); return(ERROR(ERRSRV,ERRerror)); } @@ -3589,14 +3618,13 @@ int reply_trans(connection_struct *conn, char *inbuf,char *outbuf, int size, int DEBUG(3,("trans <%s> data=%d params=%d setup=%d\n", name,tdscnt,tpscnt,suwcnt)); - /* - * WinCE wierdness.... - */ + /* + * WinCE wierdness.... + */ - if (name[0] == '\\' && (StrnCaseCmp(&name[1],local_machine, - strlen(local_machine)) == 0)) { - name_offset = strlen(local_machine)+1; - } + if (name[0] == '\\' && (StrnCaseCmp(&name[1],local_machine, strlen(local_machine)) == 0) && + (name[strlen(local_machine)+1] == '\\')) + name_offset = strlen(local_machine)+1; if (strncmp(&name[name_offset],"\\PIPE\\",strlen("\\PIPE\\")) == 0) { DEBUG(5,("calling named_pipe\n")); @@ -3609,9 +3637,12 @@ int reply_trans(connection_struct *conn, char *inbuf,char *outbuf, int size, int } - if (data) free(data); - if (params) free(params); - if (setup) free(setup); + if (data) + free(data); + if (params) + free(params); + if (setup) + free(setup); if (close_on_completion) close_cnum(conn,vuid); diff --git a/source3/smbd/mangle.c b/source3/smbd/mangle.c index b829746a32..48e16e5332 100644 --- a/source3/smbd/mangle.c +++ b/source3/smbd/mangle.c @@ -63,11 +63,13 @@ extern BOOL case_mangle; /* If true, all chars in 8.3 should be same case. */ * global. There is a call to lp_magicchar() in server.c * that is used to override the initial value. * - * basechars - The set of 36 characters used for name mangling. This + * MANGLE_BASE - This is the number of characters we use for name mangling. + * + * basechars - The set characters used for name mangling. This * is static (scope is this file only). * - * base36() - Macro used to select a character from basechars (i.e., - * base36(n) will return the nth digit, modulo 36). + * mangle() - Macro used to select a character from basechars (i.e., + * mangle(n) will return the nth digit, modulo MANGLE_BASE). * * chartest - array 0..255. The index range is the set of all possible * values of a byte. For each byte value, the content is a @@ -110,12 +112,13 @@ extern BOOL case_mangle; /* If true, all chars in 8.3 should be same case. */ char magic_char = '~'; -static char basechars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +static char basechars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%"; +#define MANGLE_BASE (sizeof(basechars)/sizeof(char)-1) static unsigned char chartest[256] = { 0 }; static BOOL ct_initialized = False; -#define base36(V) ((char)(basechars[(V) % 36])) +#define mangle(V) ((char)(basechars[(V) % MANGLE_BASE])) #define BASECHAR_MASK 0xf0 #define ILLEGAL_MASK 0x0f #define isbasechar(C) ( (chartest[ ((C) & 0xff) ]) & BASECHAR_MASK ) @@ -148,7 +151,7 @@ static void init_chartest( void ) char *illegalchars = "*\\/?<>|\":"; unsigned char *s; - bzero( (char *)chartest, 256 ); + memset( (char *)chartest, '\0', 256 ); for( s = (unsigned char *)illegalchars; *s; s++ ) chartest[*s] = ILLEGAL_MASK; @@ -514,7 +517,7 @@ void reset_mangled_cache( void ) * * If the extension of the raw name maps directly to the * extension of the mangled name, then we'll store both names - * *without* extensions. That way, we can provide consistant + * *without* extensions. That way, we can provide consistent * reverse mangling for all names that match. The test here is * a bit more careful than the one done in earlier versions of * mangle.c: @@ -533,9 +536,9 @@ static void cache_mangled_name( char *mangled_name, char *raw_name ) ubi_cacheEntryPtr new_entry; char *s1; char *s2; - int mangled_len; - int raw_len; - int i; + size_t mangled_len; + size_t raw_len; + size_t i; /* If the cache isn't initialized, give up. */ if( !mc_initialized ) @@ -561,7 +564,7 @@ static void cache_mangled_name( char *mangled_name, char *raw_name ) } } - /* Allocate a new cache entry. If the allcoation fails, just return. */ + /* Allocate a new cache entry. If the allocation fails, just return. */ i = sizeof( ubi_cacheEntry ) + mangled_len + raw_len + 2; new_entry = malloc( i ); if( !new_entry ) @@ -589,11 +592,13 @@ static void cache_mangled_name( char *mangled_name, char *raw_name ) * * ************************************************************************** ** */ + BOOL check_mangled_cache( char *s ) - { +{ ubi_cacheEntryPtr FoundPtr; char *ext_start = NULL; char *found_name; + char *saved_ext = NULL; /* If the cache isn't initialized, give up. */ if( !mc_initialized ) @@ -603,19 +608,34 @@ BOOL check_mangled_cache( char *s ) /* If we didn't find the name *with* the extension, try without. */ if( !FoundPtr ) - { + { ext_start = strrchr( s, '.' ); if( ext_start ) - { + { + if((saved_ext = strdup(ext_start)) == NULL) + return False; + *ext_start = '\0'; FoundPtr = ubi_cacheGet( mangled_cache, (ubi_trItemPtr)s ); - *ext_start = '.'; - } + /* + * At this point s is the name without the + * extension. We re-add the extension if saved_ext + * is not null, before freeing saved_ext. + */ } + } /* Okay, if we haven't found it we're done. */ if( !FoundPtr ) + { + if(saved_ext) + { + /* Replace the saved_ext as it was truncated. */ + (void)pstrcat( s, saved_ext ); + free(saved_ext); + } return( False ); + } /* If we *did* find it, we need to copy it into the string buffer. */ found_name = (char *)(FoundPtr + 1); @@ -624,13 +644,17 @@ BOOL check_mangled_cache( char *s ) DEBUG( 3, ("Found %s on mangled stack ", s) ); (void)pstrcpy( s, found_name ); - if( ext_start ) - (void)pstrcat( s, ext_start ); + if( saved_ext ) + { + /* Replace the saved_ext as it was truncated. */ + (void)pstrcat( s, saved_ext ); + free(saved_ext); + } DEBUG( 3, ("as %s\n", s) ); return( True ); - } /* check_mangled_cache */ +} /* check_mangled_cache */ /* ************************************************************************** ** @@ -652,6 +676,12 @@ static char *map_filename( char *s, /* This is null terminated */ pstrcpy( matching_bit, "" ); /* Match but no star gets this. */ pp = pat; /* Initialize the pointers. */ sp = s; + + if( strequal(s, ".") || strequal(s, "..")) + { + return NULL; /* Do not map '.' and '..' */ + } + if( (len == 1) && (*pattern == '*') ) { return NULL; /* Impossible, too ambiguous for */ @@ -807,7 +837,7 @@ static void do_fwd_mangled_map(char *s, char *MangledMap) */ void mangle_name_83( char *s) { - int csum = str_checksum(s); + int csum; char *p; char extension[4]; char base[9]; @@ -829,7 +859,11 @@ void mangle_name_83( char *s) csum = str_checksum( s ); *p = '.'; } + else + csum = str_checksum(s); } + else + csum = str_checksum(s); strupper( s ); @@ -855,7 +889,7 @@ void mangle_name_83( char *s) } else { - extension[extlen++] = base36( (unsigned char)*p ); + extension[extlen++] = mangle( (unsigned char)*p ); } p += 2; break; @@ -889,7 +923,7 @@ void mangle_name_83( char *s) } else { - base[baselen++] = base36( (unsigned char)*p ); + base[baselen++] = mangle( (unsigned char)*p ); } p += 2; break; @@ -906,10 +940,10 @@ void mangle_name_83( char *s) } base[baselen] = 0; - csum = csum % (36*36); + csum = csum % (MANGLE_BASE*MANGLE_BASE); (void)slprintf(s, 12, "%s%c%c%c", - base, magic_char, base36( csum/36 ), base36( csum ) ); + base, magic_char, mangle( csum/MANGLE_BASE ), mangle( csum ) ); if( *extension ) { @@ -935,6 +969,11 @@ void mangle_name_83( char *s) * signal that a client does not require name mangling, * thus skipping the name mangling even on shares which * have name-mangling turned on. + * cache83 - If False, the mangled name cache will not be updated. + * This is usually used to prevent that we overwrite + * a conflicting cache entry prematurely, i.e. before + * we know whether the client is really interested in the + * current name. (See PR#13758). UKD. * snum - Share number. This identifies the share in which the * name exists. * @@ -943,11 +982,11 @@ void mangle_name_83( char *s) * * **************************************************************************** */ -BOOL name_map_mangle(char *OutName, BOOL need83, int snum) +BOOL name_map_mangle(char *OutName, BOOL need83, BOOL cache83, int snum) { char *map; - DEBUG(5,("name_map_mangle( %s, %s, %d )\n", - OutName, need83?"TRUE":"FALSE", snum)); + DEBUG(5,("name_map_mangle( %s, need83 = %s, cache83 = %s, %d )\n", OutName, + need83 ? "TRUE" : "FALSE", cache83 ? "TRUE" : "FALSE", snum)); #ifdef MANGLE_LONG_FILENAMES if( !need83 && is_illegal_name(OutName) ) @@ -963,17 +1002,19 @@ BOOL name_map_mangle(char *OutName, BOOL need83, int snum) /* check if it's already in 8.3 format */ if (need83 && !is_8_3(OutName, True)) { - char *tmp; + char *tmp = NULL; if (!lp_manglednames(snum)) { return(False); } /* mangle it into 8.3 */ - tmp = strdup(OutName); + if (cache83) + tmp = strdup(OutName); + mangle_name_83(OutName); - if(tmp) { + if(tmp != NULL) { cache_mangled_name(OutName, tmp); free(tmp); } diff --git a/source3/smbd/message.c b/source3/smbd/message.c index d13dfda1e0..2f94bdf111 100644 --- a/source3/smbd/message.c +++ b/source3/smbd/message.c @@ -54,7 +54,7 @@ static void msg_deliver(void) /* put it in a temporary file */ slprintf(s,sizeof(s)-1, "%s/msg.XXXXXX",tmpdir()); - fstrcpy(name,(char *)mktemp(s)); + fstrcpy(name,(char *)smbd_mktemp(s)); fd = sys_open(name,O_WRONLY|O_CREAT|O_TRUNC|O_EXCL,0600); if (fd == -1) { @@ -74,10 +74,13 @@ static void msg_deliver(void) /* run the command */ if (*lp_msg_command()) { + fstring alpha_msgfrom; + fstring alpha_msgto; + pstrcpy(s,lp_msg_command()); - string_sub(s,"%s",name); - string_sub(s,"%f",msgfrom); - string_sub(s,"%t",msgto); + pstring_sub(s,"%s",name); + pstring_sub(s,"%f",alpha_strcpy(alpha_msgfrom,msgfrom,sizeof(alpha_msgfrom))); + pstring_sub(s,"%t",alpha_strcpy(alpha_msgto,msgto,sizeof(alpha_msgto))); standard_sub_basic(s); smbrun(s,NULL,False); } @@ -99,7 +102,6 @@ int reply_sends(connection_struct *conn, msgpos = 0; - if (! (*lp_msg_command())) return(ERROR(ERRSRV,ERRmsgoff)); @@ -113,7 +115,9 @@ int reply_sends(connection_struct *conn, fstrcpy(msgto,dest); len = SVAL(msg,0); - len = MIN(len,1600-msgpos); + len = MIN(len,sizeof(msgbuf)-msgpos); + + memset(msgbuf,'\0',sizeof(msgbuf)); memcpy(&msgbuf[msgpos],msg+2,len); msgpos += len; @@ -140,6 +144,7 @@ int reply_sendstrt(connection_struct *conn, outsize = set_message(outbuf,1,0,True); + memset(msgbuf,'\0',sizeof(msgbuf)); msgpos = 0; orig = smb_buf(inbuf)+1; @@ -172,7 +177,7 @@ int reply_sendtxt(connection_struct *conn, msg = smb_buf(inbuf) + 1; len = SVAL(msg,0); - len = MIN(len,1600-msgpos); + len = MIN(len,sizeof(msgbuf)-msgpos); memcpy(&msgbuf[msgpos],msg+2,len); msgpos += len; @@ -202,4 +207,3 @@ int reply_sendend(connection_struct *conn, return(outsize); } - diff --git a/source3/smbd/negprot.c b/source3/smbd/negprot.c index 0b48b0e2b2..b2366b0a37 100644 --- a/source3/smbd/negprot.c +++ b/source3/smbd/negprot.c @@ -26,8 +26,6 @@ extern int Protocol; extern int max_recv; extern fstring global_myworkgroup; extern fstring remote_machine; -extern pstring myhostname; -extern dfs_internal dfs_struct; /**************************************************************************** reply for the core protocol @@ -160,6 +158,16 @@ reply for the nt protocol static int reply_nt1(char *outbuf) { /* dual names + lock_and_read + nt SMBs + remote API calls */ + int capabilities = CAP_NT_FIND|CAP_LOCK_AND_READ| + (lp_nt_smb_support() ? CAP_NT_SMBS | CAP_RPC_REMOTE_APIS : 0) | + (SMB_OFF_T_BITS == 64 ? CAP_LARGE_FILES : 0); + + +/* + other valid capabilities which we may support at some time... + CAP_LARGE_READX|CAP_STATUS32|CAP_LEVEL_II_OPLOCKS; + */ + int secword=0; BOOL doencrypt = SMBENCRYPT(); time_t t = time(NULL); @@ -168,32 +176,9 @@ static int reply_nt1(char *outbuf) char cryptkey[8]; char crypt_len = 0; - int capabilities = CAP_NT_FIND|CAP_LOCK_AND_READ; - - if (lp_nt_smb_support()) - { - capabilities |= CAP_NT_SMBS | CAP_RPC_REMOTE_APIS; - } - - if (SMB_OFF_T_BITS == 64) - { - capabilities |= CAP_LARGE_FILES; - } - - if (dfs_struct.ready==True) - { - capabilities |= CAP_DFS; - } - -/* - other valid capabilities which we may support at some time... - CAP_LARGE_READX|CAP_STATUS32|CAP_LEVEL_II_OPLOCKS; - */ - - if (lp_security() == SEC_SERVER) - { - cli = server_cryptkey(); - } + if (lp_security() == SEC_SERVER) { + cli = server_cryptkey(); + } if (cli) { DEBUG(3,("using password server validation\n")); @@ -218,13 +203,12 @@ static int reply_nt1(char *outbuf) if (doencrypt) secword |= 2; /* decide where (if) to put the encryption challenge, and - follow it with the OEM'd domain name in Unicode. + follow it with the OEM'd domain name */ - data_len = crypt_len + (strlen(global_myworkgroup)+1)*2; + data_len = crypt_len + strlen(global_myworkgroup) + 1; set_message(outbuf,17,data_len,True); - ascii_to_unibuf(smb_buf(outbuf)+crypt_len, global_myworkgroup, - (strlen(global_myworkgroup)+1)*2); + pstrcpy(smb_buf(outbuf)+crypt_len, global_myworkgroup); CVAL(outbuf,smb_vwv1) = secword; SSVALS(outbuf,smb_vwv16+1,crypt_len); @@ -268,6 +252,14 @@ protocol [LM1.2X002] protocol [LANMAN2.1] protocol [NT LM 0.12] +Win2K: +protocol [PC NETWORK PROGRAM 1.0] +protocol [LANMAN1.0] +protocol [Windows for Workgroups 3.1a] +protocol [LM1.2X002] +protocol [LANMAN2.1] +protocol [NT LM 0.12] + OS/2: protocol [PC NETWORK PROGRAM 1.0] protocol [XENIX CORE] @@ -281,29 +273,31 @@ protocol [LANMAN2.1] * * This appears to be the matrix of which protocol is used by which * MS product. - Protocol WfWg Win95 WinNT OS/2 - PC NETWORK PROGRAM 1.0 1 1 1 1 - XENIX CORE 2 2 + Protocol WfWg Win95 WinNT Win2K OS/2 + PC NETWORK PROGRAM 1.0 1 1 1 1 1 + XENIX CORE 2 2 MICROSOFT NETWORKS 3.0 2 2 DOS LM1.2X002 3 3 MICROSOFT NETWORKS 1.03 3 DOS LANMAN2.1 4 4 - LANMAN1.0 4 3 - Windows for Workgroups 3.1a 5 5 5 - LM1.2X002 6 4 - LANMAN2.1 7 5 - NT LM 0.12 6 8 + LANMAN1.0 4 2 3 + Windows for Workgroups 3.1a 5 5 5 3 + LM1.2X002 6 4 4 + LANMAN2.1 7 5 5 + NT LM 0.12 6 8 6 * * tim@fsg.com 09/29/95 + * Win2K added by matty 17/7/99 */ #define ARCH_WFWG 0x3 /* This is a fudge because WfWg is like Win95 */ #define ARCH_WIN95 0x2 -#define ARCH_OS2 0xC /* Again OS/2 is like NT */ -#define ARCH_WINNT 0x8 -#define ARCH_SAMBA 0x10 +#define ARCH_WINNT 0x4 +#define ARCH_WIN2K 0xC /* Win2K is like NT */ +#define ARCH_OS2 0x14 /* Again OS/2 is like NT */ +#define ARCH_SAMBA 0x20 -#define ARCH_ALL 0x1F +#define ARCH_ALL 0x3F /* List of supported protocols, most desired first */ static struct { @@ -346,17 +340,17 @@ int reply_negprot(connection_struct *conn, Index++; DEBUG(3,("Requested protocol [%s]\n",p)); if (strcsequal(p,"Windows for Workgroups 3.1a")) - arch &= ( ARCH_WFWG | ARCH_WIN95 | ARCH_WINNT ); + arch &= ( ARCH_WFWG | ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K ); else if (strcsequal(p,"DOS LM1.2X002")) arch &= ( ARCH_WFWG | ARCH_WIN95 ); else if (strcsequal(p,"DOS LANMAN2.1")) arch &= ( ARCH_WFWG | ARCH_WIN95 ); else if (strcsequal(p,"NT LM 0.12")) - arch &= ( ARCH_WIN95 | ARCH_WINNT ); + arch &= ( ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K ); else if (strcsequal(p,"LANMAN2.1")) - arch &= ( ARCH_WINNT | ARCH_OS2 ); + arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 ); else if (strcsequal(p,"LM1.2X002")) - arch &= ( ARCH_WINNT | ARCH_OS2 ); + arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 ); else if (strcsequal(p,"MICROSOFT NETWORKS 1.03")) arch &= ARCH_WINNT; else if (strcsequal(p,"XENIX CORE")) @@ -382,6 +376,9 @@ int reply_negprot(connection_struct *conn, case ARCH_WINNT: set_remote_arch(RA_WINNT); break; + case ARCH_WIN2K: + set_remote_arch(RA_WIN2K); + break; case ARCH_OS2: set_remote_arch(RA_OS2); break; @@ -394,7 +391,7 @@ int reply_negprot(connection_struct *conn, reload_services(True); /* a special case to stop password server loops */ - if (Index == 1 && strequal(remote_machine,myhostname) && + if (Index == 1 && strequal(remote_machine,myhostname()) && (lp_security()==SEC_SERVER || lp_security()==SEC_DOMAIN)) exit_server("Password server loop!"); diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index 70023e2407..9615c5ada2 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -37,17 +37,14 @@ static void remove_pending_change_notify_requests_by_mid(int mid); static char *known_nt_pipes[] = { "\\LANMAN", "\\srvsvc", - "\\svcctl", "\\samr", "\\wkssvc", - "\\browser", "\\NETLOGON", "\\ntlsa", "\\ntsvcs", "\\lsass", "\\lsarpc", "\\winreg", - "\\spoolss", NULL }; @@ -58,7 +55,7 @@ static char *known_nt_pipes[] = { HACK ! Always assumes smb_setup field is zero. ****************************************************************************/ -static int send_nt_replies(char *outbuf, int bufsize, char *params, +static int send_nt_replies(char *inbuf, char *outbuf, int bufsize, uint32 nt_error, char *params, int paramsize, char *pdata, int datasize) { extern int max_send; @@ -78,6 +75,13 @@ static int send_nt_replies(char *outbuf, int bufsize, char *params, set_message(outbuf,18,0,True); + if(nt_error != 0) { + /* NT Error. */ + SSVAL(outbuf,smb_flg2, SVAL(outbuf,smb_flg2) | FLAGS2_32_BIT_ERROR_CODES); + + ERROR(0,nt_error); + } + /* * If there genuinely are no parameters or data to send just send * the empty packet. @@ -355,18 +359,25 @@ static int map_create_disposition( uint32 create_disposition) Utility function to map share modes. ****************************************************************************/ -static int map_share_mode( char *fname, uint32 desired_access, uint32 share_access, uint32 file_attributes) +static int map_share_mode( BOOL *pstat_open_only, char *fname, + uint32 desired_access, uint32 share_access, uint32 file_attributes) { int smb_open_mode = -1; - switch( desired_access & (FILE_READ_DATA|FILE_WRITE_DATA) ) { + *pstat_open_only = False; + + switch( desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA) ) { case FILE_READ_DATA: smb_open_mode = DOS_OPEN_RDONLY; break; case FILE_WRITE_DATA: + case FILE_APPEND_DATA: + case FILE_WRITE_DATA|FILE_APPEND_DATA: smb_open_mode = DOS_OPEN_WRONLY; break; case FILE_READ_DATA|FILE_WRITE_DATA: + case FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA: + case FILE_READ_DATA|FILE_APPEND_DATA: smb_open_mode = DOS_OPEN_RDWR; break; } @@ -386,8 +397,12 @@ static int map_share_mode( char *fname, uint32 desired_access, uint32 share_acce */ if (smb_open_mode == -1) { + if(desired_access == WRITE_DAC_ACCESS || desired_access == READ_CONTROL_ACCESS) + *pstat_open_only = True; + if(desired_access & (DELETE_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS| FILE_EXECUTE|FILE_READ_ATTRIBUTES| + FILE_READ_EA|FILE_WRITE_EA|SYSTEM_SECURITY_ACCESS| FILE_WRITE_ATTRIBUTES|READ_CONTROL_ACCESS)) smb_open_mode = DOS_OPEN_RDONLY; else { @@ -464,11 +479,40 @@ static time_t fail_time; void fail_next_srvsvc_open(void) { + /* Check client is WinNT proper; Win2K doesn't like Jeremy's hack - matty */ + if (get_remote_arch() != RA_WINNT) + return; + fail_next_srvsvc = True; fail_time = time(NULL); DEBUG(10,("fail_next_srvsvc_open: setting up timeout close of \\srvsvc pipe for print fix.\n")); } +/* + * HACK alert.... see above - JRA. + */ + +BOOL should_fail_next_srvsvc_open(const char *pipename) +{ + + DEBUG(10,("should_fail_next_srvsvc_open: fail = %d, pipe = %s\n", + (int)fail_next_srvsvc, pipename)); + + if(fail_next_srvsvc && (time(NULL) > fail_time + HACK_FAIL_TIME)) { + fail_next_srvsvc = False; + fail_time = (time_t)0; + DEBUG(10,("should_fail_next_srvsvc_open: End of timeout close of \\srvsvc pipe for print fix.\n")); + } + + if(fail_next_srvsvc && strequal(pipename, "srvsvc")) { + fail_next_srvsvc = False; + DEBUG(10,("should_fail_next_srvsvc_open: Deliberately failing open of \\srvsvc pipe for print fix.\n")); + return True; + } + return False; +} + + /**************************************************************************** Reply to an NT create and X call on a pipe. ****************************************************************************/ @@ -487,32 +531,15 @@ static int nt_open_pipe(char *fname, connection_struct *conn, if( strequal(fname,known_nt_pipes[i])) break; - /* - * HACK alert.... see above - JRA. - */ - - if(fail_next_srvsvc && (time(NULL) > fail_time + HACK_FAIL_TIME)) { - fail_next_srvsvc = False; - fail_time = (time_t)0; - DEBUG(10,("nt_open_pipe: End of timeout close of \\srvsvc pipe for print fix.\n")); - } - - if(fail_next_srvsvc && strequal(fname, "\\srvsvc")) { - fail_next_srvsvc = False; - DEBUG(10,("nt_open_pipe: Deliberately failing open of \\srvsvc pipe for print fix.\n")); - return(ERROR(ERRSRV,ERRaccess)); - } - - /* - * End hack alert.... see above - JRA. - */ - if ( known_nt_pipes[i] == NULL ) return(ERROR(ERRSRV,ERRaccess)); /* Strip \\ off the name. */ fname++; + if(should_fail_next_srvsvc_open(fname)) + return (ERROR(ERRSRV,ERRaccess)); + DEBUG(3,("nt_open_pipe: Known pipe %s opening.\n", fname)); p = open_rpc_pipe_p(fname, conn, vuid); @@ -547,7 +574,7 @@ int reply_ntcreate_and_X(connection_struct *conn, /* Breakout the oplock request bits so we can set the reply bits separately. */ int oplock_request = 0; - mode_t unixmode; + mode_t unixmode; int pnum = -1; int fmode=0,rmode=0; SMB_OFF_T file_len = 0; @@ -556,6 +583,7 @@ int reply_ntcreate_and_X(connection_struct *conn, BOOL bad_path = False; files_struct *fsp=NULL; char *p = NULL; + BOOL stat_open_only = False; /* * We need to construct the open_and_X ofun value from the @@ -576,8 +604,23 @@ int reply_ntcreate_and_X(connection_struct *conn, files_struct *dir_fsp = file_fsp(inbuf,smb_ntcreate_RootDirectoryFid); size_t dir_name_len; - if(!dir_fsp || !dir_fsp->is_directory) + if(!dir_fsp) + return(ERROR(ERRDOS,ERRbadfid)); + + if(!dir_fsp->is_directory) { + /* + * Check to see if this is a mac fork of some kind. + */ + + get_filename(&fname[0], inbuf, smb_buf(inbuf)-inbuf, + smb_buflen(inbuf),fname_len); + + if( fname[0] == ':') { + SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); + return(ERROR(0, 0xc0000000|NT_STATUS_OBJECT_PATH_NOT_FOUND)); + } return(ERROR(ERRDOS,ERRbadfid)); + } /* * Copy in the base directory name. @@ -600,26 +643,17 @@ int reply_ntcreate_and_X(connection_struct *conn, get_filename(&fname[dir_name_len], inbuf, smb_buf(inbuf)-inbuf, smb_buflen(inbuf),fname_len); -#if 0 - StrnCpy(&fname[dir_name_len], smb_buf(inbuf),fname_len); - fname[dir_name_len+fname_len] = '\0'; -#endif } else { get_filename(fname, inbuf, smb_buf(inbuf)-inbuf, smb_buflen(inbuf),fname_len); - -#if 0 - StrnCpy(fname,smb_buf(inbuf),fname_len); - fname[fname_len] = '\0'; -#endif } /* If it's an IPC, use the pipe handler. */ - if (IS_IPC(conn) && lp_nt_pipe_support() && lp_security() != SEC_SHARE) - { + if (IS_IPC(conn) && lp_nt_pipe_support()) { + int ret = nt_open_pipe(fname, conn, inbuf, outbuf, &pnum); if(ret != 0) return ret; @@ -654,11 +688,13 @@ int reply_ntcreate_and_X(connection_struct *conn, * desired access and the share access. */ - if((smb_open_mode = map_share_mode(fname, desired_access, + if((smb_open_mode = map_share_mode(&stat_open_only, fname, desired_access, share_access, - file_attributes)) == -1) { + file_attributes)) == -1) return(ERROR(ERRDOS,ERRbadaccess)); - } + + oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0; + oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0; /* * Ordinary file or directory. @@ -670,11 +706,7 @@ int reply_ntcreate_and_X(connection_struct *conn, set_posix_case_semantics(file_attributes); - if (!unix_dfs_convert(fname,conn,0,&bad_path,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(fname,conn,0,&bad_path,NULL); fsp = file_new(); if (!fsp) { @@ -696,9 +728,6 @@ int reply_ntcreate_and_X(connection_struct *conn, unixmode = unix_mode(conn,smb_attr | aARCH); - oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0; - oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0; - /* * If it's a request for a directory open, deal with it separately. */ @@ -706,8 +735,8 @@ int reply_ntcreate_and_X(connection_struct *conn, if(create_options & FILE_DIRECTORY_FILE) { oplock_request = 0; - open_directory(fsp, conn, fname, smb_ofun, unixmode, - &smb_action); + open_directory(fsp, conn, fname, smb_ofun, + unixmode, &smb_action); restore_case_semantics(file_attributes); @@ -733,13 +762,12 @@ int reply_ntcreate_and_X(connection_struct *conn, * before issuing an oplock break request to * our client. JRA. */ - open_file_shared(fsp,conn,fname,smb_open_mode, - smb_ofun,unixmode, - oplock_request,&rmode,&smb_action); + open_file_shared(fsp,conn,fname,smb_open_mode, + smb_ofun,unixmode, oplock_request,&rmode,&smb_action); if (!fsp->open) { - /* We cheat here. The only case we - * care about is a directory rename, + /* We cheat here. There are two cases we + * care about. One is a directory rename, * where the NT client will attempt to * open the source directory for * DELETE access. Note that when the @@ -752,21 +780,54 @@ int reply_ntcreate_and_X(connection_struct *conn, * will generate an EISDIR error, so * we can catch this here and open a * pseudo handle that is flagged as a - * directory. JRA. */ + * directory. The second is an open + * for a permissions read only, which + * we handle in the open_file_stat case. JRA. + */ if(errno == EISDIR) { + + /* + * Fail the open if it was explicitly a non-directory file. + */ + + if (create_options & FILE_NON_DIRECTORY_FILE) { + file_free(fsp); + restore_case_semantics(file_attributes); + SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); + return(ERROR(0, 0xc0000000|NT_STATUS_FILE_IS_A_DIRECTORY)); + } + oplock_request = 0; - - open_directory(fsp, conn, fname, - smb_ofun, unixmode, - &smb_action); + open_directory(fsp, conn, fname, smb_ofun, unixmode, &smb_action); if(!fsp->open) { file_free(fsp); restore_case_semantics(file_attributes); return(UNIXERROR(ERRDOS,ERRnoaccess)); } +#ifdef EROFS + } else if (((errno == EACCES) || (errno == EROFS)) && stat_open_only) { +#else /* !EROFS */ + } else if (errno == EACCES && stat_open_only) { +#endif + /* + * We couldn't open normally and all we want + * are the permissions. Try and do a stat open. + */ + + oplock_request = 0; + + open_file_stat(fsp,conn,fname,smb_open_mode,&sbuf,&smb_action); + + if(!fsp->open) { + file_free(fsp); + restore_case_semantics(file_attributes); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + } else { + if((errno == ENOENT) && bad_path) { unix_ERR_class = ERRDOS; unix_ERR_code = ERRbadpath; @@ -782,15 +843,13 @@ int reply_ntcreate_and_X(connection_struct *conn, } if(fsp->is_directory) { - if(fsp->conn->vfs_ops.stat(dos_to_unix(fsp->fsp_name, False), - &sbuf) != 0) { - close_directory(fsp); + if(dos_stat(fsp->fsp_name, &sbuf) != 0) { + close_file(fsp,True); restore_case_semantics(file_attributes); return(ERROR(ERRDOS,ERRnoaccess)); } } else { - if (fsp->conn->vfs_ops.fstat(fsp->fd_ptr->fd,&sbuf) - != 0) { + if (!fsp->stat_open && sys_fstat(fsp->fd_ptr->fd,&sbuf) != 0) { close_file(fsp,False); restore_case_semantics(file_attributes); return(ERROR(ERRDOS,ERRnoaccess)); @@ -817,9 +876,9 @@ int reply_ntcreate_and_X(connection_struct *conn, if (oplock_request && lp_fake_oplocks(SNUM(conn))) smb_action |= EXTENDED_OPLOCK_GRANTED; - if(oplock_request && fsp->granted_oplock) + if(oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) smb_action |= EXTENDED_OPLOCK_GRANTED; - + set_message(outbuf,34,0,True); p = outbuf + smb_vwv2; @@ -828,8 +887,14 @@ int reply_ntcreate_and_X(connection_struct *conn, * Currently as we don't support level II oplocks we just report * exclusive & batch here. */ + + if (smb_action & EXTENDED_OPLOCK_GRANTED) + SCVAL(p,0, BATCH_OPLOCK_RETURN); + else if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) + SCVAL(p,0, LEVEL_II_OPLOCK_RETURN); + else + SCVAL(p,0,NO_OPLOCK_RETURN); - SCVAL(p,0, (smb_action & EXTENDED_OPLOCK_GRANTED ? 1 : 0)); p++; SSVAL(p,0,fsp->fnum); p += 2; @@ -861,6 +926,7 @@ int reply_ntcreate_and_X(connection_struct *conn, /**************************************************************************** Reply to a NT_TRANSACT_CREATE call (needs to process SD's). ****************************************************************************/ + static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize, @@ -893,6 +959,7 @@ static int call_nt_transact_create(connection_struct *conn, BOOL bad_path = False; files_struct *fsp = NULL; char *p = NULL; + BOOL stat_open_only = False; /* * We need to construct the open_and_X ofun value from the @@ -914,8 +981,24 @@ static int call_nt_transact_create(connection_struct *conn, files_struct *dir_fsp = file_fsp(params,4); size_t dir_name_len; - if(!dir_fsp || !dir_fsp->is_directory) + if(!dir_fsp) + return(ERROR(ERRDOS,ERRbadfid)); + + if(!dir_fsp->is_directory) { + /* + * Check to see if this is a mac fork of some kind. + */ + + StrnCpy(fname,params+53,fname_len); + fname[fname_len] = '\0'; + + if( fname[0] == ':') { + SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); + return(ERROR(0, 0xc0000000|NT_STATUS_OBJECT_PATH_NOT_FOUND)); + } + return(ERROR(ERRDOS,ERRbadfid)); + } /* * Copy in the base directory name. @@ -951,17 +1034,26 @@ static int call_nt_transact_create(connection_struct *conn, return ret; smb_action = FILE_WAS_OPENED; } else { + + /* + * Now contruct the smb_open_mode value from the desired access + * and the share access. + */ + + if((smb_open_mode = map_share_mode( &stat_open_only, fname, desired_access, + share_access, file_attributes)) == -1) + return(ERROR(ERRDOS,ERRbadaccess)); + + oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0; + oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0; + /* * Check if POSIX semantics are wanted. */ set_posix_case_semantics(file_attributes); - if (!unix_dfs_convert(fname,conn,0,&bad_path,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(fname,conn,0,&bad_path,NULL); fsp = file_new(); if (!fsp) { @@ -983,17 +1075,6 @@ static int call_nt_transact_create(connection_struct *conn, unixmode = unix_mode(conn,smb_attr | aARCH); - oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0; - oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0; - - /* - * Now contruct the smb_open_mode value from the desired access - * and the share access. - */ - - if((smb_open_mode = map_share_mode( fname, desired_access, share_access, file_attributes)) == -1) - return(ERROR(ERRDOS,ERRbadaccess)); - /* * If it's a request for a directory open, deal with it separately. */ @@ -1012,37 +1093,96 @@ static int call_nt_transact_create(connection_struct *conn, if(!fsp->open) { file_free(fsp); + restore_case_semantics(file_attributes); return(UNIXERROR(ERRDOS,ERRnoaccess)); } + + if(dos_stat(fsp->fsp_name, &sbuf) != 0) { + close_file(fsp,True); + restore_case_semantics(file_attributes); + return(ERROR(ERRDOS,ERRnoaccess)); + } + } else { /* * Ordinary file case. */ - open_file_shared(fsp,conn,fname,smb_open_mode,smb_ofun, - unixmode,oplock_request,&rmode,&smb_action); + open_file_shared(fsp,conn,fname,smb_open_mode,smb_ofun,unixmode, + oplock_request,&rmode,&smb_action); if (!fsp->open) { - if((errno == ENOENT) && bad_path) { - unix_ERR_class = ERRDOS; - unix_ERR_code = ERRbadpath; - } - file_free(fsp); - restore_case_semantics(file_attributes); + if(errno == EISDIR) { - return(UNIXERROR(ERRDOS,ERRnoaccess)); - } - - if (fsp->conn->vfs_ops.fstat(fsp->fd_ptr->fd,&sbuf) != 0) { - close_file(fsp,False); + /* + * Fail the open if it was explicitly a non-directory file. + */ - restore_case_semantics(file_attributes); + if (create_options & FILE_NON_DIRECTORY_FILE) { + file_free(fsp); + restore_case_semantics(file_attributes); + SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); + return(ERROR(0, 0xc0000000|NT_STATUS_FILE_IS_A_DIRECTORY)); + } + + oplock_request = 0; + open_directory(fsp, conn, fname, smb_ofun, unixmode, &smb_action); + + if(!fsp->open) { + file_free(fsp); + restore_case_semantics(file_attributes); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } +#ifdef EROFS + } else if (((errno == EACCES) || (errno == EROFS)) && stat_open_only) { +#else /* !EROFS */ + } else if (errno == EACCES && stat_open_only) { +#endif - return(ERROR(ERRDOS,ERRnoaccess)); + /* + * We couldn't open normally and all we want + * are the permissions. Try and do a stat open. + */ + + oplock_request = 0; + + open_file_stat(fsp,conn,fname,smb_open_mode,&sbuf,&smb_action); + + if(!fsp->open) { + file_free(fsp); + restore_case_semantics(file_attributes); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + } else { + + if((errno == ENOENT) && bad_path) { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + file_free(fsp); + + restore_case_semantics(file_attributes); + + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } } + if(fsp->is_directory) { + if(dos_stat(fsp->fsp_name, &sbuf) != 0) { + close_file(fsp,True); + restore_case_semantics(file_attributes); + return(ERROR(ERRDOS,ERRnoaccess)); + } + } else { + if (!fsp->stat_open && sys_fstat(fsp->fd_ptr->fd,&sbuf) != 0) { + close_file(fsp,False); + restore_case_semantics(file_attributes); + return(ERROR(ERRDOS,ERRnoaccess)); + } + } + file_len = sbuf.st_size; fmode = dos_mode(conn,fname,&sbuf); if(fmode == 0) @@ -1063,7 +1203,7 @@ static int call_nt_transact_create(connection_struct *conn, if (oplock_request && lp_fake_oplocks(SNUM(conn))) smb_action |= EXTENDED_OPLOCK_GRANTED; - if(oplock_request && fsp->granted_oplock) + if(oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) smb_action |= EXTENDED_OPLOCK_GRANTED; } } @@ -1075,8 +1215,16 @@ static int call_nt_transact_create(connection_struct *conn, if(params == NULL) return(ERROR(ERRDOS,ERRnomem)); + memset((char *)params,'\0',69); + p = params; - SCVAL(p,0, (smb_action & EXTENDED_OPLOCK_GRANTED ? 1 : 0)); + if (smb_action & EXTENDED_OPLOCK_GRANTED) + SCVAL(p,0, BATCH_OPLOCK_RETURN); + else if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) + SCVAL(p,0, LEVEL_II_OPLOCK_RETURN); + else + SCVAL(p,0,NO_OPLOCK_RETURN); + p += 2; if (IS_IPC(conn)) { SSVAL(p,0,pnum); @@ -1119,7 +1267,7 @@ static int call_nt_transact_create(connection_struct *conn, } /* Send the required number of replies */ - send_nt_replies(outbuf, bufsize, params, 69, *ppdata, 0); + send_nt_replies(inbuf, outbuf, bufsize, 0, params, 69, *ppdata, 0); return -1; } @@ -1156,6 +1304,7 @@ int reply_nttranss(connection_struct *conn, /**************************************************************************** Reply to an NT transact rename command. ****************************************************************************/ + static int call_nt_transact_rename(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize, @@ -1179,7 +1328,7 @@ static int call_nt_transact_rename(connection_struct *conn, /* * Rename was successful. */ - send_nt_replies(outbuf, bufsize, NULL, 0, NULL, 0); + send_nt_replies(inbuf, outbuf, bufsize, 0, NULL, 0, NULL, 0); DEBUG(3,("nt transact rename from = %s, to = %s succeeded.\n", fsp->fsp_name, new_name)); @@ -1191,6 +1340,18 @@ static int call_nt_transact_rename(connection_struct *conn, } /**************************************************************************** + This is the structure to keep the information needed to + determine if a directory has changed. +*****************************************************************************/ + +typedef struct { + time_t modify_time; /* Info from the directory we're monitoring. */ + time_t status_time; /* Info from the directory we're monitoring. */ + time_t total_time; /* Total time of all directory entries - don't care if it wraps. */ + unsigned int num_entries; /* Zero or the number of files in the directory. */ +} change_hash_data; + +/**************************************************************************** This is the structure to queue to implement NT change notify. It consists of smb_size bytes stored from the transact command (to keep the mid, tid etc around). @@ -1201,9 +1362,9 @@ typedef struct { ubi_slNode msg_next; files_struct *fsp; connection_struct *conn; + uint32 flags; time_t next_check_time; - time_t modify_time; /* Info from the directory we're monitoring. */ - time_t status_time; /* Info from the directory we're monitoring. */ + change_hash_data change_data; char request_buf[smb_size]; } change_notify_buf; @@ -1244,8 +1405,92 @@ static void change_notify_reply_packet(char *inbuf, int error_class, uint32 erro } /**************************************************************************** + Create the hash we will use to determine if the contents changed. +*****************************************************************************/ + +static BOOL create_directory_notify_hash( change_notify_buf *cnbp, change_hash_data *change_data) +{ + SMB_STRUCT_STAT st; + files_struct *fsp = cnbp->fsp; + + memset((char *)change_data, '\0', sizeof(change_data)); + + /* + * Store the current timestamp on the directory we are monitoring. + */ + + if(dos_stat(fsp->fsp_name, &st) < 0) { + DEBUG(0,("create_directory_notify_hash: Unable to stat name = %s. \ +Error was %s\n", fsp->fsp_name, strerror(errno) )); + return False; + } + + change_data->modify_time = st.st_mtime; + change_data->status_time = st.st_ctime; + + /* + * If we are to watch for changes that are only stored + * in inodes of files, not in the directory inode, we must + * scan the directory and produce a unique identifier with + * which we can determine if anything changed. We use the + * modify and change times from all the files in the + * directory, added together (ignoring wrapping if it's + * larger than the max time_t value). + */ + + if(cnbp->flags & (FILE_NOTIFY_CHANGE_SIZE|FILE_NOTIFY_CHANGE_LAST_WRITE)) { + pstring full_name; + char *p; + char *fname; + size_t remaining_len; + size_t fullname_len; + void *dp = OpenDir(cnbp->conn, fsp->fsp_name, True); + + if(dp == NULL) { + DEBUG(0,("create_directory_notify_hash: Unable to open directory = %s. \ +Error was %s\n", fsp->fsp_name, strerror(errno) )); + return False; + } + + change_data->num_entries = 0; + + pstrcpy(full_name, fsp->fsp_name); + pstrcat(full_name, "/"); + + fullname_len = strlen(full_name); + remaining_len = sizeof(full_name) - fullname_len - 1; + p = &full_name[fullname_len]; + + while ((fname = ReadDirName(dp))) { + if(strequal(fname, ".") || strequal(fname, "..")) + continue; + + change_data->num_entries++; + safe_strcpy( p, fname, remaining_len); + + memset(&st, '\0', sizeof(st)); + + /* + * Do the stat - but ignore errors. + */ + + if(dos_stat(full_name, &st) < 0) { + DEBUG(5,("create_directory_notify_hash: Unable to stat content file = %s. \ +Error was %s\n", fsp->fsp_name, strerror(errno) )); + } + change_data->total_time += (st.st_mtime + st.st_ctime); + } + + CloseDir(dp); + } + + return True; +} + +/**************************************************************************** Delete entries by fnum from the change notify pending queue. *****************************************************************************/ + void remove_pending_change_notify_requests_by_fid(files_struct *fsp) { change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue ); @@ -1274,7 +1519,34 @@ static void remove_pending_change_notify_requests_by_mid(int mid) while(cnbp != NULL) { if(SVAL(cnbp->request_buf,smb_mid) == mid) { - change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_CANCELLED); + change_notify_reply_packet(cnbp->request_buf,0,0xC0000000 |NT_STATUS_CANCELLED); + free((char *)ubi_slRemNext( &change_notify_queue, prev)); + cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue)); + continue; + } + + prev = cnbp; + cnbp = (change_notify_buf *)ubi_slNext(cnbp); + } +} + +/**************************************************************************** + Delete entries by filename and cnum from the change notify pending queue. + Always send reply. +*****************************************************************************/ + +void remove_pending_change_notify_requests_by_filename(files_struct *fsp) +{ + change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue ); + change_notify_buf *prev = NULL; + + while(cnbp != NULL) { + /* + * We know it refers to the same directory if the connection number and + * the filename are identical. + */ + if((cnbp->fsp->conn == fsp->conn) && strequal(cnbp->fsp->fsp_name,fsp->fsp_name)) { + change_notify_reply_packet(cnbp->request_buf,0,0xC0000000 |NT_STATUS_CANCELLED); free((char *)ubi_slRemNext( &change_notify_queue, prev)); cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue)); continue; @@ -1287,18 +1559,20 @@ static void remove_pending_change_notify_requests_by_mid(int mid) /**************************************************************************** Process the change notify queue. Note that this is only called as root. + Returns True if there are still outstanding change notify requests on the + queue. *****************************************************************************/ -void process_pending_change_notify_queue(time_t t) +BOOL process_pending_change_notify_queue(time_t t) { change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue ); change_notify_buf *prev = NULL; if(cnbp == NULL) - return; + return False; if(cnbp->next_check_time >= t) - return; + return True; /* * It's time to check. Go through the queue and see if @@ -1306,8 +1580,7 @@ void process_pending_change_notify_queue(time_t t) */ while((cnbp != NULL) && (cnbp->next_check_time <= t)) { - SMB_STRUCT_STAT st; - files_struct *fsp = cnbp->fsp; + change_hash_data change_data; connection_struct *conn = cnbp->conn; uint16 vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : SVAL(cnbp->request_buf,smb_uid); @@ -1343,9 +1616,9 @@ void process_pending_change_notify_queue(time_t t) continue; } - if(fsp->conn->vfs_ops.stat(dos_to_unix(fsp->fsp_name, False), &st) < 0) { - DEBUG(0,("process_pending_change_notify_queue: Unable to stat directory %s. \ -Error was %s.\n", fsp->fsp_name, strerror(errno) )); + if(!create_directory_notify_hash( cnbp, &change_data)) { + DEBUG(0,("process_pending_change_notify_queue: Unable to create change data for \ +directory %s\n", cnbp->fsp->fsp_name )); /* * Remove the entry and return an error to the client. */ @@ -1356,13 +1629,12 @@ Error was %s.\n", fsp->fsp_name, strerror(errno) )); continue; } - if(cnbp->modify_time != st.st_mtime || - cnbp->status_time != st.st_ctime) { + if(memcmp( (char *)&cnbp->change_data, (char *)&change_data, sizeof(change_data))) { /* * Remove the entry and return a change notify to the client. */ - DEBUG(5,("process_pending_change_notify_queue: directory name = %s changed\n", - fsp->fsp_name )); + DEBUG(5,("process_pending_change_notify_queue: directory name = %s changed.\n", + cnbp->fsp->fsp_name )); change_notify_reply_packet(cnbp->request_buf,0,NT_STATUS_NOTIFY_ENUM_DIR); free((char *)ubi_slRemNext( &change_notify_queue, prev)); cnbp = (change_notify_buf *)(prev ? ubi_slNext(prev) : ubi_slFirst(&change_notify_queue)); @@ -1378,22 +1650,34 @@ Error was %s.\n", fsp->fsp_name, strerror(errno) )); prev = cnbp; cnbp = (change_notify_buf *)ubi_slNext(cnbp); } + + return (cnbp != NULL); +} + +/**************************************************************************** + Return true if there are pending change notifies. +****************************************************************************/ + +BOOL change_notifies_pending(void) +{ + change_notify_buf *cnbp = (change_notify_buf *)ubi_slFirst( &change_notify_queue ); + return (cnbp != NULL); } /**************************************************************************** Reply to a notify change - queue the request and don't allow a directory to be opened. ****************************************************************************/ + static int call_nt_transact_notify_change(connection_struct *conn, - char *inbuf, char *outbuf, int length, + char *inbuf, char *outbuf, int length, int bufsize, char **ppsetup, - char **ppparams, char **ppdata) + char **ppparams, char **ppdata) { char *setup = *ppsetup; files_struct *fsp; change_notify_buf *cnbp; - SMB_STRUCT_STAT st; fsp = file_fsp(setup,4); @@ -1414,28 +1698,22 @@ static int call_nt_transact_notify_change(connection_struct *conn, */ if((cnbp = (change_notify_buf *)malloc(sizeof(change_notify_buf))) == NULL) { - DEBUG(0,("call_nt_transact_notify_change: Malloc fail (2) !\n" )); + DEBUG(0,("call_nt_transact_notify_change: malloc fail !\n" )); return -1; } - /* - * Store the current timestamp on the directory we are monitoring. - */ + memset((char *)cnbp, '\0', sizeof(change_notify_buf)); - if(fsp->conn->vfs_ops.stat(dos_to_unix(fsp->fsp_name, False), &st) < 0) { - DEBUG(0,("call_nt_transact_notify_change: Unable to stat name = %s. \ -Error was %s\n", fsp->fsp_name, strerror(errno) )); - free((char *)cnbp); - return(UNIXERROR(ERRDOS,ERRbadfid)); - } - memcpy(cnbp->request_buf, inbuf, smb_size); cnbp->fsp = fsp; cnbp->conn = conn; - cnbp->modify_time = st.st_mtime; - cnbp->status_time = st.st_ctime; - cnbp->next_check_time = time(NULL) + lp_change_notify_timeout(); + cnbp->flags = IVAL(setup, 0); + + if(!create_directory_notify_hash( cnbp, &cnbp->change_data )) { + free((char *)cnbp); + return(UNIXERROR(ERRDOS,ERRbadfid)); + } /* * Adding to the tail enables us to check only @@ -1452,43 +1730,661 @@ name = %s\n", fsp->fsp_name )); } /**************************************************************************** + Map unix perms to NT. +****************************************************************************/ + +static SEC_ACCESS map_unix_perms( int *pacl_type, mode_t perm, int r_mask, int w_mask, int x_mask, BOOL is_directory) +{ + SEC_ACCESS sa; + uint32 nt_mask = 0; + + *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED; + + if((perm & (r_mask|w_mask|x_mask)) == (r_mask|w_mask|x_mask)) { + nt_mask = UNIX_ACCESS_RWX; + } else if((perm & (r_mask|w_mask|x_mask)) == 0) { + nt_mask = UNIX_ACCESS_NONE; + } else { + nt_mask |= (perm & r_mask) ? UNIX_ACCESS_R : 0; + if(is_directory) + nt_mask |= (perm & w_mask) ? UNIX_ACCESS_W : 0; + else + nt_mask |= (perm & w_mask) ? UNIX_ACCESS_W : 0; + nt_mask |= (perm & x_mask) ? UNIX_ACCESS_X : 0; + } + init_sec_access(&sa,nt_mask); + return sa; +} + +/**************************************************************************** + Reply to query a security descriptor from an fsp. If it succeeds it allocates + the space for the return elements and returns True. +****************************************************************************/ + +static size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc) +{ + extern DOM_SID global_sam_sid; + extern DOM_SID global_sid_World; + SMB_STRUCT_STAT sbuf; + SEC_ACE ace_list[6]; + DOM_SID owner_sid; + DOM_SID group_sid; + size_t sec_desc_size; + SEC_ACL *psa = NULL; + SEC_ACCESS owner_access; + int owner_acl_type; + SEC_ACCESS group_access; + int grp_acl_type; + SEC_ACCESS other_access; + int other_acl_type; + int num_acls = 0; + + *ppdesc = NULL; + + if(!lp_nt_acl_support()) { + sid_copy( &owner_sid, &global_sid_World); + sid_copy( &group_sid, &global_sid_World); + } else { + + if(fsp->is_directory || fsp->fd_ptr == NULL) { + if(dos_stat(fsp->fsp_name, &sbuf) != 0) { + return 0; + } + } else { + if(sys_fstat(fsp->fd_ptr->fd,&sbuf) != 0) { + return 0; + } + } + + /* + * Get the owner, group and world SIDs. + */ + + sid_copy(&owner_sid, &global_sam_sid); + sid_copy(&group_sid, &global_sam_sid); + sid_append_rid(&owner_sid, pdb_uid_to_user_rid(sbuf.st_uid)); + sid_append_rid(&group_sid, pdb_gid_to_group_rid(sbuf.st_gid)); + + /* + * Create the generic 3 element UNIX acl. + */ + + owner_access = map_unix_perms(&owner_acl_type, sbuf.st_mode, + S_IRUSR, S_IWUSR, S_IXUSR, fsp->is_directory); + group_access = map_unix_perms(&grp_acl_type, sbuf.st_mode, + S_IRGRP, S_IWGRP, S_IXGRP, fsp->is_directory); + other_access = map_unix_perms(&other_acl_type, sbuf.st_mode, + S_IROTH, S_IWOTH, S_IXOTH, fsp->is_directory); + + if(owner_access.mask) + init_sec_ace(&ace_list[num_acls++], &owner_sid, owner_acl_type, + owner_access, 0); + + if(group_access.mask) + init_sec_ace(&ace_list[num_acls++], &group_sid, grp_acl_type, + group_access, 0); + + if(other_access.mask) + init_sec_ace(&ace_list[num_acls++], &global_sid_World, other_acl_type, + other_access, 0); + + if(fsp->is_directory) { + /* + * For directory ACLs we also add in the inherited permissions + * ACE entries. These are the permissions a file would get when + * being created in the directory. + */ + mode_t mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE); + + owner_access = map_unix_perms(&owner_acl_type, mode, + S_IRUSR, S_IWUSR, S_IXUSR, fsp->is_directory); + group_access = map_unix_perms(&grp_acl_type, mode, + S_IRGRP, S_IWGRP, S_IXGRP, fsp->is_directory); + other_access = map_unix_perms(&other_acl_type, mode, + S_IROTH, S_IWOTH, S_IXOTH, fsp->is_directory); + + if(owner_access.mask) + init_sec_ace(&ace_list[num_acls++], &owner_sid, owner_acl_type, + owner_access, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY); + + if(group_access.mask) + init_sec_ace(&ace_list[num_acls++], &group_sid, grp_acl_type, + group_access, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY); + + if(other_access.mask) + init_sec_ace(&ace_list[num_acls++], &global_sid_World, other_acl_type, + other_access, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY); + } + + if(num_acls) + if((psa = make_sec_acl( 3, num_acls, ace_list)) == NULL) { + DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n")); + return 0; + } + } + + *ppdesc = make_standard_sec_desc( &owner_sid, &group_sid, psa, &sec_desc_size); + + if(!*ppdesc) { + DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n")); + sec_desc_size = 0; + } + + free_sec_acl(&psa); + + return sec_desc_size; +} + +/**************************************************************************** Reply to query a security descriptor - currently this is not implemented (it - is planned to be though). + is planned to be though). Right now it just returns the same thing NT would + when queried on a FAT filesystem. JRA. ****************************************************************************/ + static int call_nt_transact_query_security_desc(connection_struct *conn, - char *inbuf, char *outbuf, - int length, - int bufsize, + char *inbuf, char *outbuf, + int length, int bufsize, char **ppsetup, char **ppparams, char **ppdata) { - static BOOL logged_message = False; + uint32 max_data_count = IVAL(inbuf,smb_nt_MaxDataCount); + char *params = *ppparams; + char *data = *ppdata; + prs_struct pd; + SEC_DESC *psd; + size_t sec_desc_size; - if(!logged_message) { - DEBUG(0,("call_nt_transact_query_security_desc: Currently not implemented.\n")); - logged_message = True; /* Only print this once... */ + files_struct *fsp = file_fsp(params,0); + + if(!fsp) + return(ERROR(ERRDOS,ERRbadfid)); + + DEBUG(3,("call_nt_transact_query_security_desc: file = %s\n", fsp->fsp_name )); + + params = *ppparams = Realloc(*ppparams, 4); + if(params == NULL) + return(ERROR(ERRDOS,ERRnomem)); + + /* + * Get the permissions to return. + */ + + if((sec_desc_size = get_nt_acl(fsp, &psd)) == 0) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + DEBUG(3,("call_nt_transact_query_security_desc: sec_desc_size = %d.\n",(int)sec_desc_size)); + + SIVAL(params,0,(uint32)sec_desc_size); + + if(max_data_count < sec_desc_size) { + + free_sec_desc(&psd); + + send_nt_replies(inbuf, outbuf, bufsize, 0xC0000000|NT_STATUS_BUFFER_TOO_SMALL, + params, 4, *ppdata, 0); + return -1; } - return(ERROR(ERRSRV,ERRnosupport)); + /* + * Allocate the data we will point this at. + */ + + data = *ppdata = Realloc(*ppdata, sec_desc_size); + if(data == NULL) { + free_sec_desc(&psd); + return(ERROR(ERRDOS,ERRnomem)); + } + + memset(data, '\0', sec_desc_size); + + /* + * Init the parse struct we will marshall into. + */ + + prs_init(&pd, 0, 4, MARSHALL); + + /* + * Setup the prs_struct to point at the memory we just + * allocated. + */ + + prs_give_memory( &pd, data, (uint32)sec_desc_size, False); + + /* + * Finally, linearize into the outgoing buffer. + */ + + if(!sec_io_desc( "sd data", &psd, &pd, 1)) { + free_sec_desc(&psd); + DEBUG(0,("call_nt_transact_query_security_desc: Error in marshalling \ +security descriptor.\n")); + /* + * Return access denied for want of a better error message.. + */ + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + /* + * Now we can delete the security descriptor. + */ + + free_sec_desc(&psd); + + send_nt_replies(inbuf, outbuf, bufsize, 0, params, 4, data, (int)sec_desc_size); + return -1; } - + +/**************************************************************************** + Validate a SID. +****************************************************************************/ + +static BOOL validate_unix_sid( DOM_SID *psid, uint32 *prid, DOM_SID *sd_sid) +{ + extern DOM_SID global_sam_sid; + DOM_SID sid; + + if(!sd_sid) { + DEBUG(5,("validate_unix_sid: sid missing.\n")); + return False; + } + + sid_copy(psid, sd_sid); + sid_copy(&sid, sd_sid); + + if(!sid_split_rid(&sid, prid)) { + DEBUG(5,("validate_unix_sid: cannot get RID from sid.\n")); + return False; + } + + if(!sid_equal( &sid, &global_sam_sid)) { + DEBUG(5,("validate_unix_sid: sid is not ours.\n")); + return False; + } + + return True; +} + /**************************************************************************** - Reply to set a security descriptor - currently this is not implemented (it - is planned to be though). + Map NT perms to UNIX. ****************************************************************************/ + +#define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES) +#define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES) +#define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE) + +static mode_t map_nt_perms( SEC_ACCESS sec_access, int type) +{ + mode_t mode = 0; + + switch(type) { + case S_IRUSR: + if(sec_access.mask & GENERIC_ALL_ACCESS) + mode = S_IRUSR|S_IWUSR|S_IXUSR; + else { + mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0; + mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0; + mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0; + } + break; + case S_IRGRP: + if(sec_access.mask & GENERIC_ALL_ACCESS) + mode = S_IRGRP|S_IWGRP|S_IXGRP; + else { + mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0; + mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0; + mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0; + } + break; + case S_IROTH: + if(sec_access.mask & GENERIC_ALL_ACCESS) + mode = S_IROTH|S_IWOTH|S_IXOTH; + else { + mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0; + mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0; + mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0; + } + break; + } + + return mode; +} + +/**************************************************************************** + Unpack a SEC_DESC into a owner, group and set of UNIX permissions. +****************************************************************************/ + +static BOOL unpack_nt_permissions(uid_t *puser, gid_t *pgrp, mode_t *pmode, uint32 security_info_sent, + SEC_DESC *psd, BOOL is_directory) +{ + extern DOM_SID global_sid_World; + DOM_SID owner_sid; + DOM_SID grp_sid; + uint32 owner_rid; + uint32 grp_rid; + SEC_ACL *dacl = psd->dacl; + int i; + + *pmode = 0; + *puser = (uid_t)-1; + *pgrp = (gid_t)-1; + + if(security_info_sent == 0) { + DEBUG(0,("unpack_unix_permissions: no security info sent !\n")); + return False; + } + + /* + * Validate the owner and group SID's. + */ + + memset(&owner_sid, '\0', sizeof(owner_sid)); + memset(&grp_sid, '\0', sizeof(grp_sid)); + + DEBUG(5,("unpack_unix_permissions: validating owner_sid.\n")); + + /* + * Don't immediately fail if the owner sid cannot be validated. + * This may be a group chown only set. + */ + + if(!validate_unix_sid( &owner_sid, &owner_rid, psd->owner_sid)) + DEBUG(3,("unpack_unix_permissions: unable to validate owner sid.\n")); + else if(security_info_sent & OWNER_SECURITY_INFORMATION) + *puser = pdb_user_rid_to_uid(owner_rid); + + /* + * Don't immediately fail if the group sid cannot be validated. + * This may be an owner chown only set. + */ + + if(!validate_unix_sid( &grp_sid, &grp_rid, psd->grp_sid)) + DEBUG(3,("unpack_unix_permissions: unable to validate group sid.\n")); + else if(security_info_sent & GROUP_SECURITY_INFORMATION) + *pgrp = pdb_user_rid_to_gid(grp_rid); + + /* + * If no DACL then this is a chown only security descriptor. + */ + + if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !dacl) { + *pmode = 0; + return True; + } + + /* + * Now go through the DACL and ensure that + * any owner/group sids match. + */ + + for(i = 0; i < dacl->num_aces; i++) { + DOM_SID ace_sid; + SEC_ACE *psa = &dacl->ace_list[i]; + + if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && + (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) { + DEBUG(3,("unpack_unix_permissions: unable to set anything but an ALLOW or DENY ACE.\n")); + return False; + } + + /* + * Ignore or remove bits we don't care about on a directory ACE. + */ + + if(is_directory) { + if(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY) { + DEBUG(3,("unpack_unix_permissions: ignoring inherit only ACE.\n")); + continue; + } + + psa->flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT); + } + + if(psa->flags != 0) { + DEBUG(1,("unpack_unix_permissions: unable to set ACE flags (%x).\n", + (unsigned int)psa->flags)); + return False; + } + + /* + * The security mask may be UNIX_ACCESS_NONE which should map into + * no permissions (we overload the WRITE_OWNER bit for this) or it + * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this + * to be so. Any other bits override the UNIX_ACCESS_NONE bit. + */ + + psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS| + GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES); + + if(psa->info.mask != UNIX_ACCESS_NONE) + psa->info.mask &= ~UNIX_ACCESS_NONE; + + sid_copy(&ace_sid, &psa->sid); + + if(sid_equal(&ace_sid, &owner_sid)) { + /* + * Map the desired permissions into owner perms. + */ + + if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) + *pmode |= map_nt_perms( psa->info, S_IRUSR); + else + *pmode &= ~(map_nt_perms( psa->info, S_IRUSR)); + + } else if( sid_equal(&ace_sid, &grp_sid)) { + /* + * Map the desired permissions into group perms. + */ + + if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) + *pmode |= map_nt_perms( psa->info, S_IRGRP); + else + *pmode &= ~(map_nt_perms( psa->info, S_IRGRP)); + + } else if( sid_equal(&ace_sid, &global_sid_World)) { + /* + * Map the desired permissions into other perms. + */ + + if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) + *pmode |= map_nt_perms( psa->info, S_IROTH); + else + *pmode &= ~(map_nt_perms( psa->info, S_IROTH)); + + } else { + DEBUG(0,("unpack_unix_permissions: unknown SID used in ACL.\n")); + return False; + } + } + + return True; +} + +/**************************************************************************** + Reply to set a security descriptor. Map to UNIX perms. +****************************************************************************/ + static int call_nt_transact_set_security_desc(connection_struct *conn, - char *inbuf, char *outbuf, - int length, - int bufsize, - char **ppsetup, - char **ppparams, char **ppdata) + char *inbuf, char *outbuf, int length, + int bufsize, char **ppsetup, + char **ppparams, char **ppdata) { - static BOOL logged_message = False; + uint32 total_parameter_count = IVAL(inbuf, smb_nts_TotalParameterCount); + char *params= *ppparams; + char *data = *ppdata; + prs_struct pd; + SEC_DESC *psd = NULL; + uint32 total_data_count = (uint32)IVAL(inbuf, smb_nts_TotalDataCount); + uid_t user = (uid_t)-1; + gid_t grp = (gid_t)-1; + mode_t perms = 0; + SMB_STRUCT_STAT sbuf; + files_struct *fsp = NULL; + uint32 security_info_sent = 0; + BOOL got_dacl = False; - if(!logged_message) { - DEBUG(0,("call_nt_transact_set_security_desc: Currently not implemented.\n")); - logged_message = True; /* Only print this once... */ + if(!lp_nt_acl_support()) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + if(total_parameter_count < 8) + return(ERROR(ERRDOS,ERRbadfunc)); + + if((fsp = file_fsp(params,0)) == NULL) + return(ERROR(ERRDOS,ERRbadfid)); + + security_info_sent = IVAL(params,4); + + DEBUG(3,("call_nt_transact_set_security_desc: file = %s, sent 0x%x\n", fsp->fsp_name, + (unsigned int)security_info_sent )); + + /* + * Init the parse struct we will unmarshall from. + */ + + prs_init(&pd, 0, 4, UNMARSHALL); + + /* + * Setup the prs_struct to point at the memory we just + * allocated. + */ + + prs_give_memory( &pd, data, total_data_count, False); + + /* + * Finally, unmarshall from the data buffer. + */ + + if(!sec_io_desc( "sd data", &psd, &pd, 1)) { + free_sec_desc(&psd); + DEBUG(0,("call_nt_transact_set_security_desc: Error in unmarshalling \ +security descriptor.\n")); + /* + * Return access denied for want of a better error message.. + */ + return(UNIXERROR(ERRDOS,ERRnoaccess)); } - return(ERROR(ERRSRV,ERRnosupport)); + + /* + * Unpack the user/group/world id's and permissions. + */ + + if(!unpack_nt_permissions( &user, &grp, &perms, security_info_sent, psd, fsp->is_directory)) { + free_sec_desc(&psd); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + if (psd->dacl != NULL) + got_dacl = True; + + free_sec_desc(&psd); + + /* + * Get the current state of the file. + */ + + if(fsp->is_directory) { + if(dos_stat(fsp->fsp_name, &sbuf) != 0) { + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + } else { + + int ret; + + if(fsp->fd_ptr == NULL) + ret = dos_stat(fsp->fsp_name, &sbuf); + else + ret = sys_fstat(fsp->fd_ptr->fd,&sbuf); + + if(ret != 0) { + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + } + + /* + * Do we need to chown ? + */ + + if((user != (uid_t)-1 || grp != (uid_t)-1) && (sbuf.st_uid != user || sbuf.st_gid != grp)) { + + DEBUG(3,("call_nt_transact_set_security_desc: chown %s. uid = %u, gid = %u.\n", + fsp->fsp_name, (unsigned int)user, (unsigned int)grp )); + + if(dos_chown( fsp->fsp_name, user, grp) == -1) { + DEBUG(3,("call_nt_transact_set_security_desc: chown %s, %u, %u failed. Error = %s.\n", + fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) )); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + /* + * Recheck the current state of the file, which may have changed. + * (suid/sgid bits, for instance) + */ + + if(fsp->is_directory) { + if(dos_stat(fsp->fsp_name, &sbuf) != 0) { + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + } else { + + int ret; + + if(fsp->fd_ptr == NULL) + ret = dos_stat(fsp->fsp_name, &sbuf); + else + ret = sys_fstat(fsp->fd_ptr->fd,&sbuf); + + if(ret != 0) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + } + + /* + * Only change security if we got a DACL. + */ + + if((security_info_sent & DACL_SECURITY_INFORMATION) && got_dacl) { + + /* + * Check to see if we need to change anything. + * Enforce limits on modified bits *only*. Don't enforce masks + * on bits not changed by the user. + */ + + if(fsp->is_directory) { + + perms &= (lp_dir_security_mask(SNUM(conn)) | sbuf.st_mode); + perms |= (lp_force_dir_security_mode(SNUM(conn)) & ( perms ^ sbuf.st_mode )); + + } else { + + perms &= (lp_security_mask(SNUM(conn)) | sbuf.st_mode); + perms |= (lp_force_security_mode(SNUM(conn)) & ( perms ^ sbuf.st_mode )); + + } + + /* + * Preserve special bits. + */ + + perms |= (sbuf.st_mode & ~0777); + + /* + * Do we need to chmod ? + */ + + if(sbuf.st_mode != perms) { + + DEBUG(3,("call_nt_transact_set_security_desc: chmod %s. perms = 0%o.\n", + fsp->fsp_name, (unsigned int)perms )); + + if(dos_chmod( fsp->fsp_name, perms) == -1) { + DEBUG(3,("call_nt_transact_set_security_desc: chmod %s, 0%o failed. Error = %s.\n", + fsp->fsp_name, (unsigned int)perms, strerror(errno) )); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + } + } + + send_nt_replies(inbuf, outbuf, bufsize, 0, NULL, 0, NULL, 0); + return -1; } /**************************************************************************** @@ -1675,7 +2571,6 @@ due to being in oplock break state.\n" )); length, bufsize, &setup, ¶ms, &data); break; - default: /* Error in request */ DEBUG(0,("reply_nttrans: Unknown request %d in nttrans call\n", function_code)); diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 2c12d425b9..4491082b2c 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -25,22 +25,22 @@ extern int DEBUGLEVEL; extern pstring sesssetup_user; extern uint16 global_oplock_port; - +extern BOOL global_client_failed_oplock_break; /**************************************************************************** fd support routines - attempt to do a dos_open ****************************************************************************/ -static int fd_attempt_open(struct connection_struct *conn, char *fname, - int flags, mode_t mode) + +static int fd_attempt_open(char *fname, int flags, mode_t mode) { - int fd = conn->vfs_ops.open(fname,flags,mode); + int fd = dos_open(fname,flags,mode); /* Fix for files ending in '.' */ if((fd == -1) && (errno == ENOENT) && (strchr(fname,'.')==NULL)) { pstrcat(fname,"."); - fd = conn->vfs_ops.open(fname,flags,mode); + fd = dos_open(fname,flags,mode); } #if (defined(ENAMETOOLONG) && defined(HAVE_PATHCONF)) @@ -71,7 +71,7 @@ static int fd_attempt_open(struct connection_struct *conn, char *fname, char tmp = p[max_len]; p[max_len] = '\0'; - if ((fd = conn->vfs_ops.open(fname,flags,mode)) == -1) + if ((fd = dos_open(fname,flags,mode)) == -1) p[max_len] = tmp; } } @@ -83,6 +83,7 @@ static int fd_attempt_open(struct connection_struct *conn, char *fname, Cache a uid_t currently with this file open. This is an optimization only used when multiple sessionsetup's have been done to one smbd. ****************************************************************************/ + void fd_add_to_uid_cache(file_fd_struct *fd_ptr, uid_t u) { if(fd_ptr->uid_cache_count >= sizeof(fd_ptr->uid_users_cache)/sizeof(uid_t)) @@ -94,6 +95,7 @@ void fd_add_to_uid_cache(file_fd_struct *fd_ptr, uid_t u) Remove a uid_t that currently has this file open. This is an optimization only used when multiple sessionsetup's have been done to one smbd. ****************************************************************************/ + static void fd_remove_from_uid_cache(file_fd_struct *fd_ptr, uid_t u) { int i; @@ -111,6 +113,7 @@ static void fd_remove_from_uid_cache(file_fd_struct *fd_ptr, uid_t u) Check if a uid_t that currently has this file open is present. This is an optimization only used when multiple sessionsetup's have been done to one smbd. ****************************************************************************/ + static BOOL fd_is_in_uid_cache(file_fd_struct *fd_ptr, uid_t u) { int i; @@ -120,11 +123,11 @@ static BOOL fd_is_in_uid_cache(file_fd_struct *fd_ptr, uid_t u) return False; } - /**************************************************************************** fd support routines - attempt to re-open an already open fd as O_RDWR. Save the already open fd (we cannot close due to POSIX file locking braindamage. ****************************************************************************/ + static void fd_attempt_reopen(char *fname, mode_t mode, file_fd_struct *fd_ptr) { int fd = dos_open( fname, O_RDWR, mode); @@ -145,17 +148,13 @@ static void fd_attempt_reopen(char *fname, mode_t mode, file_fd_struct *fd_ptr) fd support routines - attempt to close the file referenced by this fd. Decrements the ref_count and returns it. ****************************************************************************/ -uint16 fd_attempt_close(files_struct *fsp) + +uint16 fd_attempt_close(file_fd_struct *fd_ptr, int *err_ret) { extern struct current_user current_user; - file_fd_struct *fd_ptr = fsp->fd_ptr; - uint16 ret_ref; + uint16 ret_ref = fd_ptr->ref_count; - if (fd_ptr != NULL) { - ret_ref = fd_ptr->ref_count; - } else { - return 0; - } + *err_ret = 0; DEBUG(3,("fd_attempt_close fd = %d, dev = %x, inode = %.0f, open_flags = %d, ref_count = %d.\n", fd_ptr->fd, (unsigned int)fd_ptr->dev, (double)fd_ptr->inode, @@ -168,12 +167,26 @@ uint16 fd_attempt_close(files_struct *fsp) ret_ref = fd_ptr->ref_count; if(fd_ptr->ref_count == 0) { - if(fd_ptr->fd != -1) - fsp->conn->vfs_ops.close(fd_ptr->fd); - if(fd_ptr->fd_readonly != -1) - fsp->conn->vfs_ops.close(fd_ptr->fd_readonly); - if(fd_ptr->fd_writeonly != -1) - fsp->conn->vfs_ops.close(fd_ptr->fd_writeonly); + + if(fd_ptr->fd != -1) { + if(close(fd_ptr->fd) < 0) + *err_ret = errno; + } + + if(fd_ptr->fd_readonly != -1) { + if(close(fd_ptr->fd_readonly) < 0) { + if(*err_ret == 0) + *err_ret = errno; + } + } + + if(fd_ptr->fd_writeonly != -1) { + if( close(fd_ptr->fd_writeonly) < 0) { + if(*err_ret == 0) + *err_ret = errno; + } + } + /* * Delete this fd_ptr. */ @@ -182,7 +195,7 @@ uint16 fd_attempt_close(files_struct *fsp) fd_remove_from_uid_cache(fd_ptr, (uid_t)current_user.uid); } - return ret_ref; + return ret_ref; } /**************************************************************************** @@ -192,14 +205,21 @@ This is really ugly code, as due to POSIX locking braindamage we must fork and then attempt to open the file, and return success or failure via an exit code. ****************************************************************************/ -static BOOL check_access_allowed_for_current_user(struct connection_struct - *conn, char *fname, - int accmode ) + +static BOOL check_access_allowed_for_current_user( char *fname, int accmode ) { pid_t child_pid; + /* + * We need to temporarily stop CatchChild from eating + * SIGCLD signals as it also eats the exit status code. JRA. + */ + + CatchChildLeaveStatus(); + if((child_pid = fork()) < 0) { DEBUG(0,("check_access_allowed_for_current_user: fork failed.\n")); + CatchChild(); return False; } @@ -209,11 +229,23 @@ static BOOL check_access_allowed_for_current_user(struct connection_struct */ pid_t wpid; int status_code; - if ((wpid = sys_waitpid(child_pid, &status_code, 0)) < 0) { - DEBUG(0,("check_access_allowed_for_current_user: The process is no longer waiting!\n")); + + while ((wpid = sys_waitpid(child_pid, &status_code, 0)) < 0) { + if(errno == EINTR) { + errno = 0; + continue; + } + DEBUG(0,("check_access_allowed_for_current_user: The process \ +is no longer waiting ! Error = %s\n", strerror(errno) )); + CatchChild(); return(False); } + /* + * Go back to ignoring children. + */ + CatchChild(); + if (child_pid != wpid) { DEBUG(0,("check_access_allowed_for_current_user: We were waiting for the wrong process ID\n")); return(False); @@ -245,7 +277,7 @@ static BOOL check_access_allowed_for_current_user(struct connection_struct */ int fd; DEBUG(9,("check_access_allowed_for_current_user: Child - attempting to open %s with mode %d.\n", fname, accmode )); - if((fd = fd_attempt_open(conn, fname, accmode, 0)) < 0) { + if((fd = fd_attempt_open( fname, accmode, 0)) < 0) { /* Access denied. */ _exit(EACCES); } @@ -260,11 +292,12 @@ static BOOL check_access_allowed_for_current_user(struct connection_struct /**************************************************************************** check a filename for the pipe string ****************************************************************************/ + static void check_for_pipe(char *fname) { /* special case of pipe opens */ char s[10]; - StrnCpy(s,fname,9); + StrnCpy(s,fname,sizeof(s)-1); strlower(s); if (strstr(s,"pipe/")) { DEBUG(3,("Rejecting named pipe open for %s\n",fname)); @@ -276,6 +309,7 @@ static void check_for_pipe(char *fname) /**************************************************************************** open a file ****************************************************************************/ + static void open_file(files_struct *fsp,connection_struct *conn, char *fname1,int flags,mode_t mode, SMB_STRUCT_STAT *sbuf) { @@ -287,7 +321,7 @@ static void open_file(files_struct *fsp,connection_struct *conn, fsp->open = False; fsp->fd_ptr = 0; - fsp->granted_oplock = False; + fsp->oplock_type = NO_OPLOCK; errno = EPERM; pstrcpy(fname,fname1); @@ -304,7 +338,7 @@ static void open_file(files_struct *fsp,connection_struct *conn, * JRA. */ - if (conn->read_only && !conn->printer) { + if (!CAN_WRITE(conn) && !conn->printer) { /* It's a read-only share - fail if we wanted to write. */ if(accmode != O_RDONLY) { DEBUG(3,("Permission denied opening %s\n",fname)); @@ -335,7 +369,7 @@ static void open_file(files_struct *fsp,connection_struct *conn, * open fd table. */ if(sbuf == 0) { - if(conn->vfs_ops.stat(dos_to_unix(fname,False), &statbuf) < 0) { + if(dos_stat(fname, &statbuf) < 0) { if(errno != ENOENT) { DEBUG(3,("Error doing stat on file %s (%s)\n", fname,strerror(errno))); @@ -376,7 +410,7 @@ static void open_file(files_struct *fsp,connection_struct *conn, */ if(!fd_is_in_uid_cache(fd_ptr, (uid_t)current_user.uid)) { - if(!check_access_allowed_for_current_user(conn, fname, accmode )) { + if(!check_access_allowed_for_current_user( fname, accmode )) { /* Error - permission denied. */ DEBUG(3,("Permission denied opening file %s (flags=%d, accmode = %d)\n", fname, flags, accmode)); @@ -433,7 +467,7 @@ static void open_file(files_struct *fsp,connection_struct *conn, fd_ptr->real_open_flags = O_RDWR; /* Set the flags as needed without the read/write modes. */ open_flags = flags & ~(O_RDWR|O_WRONLY|O_RDONLY); - fd_ptr->fd = fd_attempt_open(conn, fname, open_flags|O_RDWR, mode); + fd_ptr->fd = fd_attempt_open(fname, open_flags|O_RDWR, mode); /* * On some systems opening a file for R/W access on a read only * filesystems sets errno to EROFS. @@ -444,7 +478,7 @@ static void open_file(files_struct *fsp,connection_struct *conn, if((fd_ptr->fd == -1) && (errno == EACCES)) { #endif /* EROFS */ if(accmode != O_RDWR) { - fd_ptr->fd = fd_attempt_open(conn, fname, open_flags|accmode, mode); + fd_ptr->fd = fd_attempt_open(fname, open_flags|accmode, mode); fd_ptr->real_open_flags = accmode; } } @@ -458,10 +492,10 @@ static void open_file(files_struct *fsp,connection_struct *conn, pstrcpy(dname,fname); p = strrchr(dname,'/'); if (p) *p = 0; - if (conn->vfs_ops.disk_free(dname,&dum1,&dum2,&dum3) < - (SMB_BIG_UINT)lp_minprintspace(SNUM(conn))) { - if(fd_attempt_close(fsp) == 0) - conn->vfs_ops.unlink(fname); + if (sys_disk_free(dname,False,&dum1,&dum2,&dum3) < (SMB_BIG_UINT)lp_minprintspace(SNUM(conn))) { + int err; + if(fd_attempt_close(fd_ptr, &err) == 0) + dos_unlink(fname); fsp->fd_ptr = 0; errno = ENOSPC; return; @@ -470,10 +504,11 @@ static void open_file(files_struct *fsp,connection_struct *conn, if (fd_ptr->fd < 0) { + int err; DEBUG(3,("Error opening file %s (%s) (flags=%d)\n", fname,strerror(errno),flags)); /* Ensure the ref_count is decremented. */ - fd_attempt_close(fsp); + fd_attempt_close(fd_ptr,&err); check_for_pipe(fname); return; } @@ -482,12 +517,13 @@ static void open_file(files_struct *fsp,connection_struct *conn, { if(sbuf == 0) { /* Do the fstat */ - if(conn->vfs_ops.fstat(fd_ptr->fd, &statbuf) == -1) { + if(sys_fstat(fd_ptr->fd, &statbuf) == -1) { + int err; /* Error - backout !! */ DEBUG(3,("Error doing fstat on fd %d, file %s (%s)\n", fd_ptr->fd, fname,strerror(errno))); /* Ensure the ref_count is decremented. */ - fd_attempt_close(fsp); + fd_attempt_close(fd_ptr,&err); return; } sbuf = &statbuf; @@ -505,17 +541,17 @@ static void open_file(files_struct *fsp,connection_struct *conn, fsp->size = 0; fsp->pos = -1; fsp->open = True; - fsp->mmap_ptr = NULL; - fsp->mmap_size = 0; fsp->can_lock = True; fsp->can_read = ((flags & O_WRONLY)==0); fsp->can_write = ((flags & (O_WRONLY|O_RDWR))!=0); fsp->share_mode = 0; fsp->print_file = conn->printer; fsp->modified = False; - fsp->granted_oplock = False; - fsp->sent_oplock_break = False; + fsp->oplock_type = NO_OPLOCK; + fsp->sent_oplock_break = NO_BREAK_SENT; fsp->is_directory = False; + fsp->stat_open = False; + fsp->directory_delete_on_close = False; fsp->conn = conn; /* * Note that the file name here is the *untranslated* name @@ -526,6 +562,7 @@ static void open_file(files_struct *fsp,connection_struct *conn, */ string_set(&fsp->fsp_name,fname); fsp->wbmpx_ptr = NULL; + fsp->wcp = NULL; /* Write cache pointer. */ /* * If the printer is marked as postscript output a leading @@ -536,7 +573,7 @@ static void open_file(files_struct *fsp,connection_struct *conn, */ if (fsp->print_file && lp_postscript(SNUM(conn)) && fsp->can_write) { DEBUG(3,("Writing postscript line\n")); - conn->vfs_ops.write(fsp->fd_ptr->fd,"%!\n",3); + write_file(fsp,"%!\n",-1,3); } DEBUG(2,("%s opened file %s read=%s write=%s (numopen=%d)\n", @@ -548,39 +585,11 @@ static void open_file(files_struct *fsp,connection_struct *conn, } /**************************************************************************** - If it's a read-only file, and we were compiled with mmap enabled, - try and mmap the file. This is split out from open_file() above - as mmap'ing the file can cause the kernel reference count to - be incremented, which can cause kernel oplocks to be refused. - Splitting this call off allows the kernel oplock to be granted, then - the file mmap'ed. -****************************************************************************/ - -static void mmap_open_file(files_struct *fsp) -{ -#if WITH_MMAP - /* mmap it if read-only */ - if (!fsp->can_write) { - fsp->mmap_size = dos_file_size(fsp->fsp_name); - if (fsp->mmap_size < MAX_MMAP_SIZE) { - fsp->mmap_ptr = (char *)sys_mmap(NULL,fsp->mmap_size, - PROT_READ,MAP_SHARED,fsp->fd_ptr->fd,(SMB_OFF_T)0); - - if (fsp->mmap_ptr == (char *)-1 || !fsp->mmap_ptr) { - DEBUG(3,("Failed to mmap() %s - %s\n", - fsp->fsp_name,strerror(errno))); - fsp->mmap_ptr = NULL; - } - } - } -#endif -} - -/**************************************************************************** C. Hoch 11/22/95 Helper for open_file_shared. Truncate a file after checking locking; close file if locked. **************************************************************************/ + static void truncate_unless_locked(files_struct *fsp, connection_struct *conn, int token, BOOL *share_locked) { @@ -606,19 +615,19 @@ static void truncate_unless_locked(files_struct *fsp, connection_struct *conn, i } } - enum {AFAIL,AREAD,AWRITE,AALL}; /******************************************************************* reproduce the share mode access table ********************************************************************/ + static int access_table(int new_deny,int old_deny,int old_mode, - int share_pid,char *fname) + pid_t share_pid,char *fname) { if (new_deny == DENY_ALL || old_deny == DENY_ALL) return(AFAIL); if (new_deny == DENY_DOS || old_deny == DENY_DOS) { - int pid = getpid(); + pid_t pid = getpid(); if (old_deny == new_deny && share_pid == pid) return(AALL); @@ -660,10 +669,10 @@ static int access_table(int new_deny,int old_deny,int old_mode, return(AFAIL); } - /**************************************************************************** check if we can open a file with a share mode ****************************************************************************/ + static int check_share_mode( share_mode_entry *share, int deny_mode, char *fname, BOOL fcbopen, int *flags) @@ -707,7 +716,7 @@ static int check_share_mode( share_mode_entry *share, int deny_mode, { DEBUG(2,("Share violation on file (%d,%d,%d,%d,%s,fcbopen = %d, flags = %d) = %d\n", deny_mode,old_deny_mode,old_open_mode, - share->pid,fname, fcbopen, *flags, access_allowed)); + (int)share->pid,fname, fcbopen, *flags, access_allowed)); unix_ERR_class = ERRDOS; unix_ERR_code = ERRbadshare; @@ -726,34 +735,39 @@ static int check_share_mode( share_mode_entry *share, int deny_mode, return True; } - /**************************************************************************** open a file with a share mode ****************************************************************************/ -void open_file_shared(files_struct *fsp, connection_struct *conn, - char *fname, int share_mode, int ofun, - mode_t mode, int oplock_request, int *Access, - int *action) + +void open_file_shared(files_struct *fsp,connection_struct *conn,char *fname,int share_mode,int ofun, + mode_t mode,int oplock_request, int *Access,int *action) { int flags=0; int flags2=0; int deny_mode = GET_DENY_MODE(share_mode); BOOL allow_share_delete = GET_ALLOW_SHARE_DELETE(share_mode); SMB_STRUCT_STAT sbuf; - BOOL file_existed = vfs_file_exist(conn, dos_to_unix(fname,False), &sbuf); + BOOL file_existed = dos_file_exist(fname,&sbuf); BOOL share_locked = False; BOOL fcbopen = False; - int token; + int token = 0; SMB_DEV_T dev = 0; SMB_INO_T inode = 0; int num_share_modes = 0; - + int oplock_contention_count = 0; + BOOL all_current_opens_are_level_II = False; fsp->open = False; fsp->fd_ptr = 0; DEBUG(10,("open_file_shared: fname = %s, share_mode = %x, ofun = %x, mode = %o, oplock request = %d\n", fname, share_mode, ofun, (int)mode, oplock_request )); + + /* ignore any oplock requests if oplocks are disabled */ + if (!lp_oplocks(SNUM(conn)) || global_client_failed_oplock_break) { + oplock_request = 0; + } + /* this is for OS/2 EAs - try and say we don't support them */ if (strstr(fname,".+,;=[].")) { @@ -861,6 +875,8 @@ void open_file_shared(files_struct *fsp, connection_struct *conn, { broke_oplock = False; + all_current_opens_are_level_II = True; + for(i = 0; i < num_share_modes; i++) { share_mode_entry *share_entry = &old_shares[i]; @@ -872,7 +888,8 @@ void open_file_shared(files_struct *fsp, connection_struct *conn, * Check if someone has an oplock on this file. If so we must break * it before continuing. */ - if(share_entry->op_type & (EXCLUSIVE_OPLOCK|BATCH_OPLOCK)) + if((oplock_request && EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) || + (!oplock_request && (share_entry->op_type != NO_OPLOCK))) { DEBUG(5,("open_file_shared: breaking oplock (%x) on file %s, \ @@ -894,7 +911,10 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou } lock_share_entry(conn, dev, inode, &token); broke_oplock = True; + all_current_opens_are_level_II = False; break; + } else if (!LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) { + all_current_opens_are_level_II = False; } /* someone else has a share lock on it, check to see @@ -913,6 +933,7 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou { free((char *)old_shares); num_share_modes = get_share_modes(conn, token, dev, inode, &old_shares); + oplock_contention_count++; } } while(broke_oplock); } @@ -921,6 +942,18 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou free((char *)old_shares); } + /* + * Refuse to grant an oplock in case the contention limit is + * reached when going through the lock list multiple times. + */ + + if(oplock_contention_count >= lp_oplock_contention_limit(SNUM(conn))) + { + oplock_request = 0; + DEBUG(4,("open_file_shared: oplock contention = %d. Not granting oplock.\n", + oplock_contention_count )); + } + DEBUG(4,("calling open_file with flags=0x%X flags2=0x%X mode=0%o\n", flags,flags2,(int)mode)); @@ -978,18 +1011,19 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou { uint16 port = 0; - /* JRA. Currently this only services Exlcusive and batch - oplocks (no other opens on this file). This needs to - be extended to level II oplocks (multiple reader - oplocks). */ + /* + * Setup the oplock info in both the shared memory and + * file structs. + */ - if((oplock_request) && (num_share_modes == 0) && lp_oplocks(SNUM(conn)) && - !IS_VETO_OPLOCK_PATH(conn,fname) && set_file_oplock(fsp) ) - { + if(oplock_request && (num_share_modes == 0) && + !IS_VETO_OPLOCK_PATH(conn,fname) && set_file_oplock(fsp, oplock_request) ) { port = global_oplock_port; - } - else - { + } else if (oplock_request && all_current_opens_are_level_II) { + port = global_oplock_port; + oplock_request = LEVEL_II_OPLOCK; + set_file_oplock(fsp, oplock_request); + } else { port = 0; oplock_request = 0; } @@ -999,13 +1033,6 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou if ((flags2&O_TRUNC) && file_existed) truncate_unless_locked(fsp,conn,token,&share_locked); - - /* - * Attempt to mmap a read only file. - * Moved until after a kernel oplock may - * be granted due to reference count issues. JRA. - */ - mmap_open_file(fsp); } if (share_locked && lp_share_modes(SNUM(conn))) @@ -1013,6 +1040,69 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou } /**************************************************************************** + Open a file for permissions read only. Return a pseudo file entry + with the 'stat_open' flag set and a fd_ptr of NULL. +****************************************************************************/ + +int open_file_stat(files_struct *fsp,connection_struct *conn, + char *fname, int smb_ofun, SMB_STRUCT_STAT *pst, int *action) +{ + extern struct current_user current_user; + + if(dos_stat(fname, pst) < 0) { + DEBUG(0,("open_file_stat: unable to stat name = %s. Error was %s\n", + fname, strerror(errno) )); + return -1; + } + + if(S_ISDIR(pst->st_mode)) { + DEBUG(0,("open_file_stat: %s is a directory !\n", fname )); + return -1; + } + + *action = FILE_WAS_OPENED; + + DEBUG(5,("open_file_stat: opening file %s as a stat entry\n", fname)); + + /* + * Setup the files_struct for it. + */ + + fsp->fd_ptr = NULL; + conn->num_files_open++; + fsp->mode = 0; + GetTimeOfDay(&fsp->open_time); + fsp->vuid = current_user.vuid; + fsp->size = 0; + fsp->pos = -1; + fsp->open = True; + fsp->can_lock = False; + fsp->can_read = False; + fsp->can_write = False; + fsp->share_mode = 0; + fsp->print_file = False; + fsp->modified = False; + fsp->oplock_type = NO_OPLOCK; + fsp->sent_oplock_break = NO_BREAK_SENT; + fsp->is_directory = False; + fsp->stat_open = True; + fsp->directory_delete_on_close = False; + fsp->conn = conn; + /* + * Note that the file name here is the *untranslated* name + * ie. it is still in the DOS codepage sent from the client. + * All use of this filename will pass though the sys_xxxx + * functions which will do the dos_to_unix translation before + * mapping into a UNIX filename. JRA. + */ + string_set(&fsp->fsp_name,fname); + fsp->wbmpx_ptr = NULL; + fsp->wcp = NULL; /* Write cache pointer. */ + + return 0; +} + +/**************************************************************************** Open a directory from an NT SMB call. ****************************************************************************/ @@ -1021,26 +1111,55 @@ int open_directory(files_struct *fsp,connection_struct *conn, { extern struct current_user current_user; SMB_STRUCT_STAT st; + BOOL got_stat = False; - if (smb_ofun & 0x10) { - /* - * Create the directory. - */ + if(dos_stat(fname, &st) == 0) { + got_stat = True; + } - if(conn->vfs_ops.mkdir(dos_to_unix(fname,False), - unix_mode(conn,aDIR)) < 0) { - DEBUG(0,("open_directory: unable to create %s. Error was %s\n", - fname, strerror(errno) )); - return -1; - } + if (got_stat && (GET_FILE_OPEN_DISPOSITION(smb_ofun) == FILE_EXISTS_FAIL)) { + errno = EEXIST; /* Setup so correct error is returned to client. */ + return -1; + } + + if (GET_FILE_CREATE_DISPOSITION(smb_ofun) == FILE_CREATE_IF_NOT_EXIST) { + + if (got_stat) { + + if(!S_ISDIR(st.st_mode)) { + DEBUG(0,("open_directory: %s is not a directory !\n", fname )); + errno = EACCES; + return -1; + } + *action = FILE_WAS_OPENED; + + } else { + + /* + * Try and create the directory. + */ + + if(!CAN_WRITE(conn)) { + DEBUG(2,("open_directory: failing create on read-only share\n")); + errno = EACCES; + return -1; + } - *action = FILE_WAS_CREATED; + if(dos_mkdir(fname, unix_mode(conn,aDIR)) < 0) { + DEBUG(0,("open_directory: unable to create %s. Error was %s\n", + fname, strerror(errno) )); + return -1; + } + *action = FILE_WAS_CREATED; + + } } else { + /* - * Check that it *was* a directory. + * Don't create - just check that it *was* a directory. */ - if(conn->vfs_ops.stat(dos_to_unix(fname,False), &st) < 0) { + if(!got_stat) { DEBUG(0,("open_directory: unable to stat name = %s. Error was %s\n", fname, strerror(errno) )); return -1; @@ -1050,6 +1169,7 @@ int open_directory(files_struct *fsp,connection_struct *conn, DEBUG(0,("open_directory: %s is not a directory !\n", fname )); return -1; } + *action = FILE_WAS_OPENED; } @@ -1068,17 +1188,16 @@ int open_directory(files_struct *fsp,connection_struct *conn, fsp->size = 0; fsp->pos = -1; fsp->open = True; - fsp->mmap_ptr = NULL; - fsp->mmap_size = 0; fsp->can_lock = True; fsp->can_read = False; fsp->can_write = False; fsp->share_mode = 0; fsp->print_file = False; fsp->modified = False; - fsp->granted_oplock = False; - fsp->sent_oplock_break = False; + fsp->oplock_type = NO_OPLOCK; + fsp->sent_oplock_break = NO_BREAK_SENT; fsp->is_directory = True; + fsp->directory_delete_on_close = False; fsp->conn = conn; /* * Note that the file name here is the *untranslated* name @@ -1093,10 +1212,9 @@ int open_directory(files_struct *fsp,connection_struct *conn, return 0; } - /******************************************************************* -check if the share mode on a file allows it to be deleted or unlinked -return True if sharing doesn't prevent the operation + Check if the share mode on a file allows it to be deleted or unlinked. + Return True if sharing doesn't prevent the operation. ********************************************************************/ BOOL check_file_sharing(connection_struct *conn,char *fname, BOOL rename_op) @@ -1107,14 +1225,14 @@ BOOL check_file_sharing(connection_struct *conn,char *fname, BOOL rename_op) int num_share_modes; SMB_STRUCT_STAT sbuf; int token; - int pid = getpid(); + pid_t pid = getpid(); SMB_DEV_T dev; SMB_INO_T inode; if(!lp_share_modes(SNUM(conn))) return True; - if (conn->vfs_ops.stat(dos_to_unix(fname,False),&sbuf) == -1) return(True); + if (dos_stat(fname,&sbuf) == -1) return(True); dev = sbuf.st_dev; inode = sbuf.st_ino; @@ -1144,9 +1262,16 @@ BOOL check_file_sharing(connection_struct *conn,char *fname, BOOL rename_op) * Check if someone has an oplock on this file. If so we must * break it before continuing. */ - if(share_entry->op_type & BATCH_OPLOCK) + if(BATCH_OPLOCK_TYPE(share_entry->op_type)) { +#if 0 + +/* JRA. Try removing this code to see if the new oplock changes + fix the problem. I'm dubious, but Andrew is recommending we + try this.... +*/ + /* * It appears that the NT redirector may have a bug, in that * it tries to do an SMBmv on a file that it has open with a @@ -1176,6 +1301,7 @@ batch oplocked file %s, dev = %x, inode = %.0f\n", fname, (unsigned int)dev, (do continue; } else +#endif /* 0 */ { DEBUG(5,("check_file_sharing: breaking oplock (%x) on file %s, \ diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c index 99fe7a69fa..a64dd2d0ef 100644 --- a/source3/smbd/oplock.c +++ b/source3/smbd/oplock.c @@ -26,18 +26,30 @@ extern int DEBUGLEVEL; /* Oplock ipc UDP socket. */ static int oplock_sock = -1; uint16 global_oplock_port = 0; -#if defined(HAVE_KERNEL_OPLOCKS) static int oplock_pipe_read = -1; + +#if defined(HAVE_KERNEL_OPLOCKS) static int oplock_pipe_write = -1; -#endif /* HAVE_KERNEL_OPLOCKS */ +#endif /* Current number of oplocks we have outstanding. */ -int32 global_oplocks_open = 0; +static int32 exclusive_oplocks_open = 0; +static int32 level_II_oplocks_open = 0; +BOOL global_client_failed_oplock_break = False; BOOL global_oplock_break = False; extern int smb_read_error; -static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval); +static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval, BOOL local); + +/**************************************************************************** + Get the number of current exclusive oplocks. +****************************************************************************/ + +int32 get_number_of_exclusive_open_oplocks(void) +{ + return exclusive_oplocks_open; +} /**************************************************************************** Setup the kernel level oplock backchannel for this process. @@ -63,8 +75,9 @@ BOOL setup_kernel_oplock_pipe(void) } /**************************************************************************** - open the oplock IPC socket communication + Open the oplock IPC socket communication. ****************************************************************************/ + BOOL open_oplock_ipc(void) { struct sockaddr_in sock_name; @@ -73,7 +86,7 @@ BOOL open_oplock_ipc(void) DEBUG(3,("open_oplock_ipc: opening loopback UDP socket.\n")); /* Open a lookback UDP socket on a random port. */ - oplock_sock = open_socket_in(SOCK_DGRAM, 0, 0, htonl(INADDR_LOOPBACK)); + oplock_sock = open_socket_in(SOCK_DGRAM, 0, 0, htonl(INADDR_LOOPBACK),False); if (oplock_sock == -1) { DEBUG(0,("open_oplock_ipc: Failed to get local UDP socket for \ @@ -127,15 +140,13 @@ BOOL receive_local_message(fd_set *fds, char *buffer, int buffer_len, int timeou int selrtn; int maxfd = oplock_sock; -#if defined(HAVE_KERNEL_OPLOCKS) - if(lp_kernel_oplocks()) + if(lp_kernel_oplocks() && (oplock_pipe_read != -1)) maxfd = MAX(maxfd, oplock_pipe_read); -#endif /* HAVE_KERNEL_OPLOCKS */ to.tv_sec = timeout / 1000; to.tv_usec = (timeout % 1000) * 1000; - selrtn = sys_select(maxfd+1,fds,NULL, &to); + selrtn = sys_select(maxfd+1,fds,&to); /* Check if error */ if(selrtn == -1) { @@ -196,7 +207,7 @@ Error was %s.\n", strerror(errno) )); } dev = (SMB_DEV_T)os.os_dev; - inode = (SMB_DEV_T)os.os_ino; + inode = (SMB_INO_T)os.os_ino; DEBUG(5,("receive_local_message: kernel oplock break request received for \ dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode )); @@ -212,14 +223,9 @@ dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode )); buffer += OPBRK_CMD_HEADER_LEN; SSVAL(buffer,OPBRK_MESSAGE_CMD_OFFSET,KERNEL_OPLOCK_BREAK_CMD); - SIVAL(buffer,KERNEL_OPLOCK_BREAK_DEV_OFFSET,dev); -#ifdef LARGE_SMB_INO_T - SIVAL(buffer,KERNEL_OPLOCK_BREAK_INODE_OFFSET,inode & 0xFFFFFFFF); - SIVAL(buffer,KERNEL_OPLOCK_BREAK_INODE_OFFSET+4, (inode >> 32 ) & 0xFFFFFFFF ); -#else /* LARGE_SMB_INO_T */ - SIVAL(buffer,KERNEL_OPLOCK_BREAK_INODE_OFFSET,inode); -#endif /* LARGE_SMB_INO_T */ + memcpy(buffer + KERNEL_OPLOCK_BREAK_DEV_OFFSET, (char *)&dev, sizeof(dev)); + memcpy(buffer + KERNEL_OPLOCK_BREAK_INODE_OFFSET, (char *)&inode, sizeof(inode)); return True; } @@ -265,11 +271,11 @@ dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode )); } /**************************************************************************** - Attempt to set an oplock on a file. Always succeeds if kernel oplocks are - disabled (just sets flags). Returns True if oplock set. + Attempt to set an kernel oplock on a file. Always returns True if kernel + oplocks not available. ****************************************************************************/ -BOOL set_file_oplock(files_struct *fsp) +static BOOL set_kernel_oplock(files_struct *fsp, int oplock_type) { #if defined(HAVE_KERNEL_OPLOCKS) if(lp_kernel_oplocks()) { @@ -293,23 +299,38 @@ inode = %.0f. Another process had the file open.\n", } #endif /* HAVE_KERNEL_OPLOCKS */ + return True; +} - DEBUG(5,("set_file_oplock: granted oplock on file %s, dev = %x, inode = %.0f\n", - fsp->fsp_name, (unsigned int)fsp->fd_ptr->dev, (double)fsp->fd_ptr->inode)); +/**************************************************************************** + Attempt to set an oplock on a file. Always succeeds if kernel oplocks are + disabled (just sets flags). Returns True if oplock set. +****************************************************************************/ - fsp->granted_oplock = True; - fsp->sent_oplock_break = False; - global_oplocks_open++; +BOOL set_file_oplock(files_struct *fsp, int oplock_type) +{ + if (!set_kernel_oplock(fsp, oplock_type)) + return False; + + fsp->oplock_type = oplock_type; + fsp->sent_oplock_break = NO_BREAK_SENT; + if ( oplock_type == LEVEL_II_OPLOCK) + level_II_oplocks_open++; + else + exclusive_oplocks_open++; + + DEBUG(5,("set_file_oplock: granted oplock on file %s, dev = %x, inode = %.0f, tv_sec = %x, tv_usec = %x\n", + fsp->fsp_name, (unsigned int)fsp->fd_ptr->dev, (double)fsp->fd_ptr->inode, + (int)fsp->open_time.tv_sec, (int)fsp->open_time.tv_usec )); return True; } /**************************************************************************** - Attempt to release an oplock on a file. Always succeeds if kernel oplocks are - disabled (just clears flags). + Release a kernel oplock on a file. ****************************************************************************/ -static void release_file_oplock(files_struct *fsp) +static void release_kernel_oplock(files_struct *fsp) { #if defined(HAVE_KERNEL_OPLOCKS) @@ -322,20 +343,20 @@ static void release_file_oplock(files_struct *fsp) * oplock state of this file. */ int state = fcntl(fsp->fd_ptr->fd, F_OPLKACK, -1); - dbgtext("release_file_oplock: file %s, dev = %x, inode = %.0f has kernel \ + dbgtext("release_kernel_oplock: file %s, dev = %x, inode = %.0f has kernel \ oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->fd_ptr->dev, (double)fsp->fd_ptr->inode, state ); } /* - * Remote the kernel oplock on this file. + * Remove the kernel oplock on this file. */ if(fcntl(fsp->fd_ptr->fd, F_OPLKACK, OP_REVOKE) < 0) { if( DEBUGLVL( 0 )) { - dbgtext("release_file_oplock: Error when removing kernel oplock on file " ); + dbgtext("release_kernel_oplock: Error when removing kernel oplock on file " ); dbgtext("%s, dev = %x, inode = %.0f. Error was %s\n", fsp->fsp_name, (unsigned int)fsp->fd_ptr->dev, (double)fsp->fd_ptr->inode, strerror(errno) ); @@ -343,10 +364,91 @@ oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->fd_ptr->dev, } } #endif /* HAVE_KERNEL_OPLOCKS */ +} + + +/**************************************************************************** + Attempt to release an oplock on a file. Decrements oplock count. +****************************************************************************/ + +void release_file_oplock(files_struct *fsp) +{ + release_kernel_oplock(fsp); + + if (fsp->oplock_type == LEVEL_II_OPLOCK) + level_II_oplocks_open--; + else + exclusive_oplocks_open--; + + fsp->oplock_type = NO_OPLOCK; + fsp->sent_oplock_break = NO_BREAK_SENT; + + flush_write_cache(fsp, OPLOCK_RELEASE_FLUSH); +} + +/**************************************************************************** + Attempt to downgrade an oplock on a file. Doesn't decrement oplock count. +****************************************************************************/ + +static void downgrade_file_oplock(files_struct *fsp) +{ + release_kernel_oplock(fsp); + fsp->oplock_type = LEVEL_II_OPLOCK; + exclusive_oplocks_open--; + level_II_oplocks_open++; + fsp->sent_oplock_break = NO_BREAK_SENT; +} + +/**************************************************************************** + Remove a file oplock. Copes with level II and exclusive. + Locks then unlocks the share mode lock. +****************************************************************************/ + +BOOL remove_oplock(files_struct *fsp) +{ + int token; + SMB_DEV_T dev = fsp->fd_ptr->dev; + SMB_INO_T inode = fsp->fd_ptr->inode; + BOOL ret = True; + + /* Remove the oplock flag from the sharemode. */ + if (lock_share_entry(fsp->conn, dev, inode, &token) == False) { + DEBUG(0,("remove_oplock: failed to lock share entry for file %s\n", + fsp->fsp_name )); + ret = False; + } + + if (fsp->sent_oplock_break == EXCLUSIVE_BREAK_SENT) { + + /* + * Deal with a reply when a break-to-none was sent. + */ + + if(remove_share_oplock(token, fsp)==False) { + DEBUG(0,("remove_oplock: failed to remove share oplock for file %s fnum %d, \ +dev = %x, inode = %.0f\n", fsp->fsp_name, fsp->fnum, (unsigned int)dev, (double)inode)); + ret = False; + } + + release_file_oplock(fsp); + + } else { + + /* + * Deal with a reply when a break-to-level II was sent. + */ + + if(downgrade_share_oplock(token, fsp)==False) { + DEBUG(0,("remove_oplock: failed to downgrade share oplock for file %s fnum %d, \ +dev = %x, inode = %.0f\n", fsp->fsp_name, fsp->fnum, (unsigned int)dev, (double)inode)); + ret = False; + } - fsp->granted_oplock = False; - fsp->sent_oplock_break = False; - global_oplocks_open--; + downgrade_file_oplock(fsp); + } + + unlock_share_entry(fsp->conn, dev, inode, token); + return ret; } /**************************************************************************** @@ -358,14 +460,16 @@ oplock state of %x.\n", fsp->fsp_name, (unsigned int)fsp->fd_ptr->dev, int setup_oplock_select_set( fd_set *fds) { int maxfd = oplock_sock; + + if(oplock_sock == -1) + return 0; + FD_SET(oplock_sock,fds); -#if defined(HAVE_KERNEL_OPLOCKS) - if(lp_kernel_oplocks()) { + if(lp_kernel_oplocks() && (oplock_pipe_read != -1)) { FD_SET(oplock_pipe_read,fds); maxfd = MAX(maxfd,oplock_pipe_read); } -#endif /* HAVE_KERNEL_OPLOCKS */ return maxfd; } @@ -382,9 +486,10 @@ BOOL process_local_message(char *buffer, int buf_size) char *msg_start; SMB_DEV_T dev; SMB_INO_T inode; - uint32 remotepid; + pid_t remotepid; struct timeval tval; struct timeval *ptval = NULL; + uint16 break_cmd_type; msg_len = IVAL(buffer,OPBRK_CMD_LEN_OFFSET); from_port = SVAL(buffer,OPBRK_CMD_PORT_OFFSET); @@ -398,7 +503,9 @@ BOOL process_local_message(char *buffer, int buf_size) * Pull the info out of the requesting packet. */ - switch(SVAL(msg_start,OPBRK_MESSAGE_CMD_OFFSET)) + break_cmd_type = SVAL(msg_start,OPBRK_MESSAGE_CMD_OFFSET); + + switch(break_cmd_type) { #if defined(HAVE_KERNEL_OPLOCKS) case KERNEL_OPLOCK_BREAK_CMD: @@ -410,18 +517,8 @@ should be %d).\n", msg_len, KERNEL_OPLOCK_BREAK_MSG_LEN)); return False; } { - /* - * Warning - beware of SMB_INO_T <> 4 bytes. !! - */ -#ifdef LARGE_SMB_INO_T - SMB_INO_T inode_low = IVAL(msg_start, KERNEL_OPLOCK_BREAK_INODE_OFFSET); - SMB_INO_T inode_high = IVAL(msg_start, KERNEL_OPLOCK_BREAK_INODE_OFFSET + 4); - inode = inode_low | (inode_high << 32); -#else /* LARGE_SMB_INO_T */ - inode = IVAL(msg_start, KERNEL_OPLOCK_BREAK_INODE_OFFSET); -#endif /* LARGE_SMB_INO_T */ - - dev = IVAL(msg_start,KERNEL_OPLOCK_BREAK_DEV_OFFSET); + memcpy((char *)&inode, msg_start+KERNEL_OPLOCK_BREAK_INODE_OFFSET, sizeof(inode)); + memcpy((char *)&dev, msg_start+KERNEL_OPLOCK_BREAK_DEV_OFFSET, sizeof(dev)); ptval = NULL; @@ -432,36 +529,34 @@ file dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode)); #endif /* HAVE_KERNEL_OPLOCKS */ case OPLOCK_BREAK_CMD: + case LEVEL_II_OPLOCK_BREAK_CMD: + /* Ensure that the msg length is correct. */ if(msg_len != OPLOCK_BREAK_MSG_LEN) { - DEBUG(0,("process_local_message: incorrect length for OPLOCK_BREAK_CMD (was %d, \ -should be %d).\n", msg_len, OPLOCK_BREAK_MSG_LEN)); + DEBUG(0,("process_local_message: incorrect length for OPLOCK_BREAK_CMD (was %d, should be %d).\n", + (int)msg_len, (int)OPLOCK_BREAK_MSG_LEN)); return False; } { - /* - * Warning - beware of SMB_INO_T <> 4 bytes. !! - */ -#ifdef LARGE_SMB_INO_T - SMB_INO_T inode_low = IVAL(msg_start, OPLOCK_BREAK_INODE_OFFSET); - SMB_INO_T inode_high = IVAL(msg_start, OPLOCK_BREAK_INODE_OFFSET + 4); - inode = inode_low | (inode_high << 32); -#else /* LARGE_SMB_INO_T */ - inode = IVAL(msg_start, OPLOCK_BREAK_INODE_OFFSET); -#endif /* LARGE_SMB_INO_T */ + long usec; + time_t sec; - dev = IVAL(msg_start,OPLOCK_BREAK_DEV_OFFSET); - - tval.tv_sec = (time_t)IVAL(msg_start, OPLOCK_BREAK_SEC_OFFSET); - tval.tv_usec = (long)IVAL(msg_start, OPLOCK_BREAK_USEC_OFFSET); + memcpy((char *)&inode, msg_start+OPLOCK_BREAK_INODE_OFFSET,sizeof(inode)); + memcpy((char *)&dev, msg_start+OPLOCK_BREAK_DEV_OFFSET,sizeof(dev)); + memcpy((char *)&sec, msg_start+OPLOCK_BREAK_SEC_OFFSET,sizeof(sec)); + tval.tv_sec = sec; + memcpy((char *)&usec, msg_start+OPLOCK_BREAK_USEC_OFFSET, sizeof(usec)); + tval.tv_usec = usec; ptval = &tval; - remotepid = IVAL(msg_start,OPLOCK_BREAK_PID_OFFSET); + memcpy((char *)&remotepid, msg_start+OPLOCK_BREAK_PID_OFFSET,sizeof(remotepid)); - DEBUG(5,("process_local_message: oplock break request from \ -pid %d, port %d, dev = %x, inode = %.0f\n", remotepid, from_port, (unsigned int)dev, (double)inode)); + DEBUG(5,("process_local_message: (%s) oplock break request from \ +pid %d, port %d, dev = %x, inode = %.0f\n", + (break_cmd_type == OPLOCK_BREAK_CMD) ? "exclusive" : "level II", + (int)remotepid, from_port, (unsigned int)dev, (double)inode)); } break; @@ -475,27 +570,17 @@ reply - dumping info.\n")); if(msg_len != OPLOCK_BREAK_MSG_LEN) { DEBUG(0,("process_local_message: ubr: incorrect length for reply \ -(was %d, should be %d).\n", msg_len, OPLOCK_BREAK_MSG_LEN)); +(was %d, should be %d).\n", (int)msg_len, (int)OPLOCK_BREAK_MSG_LEN)); return False; } { - /* - * Warning - beware of SMB_INO_T <> 4 bytes. !! - */ -#ifdef LARGE_SMB_INO_T - SMB_INO_T inode_low = IVAL(msg_start, OPLOCK_BREAK_INODE_OFFSET); - SMB_INO_T inode_high = IVAL(msg_start, OPLOCK_BREAK_INODE_OFFSET + 4); - inode = inode_low | (inode_high << 32); -#else /* LARGE_SMB_INO_T */ - inode = IVAL(msg_start, OPLOCK_BREAK_INODE_OFFSET); -#endif /* LARGE_SMB_INO_T */ - - remotepid = IVAL(msg_start,OPLOCK_BREAK_PID_OFFSET); - dev = IVAL(msg_start,OPLOCK_BREAK_DEV_OFFSET); + memcpy((char *)&inode, msg_start+OPLOCK_BREAK_INODE_OFFSET,sizeof(inode)); + memcpy((char *)&remotepid, msg_start+OPLOCK_BREAK_PID_OFFSET,sizeof(remotepid)); + memcpy((char *)&dev, msg_start+OPLOCK_BREAK_DEV_OFFSET,sizeof(dev)); DEBUG(0,("process_local_message: unsolicited oplock break reply from \ -pid %d, port %d, dev = %x, inode = %.0f\n", remotepid, from_port, (unsigned int)dev, (double)inode)); +pid %d, port %d, dev = %x, inode = %.0f\n", (int)remotepid, from_port, (unsigned int)dev, (double)inode)); } return False; @@ -510,9 +595,9 @@ pid %d, port %d, dev = %x, inode = %.0f\n", remotepid, from_port, (unsigned int) * Now actually process the break request. */ - if(global_oplocks_open != 0) + if((exclusive_oplocks_open + level_II_oplocks_open) != 0) { - if(oplock_break(dev, inode, ptval) == False) + if (oplock_break(dev, inode, ptval, False) == False) { DEBUG(0,("process_local_message: oplock break failed.\n")); return False; @@ -531,7 +616,7 @@ oplocks. Returning success.\n")); } /* - * Do the appropriate reply - none in the kernel case. + * Do the appropriate reply - none in the kernel or level II case. */ if(SVAL(msg_start,OPBRK_MESSAGE_CMD_OFFSET) == OPLOCK_BREAK_CMD) @@ -541,7 +626,7 @@ oplocks. Returning success.\n")); /* Send the message back after OR'ing in the 'REPLY' bit. */ SSVAL(msg_start,OPBRK_MESSAGE_CMD_OFFSET,OPLOCK_BREAK_CMD | CMD_REPLY); - bzero((char *)&toaddr,sizeof(toaddr)); + memset((char *)&toaddr,'\0',sizeof(toaddr)); toaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); toaddr.sin_port = htons(from_port); toaddr.sin_family = AF_INET; @@ -550,40 +635,77 @@ oplocks. Returning success.\n")); (struct sockaddr *)&toaddr, sizeof(toaddr)) < 0) { DEBUG(0,("process_local_message: sendto process %d failed. Errno was %s\n", - remotepid, strerror(errno))); + (int)remotepid, strerror(errno))); return False; } DEBUG(5,("process_local_message: oplock break reply sent to \ pid %d, port %d, for file dev = %x, inode = %.0f\n", - remotepid, from_port, (unsigned int)dev, (double)inode)); + (int)remotepid, from_port, (unsigned int)dev, (double)inode)); } return True; } /**************************************************************************** - Process an oplock break directly. + Set up an oplock break message. ****************************************************************************/ -static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval) +static void prepare_break_message(char *outbuf, files_struct *fsp, BOOL level2) +{ + memset(outbuf,'\0',smb_size); + set_message(outbuf,8,0,True); + + SCVAL(outbuf,smb_com,SMBlockingX); + SSVAL(outbuf,smb_tid,fsp->conn->cnum); + SSVAL(outbuf,smb_pid,0xFFFF); + SSVAL(outbuf,smb_uid,0); + SSVAL(outbuf,smb_mid,0xFFFF); + SCVAL(outbuf,smb_vwv0,0xFF); + SSVAL(outbuf,smb_vwv2,fsp->fnum); + SCVAL(outbuf,smb_vwv3,LOCKING_ANDX_OPLOCK_RELEASE); + SCVAL(outbuf,smb_vwv3+1,level2 ? OPLOCKLEVEL_II : OPLOCKLEVEL_NONE); +} + +/**************************************************************************** + Function to do the waiting before sending a local break. +****************************************************************************/ + +static void wait_before_sending_break(BOOL local_request) +{ + extern struct timeval smb_last_time; + + if(local_request) { + struct timeval cur_tv; + long wait_left = (long)lp_oplock_break_wait_time(); + + GetTimeOfDay(&cur_tv); + + wait_left -= ((cur_tv.tv_sec - smb_last_time.tv_sec)*1000) + + ((cur_tv.tv_usec - smb_last_time.tv_usec)/1000); + + if(wait_left > 0) { + wait_left = MIN(wait_left, 1000); + sys_usleep(wait_left * 1000); + } + } +} + +/**************************************************************************** + Ensure that we have a valid oplock. +****************************************************************************/ + +static files_struct *initial_break_processing(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval) { - extern struct current_user current_user; - extern int Client; - char *inbuf = NULL; - char *outbuf = NULL; files_struct *fsp = NULL; - time_t start_time; - BOOL shutdown_server = False; - connection_struct *saved_conn; - int saved_vuid; - pstring saved_dir; - int break_counter = OPLOCK_BREAK_RESENDS; if( DEBUGLVL( 3 ) ) { - dbgtext( "oplock_break: called for dev = %x, inode = %.0f.\n", (unsigned int)dev, (double)inode ); - dbgtext( "Current global_oplocks_open = %d\n", global_oplocks_open ); + dbgtext( "initial_break_processing: called for dev = %x, inode = %.0f tv_sec = %x, tv_usec = %x.\n", + (unsigned int)dev, (double)inode, tval ? (int)tval->tv_sec : 0, + tval ? (int)tval->tv_usec : 0); + dbgtext( "Current oplocks_open (exclusive = %d, levelII = %d)\n", + exclusive_oplocks_open, level_II_oplocks_open ); } /* We need to search the file open table for the @@ -594,13 +716,13 @@ static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval) if(fsp == NULL) { /* The file could have been closed in the meantime - return success. */ - if( DEBUGLVL( 0 ) ) + if( DEBUGLVL( 3 ) ) { - dbgtext( "oplock_break: cannot find open file with " ); + dbgtext( "initial_break_processing: cannot find open file with " ); dbgtext( "dev = %x, inode = %.0f ", (unsigned int)dev, (double)inode); dbgtext( "allowing break to succeed.\n" ); } - return True; + return NULL; } /* Ensure we have an oplock on the file */ @@ -612,18 +734,130 @@ static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval) as we may have just freed it. */ - if(!fsp->granted_oplock) + if(fsp->oplock_type == NO_OPLOCK) { - if( DEBUGLVL( 0 ) ) + if( DEBUGLVL( 3 ) ) { - dbgtext( "oplock_break: file %s ", fsp->fsp_name ); + dbgtext( "initial_break_processing: file %s ", fsp->fsp_name ); dbgtext( "(dev = %x, inode = %.0f) has no oplock.\n", (unsigned int)dev, (double)inode ); dbgtext( "Allowing break to succeed regardless.\n" ); } - return True; + return NULL; } - /* mark the oplock break as sent - we don't want to send twice! */ + return fsp; +} + +/**************************************************************************** + Process a level II oplock break directly. +****************************************************************************/ + +BOOL oplock_break_level2(files_struct *fsp, BOOL local_request, int token) +{ + extern int Client; + extern uint32 global_client_caps; + char outbuf[128]; + BOOL got_lock = False; + SMB_DEV_T dev = fsp->fd_ptr->dev; + SMB_INO_T inode = fsp->fd_ptr->inode; + + /* + * We can have a level II oplock even if the client is not + * level II oplock aware. In this case just remove the + * flags and don't send the break-to-none message to + * the client. + */ + + if (global_client_caps & CAP_LEVEL_II_OPLOCKS) { + /* + * If we are sending an oplock break due to an SMB sent + * by our own client we ensure that we wait at leat + * lp_oplock_break_wait_time() milliseconds before sending + * the packet. Sending the packet sooner can break Win9x + * and has reported to cause problems on NT. JRA. + */ + + wait_before_sending_break(local_request); + + /* Prepare the SMBlockingX message. */ + + prepare_break_message( outbuf, fsp, False); + send_smb(Client, outbuf); + } + + /* + * Now we must update the shared memory structure to tell + * everyone else we no longer have a level II oplock on + * this open file. If local_request is true then token is + * the existing lock on the shared memory area. + */ + + if(!local_request && lock_share_entry(fsp->conn, dev, inode, &token) == False) { + DEBUG(0,("oplock_break_level2: unable to lock share entry for file %s\n", fsp->fsp_name )); + } else { + got_lock = True; + } + + if(remove_share_oplock(token, fsp)==False) { + DEBUG(0,("oplock_break_level2: unable to remove level II oplock for file %s\n", fsp->fsp_name )); + } + + if (!local_request && got_lock) + unlock_share_entry(fsp->conn, dev, inode, token); + + fsp->oplock_type = NO_OPLOCK; + level_II_oplocks_open--; + + if(level_II_oplocks_open < 0) + { + DEBUG(0,("oplock_break_level2: level_II_oplocks_open < 0 (%d). PANIC ERROR\n", + level_II_oplocks_open)); + abort(); + } + + if( DEBUGLVL( 3 ) ) + { + dbgtext( "oplock_break_level2: returning success for " ); + dbgtext( "dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode ); + dbgtext( "Current level II oplocks_open = %d\n", level_II_oplocks_open ); + } + + return True; +} + +/**************************************************************************** + Process an oplock break directly. +****************************************************************************/ + +static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval, BOOL local_request) +{ + extern uint32 global_client_caps; + extern struct current_user current_user; + extern int Client; + char *inbuf = NULL; + char *outbuf = NULL; + files_struct *fsp = NULL; + time_t start_time; + BOOL shutdown_server = False; + BOOL oplock_timeout = False; + connection_struct *saved_conn; + int saved_vuid; + pstring saved_dir; + int timeout = (OPLOCK_BREAK_TIMEOUT * 1000); + pstring file_name; + BOOL using_levelII; + + if((fsp = initial_break_processing(dev, inode, tval)) == NULL) + return True; + + /* + * Deal with a level II oplock going break to none separately. + */ + + if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) + return oplock_break_level2(fsp, local_request, -1); + + /* Mark the oplock break as sent - we don't want to send twice! */ if (fsp->sent_oplock_break) { if( DEBUGLVL( 0 ) ) @@ -641,6 +875,11 @@ static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval) return False; } + if(global_oplock_break) { + DEBUG(0,("ABORT : ABORT : recursion in oplock_break !!!!!\n")); + abort(); + } + /* Now comes the horrid part. We must send an oplock break to the client, and then process incoming messages until we get a close or oplock release. At this point we know we need a new inbuf/outbuf buffer pair. @@ -662,25 +901,30 @@ static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval) return False; } + /* + * If we are sending an oplock break due to an SMB sent + * by our own client we ensure that we wait at leat + * lp_oplock_break_wait_time() milliseconds before sending + * the packet. Sending the packet sooner can break Win9x + * and has reported to cause problems on NT. JRA. + */ + + wait_before_sending_break(local_request); + /* Prepare the SMBlockingX message. */ - bzero(outbuf,smb_size); - set_message(outbuf,8,0,True); - SCVAL(outbuf,smb_com,SMBlockingX); - SSVAL(outbuf,smb_tid,fsp->conn->cnum); - SSVAL(outbuf,smb_pid,0xFFFF); - SSVAL(outbuf,smb_uid,0); - SSVAL(outbuf,smb_mid,0xFFFF); - SCVAL(outbuf,smb_vwv0,0xFF); - SSVAL(outbuf,smb_vwv2,fsp->fnum); - SCVAL(outbuf,smb_vwv3,LOCKING_ANDX_OPLOCK_RELEASE); - /* Change this when we have level II oplocks. */ - SCVAL(outbuf,smb_vwv3+1,OPLOCKLEVEL_NONE); - - send_smb(Client, outbuf); + if ((global_client_caps & CAP_LEVEL_II_OPLOCKS) && !lp_kernel_oplocks() && lp_level2_oplocks(SNUM(fsp->conn))) { + using_levelII = True; + } else { + using_levelII = False; + } + + prepare_break_message( outbuf, fsp, using_levelII); + /* Remember if we just sent a break to level II on this file. */ + fsp->sent_oplock_break = using_levelII? + LEVEL_II_BREAK_SENT:EXCLUSIVE_BREAK_SENT; - /* Remember we just sent an oplock break on this file. */ - fsp->sent_oplock_break = True; + send_smb(Client, outbuf); /* We need this in case a readraw crosses on the wire. */ global_oplock_break = True; @@ -703,40 +947,39 @@ static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval) /* Save the chain fnum. */ file_chain_save(); - while(OPEN_FSP(fsp) && fsp->granted_oplock) - { - if(receive_smb(Client,inbuf, - (OPLOCK_BREAK_TIMEOUT/OPLOCK_BREAK_RESENDS) * 1000) == False) - { + /* + * From Charles Hoch <hoch@exemplary.com>. If the break processing + * code closes the file (as it often does), then the fsp pointer here + * points to free()'d memory. We *must* revalidate fsp each time + * around the loop. + */ - /* Isaac suggestd that if a MS client doesn't respond to a - oplock break request then we might try resending - it. Certainly it's no worse than just dropping the - socket! */ - if (smb_read_error == READ_TIMEOUT && break_counter--) { - DEBUG(2, ( "oplock_break resend\n" ) ); - send_smb(Client, outbuf); - continue; - } + pstrcpy(file_name, fsp->fsp_name); + while((fsp = initial_break_processing(dev, inode, tval)) && + OPEN_FSP(fsp) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) + { + if(receive_smb(Client,inbuf, timeout) == False) + { /* * Die if we got an error. */ - if (smb_read_error == READ_EOF) + if (smb_read_error == READ_EOF) { DEBUG( 0, ( "oplock_break: end of file from client\n" ) ); - - if (smb_read_error == READ_ERROR) + shutdown_server = True; + } else if (smb_read_error == READ_ERROR) { DEBUG( 0, ("oplock_break: receive_smb error (%s)\n", strerror(errno)) ); - - if (smb_read_error == READ_TIMEOUT) + shutdown_server = True; + } else if (smb_read_error == READ_TIMEOUT) { DEBUG( 0, ( "oplock_break: receive_smb timed out after %d seconds.\n", OPLOCK_BREAK_TIMEOUT ) ); + oplock_timeout = True; + } - DEBUGADD( 0, ( "oplock_break failed for file %s ", fsp->fsp_name ) ); + DEBUGADD( 0, ( "oplock_break failed for file %s ", file_name ) ); DEBUGADD( 0, ( "(dev = %x, inode = %.0f).\n", (unsigned int)dev, (double)inode)); - shutdown_server = True; break; } @@ -758,13 +1001,13 @@ static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval) if((time(NULL) - start_time) > OPLOCK_BREAK_TIMEOUT) { if( DEBUGLVL( 0 ) ) - { + { dbgtext( "oplock_break: no break received from client " ); dbgtext( "within %d seconds.\n", OPLOCK_BREAK_TIMEOUT ); dbgtext( "oplock_break failed for file %s ", fsp->fsp_name ); dbgtext( "(dev = %x, inode = %.0f).\n", (unsigned int)dev, (double)inode ); - } - shutdown_server = True; + } + oplock_timeout = True; break; } } @@ -796,7 +1039,21 @@ static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval) global_oplock_break = False; /* - * If the client did not respond we must die. + * If the client timed out then clear the oplock (or go to level II) + * and continue. This seems to be what NT does and is better than dropping + * the connection. + */ + + if(oplock_timeout && (fsp = initial_break_processing(dev, inode, tval)) && + OPEN_FSP(fsp) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) + { + DEBUG(0,("oplock_break: client failure in oplock break in file %s\n", fsp->fsp_name)); + remove_oplock(fsp); + global_client_failed_oplock_break = True; /* Never grant this client an oplock again. */ + } + + /* + * If the client had an error we must die. */ if(shutdown_server) @@ -808,27 +1065,19 @@ static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, struct timeval *tval) exit_server("oplock break failure"); } - if(OPEN_FSP(fsp)) - { - /* The lockingX reply will have removed the oplock flag - from the sharemode. */ - release_file_oplock(fsp); - } - /* Santity check - remove this later. JRA */ - if(global_oplocks_open < 0) + if(exclusive_oplocks_open < 0) { - DEBUG(0,("oplock_break: global_oplocks_open < 0 (%d). PANIC ERROR\n", - global_oplocks_open)); - exit_server("oplock_break: global_oplocks_open < 0"); + DEBUG(0,("oplock_break: exclusive_oplocks_open < 0 (%d). PANIC ERROR\n", + exclusive_oplocks_open)); + abort(); } - if( DEBUGLVL( 3 ) ) { dbgtext( "oplock_break: returning success for " ); dbgtext( "dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode ); - dbgtext( "Current global_oplocks_open = %d\n", global_oplocks_open ); + dbgtext( "Current exclusive_oplocks_open = %d\n", exclusive_oplocks_open ); } return True; @@ -844,9 +1093,11 @@ BOOL request_oplock_break(share_mode_entry *share_entry, { char op_break_msg[OPLOCK_BREAK_MSG_LEN]; struct sockaddr_in addr_out; - int pid = getpid(); + pid_t pid = getpid(); time_t start_time; int time_left; + long usec; + time_t sec; if(pid == share_entry->pid) { @@ -854,36 +1105,34 @@ BOOL request_oplock_break(share_mode_entry *share_entry, if(share_entry->op_port != global_oplock_port) { DEBUG(0,("request_oplock_break: corrupt share mode entry - pid = %d, port = %d \ -should be %d\n", pid, share_entry->op_port, global_oplock_port)); +should be %d\n", (int)pid, share_entry->op_port, global_oplock_port)); return False; } DEBUG(5,("request_oplock_break: breaking our own oplock\n")); /* Call oplock break direct. */ - return oplock_break(dev, inode, &share_entry->time); + return oplock_break(dev, inode, &share_entry->time, True); } /* We need to send a OPLOCK_BREAK_CMD message to the port in the share mode entry. */ - SSVAL(op_break_msg,OPBRK_MESSAGE_CMD_OFFSET,OPLOCK_BREAK_CMD); - SIVAL(op_break_msg,OPLOCK_BREAK_PID_OFFSET,pid); - SIVAL(op_break_msg,OPLOCK_BREAK_SEC_OFFSET,(uint32)share_entry->time.tv_sec); - SIVAL(op_break_msg,OPLOCK_BREAK_USEC_OFFSET,(uint32)share_entry->time.tv_usec); - SIVAL(op_break_msg,OPLOCK_BREAK_DEV_OFFSET,dev); - /* - * WARNING - beware of SMB_INO_T <> 4 bytes. - */ -#ifdef LARGE_SMB_INO_T - SIVAL(op_break_msg,OPLOCK_BREAK_INODE_OFFSET,(inode & 0xFFFFFFFFL)); - SIVAL(op_break_msg,OPLOCK_BREAK_INODE_OFFSET+4,((inode >> 32) & 0xFFFFFFFFL)); -#else /* LARGE_SMB_INO_T */ - SIVAL(op_break_msg,OPLOCK_BREAK_INODE_OFFSET,inode); -#endif /* LARGE_SMB_INO_T */ + if (LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) { + SSVAL(op_break_msg,OPBRK_MESSAGE_CMD_OFFSET,LEVEL_II_OPLOCK_BREAK_CMD); + } else { + SSVAL(op_break_msg,OPBRK_MESSAGE_CMD_OFFSET,OPLOCK_BREAK_CMD); + } + memcpy(op_break_msg+OPLOCK_BREAK_PID_OFFSET,(char *)&pid,sizeof(pid)); + sec = (time_t)share_entry->time.tv_sec; + memcpy(op_break_msg+OPLOCK_BREAK_SEC_OFFSET,(char *)&sec,sizeof(sec)); + usec = (long)share_entry->time.tv_usec; + memcpy(op_break_msg+OPLOCK_BREAK_USEC_OFFSET,(char *)&usec,sizeof(usec)); + memcpy(op_break_msg+OPLOCK_BREAK_DEV_OFFSET,(char *)&dev,sizeof(dev)); + memcpy(op_break_msg+OPLOCK_BREAK_INODE_OFFSET,(char *)&inode,sizeof(inode)); /* set the address and port */ - bzero((char *)&addr_out,sizeof(addr_out)); + memset((char *)&addr_out,'\0',sizeof(addr_out)); addr_out.sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr_out.sin_port = htons( share_entry->op_port ); addr_out.sin_family = AF_INET; @@ -891,8 +1140,11 @@ should be %d\n", pid, share_entry->op_port, global_oplock_port)); if( DEBUGLVL( 3 ) ) { dbgtext( "request_oplock_break: sending a oplock break message to " ); - dbgtext( "pid %d on port %d ", share_entry->pid, share_entry->op_port ); - dbgtext( "for dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode ); + dbgtext( "pid %d on port %d ", (int)share_entry->pid, share_entry->op_port ); + dbgtext( "for dev = %x, inode = %.0f, tv_sec = %x, tv_usec = %x\n", + (unsigned int)dev, (double)inode, (int)share_entry->time.tv_sec, + (int)share_entry->time.tv_usec ); + } if(sendto(oplock_sock,op_break_msg,OPLOCK_BREAK_MSG_LEN,0, @@ -901,15 +1153,27 @@ should be %d\n", pid, share_entry->op_port, global_oplock_port)); if( DEBUGLVL( 0 ) ) { dbgtext( "request_oplock_break: failed when sending a oplock " ); - dbgtext( "break message to pid %d ", share_entry->pid ); + dbgtext( "break message to pid %d ", (int)share_entry->pid ); dbgtext( "on port %d ", share_entry->op_port ); - dbgtext( "for dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode ); + dbgtext( "for dev = %x, inode = %.0f, tv_sec = %x, tv_usec = %x\n", + (unsigned int)dev, (double)inode, (int)share_entry->time.tv_sec, + (int)share_entry->time.tv_usec ); dbgtext( "Error was %s\n", strerror(errno) ); } return False; } /* + * If we just sent a message to a level II oplock share entry then + * we are done and may return. + */ + + if (LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) { + DEBUG(3,("request_oplock_break: sent break message to level II entry.\n")); + return True; + } + + /* * Now we must await the oplock broken message coming back * from the target smbd process. Timeout if it fails to * return in (OPLOCK_BREAK_TIMEOUT + OPLOCK_BREAK_TIMEOUT_FUDGEFACTOR) seconds. @@ -929,10 +1193,8 @@ should be %d\n", pid, share_entry->op_port, global_oplock_port)); FD_ZERO(&fds); FD_SET(oplock_sock,&fds); -#if defined(HAVE_KERNEL_OPLOCKS) - if(lp_kernel_oplocks()) + if(lp_kernel_oplocks() && (oplock_pipe_read != -1)) FD_SET(oplock_pipe_read,&fds); -#endif /* HAVE_KERNEL_OPLOCKS */ if(receive_local_message(&fds, op_break_reply, sizeof(op_break_reply), time_left ? time_left * 1000 : 1) == False) @@ -942,9 +1204,12 @@ should be %d\n", pid, share_entry->op_port, global_oplock_port)); if( DEBUGLVL( 0 ) ) { dbgtext( "request_oplock_break: no response received to oplock " ); - dbgtext( "break request to pid %d ", share_entry->pid ); + dbgtext( "break request to pid %d ", (int)share_entry->pid ); dbgtext( "on port %d ", share_entry->op_port ); dbgtext( "for dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode ); + dbgtext( "for dev = %x, inode = %.0f, tv_sec = %x, tv_usec = %x\n", + (unsigned int)dev, (double)inode, (int)share_entry->time.tv_sec, + (int)share_entry->time.tv_usec ); } /* @@ -960,9 +1225,11 @@ should be %d\n", pid, share_entry->op_port, global_oplock_port)); if( DEBUGLVL( 0 ) ) { dbgtext( "request_oplock_break: error in response received " ); - dbgtext( "to oplock break request to pid %d ", share_entry->pid ); + dbgtext( "to oplock break request to pid %d ", (int)share_entry->pid ); dbgtext( "on port %d ", share_entry->op_port ); - dbgtext( "for dev = %x, inode = %.0f\n", (unsigned int)dev, (double)inode ); + dbgtext( "for dev = %x, inode = %.0f, tv_sec = %x, tv_usec = %x\n", + (unsigned int)dev, (double)inode, (int)share_entry->time.tv_sec, + (int)share_entry->time.tv_usec ); dbgtext( "Error was (%s).\n", strerror(errno) ); } return False; @@ -1036,16 +1303,17 @@ should be %d\n", pid, share_entry->op_port, global_oplock_port)); Used as a last ditch attempt to free a space in the file table when we have run out. ****************************************************************************/ + BOOL attempt_close_oplocked_file(files_struct *fsp) { DEBUG(5,("attempt_close_oplocked_file: checking file %s.\n", fsp->fsp_name)); - if (fsp->open && fsp->granted_oplock && !fsp->sent_oplock_break && (fsp->fd_ptr != NULL)) { + if (fsp->open && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !fsp->sent_oplock_break && (fsp->fd_ptr != NULL)) { /* Try and break the oplock. */ file_fd_struct *fd_ptr = fsp->fd_ptr; - if(oplock_break( fd_ptr->dev, fd_ptr->inode, &fsp->open_time)) { + if(oplock_break( fd_ptr->dev, fd_ptr->inode, &fsp->open_time, True)) { if(!fsp->open) /* Did the oplock break close the file ? */ return True; } diff --git a/source3/smbd/password.c b/source3/smbd/password.c index e15e08a1af..9792c9ebc8 100644 --- a/source3/smbd/password.c +++ b/source3/smbd/password.c @@ -23,6 +23,7 @@ extern int DEBUGLEVEL; extern int Protocol; +extern struct in_addr ipzero; /* users from session setup */ static pstring session_users=""; @@ -31,6 +32,261 @@ extern pstring scope; extern pstring global_myname; extern fstring global_myworkgroup; +/* Data to do lanman1/2 password challenge. */ +static unsigned char saved_challenge[8]; +static BOOL challenge_sent=False; + +/******************************************************************* +Get the next challenge value - no repeats. +********************************************************************/ +void generate_next_challenge(char *challenge) +{ +#if 0 + /* + * Leave this ifdef'd out while we test + * the new crypto random number generator. + * JRA. + */ + unsigned char buf[16]; + static int counter = 0; + struct timeval tval; + int v1,v2; + + /* get a sort-of random number */ + GetTimeOfDay(&tval); + v1 = (counter++) + getpid() + tval.tv_sec; + v2 = (counter++) * getpid() + tval.tv_usec; + SIVAL(challenge,0,v1); + SIVAL(challenge,4,v2); + + /* mash it up with md4 */ + mdfour(buf, (unsigned char *)challenge, 8); +#else + unsigned char buf[8]; + + generate_random_buffer(buf,8,False); +#endif + memcpy(saved_challenge, buf, 8); + memcpy(challenge,buf,8); + challenge_sent = True; +} + +/******************************************************************* +set the last challenge sent, usually from a password server +********************************************************************/ +BOOL set_challenge(unsigned char *challenge) +{ + memcpy(saved_challenge,challenge,8); + challenge_sent = True; + return(True); +} + +/******************************************************************* +get the last challenge sent +********************************************************************/ +static BOOL last_challenge(unsigned char *challenge) +{ + if (!challenge_sent) return(False); + memcpy(challenge,saved_challenge,8); + return(True); +} + +/* this holds info on user ids that are already validated for this VC */ +static user_struct *validated_users = NULL; +static int num_validated_users = 0; + +/**************************************************************************** +check if a uid has been validated, and return an pointer to the user_struct +if it has. NULL if not. vuid is biased by an offset. This allows us to +tell random client vuid's (normally zero) from valid vuids. +****************************************************************************/ +user_struct *get_valid_user_struct(uint16 vuid) +{ + if (vuid == UID_FIELD_INVALID) + return NULL; + vuid -= VUID_OFFSET; + if ((vuid >= (uint16)num_validated_users) || + (validated_users[vuid].uid == (uid_t)-1) || (validated_users[vuid].gid == (gid_t)-1)) + return NULL; + return &validated_users[vuid]; +} + +/**************************************************************************** +invalidate a uid +****************************************************************************/ +void invalidate_vuid(uint16 vuid) +{ + user_struct *vuser = get_valid_user_struct(vuid); + + if (vuser == NULL) return; + + vuser->uid = (uid_t)-1; + vuser->gid = (gid_t)-1; + + vuser->n_sids = 0; + + /* same number of igroups as groups */ + vuser->n_groups = 0; + + if (vuser->groups) + free((char *)vuser->groups); + + if (vuser->sids) + free((char *)vuser->sids); + + vuser->sids = NULL; + vuser->groups = NULL; +} + + +/**************************************************************************** +return a validated username +****************************************************************************/ +char *validated_username(uint16 vuid) +{ + user_struct *vuser = get_valid_user_struct(vuid); + if (vuser == NULL) + return 0; + return(vuser->name); +} + + +/**************************************************************************** +Setup the groups a user belongs to. +****************************************************************************/ +int setup_groups(char *user, uid_t uid, gid_t gid, int *p_ngroups, gid_t **p_groups) +{ + int i,ngroups; + gid_t grp = 0; + gid_t *groups = NULL; + + if (-1 == initgroups(user,gid)) + { + DEBUG(0,("Unable to initgroups. Error was %s\n", strerror(errno) )); + if (getuid() == 0) + { + if (gid < 0 || gid > 32767 || uid < 0 || uid > 32767) + { + DEBUG(0,("This is probably a problem with the account %s\n", user)); + } + } + return -1; + } + + ngroups = sys_getgroups(0,&grp); + if (ngroups <= 0) + { + ngroups = 32; + } + +#ifdef NGROUPS_MAX + if((groups = (gid_t *)malloc(sizeof(gid_t)*NGROUPS_MAX)) == NULL) +#else /* NGROUPS_MAX */ + if((groups = (gid_t *)malloc(sizeof(gid_t)*ngroups)) == NULL) +#endif /* NGROUPS_MAX */ + { + DEBUG(0,("setup_groups malloc fail !\n")); + return -1; + } + + ngroups = sys_getgroups(ngroups,groups); + + (*p_ngroups) = ngroups; + (*p_groups) = groups; + + DEBUG( 3, ( "%s is in %d groups: ", user, ngroups ) ); + for (i = 0; i < ngroups; i++ ) + { + DEBUG( 3, ( "%s%d", (i ? ", " : ""), (int)groups[i] ) ); + } + DEBUG( 3, ( "\n" ) ); + + return 0; +} + + +/**************************************************************************** +register a uid/name pair as being valid and that a valid password +has been given. vuid is biased by an offset. This allows us to +tell random client vuid's (normally zero) from valid vuids. +****************************************************************************/ +uint16 register_vuid(uid_t uid,gid_t gid, char *unix_name, char *requested_name, BOOL guest) +{ + user_struct *vuser; + struct passwd *pwfile; /* for getting real name from passwd file */ + + /* Ensure no vuid gets registered in share level security. */ + if(lp_security() == SEC_SHARE) + return UID_FIELD_INVALID; + +#if 0 + /* + * After observing MS-Exchange services writing to a Samba share + * I belive this code is incorrect. Each service does its own + * sessionsetup_and_X for the same user, and as each service shuts + * down, it does a user_logoff_and_X. As we are consolidating multiple + * sessionsetup_and_X's onto the same vuid here, when the first service + * shuts down, it invalidates all the open files for the other services. + * Hence I am removing this code and forcing each sessionsetup_and_X + * to get a new vuid. + * Jeremy Allison. (jallison@whistle.com). + */ + + int i; + for(i = 0; i < num_validated_users; i++) { + vuser = &validated_users[i]; + if ( vuser->uid == uid ) + return (uint16)(i + VUID_OFFSET); /* User already validated */ + } +#endif + + validated_users = (user_struct *)Realloc(validated_users, + sizeof(user_struct)* + (num_validated_users+1)); + + if (!validated_users) + { + DEBUG(0,("Failed to realloc users struct!\n")); + num_validated_users = 0; + return UID_FIELD_INVALID; + } + + vuser = &validated_users[num_validated_users]; + num_validated_users++; + + vuser->uid = uid; + vuser->gid = gid; + vuser->guest = guest; + fstrcpy(vuser->name,unix_name); + fstrcpy(vuser->requested_name,requested_name); + + vuser->n_sids = 0; + vuser->sids = NULL; + + vuser->n_groups = 0; + vuser->groups = NULL; + + /* Find all the groups this uid is in and store them. + Used by become_user() */ + setup_groups(unix_name,uid,gid, + &vuser->n_groups, + &vuser->groups); + + DEBUG(3,("uid %d registered to name %s\n",(int)uid,unix_name)); + + DEBUG(3, ("Clearing default real name\n")); + fstrcpy(vuser->real_name, "<Full Name>\0"); + if (lp_unix_realname()) { + if ((pwfile=sys_getpwnam(vuser->name))!= NULL) + { + DEBUG(3, ("User name: %s\tReal name: %s\n",vuser->name,pwfile->pw_gecos)); + fstrcpy(vuser->real_name, pwfile->pw_gecos); + } + } + + return (uint16)((num_validated_users - 1) + VUID_OFFSET); +} + /**************************************************************************** add a name to the session users list @@ -71,12 +327,17 @@ static BOOL update_smbpassword_file(char *user, char *password) DEBUG(0,("getsmbpwnam returned NULL\n")); return False; } - + + /* + * Remove the account disabled flag - we are updating the + * users password from a login. + */ + smbpw->acct_ctrl &= ~ACB_DISABLED; + /* Here, the flag is one, because we want to ignore the XXXXXXX'd out password */ ret = change_oem_password( smbpw, password, True); - if (!ret) - { + if (ret == False) { DEBUG(3,("change_oem_password returned False\n")); } @@ -88,16 +349,201 @@ static BOOL update_smbpassword_file(char *user, char *password) /**************************************************************************** +core of smb password checking routine. +****************************************************************************/ +BOOL smb_password_check(char *password, unsigned char *part_passwd, unsigned char *c8) +{ + /* Finish the encryption of part_passwd. */ + unsigned char p21[21]; + unsigned char p24[24]; + + if (part_passwd == NULL) + DEBUG(10,("No password set - allowing access\n")); + /* No password set - always true ! */ + if (part_passwd == NULL) + return 1; + + memset(p21,'\0',21); + memcpy(p21,part_passwd,16); + E_P24(p21, c8, p24); +#if DEBUG_PASSWORD + { + int i; + DEBUG(100,("Part password (P16) was |")); + for(i = 0; i < 16; i++) + DEBUG(100,("%X ", (unsigned char)part_passwd[i])); + DEBUG(100,("|\n")); + DEBUG(100,("Password from client was |")); + for(i = 0; i < 24; i++) + DEBUG(100,("%X ", (unsigned char)password[i])); + DEBUG(100,("|\n")); + DEBUG(100,("Given challenge was |")); + for(i = 0; i < 8; i++) + DEBUG(100,("%X ", (unsigned char)c8[i])); + DEBUG(100,("|\n")); + DEBUG(100,("Value from encryption was |")); + for(i = 0; i < 24; i++) + DEBUG(100,("%X ", (unsigned char)p24[i])); + DEBUG(100,("|\n")); + } +#endif + return (memcmp(p24, password, 24) == 0); +} + +/**************************************************************************** + Do a specific test for an smb password being correct, given a smb_password and + the lanman and NT responses. +****************************************************************************/ +BOOL smb_password_ok(struct smb_passwd *smb_pass, uchar chal[8], + uchar lm_pass[24], uchar nt_pass[24]) +{ + uchar challenge[8]; + + if (!lm_pass || !smb_pass) return(False); + + DEBUG(4,("Checking SMB password for user %s\n", + smb_pass->smb_name)); + + if(smb_pass->acct_ctrl & ACB_DISABLED) { + DEBUG(1,("account for user %s was disabled.\n", + smb_pass->smb_name)); + return(False); + } + + if (chal == NULL) + { + DEBUG(5,("use last SMBnegprot challenge\n")); + if (!last_challenge(challenge)) + { + DEBUG(1,("no challenge done - password failed\n")); + return False; + } + } + else + { + DEBUG(5,("challenge received\n")); + memcpy(challenge, chal, 8); + } + + if ((Protocol >= PROTOCOL_NT1) && (smb_pass->smb_nt_passwd != NULL)) { + /* We have the NT MD4 hash challenge available - see if we can + use it (ie. does it exist in the smbpasswd file). + */ + DEBUG(4,("smb_password_ok: Checking NT MD4 password\n")); + if (smb_password_check((char *)nt_pass, + (uchar *)smb_pass->smb_nt_passwd, + challenge)) { + DEBUG(4,("NT MD4 password check succeeded\n")); + return(True); + } + DEBUG(4,("NT MD4 password check failed\n")); + } + + /* Try against the lanman password. smb_pass->smb_passwd == NULL means + no password, allow access. */ + + DEBUG(4,("Checking LM MD4 password\n")); + + if((smb_pass->smb_passwd == NULL) && + (smb_pass->acct_ctrl & ACB_PWNOTREQ)) { + DEBUG(4,("no password required for user %s\n", + smb_pass->smb_name)); + return True; + } + + if((smb_pass->smb_passwd != NULL) && + smb_password_check((char *)lm_pass, + (uchar *)smb_pass->smb_passwd, challenge)) { + DEBUG(4,("LM MD4 password check succeeded\n")); + return(True); + } + + DEBUG(4,("LM MD4 password check failed\n")); + + return False; +} + + +/**************************************************************************** +check if a username/password is OK assuming the password is a 24 byte +SMB hash +return True if the password is correct, False otherwise +****************************************************************************/ + +BOOL pass_check_smb(char *user, char *domain, + uchar *chal, uchar *lm_pwd, uchar *nt_pwd, + struct passwd *pwd) +{ + struct passwd *pass; + struct smb_passwd *smb_pass; + + if (!lm_pwd || !nt_pwd) + { + return(False); + } + + if (pwd != NULL && user == NULL) + { + pass = (struct passwd *) pwd; + user = pass->pw_name; + } + else + { + pass = Get_Pwnam(user,True); + } + + if (pass == NULL) + { + DEBUG(1,("Couldn't find user '%s' in UNIX password database.\n",user)); + return(False); + } + + smb_pass = getsmbpwnam(user); + + if (smb_pass == NULL) + { + DEBUG(1,("Couldn't find user '%s' in smb_passwd file.\n", user)); + return(False); + } + + /* Quit if the account was disabled. */ + if(smb_pass->acct_ctrl & ACB_DISABLED) { + DEBUG(1,("Account for user '%s' was disabled.\n", user)); + return(False); + } + + /* Ensure the uid's match */ + if (smb_pass->smb_userid != pass->pw_uid) + { + DEBUG(0,("Error : UNIX and SMB uids in password files do not match for user '%s'!\n", user)); + return(False); + } + + if (lm_pwd[0] == '\0' && IS_BITS_SET_ALL(smb_pass->acct_ctrl, ACB_PWNOTREQ) && lp_null_passwords()) + { + DEBUG(3,("Account for user '%s' has no password and null passwords are allowed.\n", smb_pass->smb_name)); + return(True); + } + + if (smb_password_ok(smb_pass, chal, lm_pwd, nt_pwd)) + { + return(True); + } + + DEBUG(2,("pass_check_smb failed - invalid password for user [%s]\n", user)); + return False; +} + +/**************************************************************************** check if a username/password pair is OK either via the system password database or the encrypted SMB password database return True if the password is correct, False otherwise ****************************************************************************/ -BOOL password_ok(char *user, char *password, int pwlen, struct passwd *pwd, - uchar user_sess_key[16]) +BOOL password_ok(char *user, char *password, int pwlen, struct passwd *pwd) { - if (pwlen >= 24 || (lp_encrypted_passwords() && (pwlen == 0) && lp_null_passwords())) + if (pwlen == 24 || (lp_encrypted_passwords() && (pwlen == 0) && lp_null_passwords())) { - /* if 24 bytes or longer assume it is an encrypted password */ + /* if 24 bytes long assume it is an encrypted password */ uchar challenge[8]; if (!last_challenge(challenge)) @@ -106,10 +552,8 @@ BOOL password_ok(char *user, char *password, int pwlen, struct passwd *pwd, return False; } - return pass_check_smb(getsmbpwnam(user), global_myworkgroup, - challenge, (uchar *)password, - pwlen, (uchar *)password, pwlen, - pwd, user_sess_key); + return pass_check_smb(user, global_myworkgroup, + challenge, (uchar *)password, (uchar *)password, pwd); } return pass_check(user, password, pwlen, pwd, @@ -117,71 +561,84 @@ BOOL password_ok(char *user, char *password, int pwlen, struct passwd *pwd, update_smbpassword_file : NULL); } +/**************************************************************************** +check if a username is valid +****************************************************************************/ +BOOL user_ok(char *user,int snum) +{ + pstring valid, invalid; + BOOL ret; + + StrnCpy(valid, lp_valid_users(snum), sizeof(pstring)-1); + StrnCpy(invalid, lp_invalid_users(snum), sizeof(pstring)-1); + + pstring_sub(valid,"%S",lp_servicename(snum)); + pstring_sub(invalid,"%S",lp_servicename(snum)); + + ret = !user_in_list(user,invalid); + + if (ret && valid && *valid) { + ret = user_in_list(user,valid); + } + + if (ret && lp_onlyuser(snum)) { + char *user_list = lp_username(snum); + pstring_sub(user_list,"%S",lp_servicename(snum)); + ret = user_in_list(user,user_list); + } + + return(ret); +} + /**************************************************************************** validate a group username entry. Return the username or NULL ****************************************************************************/ -static char *validate_group(char *group,char *password,int pwlen,int snum, - uchar user_sess_key[16]) +static char *validate_group(char *group,char *password,int pwlen,int snum) { -#if defined(HAVE_NETGROUP) && defined(HAVE_GETNETGRENT) && defined(HAVE_SETNETGRENT) && defined(HAVE_ENDNETGRENT) - { - char *host, *user, *domain; - setnetgrent(group); - while (getnetgrent(&host, &user, &domain)) { - if (user) { - if (user_ok(user, snum) && - password_ok(user,password,pwlen,NULL, user_sess_key)) { - endnetgrent(); - return(user); +#ifdef HAVE_NETGROUP + { + char *host, *user, *domain; + setnetgrent(group); + while (getnetgrent(&host, &user, &domain)) { + if (user) { + if (user_ok(user, snum) && + password_ok(user,password,pwlen,NULL)) { + endnetgrent(); + return(user); + } + } + } + endnetgrent(); } - } - } - endnetgrent(); - } #endif -#ifdef HAVE_GETGRNAM - { - struct group *gptr = (struct group *)getgrnam(group); - char **member; - if (gptr) - { - member = gptr->gr_mem; - while (member && *member) - { - static fstring name; - fstrcpy(name,*member); - if (user_ok(name,snum) && - password_ok(name,password,pwlen,NULL, user_sess_key)) - return(&name[0]); - member++; - } -#ifdef GROUP_CHECK_PWENT +#ifdef HAVE_GETGRENT { - struct passwd *pwd; - static fstring tm; - - setpwent (); - while (pwd = getpwent ()) { - if (*(pwd->pw_passwd) && pwd->pw_gid == gptr->gr_gid) { - /* This Entry have PASSWORD and same GID then check pwd */ - if (password_ok(NULL, password, pwlen, pwd, user_sess_key)) { - fstrcpy(tm, pwd->pw_name); - endpwent (); - return tm; - } - } - } - endpwent (); + struct group *gptr; + char **member; + setgrent(); + while ((gptr = (struct group *)getgrent())) { + if (!strequal(gptr->gr_name,group)) + continue; + member = gptr->gr_mem; + while (member && *member) { + static fstring name; + fstrcpy(name,*member); + if (user_ok(name,snum) && + password_ok(name,password,pwlen,NULL)) { + endgrent(); + return(&name[0]); + } + member++; + } + } + endgrent(); } -#endif /* GROUP_CHECK_PWENT */ - } - } #endif - return(NULL); + return(NULL); } @@ -221,14 +678,14 @@ BOOL authorise_login(int snum,char *user,char *password, int pwlen, /* check the given username and password */ if (!ok && (*user) && user_ok(user,snum)) { - ok = password_ok(user,password, pwlen, NULL, vuser->user_sess_key); + ok = password_ok(user,password, pwlen, NULL); if (ok) DEBUG(3,("ACCEPTED: given username password ok\n")); } /* check for a previously registered guest username */ if (!ok && (vuser != 0) && vuser->guest) { if (user_ok(vuser->name,snum) && - password_ok(vuser->name, password, pwlen, NULL, vuser->user_sess_key)) { + password_ok(vuser->name, password, pwlen, NULL)) { fstrcpy(user, vuser->name); vuser->guest = False; DEBUG(3,("ACCEPTED: given password with registered user %s\n", user)); @@ -242,7 +699,7 @@ BOOL authorise_login(int snum,char *user,char *password, int pwlen, { char *auser; char *user_list = strdup(session_users); - if (!user_list) return False; + if (!user_list) return(False); for (auser=strtok(user_list,LIST_SEP); !ok && auser; @@ -252,7 +709,7 @@ BOOL authorise_login(int snum,char *user,char *password, int pwlen, fstrcpy(user2,auser); if (!user_ok(user2,snum)) continue; - if (password_ok(user2,password, pwlen, NULL, vuser->user_sess_key)) { + if (password_ok(user2,password, pwlen, NULL)) { ok = True; fstrcpy(user,user2); DEBUG(3,("ACCEPTED: session list username and given password ok\n")); @@ -283,7 +740,7 @@ BOOL authorise_login(int snum,char *user,char *password, int pwlen, pstring user_list; StrnCpy(user_list,lp_username(snum),sizeof(pstring)); - string_sub(user_list,"%S",lp_servicename(snum)); + pstring_sub(user_list,"%S",lp_servicename(snum)); for (auser=strtok(user_list,LIST_SEP); auser && !ok; @@ -291,7 +748,7 @@ BOOL authorise_login(int snum,char *user,char *password, int pwlen, { if (*auser == '@') { - auser = validate_group(auser+1,password,pwlen,snum, vuser->user_sess_key); + auser = validate_group(auser+1,password,pwlen,snum); if (auser) { ok = True; @@ -304,7 +761,7 @@ BOOL authorise_login(int snum,char *user,char *password, int pwlen, fstring user2; fstrcpy(user2,auser); if (user_ok(user2,snum) && - password_ok(user2,password,pwlen,NULL, vuser->user_sess_key)) + password_ok(user2,password,pwlen,NULL)) { ok = True; fstrcpy(user,user2); @@ -454,10 +911,10 @@ BOOL check_hosts_equiv(char *user) { char *fname = NULL; pstring rhostsfile; - const struct passwd *pass = Get_Pwnam(user,True); + struct passwd *pass = Get_Pwnam(user,True); if (!pass) - return False; + return(False); fname = lp_hosts_equiv(); @@ -470,7 +927,7 @@ BOOL check_hosts_equiv(char *user) if (lp_use_rhosts()) { - char *home = get_unixhome_dir(user); + char *home = get_user_home_dir(user); if (home) { extern int Client; slprintf(rhostsfile, sizeof(rhostsfile)-1, "%s/.rhosts", home); @@ -479,13 +936,14 @@ BOOL check_hosts_equiv(char *user) } } - return False; + return(False); } /**************************************************************************** -return the client state structure + Return the client state structure. ****************************************************************************/ + struct cli_state *server_client(void) { static struct cli_state pw_cli; @@ -493,20 +951,77 @@ struct cli_state *server_client(void) } /**************************************************************************** -support for server level security + Support for server level security. ****************************************************************************/ + struct cli_state *server_cryptkey(void) { - if (cli_connect_serverlist(server_client(), lp_passwordserver())) - { - return server_client(); + struct cli_state *cli; + fstring desthost; + struct in_addr dest_ip; + char *p; + BOOL connected_ok = False; + + cli = server_client(); + + if (!cli_initialise(cli)) + return NULL; + + p = lp_passwordserver(); + while(p && next_token( &p, desthost, LIST_SEP, sizeof(desthost))) { + standard_sub_basic(desthost); + strupper(desthost); + + if(!resolve_name( desthost, &dest_ip, 0x20)) { + DEBUG(1,("server_cryptkey: Can't resolve address for %s\n",desthost)); + continue; + } + + if (ismyip(dest_ip)) { + DEBUG(1,("Password server loop - disabling password server %s\n",desthost)); + continue; + } + + if (cli_connect(cli, desthost, &dest_ip)) { + DEBUG(3,("connected to password server %s\n",desthost)); + connected_ok = True; + break; + } + } + + if (!connected_ok) { + DEBUG(0,("password server not available\n")); + cli_shutdown(cli); + return NULL; } - return NULL; + + if (!attempt_netbios_session_request(cli, global_myname, desthost, &dest_ip)) + return NULL; + + DEBUG(3,("got session\n")); + + if (!cli_negprot(cli)) { + DEBUG(1,("%s rejected the negprot\n",desthost)); + cli_shutdown(cli); + return NULL; + } + + if (cli->protocol < PROTOCOL_LANMAN2 || + !(cli->sec_mode & 1)) { + DEBUG(1,("%s isn't in user level security mode\n",desthost)); + cli_shutdown(cli); + return NULL; + } + + DEBUG(3,("password server OK\n")); + + return cli; } /**************************************************************************** -validate a password with the password server + Validate a password with the password server. ****************************************************************************/ + BOOL server_validate(char *user, char *domain, char *pass, int passlen, char *ntpass, int ntpasslen) @@ -520,7 +1035,7 @@ BOOL server_validate(char *user, char *domain, if (!cli->initialised) { DEBUG(1,("password server %s is not connected\n", cli->desthost)); - return False; + return(False); } if(badpass[0] == 0) @@ -541,8 +1056,7 @@ BOOL server_validate(char *user, char *domain, */ if(!tested_password_server) { - if (cli_session_setup(cli, global_myname, - user, (char *)badpass, sizeof(badpass), + if (cli_session_setup(cli, user, (char *)badpass, sizeof(badpass), (char *)badpass, sizeof(badpass), domain)) { /* @@ -587,8 +1101,7 @@ use this machine as the password server.\n")); * not guest enabled, we can try with the real password. */ - if (!cli_session_setup(cli, global_myname, - user, pass, passlen, ntpass, ntpasslen, domain)) { + if (!cli_session_setup(cli, user, pass, passlen, ntpass, ntpasslen, domain)) { DEBUG(1,("password server %s rejected the password\n", cli->desthost)); return False; } @@ -597,12 +1110,365 @@ use this machine as the password server.\n")); if ((SVAL(cli->inbuf,smb_vwv2) & 1) != 0) { DEBUG(1,("password server %s gave us guest only\n", cli->desthost)); cli_ulogoff(cli); - return False; + return(False); } - cli_ulogoff(cli); return(True); } +/*********************************************************************** + Connect to a remote machine for domain security authentication + given a name or IP address. +************************************************************************/ + +static BOOL connect_to_domain_password_server(struct cli_state *pcli, char *remote_machine, + unsigned char *trust_passwd) +{ + struct in_addr dest_ip; + + if(cli_initialise(pcli) == False) { + DEBUG(0,("connect_to_domain_password_server: unable to initialize client connection.\n")); + return False; + } + + standard_sub_basic(remote_machine); + strupper(remote_machine); + + if(!resolve_name( remote_machine, &dest_ip, 0x20)) { + DEBUG(1,("connect_to_domain_password_server: Can't resolve address for %s\n", remote_machine)); + cli_shutdown(pcli); + return False; + } + + if (ismyip(dest_ip)) { + DEBUG(1,("connect_to_domain_password_server: Password server loop - not using password server %s\n", + remote_machine)); + cli_shutdown(pcli); + return False; + } + + if (!cli_connect(pcli, remote_machine, &dest_ip)) { + DEBUG(0,("connect_to_domain_password_server: unable to connect to SMB server on \ +machine %s. Error was : %s.\n", remote_machine, cli_errstr(pcli) )); + cli_shutdown(pcli); + return False; + } + + if (!attempt_netbios_session_request(pcli, global_myname, remote_machine, &dest_ip)) { + DEBUG(0,("connect_to_password_server: machine %s rejected the NetBIOS \ +session request. Error was : %s.\n", remote_machine, cli_errstr(pcli) )); + return False; + } + + pcli->protocol = PROTOCOL_NT1; + + if (!cli_negprot(pcli)) { + DEBUG(0,("connect_to_domain_password_server: machine %s rejected the negotiate protocol. \ +Error was : %s.\n", remote_machine, cli_errstr(pcli) )); + cli_shutdown(pcli); + return False; + } + + if (pcli->protocol != PROTOCOL_NT1) { + DEBUG(0,("connect_to_domain_password_server: machine %s didn't negotiate NT protocol.\n", + remote_machine)); + cli_shutdown(pcli); + return False; + } + + /* + * Do an anonymous session setup. + */ + + if (!cli_session_setup(pcli, "", "", 0, "", 0, "")) { + DEBUG(0,("connect_to_domain_password_server: machine %s rejected the session setup. \ +Error was : %s.\n", remote_machine, cli_errstr(pcli) )); + cli_shutdown(pcli); + return False; + } + + if (!(pcli->sec_mode & 1)) { + DEBUG(1,("connect_to_domain_password_server: machine %s isn't in user level security mode\n", + remote_machine)); + cli_shutdown(pcli); + return False; + } + + if (!cli_send_tconX(pcli, "IPC$", "IPC", "", 1)) { + DEBUG(0,("connect_to_domain_password_server: machine %s rejected the tconX on the IPC$ share. \ +Error was : %s.\n", remote_machine, cli_errstr(pcli) )); + cli_shutdown(pcli); + return False; + } + + /* + * We now have an anonymous connection to IPC$ on the domain password server. + */ + + /* + * Even if the connect succeeds we need to setup the netlogon + * pipe here. We do this as we may just have changed the domain + * account password on the PDC and yet we may be talking to + * a BDC that doesn't have this replicated yet. In this case + * a successful connect to a DC needs to take the netlogon connect + * into account also. This patch from "Bjart Kvarme" <bjart.kvarme@usit.uio.no>. + */ + + if(cli_nt_session_open(pcli, PIPE_NETLOGON) == False) { + DEBUG(0,("connect_to_domain_password_server: unable to open the domain client session to \ +machine %s. Error was : %s.\n", remote_machine, cli_errstr(pcli))); + cli_nt_session_close(pcli); + cli_ulogoff(pcli); + cli_shutdown(pcli); + return False; + } + + if (cli_nt_setup_creds(pcli, trust_passwd) == False) { + DEBUG(0,("connect_to_domain_password_server: unable to setup the PDC credentials to machine \ +%s. Error was : %s.\n", remote_machine, cli_errstr(pcli))); + cli_nt_session_close(pcli); + cli_ulogoff(pcli); + cli_shutdown(pcli); + return(False); + } + + return True; +} + +/*********************************************************************** + Utility function to attempt a connection to an IP address of a DC. +************************************************************************/ + +static BOOL attempt_connect_to_dc(struct cli_state *pcli, struct in_addr *ip, unsigned char *trust_passwd) +{ + fstring dc_name; + + /* + * Ignore addresses we have already tried. + */ + + if(ip_equal(ipzero, *ip)) + return False; + + if(!lookup_pdc_name(global_myname, lp_workgroup(), ip, dc_name)) + return False; + + return connect_to_domain_password_server(pcli, dc_name, trust_passwd); +} + +/*********************************************************************** + Do the same as security=server, but using NT Domain calls and a session + key from the machine password. +************************************************************************/ + +BOOL domain_client_validate( char *user, char *domain, + char *smb_apasswd, int smb_apasslen, + char *smb_ntpasswd, int smb_ntpasslen, + BOOL *user_exists) +{ + unsigned char local_challenge[8]; + unsigned char local_lm_response[24]; + unsigned char local_nt_reponse[24]; + unsigned char trust_passwd[16]; + fstring remote_machine; + char *p; + NET_ID_INFO_CTR ctr; + NET_USER_INFO_3 info3; + struct cli_state cli; + uint32 smb_uid_low; + BOOL connected_ok = False; + + if(user_exists != NULL) + *user_exists = True; /* Only set false on a very specific error. */ + + /* + * Check that the requested domain is not our own machine name. + * If it is, we should never check the PDC here, we use our own local + * password file. + */ + + if(strequal( domain, global_myname)) { + DEBUG(3,("domain_client_validate: Requested domain was for this machine.\n")); + return False; + } + + /* + * Next, check that the passwords given were encrypted. + */ + + if(((smb_apasslen != 24) && (smb_apasslen != 0)) || + ((smb_ntpasslen != 24) && (smb_ntpasslen != 0))) { + + /* + * Not encrypted - do so. + */ + + DEBUG(3,("domain_client_validate: User passwords not in encrypted format.\n")); + generate_random_buffer( local_challenge, 8, False); + SMBencrypt( (uchar *)smb_apasswd, local_challenge, local_lm_response); + SMBNTencrypt((uchar *)smb_ntpasswd, local_challenge, local_nt_reponse); + smb_apasslen = 24; + smb_ntpasslen = 24; + smb_apasswd = (char *)local_lm_response; + smb_ntpasswd = (char *)local_nt_reponse; + } else { + + /* + * Encrypted - get the challenge we sent for these + * responses. + */ + + if (!last_challenge(local_challenge)) { + DEBUG(0,("domain_client_validate: no challenge done - password failed\n")); + return False; + } + } + + /* + * Get the machine account password. + */ + if (!trust_get_passwd( trust_passwd, global_myworkgroup, global_myname)) + { + return False; + } + + /* + * At this point, smb_apasswd points to the lanman response to + * the challenge in local_challenge, and smb_ntpasswd points to + * the NT response to the challenge in local_challenge. Ship + * these over the secure channel to a domain controller and + * see if they were valid. + */ + + ZERO_STRUCT(cli); + + /* + * Treat each name in the 'password server =' line as a potential + * PDC/BDC. Contact each in turn and try and authenticate. + */ + + p = lp_passwordserver(); + while(!connected_ok && p && + next_token(&p,remote_machine,LIST_SEP,sizeof(remote_machine))) { + if(strequal(remote_machine, "*")) { + + /* + * We have been asked to dynamcially determine the IP addresses of + * the PDC and BDC's for this DOMAIN, and query them in turn. + */ + + struct in_addr *ip_list = NULL; + int count = 0; + int i; + + if(!get_dc_list(lp_workgroup(), &ip_list, &count)) + continue; + + /* + * Firstly try and contact a PDC/BDC who has the same + * network address as any of our interfaces. + */ + + for(i = 0; i < count; i++) { + if(!is_local_net(ip_list[i])) + continue; + + if((connected_ok = attempt_connect_to_dc(&cli, &ip_list[i], trust_passwd))) + break; + + ip_list[i] = ipzero; /* Tried and failed. */ + } + + /* + * Secondly try and contact a random PDC/BDC. + */ + + if(!connected_ok) { + i = (sys_random() % count); + + if(!(connected_ok = attempt_connect_to_dc(&cli, &ip_list[i], trust_passwd))) + ip_list[i] = ipzero; /* Tried and failed. */ + } + + /* + * Finally go through the IP list in turn, ignoring any addresses + * we have already tried. + */ + + if(!connected_ok) { + + /* + * Try and connect to any of the other IP addresses in the PDC/BDC list. + * Note that from a WINS server the #1 IP address is the PDC. + */ + + for(i = 0; i < count; i++) { + if((connected_ok = attempt_connect_to_dc(&cli, &ip_list[i], trust_passwd))) + break; + } + } + + if(ip_list != NULL) + free((char *)ip_list); + + } else { + connected_ok = connect_to_domain_password_server(&cli, remote_machine, trust_passwd); + } + } + + if (!connected_ok) { + DEBUG(0,("domain_client_validate: Domain password server not available.\n")); + cli_shutdown(&cli); + return False; + } + + /* We really don't care what LUID we give the user. */ + generate_random_buffer( (unsigned char *)&smb_uid_low, 4, False); + + if(cli_nt_login_network(&cli, domain, user, smb_uid_low, (char *)local_challenge, + ((smb_apasslen != 0) ? smb_apasswd : NULL), + ((smb_ntpasslen != 0) ? smb_ntpasswd : NULL), + &ctr, &info3) == False) { + uint32 nt_rpc_err; + + cli_error(&cli, NULL, NULL, &nt_rpc_err); + DEBUG(0,("domain_client_validate: unable to validate password for user %s in domain \ +%s to Domain controller %s. Error was %s.\n", user, domain, remote_machine, cli_errstr(&cli))); + cli_nt_session_close(&cli); + cli_ulogoff(&cli); + cli_shutdown(&cli); + + if((nt_rpc_err == NT_STATUS_NO_SUCH_USER) && (user_exists != NULL)) + *user_exists = False; + + return False; + } + + /* + * Here, if we really want it, we have lots of info about the user in info3. + */ + +#if 0 + /* + * We don't actually need to do this - plus it fails currently with + * NT_STATUS_INVALID_INFO_CLASS - we need to know *exactly* what to + * send here. JRA. + */ + + if(cli_nt_logoff(&cli, &ctr) == False) { + DEBUG(0,("domain_client_validate: unable to log off user %s in domain \ +%s to Domain controller %s. Error was %s.\n", user, domain, remote_machine, cli_errstr(&cli))); + cli_nt_session_close(&cli); + cli_ulogoff(&cli); + cli_shutdown(&cli); + return False; + } +#endif /* 0 */ + + cli_nt_session_close(&cli); + cli_ulogoff(&cli); + cli_shutdown(&cli); + return True; +} diff --git a/source3/smbd/pipes.c b/source3/smbd/pipes.c index e20d049834..15f52c0af6 100644 --- a/source3/smbd/pipes.c +++ b/source3/smbd/pipes.c @@ -74,10 +74,16 @@ int reply_open_pipe_and_X(connection_struct *conn, /* Strip \PIPE\ off the name. */ pstrcpy(fname,smb_buf(inbuf) + PIPELEN); + /* + * Hack for NT printers... JRA. + */ + if(should_fail_next_srvsvc_open(fname)) + return(ERROR(ERRSRV,ERRaccess)); + /* Known pipes arrive with DIR attribs. Remove it so a regular file */ /* can be opened and add it in after the open. */ DEBUG(3,("Known pipe %s opening.\n",fname)); - smb_ofun |= 0x10; /* Add Create it not exists flag */ + smb_ofun |= FILE_CREATE_IF_NOT_EXIST; p = open_rpc_pipe_p(fname, conn, vuid); if (!p) return(ERROR(ERRSRV,ERRnofids)); @@ -105,45 +111,37 @@ int reply_open_pipe_and_X(connection_struct *conn, } /**************************************************************************** - reply to a write - - This code is basically stolen from reply_write with some - wrinkles to handle pipes. + reply to a write on a pipe ****************************************************************************/ -int reply_pipe_write(char *inbuf,char *outbuf,int length,int bufsize) +int reply_pipe_write(char *inbuf,char *outbuf,int length,int dum_bufsize) { pipes_struct *p = get_rpc_pipe_p(inbuf,smb_vwv0); size_t numtowrite = SVAL(inbuf,smb_vwv1); - int nwritten = -1; + int nwritten; + int outsize; char *data; - size_t outsize; - if (!p) return(ERROR(ERRDOS,ERRbadfid)); + if (!p) + return(ERROR(ERRDOS,ERRbadfid)); data = smb_buf(inbuf) + 3; if (numtowrite == 0) - { nwritten = 0; - } else - { - nwritten = write_pipe(p, data, numtowrite); - } + nwritten = write_to_pipe(p, data, numtowrite); if ((nwritten == 0 && numtowrite != 0) || (nwritten < 0)) - { return (UNIXERROR(ERRDOS,ERRnoaccess)); - } outsize = set_message(outbuf,1,0,True); SSVAL(outbuf,smb_vwv0,nwritten); - + DEBUG(3,("write-IPC pnum=%04x nwritten=%d\n", p->pnum, nwritten)); - return outsize; + return(outsize); } /**************************************************************************** @@ -160,23 +158,18 @@ int reply_pipe_write_and_X(char *inbuf,char *outbuf,int length,int bufsize) int smb_doff = SVAL(inbuf, smb_vwv11); char *data; - if (!p) return(ERROR(ERRDOS,ERRbadfid)); + if (!p) + return(ERROR(ERRDOS,ERRbadfid)); data = smb_base(inbuf) + smb_doff; if (numtowrite == 0) - { nwritten = 0; - } else - { - nwritten = write_pipe(p, data, numtowrite); - } + nwritten = write_to_pipe(p, data, numtowrite); if ((nwritten == 0 && numtowrite != 0) || (nwritten < 0)) - { return (UNIXERROR(ERRDOS,ERRnoaccess)); - } set_message(outbuf,6,0,True); @@ -197,18 +190,24 @@ int reply_pipe_write_and_X(char *inbuf,char *outbuf,int length,int bufsize) int reply_pipe_read_and_X(char *inbuf,char *outbuf,int length,int bufsize) { pipes_struct *p = get_rpc_pipe_p(inbuf,smb_vwv2); - uint32 smb_offs = IVAL(inbuf,smb_vwv3); int smb_maxcnt = SVAL(inbuf,smb_vwv5); int smb_mincnt = SVAL(inbuf,smb_vwv6); int nread = -1; char *data; + /* we don't use the offset given to use for pipe reads. This + is deliberate, instead we always return the next lump of + data on the pipe */ +#if 0 + uint32 smb_offs = IVAL(inbuf,smb_vwv3); +#endif - if (!p) return(ERROR(ERRDOS,ERRbadfid)); + if (!p) + return(ERROR(ERRDOS,ERRbadfid)); set_message(outbuf,12,0,True); data = smb_buf(outbuf); - nread = read_pipe(p, data, smb_offs, smb_maxcnt); + nread = read_from_pipe(p, data, smb_maxcnt); if (nread < 0) return(UNIXERROR(ERRDOS,ERRnoaccess)); @@ -231,12 +230,13 @@ int reply_pipe_close(connection_struct *conn, char *inbuf,char *outbuf) pipes_struct *p = get_rpc_pipe_p(inbuf,smb_vwv0); int outsize = set_message(outbuf,0,0,True); - if (!p) return(ERROR(ERRDOS,ERRbadfid)); + if (!p) + return(ERROR(ERRDOS,ERRbadfid)); DEBUG(5,("reply_pipe_close: pnum:%x\n", p->pnum)); - if (!close_rpc_pipe_hnd(p, conn)) return(ERROR(ERRDOS,ERRbadfid)); + if (!close_rpc_pipe_hnd(p, conn)) + return(ERROR(ERRDOS,ERRbadfid)); return(outsize); } - diff --git a/source3/smbd/predict.c b/source3/smbd/predict.c index f51e54a568..de85a4a553 100644 --- a/source3/smbd/predict.c +++ b/source3/smbd/predict.c @@ -37,12 +37,12 @@ static int rp_timeout = 5; static time_t rp_time = 0; static char *rp_buffer = NULL; static BOOL predict_skip=False; -extern time_t smb_last_time; +extern struct timeval smb_last_time; /**************************************************************************** handle read prediction on a file ****************************************************************************/ -ssize_t read_predict(files_struct *fsp, int fd,SMB_OFF_T offset,char *buf,char **ptr,size_t num) +ssize_t read_predict(int fd,SMB_OFF_T offset,char *buf,char **ptr,size_t num) { ssize_t ret = 0; ssize_t possible = rp_length - (offset - rp_offset); @@ -53,7 +53,7 @@ ssize_t read_predict(files_struct *fsp, int fd,SMB_OFF_T offset,char *buf,char * if (fd == rp_fd && offset >= rp_offset && possible>0 && - smb_last_time-rp_time < rp_timeout) + smb_last_time.tv_secs - rp_time < rp_timeout) { ret = possible; if (buf) @@ -70,7 +70,7 @@ ssize_t read_predict(files_struct *fsp, int fd,SMB_OFF_T offset,char *buf,char * /* Find the end of the file - ensure we don't read predict beyond it. */ - if(fsp->conn->vfs_ops.fstat(fd,&rp_stat) < 0) + if(sys_fstat(fd,&rp_stat) < 0) { DEBUG(0,("read-prediction failed on fstat. Error was %s\n", strerror(errno))); predict_skip = True; @@ -95,7 +95,7 @@ ssize_t read_predict(files_struct *fsp, int fd,SMB_OFF_T offset,char *buf,char * /**************************************************************************** pre-read some data ****************************************************************************/ -void do_read_prediction(connection_struct *conn) +void do_read_prediction(void) { static size_t readsize = 0; @@ -134,13 +134,13 @@ void do_read_prediction(connection_struct *conn) } } - if (conn->vfs_ops.lseek(rp_fd,rp_offset,SEEK_SET) != rp_offset) { + if (sys_lseek(rp_fd,rp_offset,SEEK_SET) != rp_offset) { rp_fd = -1; rp_predict_fd = -1; return; } - rp_length = conn->vfs_ops.read(rp_fd,rp_buffer,rp_predict_length); + rp_length = read(rp_fd,rp_buffer,rp_predict_length); rp_time = time(NULL); if (rp_length < 0) rp_length = 0; diff --git a/source3/smbd/process.c b/source3/smbd/process.c index 95222d3f51..7e94ffa173 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -23,7 +23,7 @@ extern int DEBUGLEVEL; -time_t smb_last_time=(time_t)0; +struct timeval smb_last_time; char *InBuffer = NULL; char *OutBuffer = NULL; @@ -48,7 +48,7 @@ extern char *last_inbuf; extern char *InBuffer; extern char *OutBuffer; extern int smb_read_error; -extern BOOL reload_after_sighup; +extern VOLATILE SIG_ATOMIC_T reload_after_sighup; extern BOOL global_machine_password_needs_changing; extern fstring global_myworkgroup; extern pstring global_myname; @@ -173,7 +173,7 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, to.tv_sec = timeout / 1000; to.tv_usec = (timeout % 1000) * 1000; - selrtn = sys_select(MAX(maxfd,Client)+1,&fds,NULL, timeout>0?&to:NULL); + selrtn = sys_select(MAX(maxfd,Client)+1,&fds,timeout>0?&to:NULL); /* Check if error */ if(selrtn == -1) { @@ -231,6 +231,52 @@ BOOL receive_next_smb(char *inbuf, int bufsize, int timeout) return ret; } +/**************************************************************************** + We're terminating and have closed all our files/connections etc. + If there are any pending local messages we need to respond to them + before termination so that other smbds don't think we just died whilst + holding oplocks. +****************************************************************************/ + +void respond_to_all_remaining_local_messages(void) +{ + char buffer[1024]; + fd_set fds; + + /* + * Assert we have no exclusive open oplocks. + */ + + if(get_number_of_exclusive_open_oplocks()) { + DEBUG(0,("respond_to_all_remaining_local_messages: PANIC : we have %d exclusive oplocks.\n", + get_number_of_exclusive_open_oplocks() )); + return; + } + + /* + * Setup the select read fd set. + */ + + FD_ZERO(&fds); + if(!setup_oplock_select_set(&fds)) + return; + + /* + * Keep doing receive_local_message with a 1 ms timeout until + * we have no more messages. + */ + + while(receive_local_message(&fds, buffer, sizeof(buffer), 1)) { + /* Deal with oplock break requests from other smbd's. */ + process_local_message(buffer, sizeof(buffer)); + + FD_ZERO(&fds); + (void)setup_oplock_select_set(&fds); + } + + return; +} + /* These flags determine some of the permissions required to do an operation @@ -283,8 +329,8 @@ struct smb_message_struct {SMBunlink,"SMBunlink",reply_unlink,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK}, {SMBread,"SMBread",reply_read,AS_USER}, - {SMBwrite,"SMBwrite",reply_write,AS_USER | CAN_IPC}, - {SMBclose,"SMBclose",reply_close,AS_USER | CAN_IPC}, + {SMBwrite,"SMBwrite",reply_write,AS_USER | CAN_IPC }, + {SMBclose,"SMBclose",reply_close,AS_USER | CAN_IPC }, {SMBmkdir,"SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE}, {SMBrmdir,"SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE}, {SMBdskattr,"SMBdskattr",reply_dskattr,AS_USER}, @@ -319,9 +365,9 @@ struct smb_message_struct {SMBwriteBmpx,"SMBwriteBmpx",reply_writebmpx,AS_USER}, {SMBwriteBs,"SMBwriteBs",reply_writebs,AS_USER}, {SMBwritec,"SMBwritec",NULL,AS_USER}, - {SMBsetattrE,"SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE}, - {SMBgetattrE,"SMBgetattrE",reply_getattrE,AS_USER}, - {SMBtrans,"SMBtrans",reply_trans,AS_USER | CAN_IPC}, + {SMBsetattrE,"SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE }, + {SMBgetattrE,"SMBgetattrE",reply_getattrE,AS_USER }, + {SMBtrans,"SMBtrans",reply_trans,AS_USER | CAN_IPC | QUEUE_IN_OPLOCK}, {SMBtranss,"SMBtranss",NULL,AS_USER | CAN_IPC}, {SMBioctls,"SMBioctls",NULL,AS_USER}, {SMBcopy,"SMBcopy",reply_copy,AS_USER | NEED_WRITE | QUEUE_IN_OPLOCK }, @@ -330,7 +376,7 @@ struct smb_message_struct {SMBopenX,"SMBopenX",reply_open_and_X,AS_USER | CAN_IPC | QUEUE_IN_OPLOCK }, {SMBreadX,"SMBreadX",reply_read_and_X,AS_USER | CAN_IPC }, {SMBwriteX,"SMBwriteX",reply_write_and_X,AS_USER | CAN_IPC }, - {SMBlockingX,"SMBlockingX",reply_lockingX,AS_USER}, + {SMBlockingX,"SMBlockingX",reply_lockingX,AS_USER }, {SMBffirst,"SMBffirst",reply_search,AS_USER}, {SMBfunique,"SMBfunique",reply_search,AS_USER}, @@ -339,14 +385,14 @@ struct smb_message_struct /* LANMAN2.0 PROTOCOL FOLLOWS */ {SMBfindnclose, "SMBfindnclose", reply_findnclose, AS_USER}, {SMBfindclose, "SMBfindclose", reply_findclose,AS_USER}, - {SMBtrans2, "SMBtrans2", reply_trans2, AS_USER | CAN_IPC}, + {SMBtrans2, "SMBtrans2", reply_trans2, AS_USER | QUEUE_IN_OPLOCK }, {SMBtranss2, "SMBtranss2", reply_transs2, AS_USER}, /* NT PROTOCOL FOLLOWS */ {SMBntcreateX, "SMBntcreateX", reply_ntcreate_and_X, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK }, - {SMBnttrans, "SMBnttrans", reply_nttrans, AS_USER | CAN_IPC }, + {SMBnttrans, "SMBnttrans", reply_nttrans, AS_USER | CAN_IPC | QUEUE_IN_OPLOCK}, {SMBnttranss, "SMBnttranss", reply_nttranss, AS_USER | CAN_IPC }, - {SMBntcancel, "SMBntcancel", reply_ntcancel, AS_USER }, + {SMBntcancel, "SMBntcancel", reply_ntcancel, 0 }, /* messaging routines */ {SMBsends,"SMBsends",reply_sends,AS_GUEST}, @@ -368,14 +414,14 @@ do a switch on the message type, and return the response size ****************************************************************************/ static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize) { - static int pid= -1; + static pid_t pid= (pid_t)-1; int outsize = 0; static int num_smb_messages = sizeof(smb_messages) / sizeof(struct smb_message_struct); int match; extern int Client; - if (pid == -1) + if (pid == (pid_t)-1) pid = getpid(); errno = 0; @@ -399,21 +445,25 @@ static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize } else { - DEBUG(3,("switch message %s (pid %d)\n",smb_messages[match].name,pid)); + DEBUG(3,("switch message %s (pid %d)\n",smb_messages[match].name,(int)pid)); - if(global_oplock_break && (smb_messages[match].flags & QUEUE_IN_OPLOCK)) + if(global_oplock_break) { - /* - * Queue this message as we are the process of an oplock break. - */ + int flags = smb_messages[match].flags; - DEBUG( 2, ( "switch_message: queueing message due to being in " ) ); - DEBUGADD( 2, ( "oplock break state.\n" ) ); + if(flags & QUEUE_IN_OPLOCK) + { + /* + * Queue this message as we are the process of an oplock break. + */ - push_oplock_pending_smb_message( inbuf, size ); - return -1; - } + DEBUG( 2, ( "switch_message: queueing message due to being in " ) ); + DEBUGADD( 2, ( "oplock break state.\n" ) ); + push_oplock_pending_smb_message( inbuf, size ); + return -1; + } + } if (smb_messages[match].fn) { int flags = smb_messages[match].flags; @@ -508,7 +558,7 @@ static int construct_reply(char *inbuf,char *outbuf,int size,int bufsize) int msg_type = CVAL(inbuf,0); extern int chain_size; - smb_last_time = time(NULL); + GetTimeOfDay(&smb_last_time); chain_size = 0; file_chain_reset(); @@ -640,7 +690,7 @@ char *smb_fn_name(int type) void construct_reply_common(char *inbuf,char *outbuf) { - bzero(outbuf,smb_size); + memset(outbuf,'\0',smb_size); set_message(outbuf,0,0,True); CVAL(outbuf,smb_com) = CVAL(inbuf,smb_com); @@ -687,6 +737,14 @@ int chain_reply(char *inbuf,char *outbuf,int size,int bufsize) orig_outbuf = outbuf; } + /* + * The original Win95 redirector dies on a reply to + * a lockingX and read chain unless the chain reply is + * 4 byte aligned. JRA. + */ + + outsize = (outsize + 3) & ~3; + /* we need to tell the client where the next part of the reply will be */ SSVAL(outbuf,smb_vwv1,smb_offset(outbuf+outsize,outbuf)); CVAL(outbuf,smb_vwv0) = smb_com2; @@ -741,234 +799,271 @@ int chain_reply(char *inbuf,char *outbuf,int size,int bufsize) } /**************************************************************************** - process commands from the client + Setup the needed select timeout. ****************************************************************************/ -void smbd_process(void) + +static int setup_select_timeout(void) { - extern int Client; - extern int ClientPort; + int change_notify_timeout = lp_change_notify_timeout() * 1000; + int select_timeout; - InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); - OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); - if ((InBuffer == NULL) || (OutBuffer == NULL)) - return; + /* + * Increase the select timeout back to SMBD_SELECT_TIMEOUT if we + * have removed any blocking locks. JRA. + */ - InBuffer += SMB_ALIGNMENT; - OutBuffer += SMB_ALIGNMENT; + select_timeout = blocking_locks_pending() ? SMBD_SELECT_TIMEOUT_WITH_PENDING_LOCKS*1000 : + SMBD_SELECT_TIMEOUT*1000; + + if (change_notifies_pending()) + select_timeout = MIN(select_timeout, change_notify_timeout); + + return select_timeout; +} + +/**************************************************************************** + Check if services need reloading. +****************************************************************************/ + +void check_reload(int t) +{ + static time_t last_smb_conf_reload_time = 0; + + if(last_smb_conf_reload_time == 0) + last_smb_conf_reload_time = t; -#if PRIME_NMBD - DEBUG(3,("priming nmbd\n")); + if (reload_after_sighup || (t >= last_smb_conf_reload_time+SMBD_RELOAD_CHECK)) { - struct in_addr ip; - ip = *interpret_addr2("localhost"); - if (zero_ip(ip)) ip = *interpret_addr2("127.0.0.1"); - *OutBuffer = 0; - send_one_packet(OutBuffer,1,ip,NMB_PORT,SOCK_DGRAM); + reload_services(True); + reload_after_sighup = False; + last_smb_conf_reload_time = t; } -#endif +} +/**************************************************************************** + Process any timeout housekeeping. Return False if the caler should exit. +****************************************************************************/ - max_recv = MIN(lp_maxxmit(),BUFFER_SIZE); +static BOOL timeout_processing(int deadtime, int *select_timeout, time_t *last_timeout_processing_time) +{ + extern int Client; + static time_t last_keepalive_sent_time = 0; + static time_t last_idle_closed_check = 0; + time_t t; + BOOL allidle = True; + extern int keepalive; - /* re-initialise the timezone */ - TimeInit(); + if (smb_read_error == READ_EOF) + { + DEBUG(3,("end of file from client\n")); + return False; + } - /* if connection on port 445, fake session setup... */ - if(ClientPort == 445) + if (smb_read_error == READ_ERROR) { - extern fstring remote_machine; - extern fstring local_machine; + DEBUG(3,("receive_smb error (%s) exiting\n", + strerror(errno))); + return False; + } - fstrcpy(remote_machine, dns_to_netbios_name(client_name(Client))); - fstrcpy(local_machine, global_myname); - remote_machine[15] = 0; - local_machine[15] = 0; - strlower(remote_machine); - strlower(local_machine); + *last_timeout_processing_time = t = time(NULL); - DEBUG(2, ("smbd_process(): faking session setup\n" - "client_name: %s my_name: %s\n", remote_machine, local_machine)); + if(last_keepalive_sent_time == 0) + last_keepalive_sent_time = t; - add_session_user(remote_machine); + if(last_idle_closed_check == 0) + last_idle_closed_check = t; - reload_services(True); - reopen_logs(); + /* become root again if waiting */ + unbecome_user(); - if(lp_status(-1)) { - claim_connection(NULL,"STATUS.",MAXSTATUS,True); - } + /* check if we need to reload services */ + check_reload(t); + + /* automatic timeout if all connections are closed */ + if (conn_num_open()==0 && (t - last_idle_closed_check) >= IDLE_CLOSED_TIMEOUT) + { + DEBUG( 2, ( "Closing idle connection\n" ) ); + return False; } + else + last_idle_closed_check = t; - while (True) + if (keepalive && (t - last_keepalive_sent_time)>keepalive) { - int deadtime = lp_deadtime()*60; - int counter; - int last_keepalive=0; - int service_load_counter = 0; - BOOL got_smb = False; + struct cli_state *cli = server_client(); + if (!send_keepalive(Client)) { + DEBUG( 2, ( "Keepalive failed - exiting.\n" ) ); + return False; + } + /* also send a keepalive to the password server if its still + connected */ + if (cli && cli->initialised) + send_keepalive(cli->fd); + last_keepalive_sent_time = t; + } - if (deadtime <= 0) - deadtime = DEFAULT_SMBD_TIMEOUT; + /* check for connection timeouts */ + allidle = conn_idle_all(t, deadtime); -#if USE_READ_PREDICTION - if (lp_readprediction()) - do_read_prediction(); -#endif + if (allidle && conn_num_open()>0) { + DEBUG(2,("Closing idle connection 2.\n")); + return False; + } - errno = 0; + if(global_machine_password_needs_changing) + { + unsigned char trust_passwd_hash[16]; + time_t lct; + pstring remote_machine_list; + + /* + * We're in domain level security, and the code that + * read the machine password flagged that the machine + * password needs changing. + */ + + /* + * First, open the machine password file with an exclusive lock. + */ + + if(!trust_password_lock( global_myworkgroup, global_myname, True)) { + DEBUG(0,("process: unable to open the machine account password file for \ +machine %s in domain %s.\n", global_myname, global_myworkgroup )); + return True; + } - for (counter=SMBD_SELECT_LOOP; - !receive_message_or_smb(InBuffer,BUFFER_SIZE, - SMBD_SELECT_LOOP*1000,&got_smb); - counter += SMBD_SELECT_LOOP) - { - time_t t; - BOOL allidle = True; - extern int keepalive; + if(!get_trust_account_password( trust_passwd_hash, &lct)) { + DEBUG(0,("process: unable to read the machine account password for \ +machine %s in domain %s.\n", global_myname, global_myworkgroup )); + trust_password_unlock(); + return True; + } - if (counter > 365 * 3600) /* big number of seconds. */ - { - counter = 0; - service_load_counter = 0; - } + /* + * Make sure someone else hasn't already done this. + */ - if (smb_read_error == READ_EOF) - { - DEBUG(3,("end of file from client\n")); - return; - } + if(t < lct + lp_machine_password_timeout()) { + trust_password_unlock(); + global_machine_password_needs_changing = False; + return True; + } - if (smb_read_error == READ_ERROR) - { - DEBUG(3,("receive_smb error (%s) exiting\n", - strerror(errno))); - return; - } + pstrcpy(remote_machine_list, lp_passwordserver()); - t = time(NULL); + change_trust_account_password( global_myworkgroup, remote_machine_list); + trust_password_unlock(); + global_machine_password_needs_changing = False; + } - /* become root again if waiting */ - unbecome_user(); + /* + * Check to see if we have any blocking locks + * outstanding on the queue. + */ + process_blocking_lock_queue(t); - /* check for smb.conf reload */ - if (counter >= service_load_counter + SMBD_RELOAD_CHECK) - { - service_load_counter = counter; + /* + * Check to see if we have any change notifies + * outstanding on the queue. + */ + process_pending_change_notify_queue(t); - /* reload services, if files have changed. */ - reload_services(True); - } + /* + * Modify the select timeout depending upon + * what we have remaining in our queues. + */ - /* - * If reload_after_sighup == True then we got a SIGHUP - * and are being asked to reload. Fix from <branko.cibej@hermes.si> - */ + *select_timeout = setup_select_timeout(); - if (reload_after_sighup) - { - DEBUG(0,("Reloading services after SIGHUP\n")); - reload_services(False); - reload_after_sighup = False; - /* - * Use this as an excuse to print some stats. - */ - print_stat_cache_statistics(); - } + return True; +} - /* automatic timeout if all connections are closed */ - if (conn_num_open()==0 && counter >= IDLE_CLOSED_TIMEOUT) - { - DEBUG( 2, ( "Closing idle connection\n" ) ); - return; - } +/**************************************************************************** + process commands from the client +****************************************************************************/ - if (keepalive && (counter-last_keepalive)>keepalive) - { - struct cli_state *cli = server_client(); - if (!send_keepalive(Client)) { - DEBUG( 2, ( "Keepalive failed - exiting.\n" ) ); - return; - } - /* also send a keepalive to the password server if its still - connected */ - if (cli && cli->initialised) - send_keepalive(cli->fd); - last_keepalive = counter; - } +void smbd_process(void) +{ + extern int smb_echo_count; + time_t last_timeout_processing_time = time(NULL); + unsigned int num_smbs = 0; + + InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + if ((InBuffer == NULL) || (OutBuffer == NULL)) + return; - /* check for connection timeouts */ - allidle = conn_idle_all(t, deadtime); + InBuffer += SMB_ALIGNMENT; + OutBuffer += SMB_ALIGNMENT; - if (allidle && conn_num_open()>0) { - DEBUG(2,("Closing idle connection 2.\n")); - return; - } + max_recv = MIN(lp_maxxmit(),BUFFER_SIZE); - if(global_machine_password_needs_changing) - { - unsigned char trust_passwd_hash[16]; - time_t lct; - pstring remote_machine_list; - int sec_chan = SEC_CHAN_WKSTA; - - /* - * We're in domain level security, and the code that - * read the machine password flagged that the machine - * password needs changing. - */ + /* re-initialise the timezone */ + TimeInit(); - /* - * First, open the machine password file with an exclusive lock. - */ + while (True) + { + int deadtime = lp_deadtime()*60; + BOOL got_smb = False; + int select_timeout = setup_select_timeout(); - if(!trust_password_lock( global_myworkgroup, global_myname, True)) { - DEBUG(0,("process: unable to open the machine account password file for \ -machine %s in domain %s.\n", global_myname, global_myworkgroup )); - continue; - } + if (deadtime <= 0) + deadtime = DEFAULT_SMBD_TIMEOUT; - if(!get_trust_account_password( trust_passwd_hash, &lct)) { - DEBUG(0,("process: unable to read the machine account password for \ -machine %s in domain %s.\n", global_myname, global_myworkgroup )); - trust_password_unlock(); - continue; - } +#if USE_READ_PREDICTION + if (lp_readprediction()) + do_read_prediction(); +#endif - /* - * Make sure someone else hasn't already done this. - */ + errno = 0; - if(t < lct + lp_machine_password_timeout()) { - trust_password_unlock(); - global_machine_password_needs_changing = False; - continue; - } + while(!receive_message_or_smb(InBuffer,BUFFER_SIZE,select_timeout,&got_smb)) + { + if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time)) + return; + num_smbs = 0; /* Reset smb counter. */ + } - pstrcpy(remote_machine_list, lp_passwordserver()); - if (lp_server_role() == ROLE_DOMAIN_BDC) - sec_chan = SEC_CHAN_BDC; + if(got_smb) { + /* + * Ensure we do timeout processing if the SMB we just got was + * only an echo request. This allows us to set the select + * timeout in 'receive_message_or_smb()' to any value we like + * without worrying that the client will send echo requests + * faster than the select timeout, thus starving out the + * essential processing (change notify, blocking locks) that + * the timeout code does. JRA. + */ + int num_echos = smb_echo_count; + + process_smb(InBuffer, OutBuffer); - change_trust_account_password(global_myworkgroup, remote_machine_list, - sec_chan); - trust_password_unlock(); - global_machine_password_needs_changing = False; + if(smb_echo_count != num_echos) { + if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time)) + return; + num_smbs = 0; /* Reset smb counter. */ } - /* - * Check to see if we have any blocking locks - * outstanding on the queue. - */ - process_blocking_lock_queue(t); + num_smbs++; /* - * Check to see if we have any change notifies - * outstanding on the queue. + * If we are getting smb requests in a constant stream + * with no echos, make sure we attempt timeout processing + * every select_timeout milliseconds - but only check for this + * every 200 smb requests. */ - process_pending_change_notify_queue(t); - } - if(got_smb) - process_smb(InBuffer, OutBuffer); + if((num_smbs % 200) == 0) { + time_t new_check_time = time(NULL); + if(last_timeout_processing_time - new_check_time >= (select_timeout/1000)) { + if(!timeout_processing( deadtime, &select_timeout, &last_timeout_processing_time)) + return; + num_smbs = 0; /* Reset smb counter. */ + last_timeout_processing_time = new_check_time; /* Reset time. */ + } + } + } else process_local_message(InBuffer, BUFFER_SIZE); } diff --git a/source3/smbd/quotas.c b/source3/smbd/quotas.c index afabb1befd..badc0562bc 100644 --- a/source3/smbd/quotas.c +++ b/source3/smbd/quotas.c @@ -47,7 +47,6 @@ try to get the disk space from disk quotas (LINUX version) BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) { - uid_t euser_id; int r; struct dqblk D; SMB_STRUCT_STAT S; @@ -55,6 +54,9 @@ BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_U struct mntent *mnt; SMB_DEV_T devno; int found; + uid_t euser_id; + + euser_id = geteuid(); /* find the block device file */ @@ -81,10 +83,10 @@ BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_U return(False); } - euser_id=geteuid(); - seteuid(0); + save_re_uid(); + set_effective_uid(0); r=quotactl(QCMD(Q_GETQUOTA,USRQUOTA), mnt->mnt_fsname, euser_id, (caddr_t)&D); - seteuid(euser_id); + restore_re_uid(); /* Use softlimit to determine disk space, except when it has been exceeded */ *bsize = 1024; @@ -212,11 +214,10 @@ BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_U *dsize = request.qf_entry.user_q.f_use ; - if ( *dfree ) - *dfree -= *dsize ; - - if ( *dfree < 0 ) + if ( *dfree < *dsize ) *dfree = 0 ; + else + *dfree -= *dsize ; *bsize = 4096 ; /* Cray blocksize */ @@ -244,7 +245,7 @@ Quota code by Peter Urbanec (amiga@cse.unsw.edu.au). BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) { - uid_t user_id, euser_id; + uid_t euser_id; int ret; struct dqblk D; #if defined(SUNOS5) @@ -261,6 +262,8 @@ BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_U SMB_DEV_T devno ; static SMB_DEV_T devno_cached = 0 ; int found ; + + euser_id = geteuid(); if ( sys_stat(path,&sbuf) == -1 ) return(False) ; @@ -312,18 +315,14 @@ BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_U return(False) ; } - euser_id = geteuid(); - user_id = getuid(); - - setuid(0); /* Solaris seems to want to give info only to super-user */ - seteuid(0); + save_re_uid(); + set_effective_uid(0); #if defined(SUNOS5) DEBUG(5,("disk_quotas: looking for quotas file \"%s\"\n", name)); if((file=sys_open(name, O_RDONLY,0))<0) { - setuid(user_id); /* Restore the original UID status */ - seteuid(euser_id); - return(False); + restore_re_uid(); + return(False); } command.op = Q_GETQUOTA; command.uid = euser_id; @@ -335,8 +334,7 @@ BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_U ret = quotactl(Q_GETQUOTA, name, euser_id, &D); #endif - setuid(user_id); /* Restore the original uid status. */ - seteuid(euser_id); + restore_re_uid(); if (ret < 0) { DEBUG(5,("disk_quotas ioctl (Solaris) failed. Error = %s\n", strerror(errno) )); @@ -353,14 +351,13 @@ BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_U if (D.dqb_bsoftlimit==0) return(False); *bsize = DEV_BSIZE; - *dfree = D.dqb_bsoftlimit - D.dqb_curblocks; *dsize = D.dqb_bsoftlimit; - if(*dfree < 0) - { + if (D.dqb_curblocks > D.dqb_bsoftlimit) { *dfree = 0; *dsize = D.dqb_curblocks; - } + } else + *dfree = D.dqb_bsoftlimit - D.dqb_curblocks; DEBUG(5,("disk_quotas for path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n", path,(double)*bsize,(double)*dfree,(double)*dsize)); @@ -378,21 +375,26 @@ try to get the disk space from disk quotas - OSF1 version BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) { - uid_t user_id, euser_id; int r, save_errno; struct dqblk D; SMB_STRUCT_STAT S; + uid_t euser_id; + /* + * This code presumes that OSF1 will only + * give out quota info when the real uid + * matches the effective uid. JRA. + */ euser_id = geteuid(); - user_id = getuid(); + save_re_uid(); + if (set_re_uid() != 0) return False; - setreuid(euser_id, -1); r= quotactl(path,QCMD(Q_GETQUOTA, USRQUOTA),euser_id,(char *) &D); - if (r) + if (r) { save_errno = errno; + } - if (setreuid(user_id, -1) == -1) - DEBUG(5,("Unable to reset uid to %d\n", user_id)); + restore_re_uid(); *bsize = DEV_BSIZE; @@ -423,7 +425,7 @@ BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_U return (True); } -#elif defined (SGI6) +#elif defined (IRIX6) /**************************************************************************** try to get the disk space from disk quotas (IRIX 6.2 version) ****************************************************************************/ @@ -469,7 +471,8 @@ BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_U } euser_id=geteuid(); - seteuid(0); + save_re_uid(); + set_effective_uid(0); /* Use softlimit to determine disk space, except when it has been exceeded */ @@ -479,7 +482,7 @@ BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_U { r=quotactl (Q_GETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &D); - seteuid(euser_id); /* Restore the original uid status. */ + restore_re_uid(); if (r==-1) return(False); @@ -510,7 +513,7 @@ BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_U { r=quotactl (Q_XGETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &F); - seteuid(euser_id); /* Restore the original uid status. */ + restore_re_uid(); if (r==-1) return(False); @@ -539,8 +542,8 @@ BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_U } else { - seteuid(euser_id); /* Restore the original uid status. */ - return(False); + restore_re_uid(); + return(False); } return (True); @@ -570,9 +573,9 @@ try to get the disk space from disk quotas - default version BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) { - uid_t euser_id; int r; struct dqblk D; + uid_t euser_id; #if !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__) char dev_disk[256]; SMB_STRUCT_STAT S; @@ -584,36 +587,32 @@ BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_U euser_id = geteuid(); #ifdef HPUX - { - uid_t user_id; - - /* for HPUX, real uid must be same as euid to execute quotactl for euid */ - user_id = getuid(); - setresuid(euser_id,-1,-1); - r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D); - if (setresuid(user_id,-1,-1)) - DEBUG(5,("Unable to reset uid to %d\n", user_id)); - } + /* for HPUX, real uid must be same as euid to execute quotactl for euid */ + save_re_uid(); + if (set_re_uid() != 0) return False; + + r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D); + + restore_re_uid(); #else #if defined(__FreeBSD__) || defined(__OpenBSD__) { /* FreeBSD patches from Marty Moll <martym@arbor.edu> */ - uid_t user_id; gid_t egrp_id; - /* Need to be root to get quotas in FreeBSD */ - user_id = getuid(); + save_re_uid(); + set_effective_uid(0); + egrp_id = getegid(); - setuid(0); - seteuid(0); r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D); /* As FreeBSD has group quotas, if getting the user quota fails, try getting the group instead. */ - if (r) - r= quotactl(path,QCMD(Q_GETQUOTA,GRPQUOTA),egrp_id,(char *) &D); - setuid(user_id); - seteuid(euser_id); + if (r) { + r= quotactl(path,QCMD(Q_GETQUOTA,GRPQUOTA),egrp_id,(char *) &D); + } + + restore_re_uid(); } #elif defined(AIX) /* AIX has both USER and GROUP quotas: @@ -622,7 +621,7 @@ BOOL disk_quotas(char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_U #else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */ r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D); #endif /* !__FreeBSD__ && !AIX && !__OpenBSD__ */ -#endif /* HAVE_SETRES */ +#endif /* HPUX */ /* Use softlimit to determine disk space, except when it has been exceeded */ #if defined(__FreeBSD__) || defined(__OpenBSD__) diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index fed82b5e54..54f646e091 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -38,77 +38,33 @@ extern BOOL case_sensitive; extern BOOL case_preserve; extern BOOL short_case_preserve; extern pstring sesssetup_user; +extern pstring global_myname; extern fstring global_myworkgroup; -extern fstring global_myname; extern int Client; extern int global_oplock_break; uint32 global_client_caps = 0; - +unsigned int smb_echo_count = 0; /**************************************************************************** report a possible attack via the password buffer overflow bug ****************************************************************************/ + static void overflow_attack(int len) { - if( DEBUGLVL( 0 ) ) - { - dbgtext( "ERROR: Invalid password length %d.\n", len ); - dbgtext( "Your machine may be under attack by someone " ); - dbgtext( "attempting to exploit an old bug.\n" ); - dbgtext( "Attack was from IP = %s.\n", client_addr(Client) ); - } + if( DEBUGLVL( 0 ) ) { + dbgtext( "ERROR: Invalid password length %d.\n", len ); + dbgtext( "Your machine may be under attack by someone " ); + dbgtext( "attempting to exploit an old bug.\n" ); + dbgtext( "Attack was from IP = %s.\n", client_addr(Client) ); + } exit_server("possible attack"); } /**************************************************************************** - does _both_ nt->unix and unix->unix username remappings. -****************************************************************************/ -static void map_nt_and_unix_username(const char *domain, char *user) -{ - DOM_NAME_MAP gmep; - fstring nt_username; - - /* - * Pass the user through the NT -> unix user mapping - * function. - */ - - if (lp_server_role() != ROLE_DOMAIN_NONE) - { - memset(nt_username, 0, sizeof(nt_username)); - if (domain != NULL) - { - slprintf(nt_username, sizeof(nt_username)-1, "%s\\%s", - domain, user); - } - else - { - fstrcpy(nt_username, user); - } - - if (lookupsmbpwntnam(nt_username, &gmep)) - { - fstrcpy(user, gmep.unix_name); - } - } - - /* - * Pass the user through the unix -> unix user mapping - * function. - */ - - (void)map_username(user); - - /* - * Do any UNIX username case mangling. - */ - (void)Get_Pwnam( user, True); -} - -/**************************************************************************** reply to an special message ****************************************************************************/ + int reply_special(char *inbuf,char *outbuf) { int outsize = 4; @@ -122,13 +78,10 @@ int reply_special(char *inbuf,char *outbuf) *name1 = *name2 = 0; - bzero(outbuf,smb_size); + memset(outbuf,'\0',smb_size); smb_setlen(outbuf,0); - DEBUG(20,("NBT message\n")); - dump_data(20, inbuf, smb_len(inbuf)); - switch (msg_type) { case 0x81: /* session request */ CVAL(outbuf,0) = 0x82; @@ -202,11 +155,11 @@ int reply_special(char *inbuf,char *outbuf) /******************************************************************* work out what error to give to a failed connection ********************************************************************/ + static int connection_error(char *inbuf,char *outbuf,int ecode) { - if (ecode == ERRnoipc) { + if (ecode == ERRnoipc) return(ERROR(ERRDOS,ERRnoipc)); - } return(ERROR(ERRSRV,ecode)); } @@ -247,15 +200,14 @@ static void parse_connect(char *p,char *service,char *user, } } - - - /**************************************************************************** - reply to a tcon + Reply to a tcon. ****************************************************************************/ + int reply_tcon(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { + BOOL doencrypt = SMBENCRYPT(); pstring service; pstring user; pstring password; @@ -269,7 +221,25 @@ int reply_tcon(connection_struct *conn, parse_connect(smb_buf(inbuf)+1,service,user,password,&pwlen,dev); - map_nt_and_unix_username(global_myworkgroup, user); + /* + * Ensure the user and password names are in UNIX codepage format. + */ + + dos_to_unix(user,True); + if (!doencrypt) + dos_to_unix(password,True); + + /* + * Pass the user through the NT -> unix user mapping + * function. + */ + + (void)map_username(user); + + /* + * Do any UNIX username case mangling. + */ + (void)Get_Pwnam( user, True); conn = make_connection(service,user,password,pwlen,dev,vuid,&ecode); @@ -288,16 +258,17 @@ int reply_tcon(connection_struct *conn, return(outsize); } - /**************************************************************************** - reply to a tcon and X + Reply to a tcon and X. ****************************************************************************/ + int reply_tcon_and_X(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize) { pstring service; pstring user; pstring password; pstring devicename; + BOOL doencrypt = SMBENCRYPT(); int ecode = -1; uint16 vuid = SVAL(inbuf,smb_uid); int passlen = SVAL(inbuf,smb_vwv3); @@ -314,7 +285,7 @@ int reply_tcon_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt if (passlen > MAX_PASS_LEN) { overflow_attack(passlen); } - + memcpy(password,smb_buf(inbuf),passlen); password[passlen]=0; path = smb_buf(inbuf) + passlen; @@ -339,8 +310,26 @@ int reply_tcon_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt StrnCpy(devicename,path + strlen(path) + 1,6); DEBUG(4,("Got device type %s\n",devicename)); - map_nt_and_unix_username(global_myworkgroup, user); + /* + * Ensure the user and password names are in UNIX codepage format. + */ + + dos_to_unix(user,True); + if (!doencrypt) + dos_to_unix(password,True); + /* + * Pass the user through the NT -> unix user mapping + * function. + */ + + (void)map_username(user); + + /* + * Do any UNIX username case mangling. + */ + (void)Get_Pwnam(user, True); + conn = make_connection(service,user,password,passlen,devicename,vuid,&ecode); if (!conn) @@ -397,14 +386,39 @@ int reply_unknown(char *inbuf,char *outbuf) int reply_ioctl(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { - 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 + uint16 device = SVAL(inbuf,smb_vwv1); + uint16 function = SVAL(inbuf,smb_vwv2); + uint32 ioctl_code = (device << 16) + function; + int replysize, outsize; + char *p; + + DEBUG(4, ("Received IOCTL (code 0x%x)\n", ioctl_code)); + + switch (ioctl_code) + { + case IOCTL_QUERY_JOB_INFO: + replysize = 32; + break; + default: + return(ERROR(ERRSRV,ERRnosupport)); + } + + outsize = set_message(outbuf,8,replysize+1,True); + SSVAL(outbuf,smb_vwv1,replysize); /* Total data bytes returned */ + SSVAL(outbuf,smb_vwv5,replysize); /* Data bytes this buffer */ + SSVAL(outbuf,smb_vwv6,52); /* Offset to data */ + p = smb_buf(outbuf) + 1; /* Allow for alignment */ + + switch (ioctl_code) + { + case IOCTL_QUERY_JOB_INFO: + SSVAL(p,0,1); /* Job number */ + StrnCpy(p+2, global_myname, 15); /* Our NetBIOS name */ + StrnCpy(p+18, lp_servicename(SNUM(conn)), 13); /* Service name */ + break; + } + + return outsize; } /**************************************************************************** @@ -415,7 +429,6 @@ static int session_trust_account(connection_struct *conn, char *inbuf, char *out char *smb_nt_passwd, int smb_nt_passlen) { struct smb_passwd *smb_trust_acct = NULL; /* check if trust account exists */ - uchar last_chal[8]; if (lp_security() == SEC_USER) { smb_trust_acct = getsmbpwnam(user); @@ -442,10 +455,8 @@ static int session_trust_account(connection_struct *conn, char *inbuf, char *out SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); return(ERROR(0, 0xc0000000|NT_STATUS_LOGON_FAILURE)); } - if (!last_challenge(last_chal) || - !smb_password_ok(smb_trust_acct, last_chal, NULL, NULL, - (unsigned char *)smb_passwd, smb_passlen, - (unsigned char *)smb_nt_passwd, smb_nt_passlen, NULL)) + + if (!smb_password_ok(smb_trust_acct, NULL, (unsigned char *)smb_passwd, (unsigned char *)smb_nt_passwd)) { DEBUG(0,("session_trust_account: Trust Account %s - password failed\n", user)); SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); @@ -480,19 +491,154 @@ static int session_trust_account(connection_struct *conn, char *inbuf, char *out } /**************************************************************************** + Create a UNIX user on demand. +****************************************************************************/ + +static int smb_create_user(char *unix_user) +{ + pstring add_script; + int ret; + + pstrcpy(add_script, lp_adduser_script()); + pstring_sub(add_script, "%u", unix_user); + ret = smbrun(add_script,NULL,False); + DEBUG(3,("smb_create_user: Running the command `%s' gave %d\n",add_script,ret)); + return ret; +} + +/**************************************************************************** + Delete a UNIX user on demand. +****************************************************************************/ + +static int smb_delete_user(char *unix_user) +{ + pstring del_script; + int ret; + + pstrcpy(del_script, lp_deluser_script()); + pstring_sub(del_script, "%u", unix_user); + ret = smbrun(del_script,NULL,False); + DEBUG(3,("smb_delete_user: Running the command `%s' gave %d\n",del_script,ret)); + return ret; +} + +/**************************************************************************** + Check user is in correct domain if required +****************************************************************************/ + +static BOOL check_domain_match(char *user, char *domain) +{ + /* + * If we aren't serving to trusted domains, we must make sure that + * the validation request comes from an account in the same domain + * as the Samba server + */ + + if (!lp_allow_trusted_domains() && + !strequal(lp_workgroup(), domain) ) { + DEBUG(1, ("check_domain_match: Attempt to connect as user %s from domain %s denied.\n", user, domain)); + return False; + } else { + return True; + } +} + +/**************************************************************************** Check for a valid username and password in security=server mode. ****************************************************************************/ -static BOOL check_server_security(char *orig_user, char *domain, +static BOOL check_server_security(char *orig_user, char *domain, char *unix_user, char *smb_apasswd, int smb_apasslen, char *smb_ntpasswd, int smb_ntpasslen) { + BOOL ret = False; + if(lp_security() != SEC_SERVER) return False; - return server_validate(orig_user, domain, + if (!check_domain_match(orig_user, domain)) + return False; + + ret = server_validate(orig_user, domain, smb_apasswd, smb_apasslen, smb_ntpasswd, smb_ntpasslen); + if(ret) { + /* + * User validated ok against Domain controller. + * If the admin wants us to try and create a UNIX + * user on the fly, do so. + * Note that we can never delete users when in server + * level security as we never know if it was a failure + * due to a bad password, or the user really doesn't exist. + */ + if(lp_adduser_script() && !Get_Pwnam(unix_user,True)) { + smb_create_user(unix_user); + } + } + + return ret; +} + +/**************************************************************************** + Check for a valid username and password in security=domain mode. +****************************************************************************/ + +static BOOL check_domain_security(char *orig_user, char *domain, char *unix_user, + char *smb_apasswd, int smb_apasslen, + char *smb_ntpasswd, int smb_ntpasslen) +{ + BOOL ret = False; + BOOL user_exists = True; + + if(lp_security() != SEC_DOMAIN) + return False; + + if (!check_domain_match(orig_user, domain)) + return False; + + ret = domain_client_validate(orig_user, domain, + smb_apasswd, smb_apasslen, + smb_ntpasswd, smb_ntpasslen, + &user_exists); + + if(ret) { + /* + * User validated ok against Domain controller. + * If the admin wants us to try and create a UNIX + * user on the fly, do so. + */ + if(user_exists && lp_adduser_script() && !Get_Pwnam(unix_user,True)) { + smb_create_user(unix_user); + } + } else { + /* + * User failed to validate ok against Domain controller. + * If the failure was "user doesn't exist" and admin + * wants us to try and delete that UNIX user on the fly, + * do so. + */ + if(!user_exists && lp_deluser_script() && Get_Pwnam(unix_user,True)) { + smb_delete_user(unix_user); + } + } + + return ret; +} + +/**************************************************************************** + Return a bad password error configured for the correct client type. +****************************************************************************/ + +static int bad_password_error(char *inbuf,char *outbuf) +{ + enum remote_arch_types ra_type = get_remote_arch(); + + if(ra_type == RA_WINNT && (global_client_caps & (CAP_NT_SMBS | CAP_STATUS32 ))) { + SSVAL(outbuf,smb_flg2,FLAGS2_32_BIT_ERROR_CODES); + return(ERROR(0,0xc0000000|NT_STATUS_LOGON_FAILURE)); + } + + return(ERROR(ERRSRV,ERRbadpw)); } /**************************************************************************** @@ -502,9 +648,8 @@ reply to a session setup command int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize) { uint16 sess_vuid; - uchar user_sess_key[16]; - int gid; - int uid; + gid_t gid; + uid_t uid; int smb_bufsize; int smb_apasslen = 0; pstring smb_apasswd; @@ -517,63 +662,55 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf,int static BOOL done_sesssetup = False; BOOL doencrypt = SMBENCRYPT(); char *domain = ""; - uchar last_chal[8]; *smb_apasswd = 0; *smb_ntpasswd = 0; smb_bufsize = SVAL(inbuf,smb_vwv2); - if (Protocol < PROTOCOL_NT1) - { + if (Protocol < PROTOCOL_NT1) { smb_apasslen = SVAL(inbuf,smb_vwv7); if (smb_apasslen > MAX_PASS_LEN) - { - overflow_attack(smb_apasslen); - } + overflow_attack(smb_apasslen); memcpy(smb_apasswd,smb_buf(inbuf),smb_apasslen); smb_apasswd[smb_apasslen] = 0; pstrcpy(user,smb_buf(inbuf)+smb_apasslen); - + /* + * Incoming user is in DOS codepage format. Convert + * to UNIX. + */ + dos_to_unix(user,True); + if (!doencrypt && (lp_security() != SEC_SERVER)) { - smb_apasslen = strlen(smb_apasswd); + smb_apasslen = strlen(smb_apasswd); } - - if (lp_server_ntlmv2() == True) - { - DEBUG(1,("NTLMv2-only accepted with NT LANMAN 1.0 and above.\n\ -user %s attempted down-level SMB connection\n", user)); - return(ERROR(ERRSRV,ERRbadpw)); - } - } - else - { + } else { uint16 passlen1 = SVAL(inbuf,smb_vwv7); uint16 passlen2 = SVAL(inbuf,smb_vwv8); enum remote_arch_types ra_type = get_remote_arch(); char *p = smb_buf(inbuf); - global_client_caps = IVAL(inbuf,smb_vwv11); + if(global_client_caps == 0) + global_client_caps = IVAL(inbuf,smb_vwv11); /* 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(ra_type == RA_WINNT || ra_type == RA_WIN95) { if(global_client_caps & (CAP_NT_SMBS | CAP_STATUS32)) set_remote_arch( RA_WINNT); else set_remote_arch( RA_WIN95); } - if (passlen1 != 24 && passlen2 <= 24) + if (passlen1 != 24 && passlen2 != 24) doencrypt = False; if (passlen1 > MAX_PASS_LEN) { - overflow_attack(passlen1); + overflow_attack(passlen1); } passlen1 = MIN(passlen1, MAX_PASS_LEN); @@ -590,18 +727,33 @@ user %s attempted down-level SMB connection\n", user)); 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. + things up. we need to fix that one. */ - LKCLXXXX: the random value can be random 16 bit. old test - used to have ... && passlen <= 24) which of course fails - most of the time. - */ - - if (passlen1 > 0 && passlen2 > 0 && passlen2 != 1) + if (passlen1 > 0 && passlen2 > 0 && passlen2 != 24 && passlen2 != 1) passlen2 = 0; } - if (doencrypt || ((lp_security() == SEC_SERVER) || (lp_security() == SEC_DOMAIN))) { + if (lp_restrict_anonymous()) { + /* there seems to be no reason behind the differences in MS clients formatting + * various info like the domain, NativeOS, and NativeLanMan fields. Win95 + * in particular seems to have an extra null byte between the username and the + * domain, or the password length calculation is wrong, which throws off the + * string extraction routines below. This makes the value of domain be the + * empty string, which fails the restrict anonymous check further down. + * This compensates for that, and allows browsing to work in mixed NT and + * win95 environments even when restrict anonymous is true. AAB + */ + dump_data(100, p, 0x70); + DEBUG(9, ("passlen1=%d, passlen2=%d\n", passlen1, passlen2)); + if (ra_type == RA_WIN95 && !passlen1 && !passlen2 && p[0] == 0 && p[1] == 0) { + DEBUG(0, ("restrict anonymous parameter used in a win95 environment!\n")); + DEBUG(0, ("client is win95 and broken passlen1 offset -- attempting fix\n")); + DEBUG(0, ("if win95 cilents are having difficulty browsing, you will be unable to use restrict anonymous\n")); + passlen1 = 1; + } + } + + if(doencrypt || ((lp_security() == SEC_SERVER) || (lp_security() == SEC_DOMAIN))) { /* Save the lanman2 password and the NT md4 password. */ smb_apasslen = passlen1; memcpy(smb_apasswd,p,smb_apasslen); @@ -609,54 +761,77 @@ user %s attempted down-level SMB connection\n", user)); smb_ntpasslen = passlen2; memcpy(smb_ntpasswd,p+passlen1,smb_ntpasslen); smb_ntpasswd[smb_ntpasslen] = 0; + + /* + * Ensure the plaintext passwords are in UNIX format. + */ + if(!doencrypt) { + dos_to_unix(smb_apasswd,True); + dos_to_unix(smb_ntpasswd,True); + } + } else { /* we use the first password that they gave */ smb_apasslen = passlen1; StrnCpy(smb_apasswd,p,smb_apasslen); + /* + * Ensure the plaintext password is in UNIX format. + */ + dos_to_unix(smb_apasswd,True); /* 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; + smb_apasslen = 0; + *smb_apasswd = 0; } } - if (passlen2 == 0 && smb_apasslen == 0 && ra_type == RA_WIN95) - { - /* work-around for win95 NULL sessions, where NULL password is - actually put in the data stream before the domain name etc */ - p++; - } - else - { - p += passlen1 + passlen2; - } - - fstrcpy(user,p); p = skip_string(p,1); + p += passlen1 + passlen2; + fstrcpy(user,p); + p = skip_string(p,1); + /* + * Incoming user is in DOS codepage format. Convert + * to UNIX. + */ + dos_to_unix(user,True); domain = p; DEBUG(3,("Domain=[%s] NativeOS=[%s] NativeLanMan=[%s]\n", - domain, 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] == '$') && (smb_apasslen == 24) && (smb_ntpasslen == 24)) - { + + if (*user && (user[strlen(user) - 1] == '$') && (smb_apasslen == 24) && (smb_ntpasslen == 24)) { return session_trust_account(conn, inbuf, outbuf, user, smb_apasswd, smb_apasslen, smb_ntpasswd, smb_ntpasslen); } + if (done_sesssetup && lp_restrict_anonymous()) { + /* tests show that even if browsing is done over already validated connections + * without a username and password the domain is still provided, which it + * wouldn't be if it was a purely anonymous connection. So, in order to + * restrict anonymous, we only deny connections that have no session + * information. If a domain has been provided, then it's not a purely + * anonymous connection. AAB + */ + if (!*user && !*smb_apasswd && !*domain) { + DEBUG(0, ("restrict anonymous is True and anonymous connection attempted. Denying access.\n")); + return(ERROR(ERRDOS,ERRnoaccess)); + } + } + /* If no username is sent use the guest account */ - if (!*user) - { + if (!*user) { pstrcpy(user,lp_guestaccount(-1)); /* If no user and no password then set guest flag. */ if( *smb_apasswd == 0) @@ -664,6 +839,7 @@ user %s attempted down-level SMB connection\n", user)); } strlower(user); + /* * In share level security, only overwrite sesssetup_use if * it's a non null-session share. Helps keep %U and %G @@ -672,6 +848,7 @@ user %s attempted down-level SMB connection\n", user)); if((lp_security() != SEC_SHARE) || (*user && !guest)) pstrcpy(sesssetup_user,user); + reload_services(True); /* @@ -682,7 +859,17 @@ user %s attempted down-level SMB connection\n", user)); pstrcpy( orig_user, user); - map_nt_and_unix_username(domain, user); + /* + * Pass the user through the NT -> unix user mapping + * function. + */ + + (void)map_username(user); + + /* + * Do any UNIX username case mangling. + */ + (void)Get_Pwnam( user, True); add_session_user(user); @@ -699,14 +886,12 @@ user %s attempted down-level SMB connection\n", user)); */ if (!guest && - !check_server_security(orig_user, domain, + !check_server_security(orig_user, domain, user, + smb_apasswd, smb_apasslen, + smb_ntpasswd, smb_ntpasslen) && + !check_domain_security(orig_user, domain, user, smb_apasswd, smb_apasslen, smb_ntpasswd, smb_ntpasslen) && - !last_challenge(last_chal) && - !check_domain_security(orig_user, domain, - last_chal, - smb_apasswd, smb_apasslen, - smb_ntpasswd, smb_ntpasslen, user_sess_key) && !check_hosts_equiv(user) ) { @@ -721,25 +906,31 @@ user %s attempted down-level SMB connection\n", user)); * 128 length unicode. */ - if (smb_ntpasslen) + if(smb_ntpasslen) { - if(!password_ok(user, smb_ntpasswd,smb_ntpasslen,NULL,user_sess_key)) - DEBUG(0,("NT Password did not match ! Defaulting to Lanman\n")); + if(!password_ok(user, smb_ntpasswd,smb_ntpasslen,NULL)) + DEBUG(2,("NT Password did not match for user '%s' ! Defaulting to Lanman\n", user)); else valid_nt_password = True; } - if (!valid_nt_password && !password_ok(user, smb_apasswd,smb_apasslen,NULL,user_sess_key)) + if (!valid_nt_password && !password_ok(user, smb_apasswd,smb_apasslen,NULL)) { if (lp_security() >= SEC_USER) { if (lp_map_to_guest() == NEVER_MAP_TO_GUEST) - return(ERROR(ERRSRV,ERRbadpw)); + { + DEBUG(1,("Rejecting user '%s': authentication failed\n", user)); + return bad_password_error(inbuf,outbuf); + } if (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_USER) { - if (Get_Pwnam(user,True)) - return(ERROR(ERRSRV,ERRbadpw)); + if (Get_Pwnam(user,True)) + { + DEBUG(1,("Rejecting user '%s': bad password\n", user)); + return bad_password_error(inbuf,outbuf); + } } /* @@ -765,15 +956,12 @@ user %s attempted down-level SMB connection\n", user)); lp_servicenumber(user) < 0) { int homes = lp_servicenumber(HOMES_NAME); - char *home = get_unixhome_dir(user); + char *home = get_user_home_dir(user); if (homes >= 0 && home) - { - pstring home_dir; - fstrcpy(home_dir, home); - lp_add_home(user,homes,home_dir); - } + lp_add_home(user,homes,home); } + /* it's ok - setup a reply */ if (Protocol < PROTOCOL_NT1) { set_message(outbuf,3,0,True); @@ -793,10 +981,10 @@ user %s attempted down-level SMB connection\n", user)); user we should become. */ { - const struct passwd *pw = Get_Pwnam(user,False); + struct passwd *pw = Get_Pwnam(user,False); if (!pw) { DEBUG(1,("Username %s is invalid on this system\n",user)); - return(ERROR(ERRSRV,ERRbadpw)); + return bad_password_error(inbuf,outbuf); } gid = pw->pw_gid; uid = pw->pw_uid; @@ -807,7 +995,7 @@ user %s attempted down-level SMB connection\n", user)); /* register the name and uid as being validated, so further connections to a uid can get through without a password, on the same VC */ - sess_vuid = register_vuid(uid,gid,user,sesssetup_user,guest,user_sess_key); + sess_vuid = register_vuid(uid,gid,user,sesssetup_user,guest); SSVAL(outbuf,smb_uid,sess_vuid); SSVAL(inbuf,smb_uid,sess_vuid); @@ -836,14 +1024,10 @@ int reply_chkpth(connection_struct *conn, char *inbuf,char *outbuf, int dum_size SMB_STRUCT_STAT st; pstrcpy(name,smb_buf(inbuf) + 1); - if (!unix_dfs_convert(name,conn,0,&bad_path,&st)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(name,conn,0,&bad_path,&st); mode = SVAL(inbuf,smb_vwv0); - + if (check_name(name,conn)) { if(VALID_STAT(st)) ok = S_ISDIR(st.st_mode); @@ -900,7 +1084,7 @@ int reply_getatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size BOOL bad_path = False; pstrcpy(fname,smb_buf(inbuf) + 1); - + /* dos smetimes asks for a stat of "" - it returns a "hidden directory" under WfWg - weird! */ if (! (*fname)) @@ -913,14 +1097,10 @@ int reply_getatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size } else { - if (!unix_dfs_convert(fname,conn,0,&bad_path,&sbuf)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(fname,conn,0,&bad_path,&sbuf); if (check_name(fname,conn)) { - if (VALID_STAT(sbuf) || conn->vfs_ops.stat(dos_to_unix(fname,False),&sbuf) == 0) + if (VALID_STAT(sbuf) || dos_stat(fname,&sbuf) == 0) { mode = dos_mode(conn,fname,&sbuf); size = sbuf.st_size; @@ -982,11 +1162,7 @@ int reply_setatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size BOOL bad_path = False; pstrcpy(fname,smb_buf(inbuf) + 1); - if (!unix_dfs_convert(fname,conn,0,&bad_path,&st)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(fname,conn,0,&bad_path,&st); mode = SVAL(inbuf,smb_vwv0); mtime = make_unix_date3(inbuf+smb_vwv1); @@ -1025,7 +1201,7 @@ int reply_dskattr(connection_struct *conn, char *inbuf,char *outbuf, int dum_siz int outsize = 0; SMB_BIG_UINT dfree,dsize,bsize; - conn->vfs_ops.disk_free(".",&bsize,&dfree,&dsize); + sys_disk_free(".",True,&bsize,&dfree,&dsize); outsize = set_message(outbuf,5,0,True); @@ -1084,74 +1260,70 @@ int reply_search(connection_struct *conn, char *inbuf,char *outbuf, int dum_size /* dirtype &= ~aDIR; */ - DEBUG(5,("path=%s status_len=%d\n",path,status_len)); + DEBUG(5,("reply_search: path=%s status_len=%d\n",path,status_len)); if (status_len == 0) - { - pstring dir2; - - pstrcpy(directory,smb_buf(inbuf)+1); - pstrcpy(dir2,smb_buf(inbuf)+1); - if (!unix_dfs_convert(directory,conn,0,&bad_path,NULL)) { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } - unix_format(dir2); - - if (!check_name(directory,conn)) - can_open = False; + pstring dir2; - p = strrchr(dir2,'/'); - if (p == NULL) - { - pstrcpy(mask,dir2); - *dir2 = 0; - } - else - { - *p = 0; - pstrcpy(mask,p+1); - } + pstrcpy(directory,smb_buf(inbuf)+1); + pstrcpy(dir2,smb_buf(inbuf)+1); + unix_convert(directory,conn,0,&bad_path,NULL); + unix_format(dir2); - p = strrchr(directory,'/'); - if (!p) - *directory = 0; - else - *p = 0; + if (!check_name(directory,conn)) + can_open = False; - if (strlen(directory) == 0) - pstrcpy(directory,"./"); - bzero(status,21); - CVAL(status,0) = dirtype; + p = strrchr(dir2,'/'); + if (p == NULL) + { + pstrcpy(mask,dir2); + *dir2 = 0; } - else + else { - memcpy(status,smb_buf(inbuf) + 1 + strlen(path) + 4,21); - memcpy(mask,status+1,11); - mask[11] = 0; - dirtype = CVAL(status,0) & 0x1F; - conn->dirptr = dptr_fetch(status+12,&dptr_num); - if (!conn->dirptr) - goto SearchEmpty; - string_set(&conn->dirpath,dptr_path(dptr_num)); - if (!case_sensitive) - strnorm(mask); + *p = 0; + pstrcpy(mask,p+1); } + p = strrchr(directory,'/'); + if (!p) + *directory = 0; + else + *p = 0; + + if (strlen(directory) == 0) + pstrcpy(directory,"./"); + memset((char *)status,'\0',21); + CVAL(status,0) = dirtype; + } + else + { + memcpy(status,smb_buf(inbuf) + 1 + strlen(path) + 4,21); + memcpy(mask,status+1,11); + mask[11] = 0; + dirtype = CVAL(status,0) & 0x1F; + conn->dirptr = dptr_fetch(status+12,&dptr_num); + if (!conn->dirptr) + goto SearchEmpty; + string_set(&conn->dirpath,dptr_path(dptr_num)); + if (!case_sensitive) + strnorm(mask); + } + /* turn strings of spaces into a . */ { trim_string(mask,NULL," "); if ((p = strrchr(mask,' '))) - { - fstring ext; - fstrcpy(ext,p+1); - *p = 0; - trim_string(mask,NULL," "); - pstrcat(mask,"."); - pstrcat(mask,ext); - } + { + fstring ext; + fstrcpy(ext,p+1); + *p = 0; + trim_string(mask,NULL," "); + pstrcat(mask,"."); + pstrcat(mask,ext); + } } /* Convert the formatted mask. (This code lives in trans2.c) */ @@ -1179,104 +1351,104 @@ int reply_search(connection_struct *conn, char *inbuf,char *outbuf, int dum_size } if (!strchr(mask,'.') && strlen(mask)>8) - { - fstring tmp; - fstrcpy(tmp,&mask[8]); - mask[8] = '.'; - mask[9] = 0; - pstrcat(mask,tmp); - } + { + fstring tmp; + fstrcpy(tmp,&mask[8]); + mask[8] = '.'; + mask[9] = 0; + pstrcat(mask,tmp); + } DEBUG(5,("mask=%s directory=%s\n",mask,directory)); if (can_open) - { - p = smb_buf(outbuf) + 3; - - ok = True; + { + p = smb_buf(outbuf) + 3; - if (status_len == 0) - { - dptr_num = dptr_create(conn,directory,expect_close,SVAL(inbuf,smb_pid)); - if (dptr_num < 0) + ok = True; + + if (status_len == 0) + { + dptr_num = dptr_create(conn,directory,True,expect_close,SVAL(inbuf,smb_pid)); + if (dptr_num < 0) + { + if(dptr_num == -2) { - if(dptr_num == -2) + if((errno == ENOENT) && bad_path) { - if((errno == ENOENT) && bad_path) - { - unix_ERR_class = ERRDOS; - unix_ERR_code = ERRbadpath; - } - return (UNIXERROR(ERRDOS,ERRnofids)); + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; } - return(ERROR(ERRDOS,ERRnofids)); + return (UNIXERROR(ERRDOS,ERRnofids)); } - } + return(ERROR(ERRDOS,ERRnofids)); + } + } - DEBUG(4,("dptr_num is %d\n",dptr_num)); + DEBUG(4,("dptr_num is %d\n",dptr_num)); - if (ok) - { - if ((dirtype&0x1F) == aVOLID) - { - memcpy(p,status,21); - make_dir_struct(p,"???????????",volume_label(SNUM(conn)),0,aVOLID,0); - dptr_fill(p+12,dptr_num); - if (dptr_zero(p+12) && (status_len==0)) - numentries = 1; - else - numentries = 0; - p += DIR_STRUCT_SIZE; - } - else - { - DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n", - conn->dirpath,lp_dontdescend(SNUM(conn)))); - if (in_list(conn->dirpath, - lp_dontdescend(SNUM(conn)),True)) - check_descend = True; - - for (i=numentries;(i<maxentries) && !finished;i++) - { - finished = - !get_dir_entry(conn,mask,dirtype,fname,&size,&mode,&date,check_descend); - if (!finished) - { - memcpy(p,status,21); - make_dir_struct(p,mask,fname,size,mode,date); - dptr_fill(p+12,dptr_num); - numentries++; - } - p += DIR_STRUCT_SIZE; - } - } - } - } + if (ok) + { + if ((dirtype&0x1F) == aVOLID) + { + memcpy(p,status,21); + make_dir_struct(p,"???????????",volume_label(SNUM(conn)),0,aVOLID,0); + dptr_fill(p+12,dptr_num); + if (dptr_zero(p+12) && (status_len==0)) + numentries = 1; + else + numentries = 0; + p += DIR_STRUCT_SIZE; + } + else + { + DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n", + conn->dirpath,lp_dontdescend(SNUM(conn)))); + if (in_list(conn->dirpath, lp_dontdescend(SNUM(conn)),True)) + check_descend = True; + + for (i=numentries;(i<maxentries) && !finished;i++) + { + finished = + !get_dir_entry(conn,mask,dirtype,fname,&size,&mode,&date,check_descend); + if (!finished) + { + memcpy(p,status,21); + make_dir_struct(p,mask,fname,size,mode,date); + dptr_fill(p+12,dptr_num); + numentries++; + } + p += DIR_STRUCT_SIZE; + } + } + } /* if (ok ) */ + } - SearchEmpty: + SearchEmpty: if (numentries == 0 || !ok) - { - CVAL(outbuf,smb_rcls) = ERRDOS; - SSVAL(outbuf,smb_err,ERRnofiles); - } + { + CVAL(outbuf,smb_rcls) = ERRDOS; + SSVAL(outbuf,smb_err,ERRnofiles); + dptr_close(&dptr_num); + } /* If we were called as SMBffirst with smb_search_id == NULL and no entries were found then return error and close dirptr (X/Open spec) */ if(ok && expect_close && numentries == 0 && status_len == 0) - { - CVAL(outbuf,smb_rcls) = ERRDOS; - SSVAL(outbuf,smb_err,ERRnofiles); - /* Also close the dptr - we know it's gone */ - dptr_close(dptr_num); - } + { + CVAL(outbuf,smb_rcls) = ERRDOS; + SSVAL(outbuf,smb_err,ERRnofiles); + /* Also close the dptr - we know it's gone */ + dptr_close(&dptr_num); + } /* If we were called as SMBfunique, then we can close the dirptr now ! */ if(dptr_num >= 0 && CVAL(inbuf,smb_com) == SMBfunique) - dptr_close(dptr_num); + dptr_close(&dptr_num); SSVAL(outbuf,smb_vwv0,numentries); SSVAL(outbuf,smb_vwv1,3 + numentries * DIR_STRUCT_SIZE); @@ -1295,8 +1467,8 @@ int reply_search(connection_struct *conn, char *inbuf,char *outbuf, int dum_size slprintf(directory, sizeof(directory)-1, "(%s)",dptr_path(dptr_num)); DEBUG( 4, ( "%s mask=%s path=%s dtype=%d nument=%d of %d\n", - smb_fn_name(CVAL(inbuf,smb_com)), - mask, directory, dirtype, numentries, maxentries ) ); + smb_fn_name(CVAL(inbuf,smb_com)), + mask, directory, dirtype, numentries, maxentries ) ); return(outsize); } @@ -1311,7 +1483,7 @@ int reply_fclose(connection_struct *conn, char *inbuf,char *outbuf, int dum_size int status_len; char *path; char status[21]; - int dptr_num= -1; + int dptr_num= -2; outsize = set_message(outbuf,1,0,True); path = smb_buf(inbuf) + 1; @@ -1325,7 +1497,7 @@ int reply_fclose(connection_struct *conn, char *inbuf,char *outbuf, int dum_size if(dptr_fetch(status+12,&dptr_num)) { /* Close the dptr - we know it's gone */ - dptr_close(dptr_num); + dptr_close(&dptr_num); } SSVAL(outbuf,smb_vwv0,0); @@ -1358,11 +1530,7 @@ int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, share_mode = SVAL(inbuf,smb_vwv0); pstrcpy(fname,smb_buf(inbuf)+1); - if (!unix_dfs_convert(fname,conn,0,&bad_path,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(fname,conn,0,&bad_path,NULL); fsp = file_new(); if (!fsp) @@ -1381,9 +1549,8 @@ int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, unixmode = unix_mode(conn,aARCH); - open_file_shared(fsp, conn, fname, share_mode, - (FILE_FAIL_IF_NOT_EXIST | FILE_EXISTS_OPEN), - unixmode, oplock_request, &rmode, NULL); + open_file_shared(fsp,conn,fname,share_mode,(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), + unixmode, oplock_request,&rmode,NULL); if (!fsp->open) { @@ -1396,7 +1563,7 @@ int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, return(UNIXERROR(ERRDOS,ERRnoaccess)); } - if (fsp->conn->vfs_ops.fstat(fsp->fd_ptr->fd,&sbuf) != 0) { + if (sys_fstat(fsp->fd_ptr->fd,&sbuf) != 0) { close_file(fsp,False); return(ERROR(ERRDOS,ERRnoaccess)); } @@ -1425,7 +1592,7 @@ int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED; } - if(fsp->granted_oplock) + if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED; return(outsize); } @@ -1459,19 +1626,13 @@ int reply_open_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt files_struct *fsp; /* If it's an IPC, pass off the pipe handler. */ - if (IS_IPC(conn) && lp_nt_pipe_support() && lp_security() != SEC_SHARE) - { + if (IS_IPC(conn) && lp_nt_pipe_support()) return reply_open_pipe_and_X(conn, inbuf,outbuf,length,bufsize); - } /* XXXX we need to handle passed times, sattr and flags */ pstrcpy(fname,smb_buf(inbuf)); - if (!unix_dfs_convert(fname,conn,0,&bad_path,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(fname,conn,0,&bad_path,NULL); fsp = file_new(); if (!fsp) @@ -1490,8 +1651,8 @@ int reply_open_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt unixmode = unix_mode(conn,smb_attr | aARCH); - open_file_shared(fsp, conn, fname, smb_mode, smb_ofun, unixmode, - oplock_request, &rmode, &smb_action); + open_file_shared(fsp,conn,fname,smb_mode,smb_ofun,unixmode, + oplock_request, &rmode,&smb_action); if (!fsp->open) { @@ -1504,7 +1665,7 @@ int reply_open_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt return(UNIXERROR(ERRDOS,ERRnoaccess)); } - if (fsp->conn->vfs_ops.fstat(fsp->fd_ptr->fd,&sbuf) != 0) { + if (sys_fstat(fsp->fd_ptr->fd,&sbuf) != 0) { close_file(fsp,False); return(ERROR(ERRDOS,ERRnoaccess)); } @@ -1526,7 +1687,7 @@ int reply_open_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt smb_action |= EXTENDED_OPLOCK_GRANTED; } - if(ex_oplock_request && fsp->granted_oplock) { + if(ex_oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { smb_action |= EXTENDED_OPLOCK_GRANTED; } @@ -1539,7 +1700,7 @@ int reply_open_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED; } - if(core_oplock_request && fsp->granted_oplock) { + if(core_oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED; } @@ -1605,11 +1766,7 @@ int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, createmode = SVAL(inbuf,smb_vwv0); pstrcpy(fname,smb_buf(inbuf)+1); - if (!unix_dfs_convert(fname,conn,0,&bad_path,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(fname,conn,0,&bad_path,NULL); if (createmode & aVOLID) { @@ -1636,18 +1793,17 @@ int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, if(com == SMBmknew) { /* We should fail if file exists. */ - ofun = 0x10; + ofun = FILE_CREATE_IF_NOT_EXIST; } else { /* SMBcreate - Create if file doesn't exist, truncate if it does. */ - ofun = 0x12; + ofun = FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE; } /* Open file in dos compatibility share mode. */ - open_file_shared(fsp, conn, fname, - SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB), - ofun, unixmode, oplock_request, NULL, NULL); + open_file_shared(fsp,conn,fname,SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB), + ofun, unixmode, oplock_request, NULL, NULL); if (!fsp->open) { @@ -1667,7 +1823,7 @@ int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED; } - if(fsp->granted_oplock) + if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED; DEBUG( 2, ( "new file %s\n", fname ) ); @@ -1695,11 +1851,7 @@ int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, createmode = SVAL(inbuf,smb_vwv0); pstrcpy(fname,smb_buf(inbuf)+1); pstrcat(fname,"/TMXXXXXX"); - if (!unix_dfs_convert(fname,conn,0,&bad_path,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(fname,conn,0,&bad_path,NULL); unixmode = unix_mode(conn,createmode); @@ -1718,14 +1870,12 @@ int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, return(UNIXERROR(ERRDOS,ERRnoaccess)); } - pstrcpy(fname2,(char *)mktemp(fname)); + pstrcpy(fname2,(char *)smbd_mktemp(fname)); /* Open file in dos compatibility share mode. */ /* We should fail if file exists. */ - open_file_shared(fsp,conn,fname2, - SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB), - (FILE_CREATE_IF_NOT_EXIST | FILE_EXISTS_FAIL), - unixmode, oplock_request, NULL, NULL); + open_file_shared(fsp,conn,fname2,SET_DENY_MODE(DENY_FCB)|SET_OPEN_MODE(DOS_OPEN_FCB), + (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_FAIL), unixmode, oplock_request, NULL, NULL); if (!fsp->open) { @@ -1747,7 +1897,7 @@ int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED; } - if(fsp->granted_oplock) + if(EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) CVAL(outbuf,smb_flg) |= CORE_OPLOCK_GRANTED; DEBUG( 2, ( "created temp file %s\n", fname2 ) ); @@ -1768,7 +1918,7 @@ static BOOL can_delete(char *fname,connection_struct *conn, int dirtype) if (!CAN_WRITE(conn)) return(False); - if (conn->vfs_ops.lstat(fname,&sbuf) != 0) return(False); + if (dos_lstat(fname,&sbuf) != 0) return(False); fmode = dos_mode(conn,fname,&sbuf); if (fmode & aDIR) return(False); if (!lp_delete_readonly(SNUM(conn))) { @@ -1781,8 +1931,9 @@ static BOOL can_delete(char *fname,connection_struct *conn, int dirtype) } /**************************************************************************** - reply to a unlink + Reply to a unlink ****************************************************************************/ + int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { int outsize = 0; @@ -1796,6 +1947,7 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size BOOL has_wild; BOOL exists=False; BOOL bad_path = False; + BOOL rc = True; *directory = *mask = 0; @@ -1805,11 +1957,7 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size DEBUG(3,("reply_unlink : %s\n",name)); - if (!unix_dfs_convert(name,conn,0,&bad_path,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + rc = unix_convert(name,conn,0,&bad_path,NULL); p = strrchr(name,'/'); if (!p) { @@ -1821,7 +1969,16 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size pstrcpy(mask,p+1); } - if (is_mangled(mask)) + /* + * We should only check the mangled cache + * here if unix_convert failed. This means + * that the path in 'mask' doesn't exist + * on the file system and so we need to look + * for a possible mangle. This patch from + * Tine Smukavec <valentin.smukavec@hermes.si>. + */ + + if (!rc && is_mangled(mask)) check_mangled_cache( mask ); has_wild = strchr(mask,'*') || strchr(mask,'?'); @@ -1829,10 +1986,10 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size if (!has_wild) { pstrcat(directory,"/"); pstrcat(directory,mask); - if (can_delete(directory,conn,dirtype) && !conn->vfs_ops.unlink(directory)) + if (can_delete(directory,conn,dirtype) && !dos_unlink(directory)) count++; if (!count) - exists = vfs_file_exist(conn,dos_to_unix(directory,False),NULL); + exists = dos_file_exist(directory,NULL); } else { void *dirptr = NULL; char *dname; @@ -1862,7 +2019,7 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size error = ERRnoaccess; slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname); if (!can_delete(fname,conn,dirtype)) continue; - if (!conn->vfs_ops.unlink(fname)) count++; + if (!dos_unlink(fname)) count++; DEBUG(3,("reply_unlink : doing unlink on %s\n",fname)); } CloseDir(dirptr); @@ -1892,6 +2049,7 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size /**************************************************************************** reply to a readbraw (core+ protocol) ****************************************************************************/ + int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_size, int dum_buffsize) { size_t maxcount,mincount; @@ -1917,13 +2075,45 @@ int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_s fsp = file_fsp(inbuf,smb_vwv0); + if (!FNUM_OK(fsp,conn) || !fsp->can_read) { + /* + * fsp could be NULL here so use the value from the packet. JRA. + */ + DEBUG(3,("fnum %d not open in readbraw - cache prime?\n",(int)SVAL(inbuf,smb_vwv0))); + _smb_setlen(header,0); + transfer_file(0,Client,(SMB_OFF_T)0,header,4,0); + return(-1); + } + + CHECK_FSP(fsp,conn); + + flush_write_cache(fsp, READRAW_FLUSH); + startpos = IVAL(inbuf,smb_vwv1); -#ifdef LARGE_SMB_OFF_T if(CVAL(inbuf,smb_wct) == 10) { /* * This is a large offset (64 bit) read. */ +#ifdef LARGE_SMB_OFF_T + startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv8)) << 32); + +#else /* !LARGE_SMB_OFF_T */ + + /* + * Ensure we haven't been sent a >32 bit offset. + */ + + if(IVAL(inbuf,smb_vwv8) != 0) { + DEBUG(0,("readbraw - large offset (%x << 32) used and we don't support \ +64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv8) )); + _smb_setlen(header,0); + transfer_file(0,Client,(SMB_OFF_T)0,header,4,0); + return(-1); + } + +#endif /* LARGE_SMB_OFF_T */ + if(startpos < 0) { DEBUG(0,("readbraw - negative 64 bit readraw offset (%.0f) !\n", (double)startpos )); @@ -1932,7 +2122,6 @@ int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_s return(-1); } } -#endif /* LARGE_SMB_OFF_T */ maxcount = (SVAL(inbuf,smb_vwv3) & 0xFFFF); mincount = (SVAL(inbuf,smb_vwv4) & 0xFFFF); @@ -1940,13 +2129,6 @@ int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_s maxcount = MIN(65535,maxcount); maxcount = MAX(mincount,maxcount); - if (!FNUM_OK(fsp,conn) || !fsp->can_read) { - DEBUG(3,("fnum %d not open in readbraw - cache prime?\n",fsp->fnum)); - _smb_setlen(header,0); - transfer_file(0,Client,(SMB_OFF_T)0,header,4,0); - return(-1); - } - if (!is_locked(fsp,conn,maxcount,startpos, F_RDLCK)) { SMB_OFF_T size = fsp->size; @@ -1955,7 +2137,7 @@ int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_s if (size < sizeneeded) { SMB_STRUCT_STAT st; - if (fsp->conn->vfs_ops.fstat(fsp->fd_ptr->fd,&st) == 0) + if (sys_fstat(fsp->fd_ptr->fd,&st) == 0) size = st.st_size; if (!fsp->can_write) fsp->size = size; @@ -1969,7 +2151,7 @@ int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_s DEBUG( 3, ( "readbraw fnum=%d start=%.0f max=%d min=%d nread=%d\n", fsp->fnum, (double)startpos, - maxcount, mincount, nread ) ); + (int)maxcount, (int)mincount, (int)nread ) ); #if UNSAFE_READRAW { @@ -1979,11 +2161,11 @@ int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_s #if USE_READ_PREDICTION if (!fsp->can_write) - predict = read_predict(fsp, fsp->fd_ptr->fd,startpos,header+4,NULL,nread); + predict = read_predict(fsp->fd_ptr->fd,startpos,header+4,NULL,nread); #endif /* USE_READ_PREDICTION */ if ((nread-predict) > 0) { - if(conn->vfs_ops.seek(fsp,startpos + predict) == -1) { + if(seek_file(fsp,startpos + predict) == -1) { DEBUG(0,("reply_readbraw: ERROR: seek_file failed.\n")); ret = 0; seek_fail = True; @@ -1991,7 +2173,7 @@ int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_s } if(!seek_fail) - ret = (ssize_t)vfs_transfer_file(-1, fsp->fd_ptr->fd, Client, NULL, + ret = (ssize_t)transfer_file(fsp->fd_ptr->fd,Client, (SMB_OFF_T)(nread-predict),header,4+predict, startpos+predict); } @@ -2037,8 +2219,15 @@ int reply_lockread(connection_struct *conn, char *inbuf,char *outbuf, int length outsize = set_message(outbuf,5,3,True); numtoread = MIN(BUFFER_SIZE-outsize,numtoread); data = smb_buf(outbuf) + 3; - - if(!do_lock( fsp, conn, numtoread, startpos, F_RDLCK, &eclass, &ecode)) { + + /* + * NB. Discovered by Menny Hamburger at Mainsoft. This is a core+ + * protocol request that predates the read/write lock concept. + * Thus instead of asking for a read lock here we need to ask + * for a write lock. JRA. + */ + + if(!do_lock( fsp, conn, numtoread, startpos, F_WRLCK, &eclass, &ecode)) { if((ecode == ERRlock) && lp_blocking_locks(SNUM(conn))) { /* * A blocking lock was requested. Package up @@ -2062,7 +2251,7 @@ int reply_lockread(connection_struct *conn, char *inbuf,char *outbuf, int length SSVAL(smb_buf(outbuf),1,nread); DEBUG( 3, ( "lockread fnum=%d num=%d nread=%d\n", - fsp->fnum, numtoread, nread ) ); + fsp->fnum, (int)numtoread, (int)nread ) ); return(outsize); } @@ -2071,7 +2260,8 @@ int reply_lockread(connection_struct *conn, char *inbuf,char *outbuf, int length /**************************************************************************** reply to a read ****************************************************************************/ -int reply_read(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) + +int reply_read(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) { size_t numtoread; ssize_t nread = 0; @@ -2094,9 +2284,8 @@ int reply_read(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, if (is_locked(fsp,conn,numtoread,startpos, F_RDLCK)) return(ERROR(ERRDOS,ERRlock)); - if (numtoread > 0) { + if (numtoread > 0) nread = read_file(fsp,data,startpos,numtoread); - } if (nread < 0) return(UNIXERROR(ERRDOS,ERRnoaccess)); @@ -2108,7 +2297,7 @@ int reply_read(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, SSVAL(smb_buf(outbuf),1,nread); DEBUG( 3, ( "read fnum=%d num=%d nread=%d\n", - fsp->fnum, numtoread, nread ) ); + fsp->fnum, (int)numtoread, (int)nread ) ); return(outsize); } @@ -2137,28 +2326,42 @@ int reply_read_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt set_message(outbuf,12,0,True); data = smb_buf(outbuf); -#ifdef LARGE_SMB_OFF_T if(CVAL(inbuf,smb_wct) == 12) { +#ifdef LARGE_SMB_OFF_T /* * This is a large offset (64 bit) read. */ startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv10)) << 32); - } + +#else /* !LARGE_SMB_OFF_T */ + + /* + * Ensure we haven't been sent a >32 bit offset. + */ + + if(IVAL(inbuf,smb_vwv10) != 0) { + DEBUG(0,("reply_read_and_X - large offset (%x << 32) used and we don't support \ +64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv10) )); + return(ERROR(ERRDOS,ERRbadaccess)); + } + #endif /* LARGE_SMB_OFF_T */ + } + if (is_locked(fsp,conn,smb_maxcnt,startpos, F_RDLCK)) return(ERROR(ERRDOS,ERRlock)); nread = read_file(fsp,data,startpos,smb_maxcnt); - + if (nread < 0) return(UNIXERROR(ERRDOS,ERRnoaccess)); SSVAL(outbuf,smb_vwv5,nread); SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf)); SSVAL(smb_buf(outbuf),-2,nread); - + DEBUG( 3, ( "readX fnum=%d min=%d max=%d nread=%d\n", - fsp->fnum, smb_mincnt, smb_maxcnt, nread ) ); + fsp->fnum, (int)smb_mincnt, (int)smb_maxcnt, (int)nread ) ); return chain_reply(inbuf,outbuf,length,bufsize); } @@ -2166,7 +2369,8 @@ int reply_read_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt /**************************************************************************** reply to a writebraw (core+ or LANMAN1.0 protocol) ****************************************************************************/ -int reply_writebraw(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) + +int reply_writebraw(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) { ssize_t nwritten=0; ssize_t total_written=0; @@ -2203,16 +2407,11 @@ int reply_writebraw(connection_struct *conn, char *inbuf,char *outbuf, int dum_s if (is_locked(fsp,conn,tcount,startpos, F_WRLCK)) return(ERROR(ERRDOS,ERRlock)); - if (seek_file(fsp,startpos) == -1) { - DEBUG(0,("couldn't seek to %.0f in writebraw\n",(double)startpos)); - return(UNIXERROR(ERRDOS,ERRnoaccess)); - } - if (numtowrite>0) - nwritten = write_file(fsp,data,numtowrite); + nwritten = write_file(fsp,data,startpos,numtowrite); DEBUG(3,("writebraw1 fnum=%d start=%.0f num=%d wrote=%d sync=%d\n", - fsp->fnum, (double)startpos, numtowrite, nwritten, write_through)); + fsp->fnum, (double)startpos, (int)numtowrite, (int)nwritten, (int)write_through)); if (nwritten < numtowrite) return(UNIXERROR(ERRHRD,ERRdiskfull)); @@ -2237,12 +2436,11 @@ int reply_writebraw(connection_struct *conn, char *inbuf,char *outbuf, int dum_s if (tcount > nwritten+numtowrite) { DEBUG(3,("Client overestimated the write %d %d %d\n", - tcount,nwritten,numtowrite)); + (int)tcount,(int)nwritten,(int)numtowrite)); } - nwritten = vfs_transfer_file(Client, NULL, -1, fsp, - (SMB_OFF_T)numtowrite,NULL,0, - startpos+nwritten); + nwritten = transfer_file(Client,fsp->fd_ptr->fd,(SMB_OFF_T)numtowrite,NULL,0, + startpos+nwritten); total_written += nwritten; /* Set up outbuf to return the correct return */ @@ -2255,12 +2453,11 @@ int reply_writebraw(connection_struct *conn, char *inbuf,char *outbuf, int dum_s SSVAL(outbuf,smb_err,ERRdiskfull); } - if ((lp_syncalways(SNUM(conn)) || write_through) && - lp_strict_sync(SNUM(conn))) - conn->vfs_ops.sync(fsp->fd_ptr->fd); + if (lp_syncalways(SNUM(conn)) || write_through) + sync_file(conn,fsp); DEBUG(3,("writebraw2 fnum=%d start=%.0f num=%d wrote=%d\n", - fsp->fnum, (double)startpos, numtowrite, total_written)); + fsp->fnum, (double)startpos, (int)numtowrite,(int)total_written)); /* we won't return a status if write through is not selected - this follows what WfWg does */ @@ -2273,7 +2470,8 @@ int reply_writebraw(connection_struct *conn, char *inbuf,char *outbuf, int dum_s /**************************************************************************** reply to a writeunlock (core+) ****************************************************************************/ -int reply_writeunlock(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) + +int reply_writeunlock(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) { ssize_t nwritten = -1; size_t numtowrite; @@ -2295,19 +2493,16 @@ int reply_writeunlock(connection_struct *conn, char *inbuf,char *outbuf, int dum if (is_locked(fsp,conn,numtowrite,startpos, F_WRLCK)) return(ERROR(ERRDOS,ERRlock)); - if(seek_file(fsp,startpos) == -1) - return(UNIXERROR(ERRDOS,ERRnoaccess)); - /* The special X/Open SMB protocol handling of zero length writes is *NOT* done for this call */ if(numtowrite == 0) nwritten = 0; else - nwritten = write_file(fsp,data,numtowrite); + nwritten = write_file(fsp,data,startpos,numtowrite); - if (lp_syncalways(SNUM(conn)) && lp_strict_sync(SNUM(conn))) - conn->vfs_ops.sync(fsp->fd_ptr->fd); + if (lp_syncalways(SNUM(conn))) + sync_file(conn,fsp); if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) return(UNIXERROR(ERRDOS,ERRnoaccess)); @@ -2320,7 +2515,7 @@ int reply_writeunlock(connection_struct *conn, char *inbuf,char *outbuf, int dum SSVAL(outbuf,smb_vwv0,nwritten); DEBUG( 3, ( "writeunlock fnum=%d num=%d wrote=%d\n", - fsp->fnum, numtowrite, nwritten ) ); + fsp->fnum, (int)numtowrite, (int)nwritten ) ); return(outsize); } @@ -2328,7 +2523,7 @@ int reply_writeunlock(connection_struct *conn, char *inbuf,char *outbuf, int dum /**************************************************************************** reply to a write ****************************************************************************/ -int reply_write(connection_struct *conn, char *inbuf,char *outbuf,int dum_size,int dum_buffsize) +int reply_write(connection_struct *conn, char *inbuf,char *outbuf,int size,int dum_buffsize) { size_t numtowrite; ssize_t nwritten = -1; @@ -2337,9 +2532,9 @@ int reply_write(connection_struct *conn, char *inbuf,char *outbuf,int dum_size,i files_struct *fsp = file_fsp(inbuf,smb_vwv0); int outsize = 0; - /* If it's an IPC, pass off the pipe handler. */ - if (IS_IPC(conn)) - return reply_pipe_write(inbuf,outbuf,dum_size,dum_buffsize); + /* If it's an IPC, pass off the pipe handler. */ + if (IS_IPC(conn)) + return reply_pipe_write(inbuf,outbuf,size,dum_buffsize); CHECK_FSP(fsp,conn); CHECK_WRITE(fsp); @@ -2352,19 +2547,17 @@ int reply_write(connection_struct *conn, char *inbuf,char *outbuf,int dum_size,i if (is_locked(fsp,conn,numtowrite,startpos, F_WRLCK)) return(ERROR(ERRDOS,ERRlock)); - if(seek_file(fsp,startpos) == -1) - return(UNIXERROR(ERRDOS,ERRnoaccess)); - /* X/Open SMB protocol says that if smb_vwv1 is 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(fsp->fd_ptr->fd, (SMB_OFF_T)startpos); - else - nwritten = write_file(fsp,data,numtowrite); + if(numtowrite == 0) { + if((nwritten = set_filelen(fsp->fd_ptr->fd, (SMB_OFF_T)startpos)) >= 0) + set_filelen_write_cache(fsp, startpos); + } else + nwritten = write_file(fsp,data,startpos,numtowrite); - if (lp_syncalways(SNUM(conn)) && lp_strict_sync(SNUM(conn))) - conn->vfs_ops.sync(fsp->fd_ptr->fd); + if (lp_syncalways(SNUM(conn))) + sync_file(conn,fsp); if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) return(UNIXERROR(ERRDOS,ERRnoaccess)); @@ -2379,7 +2572,7 @@ int reply_write(connection_struct *conn, char *inbuf,char *outbuf,int dum_size,i } DEBUG(3,("write fnum=%d num=%d wrote=%d\n", - fsp->fnum, numtowrite, nwritten)); + fsp->fnum, (int)numtowrite, (int)nwritten)); return(outsize); } @@ -2408,21 +2601,31 @@ int reply_write_and_X(connection_struct *conn, char *inbuf,char *outbuf,int leng data = smb_base(inbuf) + smb_doff; -#ifdef LARGE_SMB_OFF_T if(CVAL(inbuf,smb_wct) == 14) { +#ifdef LARGE_SMB_OFF_T /* * This is a large offset (64 bit) write. */ startpos |= (((SMB_OFF_T)IVAL(inbuf,smb_vwv12)) << 32); - } + +#else /* !LARGE_SMB_OFF_T */ + + /* + * Ensure we haven't been sent a >32 bit offset. + */ + + if(IVAL(inbuf,smb_vwv12) != 0) { + DEBUG(0,("reply_write_and_X - large offset (%x << 32) used and we don't support \ +64 bit offsets.\n", (unsigned int)IVAL(inbuf,smb_vwv12) )); + return(ERROR(ERRDOS,ERRbadaccess)); + } + #endif /* LARGE_SMB_OFF_T */ + } if (is_locked(fsp,conn,numtowrite,startpos, F_WRLCK)) return(ERROR(ERRDOS,ERRlock)); - if(seek_file(fsp,startpos) == -1) - return(UNIXERROR(ERRDOS,ERRnoaccess)); - /* X/Open SMB protocol says that, unlike SMBwrite if the length is zero then NO truncation is done, just a write of zero. To truncate a file, @@ -2430,7 +2633,7 @@ int reply_write_and_X(connection_struct *conn, char *inbuf,char *outbuf,int leng if(numtowrite == 0) nwritten = 0; else - nwritten = write_file(fsp,data,numtowrite); + nwritten = write_file(fsp,data,startpos,numtowrite); if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) return(UNIXERROR(ERRDOS,ERRnoaccess)); @@ -2445,11 +2648,10 @@ int reply_write_and_X(connection_struct *conn, char *inbuf,char *outbuf,int leng } DEBUG(3,("writeX fnum=%d num=%d wrote=%d\n", - fsp->fnum, numtowrite, nwritten)); + fsp->fnum, (int)numtowrite, (int)nwritten)); - if ((lp_syncalways(SNUM(conn)) || write_through) && - lp_strict_sync(SNUM(conn))) - conn->vfs_ops.sync(fsp->fd_ptr->fd); + if (lp_syncalways(SNUM(conn)) || write_through) + sync_file(conn,fsp); return chain_reply(inbuf,outbuf,length,bufsize); } @@ -2458,7 +2660,8 @@ int reply_write_and_X(connection_struct *conn, char *inbuf,char *outbuf,int leng /**************************************************************************** reply to a lseek ****************************************************************************/ -int reply_lseek(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) + +int reply_lseek(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) { SMB_OFF_T startpos; SMB_OFF_T res= -1; @@ -2469,11 +2672,12 @@ int reply_lseek(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, CHECK_FSP(fsp,conn); CHECK_ERROR(fsp); + flush_write_cache(fsp, SEEK_FLUSH); + mode = SVAL(inbuf,smb_vwv1) & 3; - startpos = IVAL(inbuf,smb_vwv2); + startpos = IVALS(inbuf,smb_vwv2); - switch (mode & 3) - { + switch (mode) { case 0: umode = SEEK_SET; break; case 1: umode = SEEK_CUR; break; case 2: umode = SEEK_END; break; @@ -2481,16 +2685,48 @@ int reply_lseek(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, umode = SEEK_SET; break; } - if((res = conn->vfs_ops.lseek(fsp->fd_ptr->fd,startpos,umode)) == -1) - return(UNIXERROR(ERRDOS,ERRnoaccess)); + if((res = sys_lseek(fsp->fd_ptr->fd,startpos,umode)) == -1) { + /* + * Check for the special case where a seek before the start + * of the file sets the offset to zero. Added in the CIFS spec, + * section 4.2.7. + */ + + if(errno == EINVAL) { + SMB_OFF_T current_pos = startpos; + + if(umode == SEEK_CUR) { + + if((current_pos = sys_lseek(fsp->fd_ptr->fd,0,SEEK_CUR)) == -1) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + current_pos += startpos; + + } else if (umode == SEEK_END) { + + SMB_STRUCT_STAT sbuf; + + if(sys_fstat( fsp->fd_ptr->fd, &sbuf) == -1) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + current_pos += sbuf.st_size; + } + + if(current_pos < 0) + res = sys_lseek(fsp->fd_ptr->fd,0,SEEK_SET); + } + + if(res == -1) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } fsp->pos = res; outsize = set_message(outbuf,2,0,True); - SIVALS(outbuf,smb_vwv0,res); + SIVAL(outbuf,smb_vwv0,res); - DEBUG(3,("lseek fnum=%d ofs=%.0f mode=%d\n", - fsp->fnum, (double)startpos, mode)); + DEBUG(3,("lseek fnum=%d ofs=%.0f newpos = %.0f mode=%d\n", + fsp->fnum, (double)startpos, (double)res, mode)); return(outsize); } @@ -2498,7 +2734,8 @@ int reply_lseek(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, /**************************************************************************** reply to a flush ****************************************************************************/ -int reply_flush(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) + +int reply_flush(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) { int outsize = set_message(outbuf,0,0,True); files_struct *fsp = file_fsp(inbuf,smb_vwv0); @@ -2511,7 +2748,7 @@ int reply_flush(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, if (!fsp) { file_sync_all(conn); } else { - conn->vfs_ops.sync(fsp->fd_ptr->fd); + sync_file(conn,fsp); } DEBUG(3,("flush\n")); @@ -2535,8 +2772,8 @@ int reply_exit(connection_struct *conn, /**************************************************************************** Reply to a close - has to deal with closing a directory opened by NT SMB's. ****************************************************************************/ -int reply_close(connection_struct *conn, - char *inbuf,char *outbuf, int dum_size, int dum_buffsize) +int reply_close(connection_struct *conn, char *inbuf,char *outbuf, int size, + int dum_buffsize) { int outsize = 0; time_t mtime; @@ -2546,9 +2783,8 @@ int reply_close(connection_struct *conn, outsize = set_message(outbuf,0,0,True); /* If it's an IPC, pass off to the pipe handler. */ - if (IS_IPC(conn)) { + if (IS_IPC(conn)) return reply_pipe_close(conn, inbuf,outbuf); - } fsp = file_fsp(inbuf,smb_vwv0); @@ -2556,25 +2792,26 @@ int reply_close(connection_struct *conn, * We can only use CHECK_FSP if we know it's not a directory. */ - if(!fsp || !fsp->open || (fsp->conn != conn)) - return(ERROR(ERRDOS,ERRbadfid)); + if(!fsp || !fsp->open || (fsp->conn != conn)) + return(ERROR(ERRDOS,ERRbadfid)); if(HAS_CACHED_ERROR(fsp)) { eclass = fsp->wbmpx_ptr->wr_errclass; err = fsp->wbmpx_ptr->wr_error; } - if(fsp->is_directory) { + if(fsp->is_directory || fsp->stat_open) { /* - * Special case - close NT SMB directory + * Special case - close NT SMB directory or stat file * handle. */ - DEBUG(3,("close directory fnum=%d\n", fsp->fnum)); - close_directory(fsp); + DEBUG(3,("close %s fnum=%d\n", fsp->is_directory ? "directory" : "stat file open", fsp->fnum)); + close_file(fsp,True); } else { /* * Close ordinary file. */ + int close_err; /* * If there was a modify time outstanding, @@ -2592,10 +2829,19 @@ int reply_close(connection_struct *conn, set_filetime(conn, fsp->fsp_name,mtime); DEBUG(3,("close fd=%d fnum=%d (numopen=%d)\n", - fsp->fd_ptr->fd, fsp->fnum, + fsp->fd_ptr ? fsp->fd_ptr->fd : -1, fsp->fnum, conn->num_files_open)); - - close_file(fsp,True); + + /* + * close_file() returns the unix errno if an error + * was detected on close - normally this is due to + * a disk full error. If not then it was probably an I/O error. + */ + + if((close_err = close_file(fsp,True)) != 0) { + errno = close_err; + return (UNIXERROR(ERRHRD,ERRgeneral)); + } } /* We have a cached error */ @@ -2609,12 +2855,14 @@ int reply_close(connection_struct *conn, /**************************************************************************** reply to a writeclose (Core+ protocol) ****************************************************************************/ + int reply_writeclose(connection_struct *conn, - char *inbuf,char *outbuf, int dum_size, int dum_buffsize) + char *inbuf,char *outbuf, int size, int dum_buffsize) { size_t numtowrite; ssize_t nwritten = -1; int outsize = 0; + int close_err = 0; SMB_OFF_T startpos; char *data; time_t mtime; @@ -2631,23 +2879,25 @@ int reply_writeclose(connection_struct *conn, if (is_locked(fsp,conn,numtowrite,startpos, F_WRLCK)) return(ERROR(ERRDOS,ERRlock)); - - if(seek_file(fsp,startpos) == -1) - return(UNIXERROR(ERRDOS,ERRnoaccess)); - - nwritten = write_file(fsp,data,numtowrite); + + nwritten = write_file(fsp,data,startpos,numtowrite); set_filetime(conn, fsp->fsp_name,mtime); - close_file(fsp,True); + close_err = close_file(fsp,True); DEBUG(3,("writeclose fnum=%d num=%d wrote=%d (numopen=%d)\n", - fsp->fnum, numtowrite, nwritten, + fsp->fnum, (int)numtowrite, (int)nwritten, conn->num_files_open)); if (nwritten <= 0) return(UNIXERROR(ERRDOS,ERRnoaccess)); - + + if(close_err != 0) { + errno = close_err; + return(UNIXERROR(ERRHRD,ERRgeneral)); + } + outsize = set_message(outbuf,1,0,True); SSVAL(outbuf,smb_vwv0,nwritten); @@ -2696,7 +2946,8 @@ int reply_lock(connection_struct *conn, /**************************************************************************** reply to a unlock ****************************************************************************/ -int reply_unlock(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) + +int reply_unlock(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) { int outsize = set_message(outbuf,0,0,True); SMB_OFF_T count,offset; @@ -2775,6 +3026,8 @@ int reply_echo(connection_struct *conn, DEBUG(3,("echo %d times\n", smb_reverb)); + smb_echo_count++; + return -1; } @@ -2815,7 +3068,7 @@ int reply_printopen(connection_struct *conn, if (!fsp) return(ERROR(ERRSRV,ERRnofids)); - pstrcpy(fname2,(char *)mktemp(fname)); + pstrcpy(fname2,(char *)smbd_mktemp(fname)); if (!check_name(fname2,conn)) { file_free(fsp); @@ -2852,6 +3105,7 @@ int reply_printclose(connection_struct *conn, { int outsize = set_message(outbuf,0,0,True); files_struct *fsp = file_fsp(inbuf,smb_vwv0); + int close_err = 0; CHECK_FSP(fsp,conn); CHECK_ERROR(fsp); @@ -2862,7 +3116,12 @@ int reply_printclose(connection_struct *conn, DEBUG(3,("printclose fd=%d fnum=%d\n", fsp->fd_ptr->fd,fsp->fnum)); - close_file(fsp,True); + close_err = close_file(fsp,True); + + if(close_err != 0) { + errno = close_err; + return(UNIXERROR(ERRHRD,ERRgeneral)); + } return(outsize); } @@ -2955,7 +3214,7 @@ int reply_printwrite(connection_struct *conn, char *inbuf,char *outbuf, int dum_ numtowrite = SVAL(smb_buf(inbuf),1); data = smb_buf(inbuf) + 3; - if (write_file(fsp,data,numtowrite) != numtowrite) + if (write_file(fsp,data,-1,numtowrite) != numtowrite) return(UNIXERROR(ERRDOS,ERRnoaccess)); DEBUG( 3, ( "printwrite fnum=%d num=%d\n", fsp->fnum, numtowrite ) ); @@ -2974,15 +3233,10 @@ int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, BOOL bad_path = False; pstrcpy(directory,smb_buf(inbuf) + 1); - if (!unix_dfs_convert(directory,conn,0,&bad_path,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(directory,conn,0,&bad_path,NULL); if (check_name(directory, conn)) - ret = conn->vfs_ops.mkdir(dos_to_unix(directory,False), - unix_mode(conn,aDIR)); + ret = dos_mkdir(directory,unix_mode(conn,aDIR)); if (ret < 0) { @@ -3005,11 +3259,12 @@ int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, Static function used by reply_rmdir to delete an entire directory tree recursively. ****************************************************************************/ -static BOOL recursive_rmdir(connection_struct *conn, char *directory) + +static BOOL recursive_rmdir(char *directory) { char *dname = NULL; BOOL ret = False; - void *dirptr = OpenDir(conn, directory, False); + void *dirptr = OpenDir(NULL, directory, False); if(dirptr == NULL) return True; @@ -3033,7 +3288,7 @@ static BOOL recursive_rmdir(connection_struct *conn, char *directory) pstrcat(fullname, "/"); pstrcat(fullname, dname); - if(conn->vfs_ops.lstat(fullname, &st) != 0) + if(dos_lstat(fullname, &st) != 0) { ret = True; break; @@ -3041,18 +3296,18 @@ static BOOL recursive_rmdir(connection_struct *conn, char *directory) if(st.st_mode & S_IFDIR) { - if(recursive_rmdir(conn, fullname)!=0) + if(recursive_rmdir(fullname)!=0) { ret = True; break; } - if(conn->vfs_ops.rmdir(dos_to_unix(fullname,False)) != 0) + if(dos_rmdir(fullname) != 0) { ret = True; break; } } - else if(conn->vfs_ops.unlink(dos_to_unix(fullname,False)) != 0) + else if(dos_unlink(fullname) != 0) { ret = True; break; @@ -3063,8 +3318,97 @@ static BOOL recursive_rmdir(connection_struct *conn, char *directory) } /**************************************************************************** - reply to a rmdir + The internals of the rmdir code - called elsewhere. +****************************************************************************/ + +BOOL rmdir_internals(connection_struct *conn, char *directory) +{ + BOOL ok; + + ok = (dos_rmdir(directory) == 0); + if(!ok && ((errno == ENOTEMPTY)||(errno == EEXIST)) && lp_veto_files(SNUM(conn))) + { + /* + * 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(conn, 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(conn, dname)) + { + all_veto_files = False; + break; + } + } + if(all_veto_files) + { + SeekDir(dirptr,dirpos); + while ((dname = ReadDirName(dirptr))) + { + pstring fullname; + SMB_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); + pstrcat(fullname, "/"); + pstrcat(fullname, dname); + + if(dos_lstat(fullname, &st) != 0) + break; + if(st.st_mode & S_IFDIR) + { + if(lp_recursive_veto_delete(SNUM(conn))) + { + if(recursive_rmdir(fullname) != 0) + break; + } + if(dos_rmdir(fullname) != 0) + break; + } + else if(dos_unlink(fullname) != 0) + break; + } + CloseDir(dirptr); + /* Retry the rmdir */ + ok = (dos_rmdir(directory) == 0); + } + else + CloseDir(dirptr); + } + else + errno = ENOTEMPTY; + } + + if (!ok) + DEBUG(3,("rmdir_internals: couldn't remove directory %s : %s\n", + directory,strerror(errno))); + + return ok; +} + +/**************************************************************************** + Reply to a rmdir. ****************************************************************************/ + int reply_rmdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { pstring directory; @@ -3073,92 +3417,13 @@ int reply_rmdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, BOOL bad_path = False; pstrcpy(directory,smb_buf(inbuf) + 1); - if (!unix_dfs_convert(directory,conn, NULL,&bad_path,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(directory,conn, NULL,&bad_path,NULL); if (check_name(directory,conn)) - { - - dptr_closepath(directory,SVAL(inbuf,smb_pid)); - ok = (conn->vfs_ops.rmdir(dos_to_unix(directory,False)) == 0); - if(!ok && (errno == ENOTEMPTY) && lp_veto_files(SNUM(conn))) - { - /* 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(conn, 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(conn, dname)) - { - all_veto_files = False; - break; - } - } - if(all_veto_files) - { - SeekDir(dirptr,dirpos); - while ((dname = ReadDirName(dirptr))) - { - pstring fullname; - SMB_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); - pstrcat(fullname, "/"); - pstrcat(fullname, dname); - - if(conn->vfs_ops.lstat(fullname, &st) != 0) - break; - if(st.st_mode & S_IFDIR) - { - if(lp_recursive_veto_delete(SNUM(conn))) - { - DEBUG(0, ("ERROR: recursive_rmdir()\n")); - if(recursive_rmdir(conn, fullname) != 0) - break; - } - if(conn->vfs_ops.rmdir(dos_to_unix(fullname,False)) != 0) - break; - } - else if(conn->vfs_ops.unlink(dos_to_unix(fullname,False)) != 0) - break; - } - CloseDir(dirptr); - /* Retry the rmdir */ - ok = (conn->vfs_ops.rmdir(dos_to_unix(directory,False)) == 0); - } - else - CloseDir(dirptr); - } - else - errno = ENOTEMPTY; - } - - if (!ok) - DEBUG(3,("couldn't remove directory %s : %s\n", - directory,strerror(errno))); - } + { + dptr_closepath(directory,SVAL(inbuf,smb_pid)); + ok = rmdir_internals(conn, directory); + } if (!ok) { @@ -3251,7 +3516,7 @@ static BOOL can_rename(char *fname,connection_struct *conn) if (!CAN_WRITE(conn)) return(False); - if (conn->vfs_ops.lstat(fname,&sbuf) != 0) return(False); + if (dos_lstat(fname,&sbuf) != 0) return(False); if (!check_file_sharing(conn,fname,True)) return(False); return(True); @@ -3275,23 +3540,16 @@ int rename_internals(connection_struct *conn, int count=0; int error = ERRnoaccess; BOOL exists=False; + BOOL rc = True; *directory = *mask = 0; - if (!unix_dfs_convert(name,conn,0,&bad_path1,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } - if (!unix_dfs_convert(newname,conn,newname_last_component,&bad_path2,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + rc = unix_convert(name,conn,0,&bad_path1,NULL); + unix_convert(newname,conn,newname_last_component,&bad_path2,NULL); /* * Split the old name into directory and last component - * strings. Note that if (!unix_dfs_convert may have stripped off a + * 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 @@ -3309,7 +3567,16 @@ int rename_internals(connection_struct *conn, *p = '/'; /* Replace needed for exceptional test below. */ } - if (is_mangled(mask)) + /* + * We should only check the mangled cache + * here if unix_convert failed. This means + * that the path in 'mask' doesn't exist + * on the file system and so we need to look + * for a possible mangle. This patch from + * Tine Smukavec <valentin.smukavec@hermes.si>. + */ + + if (!rc && is_mangled(mask)) check_mangled_cache( mask ); has_wild = strchr(mask,'*') || strchr(mask,'?'); @@ -3379,23 +3646,21 @@ int rename_internals(connection_struct *conn, */ if(resolve_wildcards(directory,newname) && can_rename(directory,conn) && - !conn->vfs_ops.rename(dos_to_unix(directory,False), - newname)) + !dos_rename(directory,newname)) count++; } else { if (resolve_wildcards(directory,newname) && can_rename(directory,conn) && - !vfs_file_exist(conn,dos_to_unix(newname,False),NULL) && - !conn->vfs_ops.rename(dos_to_unix(directory,False), - newname)) + !dos_file_exist(newname,NULL) && + !dos_rename(directory,newname)) count++; } DEBUG(3,("rename_internals: %s doing rename on %s -> %s\n",(count != 0) ? "succeeded" : "failed", directory,newname)); - if (!count) exists = vfs_file_exist(conn,dos_to_unix(directory,False),NULL); - if (!count && exists && vfs_file_exist(conn,dos_to_unix(newname,False),NULL)) { + if (!count) exists = dos_file_exist(directory,NULL); + if (!count && exists && dos_file_exist(newname,NULL)) { exists = True; error = ERRrename; } @@ -3436,13 +3701,13 @@ int rename_internals(connection_struct *conn, continue; } - if (!replace_if_exists && vfs_file_exist(conn,dos_to_unix(destname,False),NULL)) { - DEBUG(6,("file_exist %s\n", destname)); + if (!replace_if_exists && dos_file_exist(destname,NULL)) { + DEBUG(6,("dos_file_exist %s\n", destname)); error = 183; continue; } - if (!conn->vfs_ops.rename(dos_to_unix(fname,False),destname)) + if (!dos_rename(fname,destname)) count++; DEBUG(3,("rename_internals: doing rename on %s -> %s\n",fname,destname)); } @@ -3492,14 +3757,16 @@ int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, in ******************************************************************/ static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun, - int count,BOOL target_is_directory) + int count,BOOL target_is_directory, int *err_ret) { int Access,action; SMB_STRUCT_STAT st; - int ret=-1; + SMB_OFF_T ret=-1; files_struct *fsp1,*fsp2; pstring dest; + *err_ret = 0; + pstrcpy(dest,dest1); if (target_is_directory) { char *p = strrchr(src,'/'); @@ -3511,16 +3778,15 @@ static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun, pstrcat(dest,p); } - if (!vfs_file_exist(conn,dos_to_unix(src,False),&st)) + if (!dos_file_exist(src,&st)) return(False); fsp1 = file_new(); - if (!fsp1) + if (!fsp1) return(False); - open_file_shared(fsp1, conn, src, - SET_DENY_MODE(DENY_NONE) | SET_OPEN_MODE(DOS_OPEN_RDONLY), - (FILE_FAIL_IF_NOT_EXIST | FILE_EXISTS_OPEN), 0, 0, &Access, &action); + open_file_shared(fsp1,conn,src,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),0,0,&Access,&action); if (!fsp1->open) { file_free(fsp1); @@ -3535,9 +3801,8 @@ static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun, close_file(fsp1,False); return(False); } - open_file_shared(fsp2, conn, dest, - SET_DENY_MODE(DENY_NONE) | SET_OPEN_MODE(DOS_OPEN_WRONLY), - ofun, st.st_mode, 0, &Access, &action); + open_file_shared(fsp2,conn,dest,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_WRONLY), + ofun,st.st_mode,0,&Access,&action); if (!fsp2->open) { close_file(fsp1,False); @@ -3546,7 +3811,7 @@ static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun, } if ((ofun&3) == 1) { - if(conn->vfs_ops.lseek(fsp2->fd_ptr->fd,0,SEEK_END) == -1) { + if(sys_lseek(fsp2->fd_ptr->fd,0,SEEK_END) == -1) { DEBUG(0,("copy_file: error - sys_lseek returned error %s\n", strerror(errno) )); /* @@ -3558,12 +3823,19 @@ static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun, } if (st.st_size) - ret = vfs_transfer_file(-1, fsp1, -1, fsp2, st.st_size, NULL, 0, 0); + ret = transfer_file(fsp1->fd_ptr->fd, + fsp2->fd_ptr->fd,st.st_size,NULL,0,0); close_file(fsp1,False); - close_file(fsp2,False); + /* + * As we are opening fsp1 read-only we only expect + * an error on close on fsp2 if we are out of space. + * Thus we don't look at the error return from the + * close of fsp1. + */ + *err_ret = close_file(fsp2,False); - return(ret == st.st_size); + return(ret == (SMB_OFF_T)st.st_size); } @@ -3580,6 +3852,7 @@ int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, char *p; int count=0; int error = ERRnoaccess; + int err = 0; BOOL has_wild; BOOL exists=False; int tid2 = SVAL(inbuf,smb_vwv0); @@ -3588,6 +3861,7 @@ int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, BOOL target_is_directory=False; BOOL bad_path1 = False; BOOL bad_path2 = False; + BOOL rc = True; *directory = *mask = 0; @@ -3602,16 +3876,8 @@ int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, return(ERROR(ERRSRV,ERRinvdevice)); } - if (!unix_dfs_convert(name,conn,0,&bad_path1,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } - if (!unix_dfs_convert(newname,conn,0,&bad_path2,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + rc = unix_convert(name,conn,0,&bad_path1,NULL); + unix_convert(newname,conn,0,&bad_path2,NULL); target_is_directory = dos_directory_exist(newname,NULL); @@ -3639,7 +3905,16 @@ int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, pstrcpy(mask,p+1); } - if (is_mangled(mask)) + /* + * We should only check the mangled cache + * here if unix_convert failed. This means + * that the path in 'mask' doesn't exist + * on the file system and so we need to look + * for a possible mangle. This patch from + * Tine Smukavec <valentin.smukavec@hermes.si>. + */ + + if (!rc && is_mangled(mask)) check_mangled_cache( mask ); has_wild = strchr(mask,'*') || strchr(mask,'?'); @@ -3649,8 +3924,12 @@ int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, pstrcat(directory,mask); if (resolve_wildcards(directory,newname) && copy_file(directory,newname,conn,ofun, - count,target_is_directory)) count++; - if (!count) exists = vfs_file_exist(conn,dos_to_unix(directory,False),NULL); + count,target_is_directory,&err)) count++; + if(!count && err) { + errno = err; + return(UNIXERROR(ERRHRD,ERRgeneral)); + } + if (!count) exists = dos_file_exist(directory,NULL); } else { void *dirptr = NULL; char *dname; @@ -3659,33 +3938,38 @@ int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, if (check_name(directory,conn)) dirptr = OpenDir(conn, directory, True); - if (dirptr) - { + if (dirptr) { error = ERRbadfile; if (strequal(mask,"????????.???")) pstrcpy(mask,"*"); - while ((dname = ReadDirName(dirptr))) - { + while ((dname = ReadDirName(dirptr))) { pstring fname; pstrcpy(fname,dname); - if(!mask_match(fname, mask, case_sensitive, False)) continue; + if(!mask_match(fname, mask, case_sensitive, False)) + continue; error = ERRnoaccess; slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname); pstrcpy(destname,newname); if (resolve_wildcards(fname,destname) && - copy_file(directory,newname,conn,ofun, - count,target_is_directory)) count++; + copy_file(fname,destname,conn,ofun, + count,target_is_directory,&err)) count++; DEBUG(3,("reply_copy : doing copy on %s -> %s\n",fname,destname)); } CloseDir(dirptr); - } + } } if (count == 0) { + if(err) { + /* Error on close... */ + errno = err; + return(UNIXERROR(ERRHRD,ERRgeneral)); + } + if (exists) return(ERROR(ERRDOS,error)); else @@ -3743,8 +4027,148 @@ int reply_setdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size } /**************************************************************************** + Get a lock count, dealing with large count requests. +****************************************************************************/ + +SMB_OFF_T get_lock_count( char *data, int data_offset, BOOL large_file_format, BOOL *err) +{ + SMB_OFF_T count = 0; + + *err = False; + + if(!large_file_format) { + count = (SMB_OFF_T)IVAL(data,SMB_LKLEN_OFFSET(data_offset)); + } else { + +#if defined(LARGE_SMB_OFF_T) && !defined(HAVE_BROKEN_FCNTL64_LOCKS) + + count = (((SMB_OFF_T) IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset))) << 32) | + ((SMB_OFF_T) IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset))); + +#else /* !LARGE_SMB_OFF_T || HAVE_BROKEN_FCNTL64_LOCKS */ + + /* + * NT4.x seems to be broken in that it sends large file + * lockingX calls even if the CAP_LARGE_FILES was *not* + * negotiated. For boxes without large file locks truncate the + * lock count by dropping the top 32 bits. + */ + + if(IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)) != 0) { + DEBUG(3,("get_lock_count: truncating lock count (high)0x%x (low)0x%x to just low count.\n", + (unsigned int)IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)), + (unsigned int)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)) )); + SIVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset),0); + } + + if(IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)) != 0) { + /* + * Before we error out, see if we can sensibly map the top bits + * down to the lower bits - or lose the top bits if they are all 1's. + * It seems that NT has this horrible bug where it will send 64 bit + * lock requests even if told not to. JRA. + */ + + if(IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)) == (uint32)0xFFFFFFFF) + count = (SMB_OFF_T)IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)); + else if (IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)) == (uint32)0xFFFFFFFF) + count = (SMB_OFF_T)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)); + else { + + DEBUG(0,("get_lock_count: Error : a large file count (%x << 32 | %x) was sent and we don't \ +support large counts.\n", (unsigned int)IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(data_offset)), + (unsigned int)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)) )); + + *err = True; + return (SMB_OFF_T)-1; + } + } + else + count = (SMB_OFF_T)IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(data_offset)); + +#endif /* LARGE_SMB_OFF_T */ + } + return count; +} + +/**************************************************************************** + Get a lock offset, dealing with large offset requests. +****************************************************************************/ + +SMB_OFF_T get_lock_offset( char *data, int data_offset, BOOL large_file_format, BOOL *err) +{ + SMB_OFF_T offset = 0; + + *err = False; + + if(!large_file_format) { + offset = (SMB_OFF_T)IVAL(data,SMB_LKOFF_OFFSET(data_offset)); + } else { + +#if defined(LARGE_SMB_OFF_T) && !defined(HAVE_BROKEN_FCNTL64_LOCKS) + + offset = (((SMB_OFF_T) IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset))) << 32) | + ((SMB_OFF_T) IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset))); + +#else /* !LARGE_SMB_OFF_T || HAVE_BROKEN_FCNTL64_LOCKS */ + + /* + * NT4.x seems to be broken in that it sends large file + * lockingX calls even if the CAP_LARGE_FILES was *not* + * negotiated. For boxes without large file locks mangle the + * lock offset by mapping the top 32 bits onto the lower 32. + */ + + if(IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset)) != 0) { + uint32 low = IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset)); + uint32 high = IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset)); + uint32 new_low = 0; + + if((new_low = map_lock_offset(high, low)) == 0) { + *err = True; + return (SMB_OFF_T)-1; + } + + DEBUG(3,("get_lock_offset: truncating lock offset (high)0x%x (low)0x%x to offset 0x%x.\n", + (unsigned int)high, (unsigned int)low, (unsigned int)new_low )); + SIVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset),0); + SIVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset),new_low); + } + + if(IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset)) != 0){ + /* + * Before we error out, see if we can sensibly map the top bits + * down to the lower bits - or lose the top bits if they are all 1's. + * It seems that NT has this horrible bug where it will send 64 bit + * lock requests even if told not to. JRA. + */ + + if(IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset)) == (uint32)0xFFFFFFFF) + offset = (SMB_OFF_T)IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset)); + else if(IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset)) == (uint32)0xFFFFFFFF) + offset = (SMB_OFF_T)IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset)); + else { + + DEBUG(0,("get_lock_count: Error : a large file offset (%x << 32 | %x) was sent and we don't \ +support large offsets.\n", (unsigned int)IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(data_offset)), + (unsigned int)IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset)) )); + + *err = True; + return (SMB_OFF_T)-1; + } + } + else + offset = (SMB_OFF_T)IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(data_offset)); + +#endif /* LARGE_SMB_OFF_T */ + } + return offset; +} + +/**************************************************************************** reply to a lockingX request ****************************************************************************/ + int reply_lockingX(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize) { files_struct *fsp = file_fsp(inbuf,smb_vwv2); @@ -3761,6 +4185,8 @@ int reply_lockingX(connection_struct *conn, char *inbuf,char *outbuf,int length, uint32 ecode=0, dummy2; int eclass=0, dummy1; BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES); + BOOL err1, err2; + CHECK_FSP(fsp,conn); CHECK_ERROR(fsp); @@ -3771,35 +4197,28 @@ int reply_lockingX(connection_struct *conn, char *inbuf,char *outbuf,int length, */ if ((locktype & LOCKING_ANDX_OPLOCK_RELEASE)) { - int token; - SMB_DEV_T dev = fsp->fd_ptr->dev; - SMB_INO_T inode = fsp->fd_ptr->inode; - DEBUG(5,("reply_lockingX: oplock break reply from client for fnum = %d\n", fsp->fnum)); + /* - * Make sure we have granted an oplock on this file. + * Make sure we have granted an exclusive or batch oplock on this file. */ - if(!fsp->granted_oplock) + + if(!EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { DEBUG(0,("reply_lockingX: Error : oplock break from client for fnum = %d and \ -no oplock granted on this file.\n", fsp->fnum)); - return ERROR(ERRDOS,ERRlock); - } - - /* Remove the oplock flag from the sharemode. */ - lock_share_entry(fsp->conn, dev, inode, &token); - if(remove_share_oplock(token, fsp)==False) { - - DEBUG(0,("reply_lockingX: failed to remove share oplock for fnum %d, \ -dev = %x, inode = %.0f\n", fsp->fnum, (unsigned int)dev, (double)inode)); +no oplock granted on this file (%s).\n", fsp->fnum, fsp->fsp_name)); - unlock_share_entry(fsp->conn, dev, inode, token); - } else { - unlock_share_entry(fsp->conn, dev, inode, token); + /* if this is a pure oplock break request then don't send a reply */ + if (num_locks == 0 && num_ulocks == 0) + return -1; + else + return ERROR(ERRDOS,ERRlock); + } - /* Clear the granted flag and return. */ - fsp->granted_oplock = False; + if (remove_oplock(fsp) == False) { + DEBUG(0,("reply_lockingX: error in removing oplock on file %s\n", + fsp->fsp_name )); } /* if this is a pure oplock break request then don't send a reply */ @@ -3817,18 +4236,14 @@ dev = %x, inode = %.0f\n", fsp->fnum, (unsigned int)dev, (double)inode)); /* Data now points at the beginning of the list of smb_unlkrng structs */ for(i = 0; i < (int)num_ulocks; i++) { - if(!large_file_format) { - count = IVAL(data,SMB_LKLEN_OFFSET(i)); - offset = IVAL(data,SMB_LKOFF_OFFSET(i)); - } -#ifdef LARGE_SMB_OFF_T - else { - count = (((SMB_OFF_T) IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(i))) << 32) | - ((SMB_OFF_T) IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(i))); - offset = (((SMB_OFF_T) IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(i))) << 32) | - ((SMB_OFF_T) IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(i))); - } -#endif /* LARGE_SMB_OFF_T */ + count = get_lock_count( data, i, large_file_format, &err1); + offset = get_lock_offset( data, i, large_file_format, &err2); + + /* + * There is no error code marked "stupid client bug".... :-). + */ + if(err1 || err2) + return ERROR(ERRDOS,ERRnoaccess); DEBUG(10,("reply_lockingX: unlock start=%.0f, len=%.0f for file %s\n", (double)offset, (double)count, fsp->fsp_name )); @@ -3847,18 +4262,14 @@ dev = %x, inode = %.0f\n", fsp->fnum, (unsigned int)dev, (double)inode)); of smb_lkrng structs */ for(i = 0; i < (int)num_locks; i++) { - if(!large_file_format) { - count = IVAL(data,SMB_LKLEN_OFFSET(i)); - offset = IVAL(data,SMB_LKOFF_OFFSET(i)); - } -#ifdef LARGE_SMB_OFF_T - else { - count = (((SMB_OFF_T) IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(i))) << 32) | - ((SMB_OFF_T) IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(i))); - offset = (((SMB_OFF_T) IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(i))) << 32) | - ((SMB_OFF_T) IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(i))); - } -#endif /* LARGE_SMB_OFF_T */ + count = get_lock_count( data, i, large_file_format, &err1); + offset = get_lock_offset( data, i, large_file_format, &err2); + + /* + * There is no error code marked "stupid client bug".... :-). + */ + if(err1 || err2) + return ERROR(ERRDOS,ERRnoaccess); DEBUG(10,("reply_lockingX: lock start=%.0f, len=%.0f for file %s\n", (double)offset, (double)count, fsp->fsp_name )); @@ -3882,19 +4293,15 @@ dev = %x, inode = %.0f\n", fsp->fnum, (unsigned int)dev, (double)inode)); all of the previous locks (X/Open spec). */ if(i != num_locks && num_locks != 0) { for(; i >= 0; i--) { - if(!large_file_format) { - count = IVAL(data,SMB_LKLEN_OFFSET(i)); - offset = IVAL(data,SMB_LKOFF_OFFSET(i)); - } -#ifdef LARGE_SMB_OFF_T - else { - count = (((SMB_OFF_T) IVAL(data,SMB_LARGE_LKLEN_OFFSET_HIGH(i))) << 32) | - ((SMB_OFF_T) IVAL(data,SMB_LARGE_LKLEN_OFFSET_LOW(i))); - offset = (((SMB_OFF_T) IVAL(data,SMB_LARGE_LKOFF_OFFSET_HIGH(i))) << 32) | - ((SMB_OFF_T) IVAL(data,SMB_LARGE_LKOFF_OFFSET_LOW(i))); - } -#endif /* LARGE_SMB_OFF_T */ + count = get_lock_count( data, i, large_file_format, &err1); + offset = get_lock_offset( data, i, large_file_format, &err2); + /* + * There is no error code marked "stupid client bug".... :-). + */ + if(err1 || err2) + return ERROR(ERRDOS,ERRnoaccess); + do_unlock(fsp,conn,count,offset,&dummy1,&dummy2); } return ERROR(eclass,ecode); @@ -3980,7 +4387,8 @@ int reply_readbmpx(connection_struct *conn, char *inbuf,char *outbuf,int length, /**************************************************************************** reply to a SMBwritebmpx (write block multiplex primary) request ****************************************************************************/ -int reply_writebmpx(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) + +int reply_writebmpx(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) { size_t numtowrite; ssize_t nwritten = -1; @@ -4011,14 +4419,10 @@ int reply_writebmpx(connection_struct *conn, char *inbuf,char *outbuf, int dum_s if (is_locked(fsp,conn,tcount,startpos,F_WRLCK)) return(ERROR(ERRDOS,ERRlock)); - if(seek_file(fsp,startpos) == -1) - return(UNIXERROR(ERRDOS,ERRnoaccess)); + nwritten = write_file(fsp,data,startpos,numtowrite); - nwritten = write_file(fsp,data,numtowrite); - - if((lp_syncalways(SNUM(conn)) || write_through) && - lp_strict_sync(SNUM(conn))) - conn->vfs_ops.sync(fsp->fd_ptr->fd); + if(lp_syncalways(SNUM(conn)) || write_through) + sync_file(conn,fsp); if(nwritten < (ssize_t)numtowrite) return(UNIXERROR(ERRHRD,ERRdiskfull)); @@ -4056,7 +4460,7 @@ int reply_writebmpx(connection_struct *conn, char *inbuf,char *outbuf, int dum_s SSVALS(outbuf,smb_vwv0,-1); /* We don't support smb_remaining */ DEBUG( 3, ( "writebmpx fnum=%d num=%d wrote=%d\n", - fsp->fnum, numtowrite, nwritten ) ); + fsp->fnum, (int)numtowrite, (int)nwritten ) ); if (write_through && tcount==nwritten) { /* we need to send both a primary and a secondary response */ @@ -4116,23 +4520,10 @@ int reply_writebs(connection_struct *conn, char *inbuf,char *outbuf, int dum_siz if(wbms->wr_discard) return -1; /* Just discard the packet */ - if(seek_file(fsp,startpos) == -1) - { - if(write_through) - { - /* We are returning an error - we can delete the aux struct */ - if (wbms) free((char *)wbms); - fsp->wbmpx_ptr = NULL; - return(UNIXERROR(ERRDOS,ERRnoaccess)); - } - return(CACHE_ERROR(wbms,ERRDOS,ERRnoaccess)); - } - - nwritten = write_file(fsp,data,numtowrite); + nwritten = write_file(fsp,data,startpos,numtowrite); - if((lp_syncalways(SNUM(conn)) || write_through) && - lp_strict_sync(SNUM(conn))) - conn->vfs_ops.sync(fsp->fd_ptr->fd); + if(lp_syncalways(SNUM(conn)) || write_through) + sync_file(conn,fsp); if (nwritten < (ssize_t)numtowrite) { @@ -4172,7 +4563,8 @@ int reply_writebs(connection_struct *conn, char *inbuf,char *outbuf, int dum_siz /**************************************************************************** reply to a SMBsetattrE ****************************************************************************/ -int reply_setattrE(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) + +int reply_setattrE(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) { struct utimbuf unix_times; int outsize = 0; @@ -4224,7 +4616,8 @@ int reply_setattrE(connection_struct *conn, char *inbuf,char *outbuf, int dum_si /**************************************************************************** reply to a SMBgetattrE ****************************************************************************/ -int reply_getattrE(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int dum_buffsize) + +int reply_getattrE(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) { SMB_STRUCT_STAT sbuf; int outsize = 0; @@ -4237,7 +4630,7 @@ int reply_getattrE(connection_struct *conn, char *inbuf,char *outbuf, int dum_si CHECK_ERROR(fsp); /* Do an fstat on this file */ - if(fsp->conn->vfs_ops.fstat(fsp->fd_ptr->fd, &sbuf)) + if(sys_fstat(fsp->fd_ptr->fd, &sbuf)) return(UNIXERROR(ERRDOS,ERRnoaccess)); mode = dos_mode(conn,fsp->fsp_name,&sbuf); diff --git a/source3/smbd/server.c b/source3/smbd/server.c index bdb2827483..1c6d6536ad 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -25,9 +25,7 @@ pstring servicesf = CONFIGFILE; extern pstring debugf; extern fstring global_myworkgroup; -extern fstring global_sam_name; extern pstring global_myname; -extern dfs_internal dfs_struct; int am_parent = 1; @@ -49,8 +47,6 @@ extern int dcelogin_atmost_once; extern fstring remote_machine; extern pstring OriginalDir; -extern pstring myhostname; - /**************************************************************************** when exiting, take the whole family @@ -76,13 +72,11 @@ static void killkids(void) static BOOL open_sockets_inetd(void) { extern int Client; - extern int ClientPort; /* Started from inetd. fd 0 is the socket. */ /* We will abort gracefully when the client or remote system goes away */ Client = dup(0); - ClientPort = SMB_PORT; /* close our standard file descriptors */ close_low_fds(); @@ -93,40 +87,19 @@ static BOOL open_sockets_inetd(void) return True; } -/**************************************************************************** - open and listen to a socket -****************************************************************************/ -static int open_server_socket(int port, uint32 ipaddr) -{ - int s; - - s = open_socket_in(SOCK_STREAM, port, 0, ipaddr); - if(s == -1) - return -1; - /* ready to listen */ - if (listen(s, 5) == -1) { - DEBUG(0,("listen: %s\n", strerror(errno))); - close(s); - return -1; - } - return s; -} /**************************************************************************** open the socket communication ****************************************************************************/ -static BOOL open_sockets(BOOL is_daemon,int port,int port445) +static BOOL open_sockets(BOOL is_daemon,int port) { extern int Client; - extern int ClientPort; int num_interfaces = iface_count(); int fd_listenset[FD_SETSIZE]; fd_set listen_set; int s; int i; - memset(&fd_listenset, 0, sizeof(fd_listenset)); - if (!is_daemon) { return open_sockets_inetd(); } @@ -154,7 +127,7 @@ static BOOL open_sockets(BOOL is_daemon,int port,int port445) socket per interface and bind to only these. */ - if(num_interfaces * 2 > FD_SETSIZE) { + if(num_interfaces > FD_SETSIZE) { DEBUG(0,("open_sockets: Too many interfaces specified to bind to. Number was %d \ max can be %d\n", num_interfaces, FD_SETSIZE)); @@ -170,11 +143,15 @@ max can be %d\n", DEBUG(0,("open_sockets: interface %d has NULL IP address !\n", i)); continue; } - s = fd_listenset[i * 2] = open_server_socket(port, ifip->s_addr); - if(s == -1) return False; - FD_SET(s,&listen_set); - s = fd_listenset[i * 2 + 1] = open_server_socket(port445, ifip->s_addr); - if(s == -1) return False; + s = fd_listenset[i] = open_socket_in(SOCK_STREAM, port, 0, ifip->s_addr, True); + if(s == -1) + return False; + /* ready to listen */ + if (listen(s, 5) == -1) { + DEBUG(0,("listen: %s\n",strerror(errno))); + close(s); + return False; + } FD_SET(s,&listen_set); } } else { @@ -183,18 +160,21 @@ max can be %d\n", num_interfaces = 1; /* open an incoming socket */ - s = open_server_socket(port, interpret_addr(lp_socket_address())); + s = open_socket_in(SOCK_STREAM, port, 0, + interpret_addr(lp_socket_address()),True); if (s == -1) return(False); + + /* ready to listen */ + if (listen(s, 5) == -1) { + DEBUG(0,("open_sockets: listen: %s\n", + strerror(errno))); + close(s); + return False; + } + fd_listenset[0] = s; FD_SET(s,&listen_set); -#if 0 - s = open_server_socket(port445, interpret_addr(lp_socket_address())); - if (s == -1) - return(False); - fd_listenset[1] = s; - FD_SET(s,&listen_set); -#endif } /* now accept incoming connections - forking a new process @@ -207,11 +187,14 @@ max can be %d\n", memcpy((char *)&lfds, (char *)&listen_set, sizeof(listen_set)); - num = sys_select(256,&lfds,NULL, NULL); + num = sys_select(FD_SETSIZE,&lfds,NULL); if (num == -1 && errno == EINTR) continue; + /* check if we need to reload services */ + check_reload(time(NULL)); + /* Find the sockets that are read-ready - accept on these. */ for( ; num > 0; num--) { @@ -220,24 +203,15 @@ max can be %d\n", s = -1; for(i = 0; i < num_interfaces; i++) { - if(FD_ISSET(fd_listenset[i * 2],&lfds)) { - s = fd_listenset[i * 2]; - ClientPort = SMB_PORT; + if(FD_ISSET(fd_listenset[i],&lfds)) { + s = fd_listenset[i]; + /* Clear this so we don't look + at it again. */ + FD_CLR(fd_listenset[i],&lfds); break; } -#if 0 - if(FD_ISSET(fd_listenset[i * 2 + 1],&lfds)) { - s = fd_listenset[i * 2 + 1]; - ClientPort = SMB_PORT2; - break; - } -#endif } - /* Clear this so we don't look - at it again. */ - FD_CLR(s,&lfds); - Client = accept(s,&addr,&in_addrlen); if (Client == -1 && errno == EINTR) @@ -357,9 +331,10 @@ BOOL reload_services(BOOL test) /**************************************************************************** -this prevents zombie child processes + Catch a sighup. ****************************************************************************/ -BOOL reload_after_sighup = False; + +VOLATILE SIG_ATOMIC_T reload_after_sighup = False; static void sig_hup(int sig) { @@ -434,6 +409,8 @@ void exit_server(char *reason) conn_close_all(); + respond_to_all_remaining_local_messages(); + #ifdef WITH_DFS if (dcelogin_atmost_once) { dfs_unlogin(); @@ -456,13 +433,6 @@ void exit_server(char *reason) locking_end(); DEBUG(3,("Server exit (%s)\n", (reason ? reason : ""))); -#ifdef MEM_MAN - { - extern FILE *dbf; - smb_mem_write_verbose(dbf); - dbgflush(); - } -#endif exit(0); } @@ -471,17 +441,34 @@ void exit_server(char *reason) /**************************************************************************** initialise connect, service and file structs ****************************************************************************/ -static void init_structs(void) +static void init_structs(void ) { + /* + * Set the machine NETBIOS name if not already + * set from the config file. + */ + + if (!*global_myname) { + char *p; + fstrcpy( global_myname, myhostname() ); + p = strchr( global_myname, '.' ); + if (p) + *p = 0; + } + + strupper( global_myname ); + conn_init(); + file_init(); - init_rpc_pipe_hnd(); /* for RPC pipes */ - if (!init_policy_hnd(MAX_SERVER_POLICY_HANDLES)) - { - exit_server("could not allocate policy handles\n"); - } + + /* for RPC pipes */ + init_rpc_pipe_hnd(); + + /* for LSA handles */ + init_lsa_policy_hnd(); + init_dptrs(); - init_dfs_table(); } /**************************************************************************** @@ -489,19 +476,21 @@ usage on the program ****************************************************************************/ static void usage(char *pname) { - DEBUG(0,("Incorrect program usage - are you sure the command line is correct?\n")); - - printf("Usage: %s [-D] [-p port] [-d debuglevel] ", pname); - printf("[-l log basename] [-s services file]\n" ); - printf("Version %s\n",VERSION); - printf("\t-D become a daemon\n"); - printf("\t-p port listen on the specified port\n"); - printf("\t-d debuglevel set the debuglevel\n"); + + printf("Usage: %s [-DaoPh?V] [-d debuglevel] [-l log basename] [-p port]\n", pname); + printf(" [-O socket options] [-s services file] [-i scope]\n"); + printf("\t-D Become a daemon\n"); + printf("\t-a Append to log file (default)\n"); + printf("\t-o Overwrite log file, don't append\n"); + printf("\t-P Passive only\n"); + printf("\t-h Print usage\n"); + printf("\t-? Print usage\n"); + printf("\t-V Print version\n"); + printf("\t-d debuglevel Set the debuglevel\n"); printf("\t-l log basename. Basename for log/debug files\n"); + printf("\t-p port Listen on the specified port\n"); + printf("\t-O socket options Socket options\n"); printf("\t-s services file. Filename of services file\n"); - printf("\t-P passive only\n"); - printf("\t-a append to log file (default)\n"); - printf("\t-o overwrite log file, don't append\n"); printf("\t-i scope NetBIOS scope to use (default none)\n"); printf("\n"); } @@ -516,7 +505,6 @@ static void usage(char *pname) /* shall I run as a daemon */ BOOL is_daemon = False; int port = SMB_PORT; - int port445 = SMB_PORT2; int opt; extern char *optarg; @@ -524,55 +512,13 @@ static void usage(char *pname) set_auth_parameters(argc,argv); #endif -#ifdef HAVE_SETLUID - /* needed for SecureWare on SCO */ - setluid(0); -#endif - - append_log = True; - - TimeInit(); - - pstrcpy(debugf,SMBLOGFILE); - - pstrcpy(remote_machine, "smb"); - - setup_logging(argv[0],False); - - charset_initialise(); - - /* make absolutely sure we run as root - to handle cases where people - are crazy enough to have it setuid */ -#ifdef HAVE_SETRESUID - setresuid(0,0,0); -#else - setuid(0); - seteuid(0); - setuid(0); - seteuid(0); -#endif - - fault_setup((void (*)(void *))exit_server); - CatchSignal(SIGTERM , SIGNAL_CAST dflt_sig); - - /* we are never interested in SIGPIPE */ - BlockSignals(True,SIGPIPE); - - /* we want total control over the permissions on created files, - so set our umask to 0 */ - umask(0); - - dos_GetWd(OriginalDir); - - init_uid(); - /* this is for people who can't start the program correctly */ while (argc > 1 && (*argv[1] != '-')) { argv++; argc--; } - while ( EOF != (opt = getopt(argc, argv, "O:i:l:s:d:Dp:h?Paof:")) ) + while ( EOF != (opt = getopt(argc, argv, "O:i:l:s:d:Dp:h?VPaof:")) ) switch (opt) { case 'O': pstrcpy(user_socket_options,optarg); @@ -626,11 +572,72 @@ static void usage(char *pname) exit(0); break; + case 'V': + printf("Version %s\n",VERSION); + exit(0); + break; default: + DEBUG(0,("Incorrect program usage - are you sure the command line is correct?\n")); usage(argv[0]); exit(1); } +#ifdef HAVE_SETLUID + /* needed for SecureWare on SCO */ + setluid(0); +#endif + + /* + * gain_root_privilege uses an assert than will cause a core + * dump if euid != 0. Ensure this is the case. + */ + + if(geteuid() != (uid_t)0) { + fprintf(stderr, "%s: Version %s : Must have effective user id of zero to run.\n", argv[0], VERSION); + exit(1); + } + + append_log = True; + + TimeInit(); + + pstrcpy(debugf,SMBLOGFILE); + + pstrcpy(remote_machine, "smb"); + + setup_logging(argv[0],False); + + charset_initialise(); + + /* we want to re-seed early to prevent time delays causing + client problems at a later date. (tridge) */ + generate_random_buffer(NULL, 0, False); + + /* make absolutely sure we run as root - to handle cases where people + are crazy enough to have it setuid */ + + gain_root_privilege(); + gain_root_group_privilege(); + + fault_setup((void (*)(void *))exit_server); + CatchSignal(SIGTERM , SIGNAL_CAST dflt_sig); + + /* we are never interested in SIGPIPE */ + BlockSignals(True,SIGPIPE); + +#if defined(SIGFPE) + /* we are never interested in SIGFPE */ + BlockSignals(True,SIGFPE); +#endif + + /* we want total control over the permissions on created files, + so set our umask to 0 */ + umask(0); + + dos_GetWd(OriginalDir); + + init_uid(); + reopen_logs(); DEBUG(1,( "smbd version %s started.\n", VERSION)); @@ -644,13 +651,15 @@ static void usage(char *pname) exit(1); } - get_myname(myhostname,NULL); + /* + * Do this before reload_services. + */ if (!reload_services(False)) return(-1); init_structs(); - + #ifdef WITH_PROFILE if (!profile_setup(False)) { DEBUG(0,("ERROR: failed to setup profiling\n")); @@ -658,16 +667,6 @@ static void usage(char *pname) } #endif - /* - * Set the machine NETBIOS name if not already - * set from the config file. - */ - if (!*global_myname) - { - fstrcpy(global_myname, dns_to_netbios_name(myhostname)); - } - strupper(global_myname); - #ifdef WITH_SSL { extern BOOL sslEnabled; @@ -679,40 +678,10 @@ static void usage(char *pname) codepage_initialise(lp_client_code_page()); - if (!pwdb_initialise(True)) - { - exit(1); - } - - if(!initialise_sam_password_db()) - { - exit(1); - } - - if(!initialise_passgrp_db()) - { - exit(1); - } + fstrcpy(global_myworkgroup, lp_workgroup()); - if(!initialise_group_db()) - { - exit(1); - } - - if(!initialise_alias_db()) - { - exit(1); - } - - if(!initialise_builtin_db()) - { - exit(1); - } - - if (!get_member_domain_sid()) - { - DEBUG(0,("ERROR: Samba cannot obtain PDC SID from PDC(s) %s.\n", - lp_passwordserver())); + if(!pdb_generate_sam_sid()) { + DEBUG(0,("ERROR: Samba cannot create a SAM SID.\n")); exit(1); } @@ -746,7 +715,7 @@ static void usage(char *pname) become_daemon(); } - check_kernel_oplocks(); + check_kernel_oplocks(); if (!directory_exist(lp_lockdir(), NULL)) { mkdir(lp_lockdir(), 0755); @@ -756,10 +725,13 @@ static void usage(char *pname) pidfile_create("smbd"); } - if (!open_sockets(is_daemon,port,port445)) + if (!locking_init(0)) exit(1); - if (!locking_init(0)) + if (!open_sockets(is_daemon,port)) + exit(1); + + if(!initialize_password_db()) exit(1); /* possibly reload the services file. */ diff --git a/source3/smbd/service.c b/source3/smbd/service.c index 64abf3de1d..92807e2d43 100644 --- a/source3/smbd/service.c +++ b/source3/smbd/service.c @@ -23,7 +23,7 @@ extern int DEBUGLEVEL; -extern time_t smb_last_time; +extern struct timeval smb_last_time; extern int case_default; extern BOOL case_preserve; extern BOOL short_case_preserve; @@ -49,7 +49,7 @@ BOOL become_service(connection_struct *conn,BOOL do_chdir) return(False); } - conn->lastused = smb_last_time; + conn->lastused = smb_last_time.tv_sec; snum = SNUM(conn); @@ -84,24 +84,23 @@ int find_service(char *service) { int iService; - string_sub(service,"\\","/"); + all_string_sub(service,"\\","/",0); iService = lp_servicenumber(service); /* now handle the special case of a home directory */ if (iService < 0) { - char *phome_dir = get_unixhome_dir(service); - pstring home_dir; + char *phome_dir = get_user_home_dir(service); - if(phome_dir == NULL) + if(!phome_dir) { /* * Try mapping the servicename, it may * be a Windows to unix mapped user name. */ if(map_username(service)) - phome_dir = get_unixhome_dir(service); + phome_dir = get_user_home_dir(service); } DEBUG(3,("checking for home directory %s gave %s\n",service, @@ -110,10 +109,9 @@ int find_service(char *service) if (phome_dir) { int iHomeService; - pstrcpy(home_dir, phome_dir); if ((iHomeService = lp_servicenumber(HOMES_NAME)) >= 0) { - lp_add_home(service,iHomeService,home_dir); + lp_add_home(service,iHomeService,phome_dir); iService = lp_servicenumber(service); } } @@ -144,11 +142,6 @@ int find_service(char *service) } } - /* Check for default vfs service? Unsure whether to implement this */ - if (iService < 0) - { - } - /* just possibly it's a default service? */ if (iService < 0) { @@ -168,7 +161,7 @@ int find_service(char *service) iService = find_service(defservice); if (iService >= 0) { - string_sub(service,"_","/"); + all_string_sub(service,"_","/",0); iService = lp_add_service(service,iService); } } @@ -194,11 +187,12 @@ int find_service(char *service) connection_struct *make_connection(char *service,char *user,char *password, int pwlen, char *dev,uint16 vuid, int *ecode) { int snum; - const struct passwd *pass = NULL; + struct passwd *pass = NULL; BOOL guest = False; BOOL force = False; extern int Client; connection_struct *conn; + int ret; strlower(service); @@ -218,21 +212,31 @@ connection_struct *make_connection(char *service,char *user,char *password, int } if (strequal(service,HOMES_NAME)) { - if (*user && Get_Pwnam(user,True)) - return(make_connection(user,user,password, + if (*user && Get_Pwnam(user,True)) { + fstring dos_username; + fstrcpy(dos_username, user); + unix_to_dos(dos_username, True); + return(make_connection(dos_username,user,password, pwlen,dev,vuid,ecode)); + } if(lp_security() != SEC_SHARE) { if (validated_username(vuid)) { - pstrcpy(user,validated_username(vuid)); - return(make_connection(user,user,password,pwlen,dev,vuid,ecode)); + fstring dos_username; + fstrcpy(user,validated_username(vuid)); + fstrcpy(dos_username, user); + unix_to_dos(dos_username, True); + return(make_connection(dos_username,user,password,pwlen,dev,vuid,ecode)); } } else { /* Security = share. Try with sesssetup_user * as the username. */ if(*sesssetup_user) { - pstrcpy(user,sesssetup_user); - return(make_connection(user,user,password,pwlen,dev,vuid,ecode)); + fstring dos_username; + fstrcpy(user,sesssetup_user); + fstrcpy(dos_username, user); + unix_to_dos(dos_username, True); + return(make_connection(dos_username,user,password,pwlen,dev,vuid,ecode)); } } } @@ -300,13 +304,13 @@ connection_struct *make_connection(char *service,char *user,char *password, int { pstring list; StrnCpy(list,lp_readlist(snum),sizeof(pstring)-1); - string_sub(list,"%S",service); + pstring_sub(list,"%S",service); if (user_in_list(user,list)) conn->read_only = True; StrnCpy(list,lp_writelist(snum),sizeof(pstring)-1); - string_sub(list,"%S",service); + pstring_sub(list,"%S",service); if (user_in_list(user,list)) conn->read_only = False; @@ -333,6 +337,7 @@ connection_struct *make_connection(char *service,char *user,char *password, int conn->vuid = vuid; conn->uid = pass->pw_uid; conn->gid = pass->pw_gid; + safe_strcpy(conn->client_address, client_addr(Client), sizeof(conn->client_address)-1); conn->num_files_open = 0; conn->lastused = time(NULL); conn->service = snum; @@ -345,65 +350,83 @@ connection_struct *make_connection(char *service,char *user,char *password, int conn->veto_oplock_list = NULL; string_set(&conn->dirpath,""); string_set(&conn->user,user); + + /* + * If force user is true, then store the + * given userid and also the primary groupid + * of the user we're forcing. + */ + + if (*lp_force_user(snum)) { + struct passwd *pass2; + pstring fuser; + pstrcpy(fuser,lp_force_user(snum)); - /* Initialise VFS function pointers */ - - if (*lp_vfsobj(SNUM(conn))) { - -#ifdef HAVE_LIBDL - - /* Loadable object file */ - - if (!vfs_init_custom(conn)) { - return NULL; - } -#else - DEBUG(0, ("No libdl present - cannot use VFS objects\n")); - conn_free(conn); - return NULL; -#endif - - } else { - - /* Normal share - initialise with disk access functions */ + /* Allow %S to be used by force user. */ + pstring_sub(fuser,"%S",service); - vfs_init_default(conn); + pass2 = (struct passwd *)Get_Pwnam(fuser,True); + if (pass2) { + conn->uid = pass2->pw_uid; + conn->gid = pass2->pw_gid; + string_set(&conn->user,fuser); + fstrcpy(user,fuser); + conn->force_user = True; + DEBUG(3,("Forced user %s\n",fuser)); + } else { + DEBUG(1,("Couldn't find user %s\n",fuser)); + } } #ifdef HAVE_GETGRNAM + /* + * If force group is true, then override + * any groupid stored for the connecting user. + */ + if (*lp_force_group(snum)) { struct group *gptr; pstring gname; + pstring tmp_gname; + BOOL user_must_be_member = False; - StrnCpy(gname,lp_force_group(snum),sizeof(pstring)-1); + StrnCpy(tmp_gname,lp_force_group(snum),sizeof(pstring)-1); + + if (tmp_gname[0] == '+') { + user_must_be_member = True; + StrnCpy(gname,&tmp_gname[1],sizeof(pstring)-2); + } else { + StrnCpy(gname,tmp_gname,sizeof(pstring)-1); + } /* default service may be a group name */ - string_sub(gname,"%S",service); + pstring_sub(gname,"%S",service); gptr = (struct group *)getgrnam(gname); if (gptr) { - conn->gid = gptr->gr_gid; - DEBUG(3,("Forced group %s\n",gname)); + /* + * If the user has been forced and the forced group starts + * with a '+', then we only set the group to be the forced + * group if the forced user is a member of that group. + * Otherwise, the meaning of the '+' would be ignored. + */ + if (conn->force_user && user_must_be_member) { + int i; + for (i = 0; gptr->gr_mem[i] != NULL; i++) { + if (strcmp(user,gptr->gr_mem[i]) == 0) { + conn->gid = gptr->gr_gid; + DEBUG(3,("Forced group %s for member %s\n",gname,user)); + break; + } + } + } else { + conn->gid = gptr->gr_gid; + DEBUG(3,("Forced group %s\n",gname)); + } } else { DEBUG(1,("Couldn't find group %s\n",gname)); } } -#endif - - if (*lp_force_user(snum)) { - const struct passwd *pass2; - fstring fuser; - fstrcpy(fuser,lp_force_user(snum)); - pass2 = (const struct passwd *)Get_Pwnam(fuser,True); - if (pass2) { - conn->uid = pass2->pw_uid; - string_set(&conn->user,fuser); - fstrcpy(user,fuser); - conn->force_user = True; - DEBUG(3,("Forced user %s\n",fuser)); - } else { - DEBUG(1,("Couldn't find user %s\n",fuser)); - } - } +#endif /* HAVE_GETGRNAM */ { pstring s; @@ -420,7 +443,7 @@ connection_struct *make_connection(char *service,char *user,char *password, int if (!IS_IPC(conn)) { /* Find all the groups this uid is in and store them. Used by become_user() */ - get_unixgroups(conn->user,conn->uid,conn->gid, + setup_groups(conn->user,conn->uid,conn->gid, &conn->ngroups,&conn->groups); /* check number of connections */ @@ -445,7 +468,13 @@ connection_struct *make_connection(char *service,char *user,char *password, int pstrcpy(cmd,lp_rootpreexec(SNUM(conn))); standard_sub(conn,cmd); DEBUG(5,("cmd=%s\n",cmd)); - smbrun(cmd,NULL,False); + ret = smbrun(cmd,NULL,False); + if (ret != 0 && lp_rootpreexec_close(SNUM(conn))) { + DEBUG(1,("preexec gave %d - failing connection\n", ret)); + conn_free(conn); + *ecode = ERRsrverror; + return NULL; + } } if (!become_user(conn, conn->vuid)) { @@ -499,7 +528,26 @@ connection_struct *make_connection(char *service,char *user,char *password, int pstring cmd; pstrcpy(cmd,lp_preexec(SNUM(conn))); standard_sub(conn,cmd); - smbrun(cmd,NULL,False); + ret = smbrun(cmd,NULL,False); + if (ret != 0 && lp_preexec_close(SNUM(conn))) { + DEBUG(1,("preexec gave %d - failing connection\n", ret)); + conn_free(conn); + *ecode = ERRsrverror; + return NULL; + } + } + + /* + * Print out the 'connected as' stuff here as we need + * to know the effective uid and gid we will be using. + */ + + if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) { + dbgtext( "%s (%s) ", remote_machine, conn->client_address ); + dbgtext( "connect to service %s ", lp_servicename(SNUM(conn)) ); + dbgtext( "as user %s ", user ); + dbgtext( "(uid=%d, gid=%d) ", (int)geteuid(), (int)getegid() ); + dbgtext( "(pid %d)\n", (int)getpid() ); } /* we've finished with the sensitive stuff */ @@ -512,61 +560,6 @@ connection_struct *make_connection(char *service,char *user,char *password, int set_namearray( &conn->veto_oplock_list, lp_veto_oplocks(SNUM(conn))); } - if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) { - extern int Client; - - dbgtext( "%s (%s) ", remote_machine, client_addr(Client) ); - dbgtext( "connect to service %s ", lp_servicename(SNUM(conn))); - dbgtext( "as user %s ", user ); - dbgtext( "(uid=%d, gid=%d) ", (int)conn->uid, (int)conn->gid ); - dbgtext( "(pid %d)\n", (int)getpid() ); - } - - /* Invoke make connection hook */ - - if (conn->vfs_ops.connect) { - struct vfs_connection_struct *vconn; - - vconn = (struct vfs_connection_struct *) - malloc(sizeof(struct vfs_connection_struct)); - - if (vconn == NULL) { - DEBUG(0, ("No memory to create vfs_connection_struct")); - return NULL; - } - - ZERO_STRUCTP(vconn); - - /* Copy across relevant data from connection struct */ - - vconn->printer = conn->printer; - vconn->ipc = conn->ipc; - vconn->read_only = conn->read_only; - vconn->admin_user = conn->admin_user; - - pstrcpy(vconn->dirpath, conn->dirpath); - pstrcpy(vconn->connectpath, conn->connectpath); - pstrcpy(vconn->origpath, conn->origpath); - - pstrcpy(vconn->service, service); - pstrcpy(vconn->user, conn->user); - - vconn->uid = conn->uid; - vconn->gid = conn->gid; - vconn->ngroups = conn->ngroups; - vconn->groups = (gid_t *)malloc(conn->ngroups * sizeof(gid_t)); - if (vconn->groups != NULL) { - memcpy(vconn->groups, conn->groups, - conn->ngroups * sizeof(gid_t)); - } - - /* Call connect hook */ - - if (conn->vfs_ops.connect(vconn, service, user) < 0) { - return NULL; - } - } - return(conn); } @@ -576,31 +569,14 @@ close a cnum ****************************************************************************/ void close_cnum(connection_struct *conn, uint16 vuid) { - extern int Client; DirCacheFlush(SNUM(conn)); unbecome_user(); DEBUG(IS_IPC(conn)?3:1, ("%s (%s) closed connection to service %s\n", - remote_machine,client_addr(Client), + remote_machine,conn->client_address, lp_servicename(SNUM(conn)))); - if (conn->vfs_ops.disconnect != NULL) { - - /* Call disconnect hook */ - - conn->vfs_ops.disconnect(); - - /* Free vfs_connection_struct */ - - if (conn->vfs_conn != NULL) { - if (conn->vfs_conn->groups != NULL) { - free(conn->vfs_conn->groups); - } - free(conn->vfs_conn); - } - } - yield_connection(conn, lp_servicename(SNUM(conn)), lp_max_connections(SNUM(conn))); @@ -632,5 +608,3 @@ void close_cnum(connection_struct *conn, uint16 vuid) conn_free(conn); } - - diff --git a/source3/smbd/ssl.c b/source3/smbd/ssl.c index 1f098b2533..be9aae7c5c 100644 --- a/source3/smbd/ssl.c +++ b/source3/smbd/ssl.c @@ -19,14 +19,14 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "includes.h" - -/* - * Hmmm, only check on WITH_SSL after we have included includes.h - * which pulls in config.h which is where WITH_SSL is defined, if - * at all :-) +/* + * since includes.h pulls in config.h which is were WITH_SSL will be + * defined, we want to include includes.h before testing for WITH_SSL + * RJS 26-Jan-1999 */ +#include "includes.h" + #ifdef WITH_SSL /* should always be defined if this module is compiled */ #include <ssl.h> diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 14fa26dd55..e971f8bbb6 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -31,7 +31,7 @@ extern int Client; extern int smb_read_error; extern fstring local_machine; extern int global_oplock_break; -extern dfs_internal dfs_struct; +extern uint32 global_client_caps; /**************************************************************************** Send the required number of replies back. @@ -113,8 +113,7 @@ static int send_trans2_replies(char *outbuf, int bufsize, char *params, SSVAL(outbuf,smb_prcnt, params_sent_thistime); if(params_sent_thistime == 0) { - /*SSVAL(outbuf,smb_proff,0);*/ - SSVAL(outbuf,smb_proff,((smb_buf(outbuf)+alignment_offset) - smb_base(outbuf))); + SSVAL(outbuf,smb_proff,0); SSVAL(outbuf,smb_prdisp,0); } else @@ -216,11 +215,7 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, /* XXXX we need to handle passed times, sattr and flags */ - if (!unix_dfs_convert(fname,conn,0,&bad_path,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(fname,conn,0,&bad_path,NULL); fsp = file_new(); if (!fsp) @@ -253,7 +248,7 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, return(UNIXERROR(ERRDOS,ERRnoaccess)); } - if (fsp->conn->vfs_ops.fstat(fsp->fd_ptr->fd,&sbuf) != 0) { + if (sys_fstat(fsp->fd_ptr->fd,&sbuf) != 0) { close_file(fsp,False); return(UNIXERROR(ERRDOS,ERRnoaccess)); } @@ -272,7 +267,7 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, if(params == NULL) return(ERROR(ERRDOS,ERRnomem)); - bzero(params,28); + memset((char *)params,'\0',28); SSVAL(params,0,fsp->fnum); SSVAL(params,2,fmode); put_dos_date2(params,4, mtime); @@ -298,12 +293,12 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, /**************************************************************************** get a level dependent lanman2 dir entry. ****************************************************************************/ -static int get_lanman2_dir_entry(connection_struct *conn, +static BOOL get_lanman2_dir_entry(connection_struct *conn, char *path_mask,int dirtype,int info_level, int requires_resume_key, BOOL dont_descend,char **ppdata, char *base_data, int space_remaining, - BOOL *out_of_space, + BOOL *out_of_space, BOOL *got_exact_match, int *last_name_off) { char *dname; @@ -329,6 +324,7 @@ static int get_lanman2_dir_entry(connection_struct *conn, *fname = 0; *out_of_space = False; + *got_exact_match = False; if (!conn->dirptr) return(False); @@ -346,6 +342,8 @@ static int get_lanman2_dir_entry(connection_struct *conn, while (!found) { + BOOL got_match; + /* Needed if we run out of space */ prev_dirpos = TellDir(conn->dirptr); dname = ReadDirName(conn->dirptr); @@ -367,7 +365,26 @@ static int get_lanman2_dir_entry(connection_struct *conn, pstrcpy(fname,dname); - if(mask_match(fname, mask, case_sensitive, True)) + if(!(got_match = *got_exact_match = exact_match(fname, mask, case_sensitive))) + got_match = mask_match(fname, mask, case_sensitive, True); + + if(!got_match && !is_8_3(fname, False)) { + + /* + * It turns out that NT matches wildcards against + * both long *and* short names. This may explain some + * of the wildcard wierdness from old DOS clients + * that some people have been seeing.... JRA. + */ + + pstring newname; + pstrcpy( newname, fname); + name_map_mangle( newname, True, False, SNUM(conn)); + if(!(got_match = *got_exact_match = exact_match(newname, mask, case_sensitive))) + got_match = mask_match(newname, mask, case_sensitive, True); + } + + if(got_match) { BOOL isdots = (strequal(fname,"..") || strequal(fname,".")); if (dont_descend && !isdots) @@ -380,7 +397,7 @@ static int get_lanman2_dir_entry(connection_struct *conn, if(needslash) pstrcat(pathreal,"/"); pstrcat(pathreal,dname); - if (conn->vfs_ops.stat(dos_to_unix(pathreal,False),&sbuf) != 0) + if (dos_stat(pathreal,&sbuf) != 0) { DEBUG(5,("get_lanman2_dir_entry:Couldn't stat [%s] (%s)\n",pathreal,strerror(errno))); continue; @@ -406,7 +423,7 @@ static int get_lanman2_dir_entry(connection_struct *conn, } } - name_map_mangle(fname,False,SNUM(conn)); + name_map_mangle(fname,False,True,SNUM(conn)); p = pdata; nameptr = p; @@ -502,7 +519,7 @@ static int get_lanman2_dir_entry(connection_struct *conn, SIVAL(p,0,0); p += 4; if (!was_8_3) { pstrcpy(p+2,fname); - if (!name_map_mangle(p+2,True,SNUM(conn))) + if (!name_map_mangle(p+2,True,True,SNUM(conn))) (p+2)[12] = 0; } else *(p+2) = 0; @@ -579,6 +596,7 @@ static int get_lanman2_dir_entry(connection_struct *conn, *last_name_off = PTR_DIFF(nameptr,base_data); /* Advance the data pointer to the next slot */ *ppdata = p; + return(found); } @@ -610,8 +628,9 @@ void mask_convert( char *mask) } /**************************************************************************** - reply to a TRANS2_FINDFIRST + Reply to a TRANS2_FINDFIRST. ****************************************************************************/ + static int call_trans2findfirst(connection_struct *conn, char *inbuf, char *outbuf, int bufsize, char **pparams, char **ppdata) @@ -669,11 +688,7 @@ static int call_trans2findfirst(connection_struct *conn, DEBUG(5,("path=%s\n",directory)); - if (!unix_dfs_convert(directory,conn,0,&bad_path,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(directory,conn,0,&bad_path,NULL); if(!check_name(directory,conn)) { if((errno == ENOENT) && bad_path) { @@ -708,40 +723,25 @@ static int call_trans2findfirst(connection_struct *conn, pdata = *ppdata = Realloc(*ppdata, max_data_bytes + 1024); if(!*ppdata) return(ERROR(ERRDOS,ERRnomem)); - bzero(pdata,max_data_bytes); + memset((char *)pdata,'\0',max_data_bytes + 1024); /* Realloc the params space */ params = *pparams = Realloc(*pparams, 10); if(params == NULL) return(ERROR(ERRDOS,ERRnomem)); - dptr_num = dptr_create(conn,directory, True ,SVAL(inbuf,smb_pid)); + dptr_num = dptr_create(conn,directory, False, True ,SVAL(inbuf,smb_pid)); if (dptr_num < 0) return(UNIXERROR(ERRDOS,ERRbadfile)); /* Convert the formatted mask. */ mask_convert(mask); -#if 0 /* JRA */ - /* - * Now we have a working mask_match in util.c, I believe - * we no longer need these hacks (in fact they break - * things). JRA. - */ - - /* a special case for 16 bit apps */ - if (strequal(mask,"????????.???")) pstrcpy(mask,"*"); - - /* handle broken clients that send us old 8.3 format */ - string_sub(mask,"????????","*"); - string_sub(mask,".???",".*"); -#endif /* JRA */ - /* Save the wildcard match and attribs we are using on this directory - needed as lanman2 assumes these are being saved between calls */ if(!(wcard = strdup(mask))) { - dptr_close(dptr_num); + dptr_close(&dptr_num); return(ERROR(ERRDOS,ERRnomem)); } @@ -763,39 +763,49 @@ static int call_trans2findfirst(connection_struct *conn, out_of_space = False; for (i=0;(i<maxentries) && !finished && !out_of_space;i++) + { + BOOL got_exact_match; + + /* this is a heuristic to avoid seeking the dirptr except when + absolutely necessary. It allows for a filename of about 40 chars */ + if (space_remaining < DIRLEN_GUESS && numentries > 0) { + out_of_space = True; + finished = False; + } + else + { + finished = !get_lanman2_dir_entry(conn,mask,dirtype,info_level, + requires_resume_key,dont_descend, + &p,pdata,space_remaining, &out_of_space, &got_exact_match, + &last_name_off); + } - /* this is a heuristic to avoid seeking the dirptr except when - absolutely necessary. It allows for a filename of about 40 chars */ - if (space_remaining < DIRLEN_GUESS && numentries > 0) - { - out_of_space = True; - finished = False; - } - else - { - finished = - !get_lanman2_dir_entry(conn,mask,dirtype,info_level, - requires_resume_key,dont_descend, - &p,pdata,space_remaining, &out_of_space, - &last_name_off); - } + if (finished && out_of_space) + finished = False; - if (finished && out_of_space) - finished = False; + if (!finished && !out_of_space) + numentries++; - if (!finished && !out_of_space) - numentries++; - space_remaining = max_data_bytes - PTR_DIFF(p,pdata); - } + /* + * As an optimisation if we know we aren't looking + * for a wildcard name (ie. the name matches the wildcard exactly) + * then we can finish on any (first) match. + * This speeds up large directory searches. JRA. + */ + + if(got_exact_match) + finished = True; + + space_remaining = max_data_bytes - PTR_DIFF(p,pdata); + } /* Check if we can close the dirptr */ if(close_after_first || (finished && close_if_end)) - { - dptr_close(dptr_num); - DEBUG(5,("call_trans2findfirst - (2) closing dptr_num %d\n", dptr_num)); - dptr_num = -1; - } + { + DEBUG(5,("call_trans2findfirst - (2) closing dptr_num %d\n", dptr_num)); + dptr_close(&dptr_num); + } /* * If there are no matching entries we must return ERRDOS/ERRbadfile - @@ -803,7 +813,10 @@ static int call_trans2findfirst(connection_struct *conn, */ if(numentries == 0) + { + dptr_close(&dptr_num); return(ERROR(ERRDOS,ERRbadfile)); + } /* At this point pdata points to numentries directory entries. */ @@ -823,6 +836,17 @@ static int call_trans2findfirst(connection_struct *conn, smb_fn_name(CVAL(inbuf,smb_com)), mask, directory, dirtype, numentries ) ); + /* + * Force a name mangle here to ensure that the + * mask as an 8.3 name is top of the mangled cache. + * The reasons for this are subtle. Don't remove + * this code unless you know what you are doing + * (see PR#13758). JRA. + */ + + if(!is_8_3( mask, False)) + name_map_mangle(mask, True, True, SNUM(conn)); + return(-1); } @@ -843,7 +867,7 @@ static int call_trans2findnext(connection_struct *conn, int max_data_bytes = SVAL(inbuf, smb_mdrcnt); char *params = *pparams; char *pdata = *ppdata; - int16 dptr_num = SVAL(params,0); + int dptr_num = SVAL(params,0); int maxentries = SVAL(params,2); uint16 info_level = SVAL(params,4); uint32 resume_key = IVAL(params,6); @@ -891,7 +915,7 @@ resume_key = %d resume name = %s continue=%d level = %d\n", pdata = *ppdata = Realloc( *ppdata, max_data_bytes + 1024); if(!*ppdata) return(ERROR(ERRDOS,ERRnomem)); - bzero(pdata,max_data_bytes); + memset((char *)pdata,'\0',max_data_bytes + 1024); /* Realloc the params space */ params = *pparams = Realloc(*pparams, 6*SIZEOFWORD); @@ -968,7 +992,7 @@ resume_key = %d resume name = %s continue=%d level = %d\n", */ if(dname != NULL) - name_map_mangle( dname, False, SNUM(conn)); + name_map_mangle( dname, False, True, SNUM(conn)); if(dname && strcsequal( resume_name, dname)) { @@ -996,7 +1020,7 @@ resume_key = %d resume name = %s continue=%d level = %d\n", */ if(dname != NULL) - name_map_mangle( dname, False, SNUM(conn)); + name_map_mangle( dname, False, True, SNUM(conn)); if(dname && strcsequal( resume_name, dname)) { @@ -1009,38 +1033,49 @@ resume_key = %d resume name = %s continue=%d level = %d\n", } /* end if requires_resume_key && !continue_bit */ for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++) + { + BOOL got_exact_match; + + /* this is a heuristic to avoid seeking the dirptr except when + absolutely necessary. It allows for a filename of about 40 chars */ + if (space_remaining < DIRLEN_GUESS && numentries > 0) { - /* this is a heuristic to avoid seeking the dirptr except when - absolutely necessary. It allows for a filename of about 40 chars */ - if (space_remaining < DIRLEN_GUESS && numentries > 0) - { - out_of_space = True; - finished = False; - } - else - { - finished = - !get_lanman2_dir_entry(conn,mask,dirtype,info_level, - requires_resume_key,dont_descend, - &p,pdata,space_remaining, &out_of_space, - &last_name_off); - } + out_of_space = True; + finished = False; + } + else + { + finished = !get_lanman2_dir_entry(conn,mask,dirtype,info_level, + requires_resume_key,dont_descend, + &p,pdata,space_remaining, &out_of_space, &got_exact_match, + &last_name_off); + } - if (finished && out_of_space) - finished = False; + if (finished && out_of_space) + finished = False; - if (!finished && !out_of_space) - numentries++; - space_remaining = max_data_bytes - PTR_DIFF(p,pdata); - } + if (!finished && !out_of_space) + numentries++; + + /* + * As an optimisation if we know we aren't looking + * for a wildcard name (ie. the name matches the wildcard exactly) + * then we can finish on any (first) match. + * This speeds up large directory searches. JRA. + */ + + if(got_exact_match) + finished = True; + + space_remaining = max_data_bytes - PTR_DIFF(p,pdata); + } /* Check if we can close the dirptr */ if(close_after_request || (finished && close_if_end)) - { - dptr_close(dptr_num); /* This frees up the saved mask */ - DEBUG(5,("call_trans2findnext: closing dptr_num = %d\n", dptr_num)); - dptr_num = -1; - } + { + DEBUG(5,("call_trans2findnext: closing dptr_num = %d\n", dptr_num)); + dptr_close(&dptr_num); /* This frees up the saved mask */ + } /* Set up the return parameter block */ @@ -1070,6 +1105,7 @@ static int call_trans2qfsinfo(connection_struct *conn, int length, int bufsize, char **pparams, char **ppdata) { + int max_data_bytes = SVAL(inbuf, smb_mdrcnt); char *pdata = *ppdata; char *params = *pparams; uint16 info_level = SVAL(params,0); @@ -1078,16 +1114,16 @@ static int call_trans2qfsinfo(connection_struct *conn, char *vname = volume_label(SNUM(conn)); int snum = SNUM(conn); char *fstype = lp_fstype(SNUM(conn)); - extern uint32 global_client_caps; DEBUG(3,("call_trans2qfsinfo: level = %d\n", info_level)); - if(conn->vfs_ops.stat(".",&st)!=0) { + if(dos_stat(".",&st)!=0) { DEBUG(2,("call_trans2qfsinfo: stat of . failed (%s)\n", strerror(errno))); return (ERROR(ERRSRV,ERRinvdevice)); } - pdata = *ppdata = Realloc(*ppdata, 1024); bzero(pdata,1024); + pdata = *ppdata = Realloc(*ppdata, max_data_bytes + 1024); + memset((char *)pdata,'\0',max_data_bytes + 1024); switch (info_level) { @@ -1095,7 +1131,7 @@ static int call_trans2qfsinfo(connection_struct *conn, { SMB_BIG_UINT dfree,dsize,bsize; data_len = 18; - conn->vfs_ops.disk_free(".",&bsize,&dfree,&dsize); + sys_disk_free(".",False,&bsize,&dfree,&dsize); SIVAL(pdata,l1_idFileSystem,st.st_dev); SIVAL(pdata,l1_cSectorUnit,bsize/512); SIVAL(pdata,l1_cUnit,dsize); @@ -1124,16 +1160,21 @@ static int call_trans2qfsinfo(connection_struct *conn, break; } case SMB_QUERY_FS_ATTRIBUTE_INFO: - data_len = 12 + 2*strlen(fstype); - SIVAL(pdata,0,FILE_CASE_PRESERVED_NAMES|FILE_CASE_SENSITIVE_SEARCH); /* FS ATTRIBUTES */ + { + int fstype_len; + SIVAL(pdata,0,FILE_CASE_PRESERVED_NAMES|FILE_CASE_SENSITIVE_SEARCH| + lp_nt_acl_support() ? FILE_PERSISTENT_ACLS : 0); /* FS ATTRIBUTES */ #if 0 /* Old code. JRA. */ SIVAL(pdata,0,0x4006); /* FS ATTRIBUTES == long filenames supported? */ #endif /* Old code. */ + SIVAL(pdata,4,128); /* Max filename component length */ - SIVAL(pdata,8,2*strlen(fstype)); - ascii_to_unibuf(pdata+12, fstype, 1024-2-12); + fstype_len = dos_PutUniCode(pdata+12,unix_to_dos(fstype,False),sizeof(pstring)/2); + SIVAL(pdata,8,fstype_len); + data_len = 12 + fstype_len; SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2)|FLAGS2_UNICODE_STRINGS); break; + } case SMB_QUERY_FS_LABEL_INFO: data_len = 4 + strlen(vname); SIVAL(pdata,0,strlen(vname)); @@ -1158,19 +1199,19 @@ static int call_trans2qfsinfo(connection_struct *conn, } else { data_len = 18 + 2*strlen(vname); SIVAL(pdata,12,strlen(vname)*2); - ascii_to_unibuf(pdata+18, vname, 1024-2-18); + dos_PutUniCode(pdata+18,unix_to_dos(vname,False),sizeof(pstring)/2); } DEBUG(5,("call_trans2qfsinfo : SMB_QUERY_FS_VOLUME_INFO namelen = %d, vol = %s\n", - strlen(vname),vname)); + (int)strlen(vname),vname)); break; case SMB_QUERY_FS_SIZE_INFO: { SMB_BIG_UINT dfree,dsize,bsize; data_len = 24; - conn->vfs_ops.disk_free(".",&bsize,&dfree,&dsize); - SIVAL(pdata,0,dsize); - SIVAL(pdata,8,dfree); + sys_disk_free(".",False,&bsize,&dfree,&dsize); + SBIG_UINT(pdata,0,dsize); + SBIG_UINT(pdata,8,dfree); SIVAL(pdata,16,bsize/512); SIVAL(pdata,20,512); break; @@ -1236,6 +1277,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn, char **pparams,char **ppdata, int total_data) { + int max_data_bytes = SVAL(inbuf, smb_mdrcnt); char *params = *pparams; char *pdata = *ppdata; uint16 tran_call = SVAL(inbuf, smb_setup0); @@ -1256,20 +1298,17 @@ static int call_trans2qfilepathinfo(connection_struct *conn, files_struct *fsp = file_fsp(params,0); info_level = SVAL(params,2); - if(fsp && fsp->open && fsp->is_directory) { + DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QFILEINFO: level = %d\n", info_level)); + + if(fsp && fsp->open && (fsp->is_directory || fsp->stat_open)) { /* * This is actually a QFILEINFO on a directory * handle (returned from an NT SMB). NT5.0 seems * to do this call. JRA. */ fname = fsp->fsp_name; - if (!unix_dfs_convert(fname,conn,0,&bad_path,&sbuf)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } - if (!check_name(fname,conn) || (!VALID_STAT(sbuf) && - conn->vfs_ops.stat(dos_to_unix(fname,False),&sbuf))) { + unix_convert(fname,conn,0,&bad_path,&sbuf); + if (!check_name(fname,conn) || (!VALID_STAT(sbuf) && dos_stat(fname,&sbuf))) { DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno))); if((errno == ENOENT) && bad_path) { @@ -1278,6 +1317,9 @@ static int call_trans2qfilepathinfo(connection_struct *conn, } return(UNIXERROR(ERRDOS,ERRbadpath)); } + + delete_pending = fsp->directory_delete_on_close; + } else { /* * Original code - this is an open file. @@ -1286,11 +1328,11 @@ static int call_trans2qfilepathinfo(connection_struct *conn, CHECK_ERROR(fsp); fname = fsp->fsp_name; - if (fsp->conn->vfs_ops.fstat(fsp->fd_ptr->fd,&sbuf) != 0) { + if (sys_fstat(fsp->fd_ptr->fd,&sbuf) != 0) { DEBUG(3,("fstat of fnum %d failed (%s)\n",fsp->fnum, strerror(errno))); return(UNIXERROR(ERRDOS,ERRbadfid)); } - if((pos = fsp->conn->vfs_ops.lseek(fsp->fd_ptr->fd,0,SEEK_CUR)) == -1) + if((pos = sys_lseek(fsp->fd_ptr->fd,0,SEEK_CUR)) == -1) return(UNIXERROR(ERRDOS,ERRnoaccess)); delete_pending = fsp->fd_ptr->delete_on_close; @@ -1298,16 +1340,13 @@ static int call_trans2qfilepathinfo(connection_struct *conn, } else { /* qpathinfo */ info_level = SVAL(params,0); + + DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QPATHINFO: level = %d\n", info_level)); + fname = &fname1[0]; pstrcpy(fname,¶ms[6]); - if (!unix_dfs_convert(fname,conn,0,&bad_path,&sbuf)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } - if (!check_name(fname,conn) || - (!VALID_STAT(sbuf) && conn->vfs_ops.stat(dos_to_unix(fname,False), - &sbuf))) { + unix_convert(fname,conn,0,&bad_path,&sbuf); + if (!check_name(fname,conn) || (!VALID_STAT(sbuf) && dos_stat(fname,&sbuf))) { DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno))); if((errno == ENOENT) && bad_path) { @@ -1335,8 +1374,9 @@ static int call_trans2qfilepathinfo(connection_struct *conn, /* from now on we only want the part after the / */ fname = p; - params = *pparams = Realloc(*pparams,2); bzero(params,2); - data_size = 1024; + params = *pparams = Realloc(*pparams,2); + memset((char *)params,'\0',2); + data_size = max_data_bytes + 1024; pdata = *ppdata = Realloc(*ppdata, data_size); if (total_data > 0 && IVAL(pdata,0) == total_data) { @@ -1345,7 +1385,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn, return(ERROR(ERRDOS,ERROR_EAS_NOT_SUPPORTED)); } - bzero(pdata,data_size); + memset((char *)pdata,'\0',data_size); switch (info_level) { @@ -1416,26 +1456,37 @@ static int call_trans2qfilepathinfo(connection_struct *conn, case SMB_QUERY_FILE_ALT_NAME_INFO: { pstring short_name; - char *data_end; - pstrcpy(short_name,p); /* Mangle if not already 8.3 */ if(!is_8_3(short_name, True)) { - if(!name_map_mangle(short_name,True,SNUM(conn))) + if(!name_map_mangle(short_name,True,True,SNUM(conn))) *short_name = '\0'; } strupper(short_name); - data_end = ascii_to_unibuf(pdata + 4, short_name, 1024-2-4); - data_size = data_end - pdata; - SIVAL(pdata,0,2*(data_size-4)); + l = strlen(short_name); + dos_PutUniCode(pdata + 4, unix_to_dos(short_name,False),sizeof(pstring)*2); + data_size = 4 + (2*l); + SIVAL(pdata,0,2*l); } break; case SMB_QUERY_FILE_NAME_INFO: + /* + * The first part of this code is essential + * to get security descriptors to work on mapped + * drives. Don't ask how I discovered this unless + * you like hearing about me suffering.... :-). JRA. + */ + if(strequal(".", fname) && (global_client_caps & CAP_UNICODE)) { + l = l*2; + SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2)|FLAGS2_UNICODE_STRINGS); + dos_PutUniCode(pdata + 4, unix_to_dos("\\",False),sizeof(pstring)*2); + } else { + pstrcpy(pdata+4,fname); + } data_size = 4 + l; SIVAL(pdata,0,l); - pstrcpy(pdata+4,fname); break; case SMB_QUERY_FILE_ALLOCATION_INFO: @@ -1527,20 +1578,15 @@ static int call_trans2setfilepathinfo(connection_struct *conn, fsp = file_fsp(params,0); info_level = SVAL(params,2); - if(fsp && fsp->open && fsp->is_directory) { + if(fsp && fsp->open && (fsp->is_directory || fsp->stat_open)) { /* * This is actually a SETFILEINFO on a directory * handle (returned from an NT SMB). NT5.0 seems * to do this call. JRA. */ fname = fsp->fsp_name; - if (!unix_dfs_convert(fname,conn,0,&bad_path,&st)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } - if (!check_name(fname,conn) || (!VALID_STAT(st) && - conn->vfs_ops.stat(dos_to_unix(fname,False),&st))) { + unix_convert(fname,conn,0,&bad_path,&st); + if (!check_name(fname,conn) || (!VALID_STAT(st) && dos_stat(fname,&st))) { DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno))); if((errno == ENOENT) && bad_path) { @@ -1559,7 +1605,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, fname = fsp->fsp_name; fd = fsp->fd_ptr->fd; - if (fsp->conn->vfs_ops.fstat(fd,&st) != 0) { + if (sys_fstat(fd,&st) != 0) { DEBUG(3,("fstat of fnum %d failed (%s)\n",fsp->fnum, strerror(errno))); return(UNIXERROR(ERRDOS,ERRbadfid)); } @@ -1569,11 +1615,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, info_level = SVAL(params,0); fname = fname1; pstrcpy(fname,¶ms[6]); - if (!unix_dfs_convert(fname,conn,0,&bad_path,&st)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(fname,conn,0,&bad_path,&st); if(!check_name(fname, conn)) { if((errno == ENOENT) && bad_path) @@ -1584,7 +1626,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, return(UNIXERROR(ERRDOS,ERRbadpath)); } - if(!VALID_STAT(st) && conn->vfs_ops.stat(dos_to_unix(fname,False),&st)!=0) { + if(!VALID_STAT(st) && dos_stat(fname,&st)!=0) { DEBUG(3,("stat of %s failed (%s)\n", fname, strerror(errno))); if((errno == ENOENT) && bad_path) { @@ -1599,10 +1641,12 @@ static int call_trans2setfilepathinfo(connection_struct *conn, tran_call,fname,info_level,total_data)); /* Realloc the parameter and data sizes */ - params = *pparams = Realloc(*pparams,2); SSVAL(params,0,0); + params = *pparams = Realloc(*pparams,2); if(params == NULL) return(ERROR(ERRDOS,ERRnomem)); + SSVAL(params,0,0); + size = st.st_size; tvs.modtime = st.st_mtime; tvs.actime = st.st_atime; @@ -1656,7 +1700,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, tvs.actime = interpret_long_date(pdata+8); /* write time + changed time, combined. */ - tvs.modtime=MAX(interpret_long_date(pdata+16), + tvs.modtime=MIN(interpret_long_date(pdata+16), interpret_long_date(pdata+24)); #if 0 /* Needs more testing... */ @@ -1672,6 +1716,26 @@ static int call_trans2setfilepathinfo(connection_struct *conn, break; } + /* + * NT seems to use this call with a size of zero + * to mean truncate the file. JRA. + */ + + case SMB_SET_FILE_ALLOCATION_INFO: + { + SMB_OFF_T newsize = IVAL(pdata,0); +#ifdef LARGE_SMB_OFF_T + newsize |= (((SMB_OFF_T)IVAL(pdata,4)) << 32); +#else /* LARGE_SMB_OFF_T */ + if (IVAL(pdata,4) != 0) /* more than 32 bits? */ + return(ERROR(ERRDOS,ERRunknownlevel)); +#endif /* LARGE_SMB_OFF_T */ + DEBUG(10,("call_trans2setfilepathinfo: Set file allocation info for file %s to %.0f\n", fname, (double)newsize )); + if(newsize == 0) + size = 0; + break; + } + case SMB_SET_FILE_END_OF_FILE_INFO: { size = IVAL(pdata,0); @@ -1681,12 +1745,10 @@ static int call_trans2setfilepathinfo(connection_struct *conn, if (IVAL(pdata,4) != 0) /* more than 32 bits? */ return(ERROR(ERRDOS,ERRunknownlevel)); #endif /* LARGE_SMB_OFF_T */ + DEBUG(10,("call_trans2setfilepathinfo: Set end of file info for file %s to %.0f\n", fname, (double)size )); break; } - case SMB_SET_FILE_ALLOCATION_INFO: - break; /* We don't need to do anything for this call. */ - case SMB_SET_FILE_DISPOSITION_INFO: /* Set delete on close for open file. */ { if ((tran_call == TRANSACT2_SETFILEINFO) && (fsp != NULL)) @@ -1694,121 +1756,134 @@ static int call_trans2setfilepathinfo(connection_struct *conn, BOOL delete_on_close = (CVAL(pdata,0) ? True : False); if(fsp->is_directory) - return(ERROR(ERRDOS,ERRnoaccess)); - - /* - * We can only set the delete on close flag if - * the share mode contained ALLOW_SHARE_DELETE - */ + { + fsp->directory_delete_on_close = delete_on_close; + DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, directory %s\n", + delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name )); - if(lp_share_modes(SNUM(conn))) + } + else if(fsp->stat_open) + { + DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, stat open %s\n", + delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name )); + } + else { - if(!GET_ALLOW_SHARE_DELETE(fsp->share_mode)) - return(ERROR(ERRDOS,ERRnoaccess)); /* - * If the flag has been set then - * modify the share mode entry for all files we have open - * on this device and inode to tell other smbds we have - * changed the delete on close flag. + * We can only set the delete on close flag if + * the share mode contained ALLOW_SHARE_DELETE */ - if(delete_on_close && !GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode)) + if(lp_share_modes(SNUM(conn))) { - int token; - int i; - files_struct *iterate_fsp; - SMB_DEV_T dev = fsp->fd_ptr->dev; - SMB_INO_T inode = fsp->fd_ptr->inode; - int num_share_modes; - share_mode_entry *current_shares = NULL; - - if(lock_share_entry(fsp->conn, dev, inode, &token) == False) + if(!GET_ALLOW_SHARE_DELETE(fsp->share_mode)) return(ERROR(ERRDOS,ERRnoaccess)); /* - * Before we allow this we need to ensure that all current opens - * on the file have the GET_ALLOW_SHARE_DELETE flag set. If they - * do not then we deny this (as we are essentially deleting the - * file at this point. + * If the flag has been set then + * modify the share mode entry for all files we have open + * on this device and inode to tell other smbds we have + * changed the delete on close flag. */ - num_share_modes = get_share_modes(conn, token, dev, inode, ¤t_shares); - for(i = 0; i < num_share_modes; i++) + if(delete_on_close && !GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode)) { - if(!GET_ALLOW_SHARE_DELETE(current_shares[i].share_mode)) + int token; + int i; + files_struct *iterate_fsp; + SMB_DEV_T dev = fsp->fd_ptr->dev; + SMB_INO_T inode = fsp->fd_ptr->inode; + int num_share_modes; + share_mode_entry *current_shares = NULL; + + if(lock_share_entry(fsp->conn, dev, inode, &token) == False) + return(ERROR(ERRDOS,ERRnoaccess)); + + /* + * Before we allow this we need to ensure that all current opens + * on the file have the GET_ALLOW_SHARE_DELETE flag set. If they + * do not then we deny this (as we are essentially deleting the + * file at this point. + */ + + num_share_modes = get_share_modes(conn, token, dev, inode, ¤t_shares); + for(i = 0; i < num_share_modes; i++) { - DEBUG(5,("call_trans2setfilepathinfo: refusing to set delete on close flag for fnum = %d, \ + if(!GET_ALLOW_SHARE_DELETE(current_shares[i].share_mode)) + { + DEBUG(5,("call_trans2setfilepathinfo: refusing to set delete on close flag for fnum = %d, \ file %s as a share exists that was not opened with FILE_DELETE access.\n", - fsp->fnum, fsp->fsp_name )); - /* - * Release the lock. - */ + fsp->fnum, fsp->fsp_name )); + /* + * Release the lock. + */ - unlock_share_entry(fsp->conn, dev, inode, token); + unlock_share_entry(fsp->conn, dev, inode, token); - /* - * current_shares was malloced by get_share_modes - free it here. - */ + /* + * current_shares was malloced by get_share_modes - free it here. + */ - free((char *)current_shares); + free((char *)current_shares); - /* - * Even though share violation would be more appropriate here, - * return ERRnoaccess as that's what NT does. - */ + /* + * Even though share violation would be more appropriate here, + * return ERRnoaccess as that's what NT does. + */ - return(ERROR(ERRDOS,ERRnoaccess)); + return(ERROR(ERRDOS,ERRnoaccess)); + } } - } - /* - * current_shares was malloced by get_share_modes - free it here. - */ + /* + * current_shares was malloced by get_share_modes - free it here. + */ - free((char *)current_shares); + free((char *)current_shares); - DEBUG(10,("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, file %s\n", - delete_on_close ? "Adding" : "Removing", fsp->fnum, fsp->fsp_name )); + DEBUG(10,("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, file %s\n", + delete_on_close ? "Adding" : "Removing", fsp->fnum, fsp->fsp_name )); - /* - * Go through all files we have open on the same device and - * inode (hanging off the same hash bucket) and set the DELETE_ON_CLOSE_FLAG. - * Other smbd's that have this file open will have to fend for themselves. We - * take care of this (rare) case in close_file(). See the comment there. - */ + /* + * Go through all files we have open on the same device and + * inode (hanging off the same hash bucket) and set the DELETE_ON_CLOSE_FLAG. + * Other smbd's that have this file open will have to fend for themselves. We + * take care of this (rare) case in close_file(). See the comment there. + */ - for(iterate_fsp = file_find_di_first(dev, inode); iterate_fsp; - iterate_fsp = file_find_di_next(iterate_fsp)) - { - int new_share_mode = (delete_on_close ? - (iterate_fsp->share_mode | DELETE_ON_CLOSE_FLAG) : - (iterate_fsp->share_mode & ~DELETE_ON_CLOSE_FLAG) ); + for(iterate_fsp = file_find_di_first(dev, inode); iterate_fsp; + iterate_fsp = file_find_di_next(iterate_fsp)) + { + int new_share_mode = (delete_on_close ? + (iterate_fsp->share_mode | DELETE_ON_CLOSE_FLAG) : + (iterate_fsp->share_mode & ~DELETE_ON_CLOSE_FLAG) ); - DEBUG(10,("call_trans2setfilepathinfo: Changing share mode for fnum %d, file %s \ + DEBUG(10,("call_trans2setfilepathinfo: Changing share mode for fnum %d, file %s \ dev = %x, inode = %.0f from %x to %x\n", - iterate_fsp->fnum, iterate_fsp->fsp_name, (unsigned int)dev, - (double)inode, iterate_fsp->share_mode, new_share_mode )); + iterate_fsp->fnum, iterate_fsp->fsp_name, (unsigned int)dev, + (double)inode, iterate_fsp->share_mode, new_share_mode )); - if(modify_share_mode(token, iterate_fsp, new_share_mode)==False) - DEBUG(0,("call_trans2setfilepathinfo: failed to change delete on close for fnum %d, \ + if(modify_share_mode(token, iterate_fsp, new_share_mode, iterate_fsp->oplock_type)==False) + DEBUG(0,("call_trans2setfilepathinfo: failed to change delete on close for fnum %d, \ dev = %x, inode = %.0f\n", iterate_fsp->fnum, (unsigned int)dev, (double)inode)); - } + } - /* - * Set the delete on close flag in the reference - * counted struct. Delete when the last reference - * goes away. - */ - fsp->fd_ptr->delete_on_close = delete_on_close; - unlock_share_entry(fsp->conn, dev, inode, token); + /* + * Set the delete on close flag in the reference + * counted struct. Delete when the last reference + * goes away. + */ + fsp->fd_ptr->delete_on_close = delete_on_close; - DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, file %s\n", - delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name )); + unlock_share_entry(fsp->conn, dev, inode, token); - } /* end if(delete_on_close && !GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode)) */ - } /* end if lp_share_modes() */ + DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, file %s\n", + delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name )); + } /* end if(delete_on_close && !GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode)) */ + } /* end if lp_share_modes() */ + } /* end if is_directory. */ } else return(ERROR(ERRDOS,ERRunknownlevel)); break; @@ -1820,23 +1895,33 @@ dev = %x, inode = %.0f\n", iterate_fsp->fnum, (unsigned int)dev, (double)inode)) } } + /* get some defaults (no modifications) if any info is zero or -1. */ + if (tvs.actime == (time_t)0 || tvs.actime == (time_t)-1) + tvs.actime = st.st_atime; + + if (tvs.modtime == (time_t)0 || tvs.modtime == (time_t)-1) + tvs.modtime = st.st_mtime; + DEBUG(6,("actime: %s " , ctime(&tvs.actime))); DEBUG(6,("modtime: %s ", ctime(&tvs.modtime))); DEBUG(6,("size: %.0f ", (double)size)); DEBUG(6,("mode: %x\n" , mode)); - /* get some defaults (no modifications) if any info is zero. */ - if (!tvs.actime) tvs.actime = st.st_atime; - if (!tvs.modtime) tvs.modtime = st.st_mtime; - if (!size) size = st.st_size; + if(!((info_level == SMB_SET_FILE_END_OF_FILE_INFO) || + (info_level == SMB_SET_FILE_ALLOCATION_INFO))) { + /* + * Only do this test if we are not explicitly + * changing the size of a file. + */ + if (!size) + size = st.st_size; + } /* Try and set the times, size and mode of this file - if they are different from the current values */ - if (st.st_mtime != tvs.modtime || st.st_atime != tvs.actime) - { - if(fsp != NULL) - { + if (st.st_mtime != tvs.modtime || st.st_atime != tvs.actime) { + if(fsp != NULL) { /* * This was a setfileinfo on an open file. * NT does this a lot. It's actually pointless @@ -1844,38 +1929,51 @@ dev = %x, inode = %.0f\n", iterate_fsp->fnum, (unsigned int)dev, (double)inode)) * on the next write, so we save the request * away and will set it on file code. JRA. */ - fsp->pending_modtime = tvs.modtime; - } - else if(file_utime(conn, fname, &tvs)!=0) - { - return(UNIXERROR(ERRDOS,ERRnoaccess)); + + if (tvs.modtime != (time_t)0 && tvs.modtime != (time_t)-1) { + DEBUG(10,("call_trans2setfilepathinfo: setting pending modtime to %s\n", + ctime(&tvs.modtime) )); + fsp->pending_modtime = tvs.modtime; + } + + } else { + + DEBUG(10,("call_trans2setfilepathinfo: setting utimes to modified values.\n")); + + if(file_utime(conn, fname, &tvs)!=0) + return(UNIXERROR(ERRDOS,ERRnoaccess)); } } /* check the mode isn't different, before changing it */ - if (mode != dos_mode(conn, fname, &st) && file_chmod(conn, fname, mode, NULL)) - { - DEBUG(2,("chmod of %s failed (%s)\n", fname, strerror(errno))); - return(UNIXERROR(ERRDOS,ERRnoaccess)); + if ((mode != 0) && (mode != dos_mode(conn, fname, &st))) { + + DEBUG(10,("call_trans2setfilepathinfo: file %s : setting dos mode %x\n", + fname, mode )); + + if(file_chmod(conn, fname, mode, NULL)) { + DEBUG(2,("chmod of %s failed (%s)\n", fname, strerror(errno))); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } } - if(size != st.st_size) - { - if (fd == -1) - { -DEBUG(0, ("@@@ 23 @@@\n")); + if(size != st.st_size) { + + DEBUG(10,("call_trans2setfilepathinfo: file %s : setting new size to %.0f\n", + fname, (double)size )); + + if (fd == -1) { fd = dos_open(fname,O_RDWR,0); if (fd == -1) - { return(UNIXERROR(ERRDOS,ERRbadpath)); - } set_filelen(fd, size); close(fd); - } - else - { + } else { set_filelen(fd, size); } + + if(fsp) + set_filelen_write_cache(fsp, size); } SSVAL(params,0,0); @@ -1904,14 +2002,9 @@ static int call_trans2mkdir(connection_struct *conn, DEBUG(3,("call_trans2mkdir : name = %s\n", directory)); - if (!unix_dfs_convert(directory,conn,0,&bad_path,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(directory,conn,0,&bad_path,NULL); if (check_name(directory,conn)) - ret = conn->vfs_ops.mkdir(dos_to_unix(directory,False), - unix_mode(conn,aDIR)); + ret = dos_mkdir(directory,unix_mode(conn,aDIR)); if(ret < 0) { @@ -2012,11 +2105,11 @@ int reply_findclose(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize) { int outsize = 0; - int16 dptr_num=SVALS(inbuf,smb_vwv0); + int dptr_num=SVALS(inbuf,smb_vwv0); DEBUG(3,("reply_findclose, dptr_num = %d\n", dptr_num)); - dptr_close(dptr_num); + dptr_close(&dptr_num); outsize = set_message(outbuf,0,0,True); @@ -2049,237 +2142,6 @@ int reply_findnclose(connection_struct *conn, return(outsize); } -#define UNICODE_DFS - -/**************************************************************************** - reply to a TRANS2_GET_DFS_REFERRAL - ****************************************************************************/ -static int call_trans2getdfsreferral(connection_struct *conn, - char *inbuf, char *outbuf, int length, - int bufsize, - char **pparams, char **ppdata, - int total_data) -{ - char *params = *pparams; - char *pdata; - char *pheader; - char *localstring_offset; - char *mangledstring_offset; - char *sharename_offset; - char *referal_offset; - - int i; - int j; - unsigned int total_params = SVAL(inbuf, smb_tpscnt); - int query_file_len=0; - int bytesreq=0; - int filename_len; - - BOOL first_one=True; - - referal_trans_param rtp; - dfs_internal_table *list=dfs_struct.table; - dfs_response reply; - - DEBUG(0,("call_trans2getdfsreferral:1\n")); - - ZERO_STRUCT(rtp); - ZERO_STRUCT(reply); - - /* decode the param member of query */ - rtp.level=SVAL(params, 0); - DEBUGADD(0,("rtp.level:[%d]\n",rtp.level)); - - DEBUGADD(0,("total_params:[%d]\n",total_params)); - for (i=0; i<(total_params-2)/2; i++) - { - rtp.directory[i]=SVAL(params, 2+2*i); - } -/* - strupper(rtp.directory); -*/ - query_file_len=strlen(rtp.directory); - DEBUGADD(0,("rtp.directory:[%s]\n",rtp.directory)); - DEBUGADD(0,("query_file_len:[%d]\n",query_file_len)); - - /* - lookup in the internal DFS table all the entries - and calculate the required data buffer size - */ - bytesreq=8; /* the header */ - reply.number_of_referal=0; - DEBUGADD(0,("call_trans2getdfsreferral:2\n")); - - for(i=0; i<dfs_struct.size; i++) - { - filename_len=list[i].localpath_length; - DEBUGADD(0,("checking against [%s][%d]\n", list[i].localpath, filename_len)); - - if( (filename_len==query_file_len) && - (!StrnCaseCmp(rtp.directory, list[i].localpath, query_file_len)) ) - { - - bytesreq+=22; /* the referal size */ - bytesreq+=2*(list[i].sharename_length+1); /* the string length */ - reply.number_of_referal++; - DEBUGADD(0,("found\n")); - - if (first_one) { - DEBUGADD(0,("first one\n")); - bytesreq+=2*(list[i].localpath_length+1); - bytesreq+=2*(list[i].mangledpath_length+1); - - reply.path_consumed=list[i].localpath_length; - - strncpy(reply.filename, list[i].localpath, list[i].localpath_length+1); - strncpy(reply.mangledname, list[i].mangledpath, list[i].mangledpath_length+1); - rtp.type=list[i].type; - first_one=False; - } - } - } - DEBUGADD(0,("call_trans2getdfsreferral:3\n")); - - /* allocate memory for the reply data */ - pdata = *ppdata = Realloc(*ppdata, bytesreq + 1024); - bzero(*ppdata, bytesreq+22); - - pdata = *ppdata; - pheader = pdata; - - localstring_offset = pdata + 8 + reply.number_of_referal*22; - -#ifdef UNICODE_DFS - mangledstring_offset = localstring_offset + 2*(1+strlen(reply.filename)); - sharename_offset = mangledstring_offset + 2*(1+strlen(reply.mangledname)); - -#else - mangledstring_offset = localstring_offset + (1+strlen(reply.filename)); - sharename_offset = mangledstring_offset + (1+strlen(reply.mangledname)); -#endif - referal_offset = pdata + 8; - - /* right now respond storage server */ -/* - reply.server_function=rtp.type; -*/ - reply.server_function=0x3; - - /* write the header */ -#ifdef UNICODE_DFS - SSVAL(pheader, 0, reply.path_consumed*2); -#else - SSVAL(pheader, 0, reply.path_consumed); -#endif - SSVAL(pheader, 2, reply.number_of_referal); - SIVAL(pheader, 4, reply.server_function); - - /* write the local path string */ -#ifdef UNICODE_DFS - for(i=0; i<strlen(reply.filename); i++) - { - SSVAL(localstring_offset, 2*i, (uint16) reply.filename[i]); - } - SSVAL(localstring_offset, 2*strlen(reply.filename), 0); -#else - - for(i=0; i<strlen(reply.filename); i++) - { - localstring_offset[i]=reply.filename[i]; - } - localstring_offset[strlen(reply.filename)]=0; -#endif - DEBUG(0,("reply.filename is [%s]:[%d], i is [%d]\n", reply.filename, strlen(reply.filename), i)); - - /* write the mangled local path string */ -#ifdef UNICODE_DFS - for(i=0; i<strlen(reply.mangledname); i++) - { - SSVAL(mangledstring_offset, 2*i, (uint16) reply.mangledname[i]); - } - SSVAL(mangledstring_offset, 2*i, 0); -#else - for(i=0; i<strlen(reply.mangledname); i++) - { - mangledstring_offset[i]=reply.mangledname[i]; - } - mangledstring_offset[i]=0; -#endif - DEBUGADD(0,("call_trans2getdfsreferral:4\n")); - - /* the order of the referals defines the load balancing */ - - /* write each referal */ - for(i=0; i<dfs_struct.size; i++) - { - filename_len=list[i].localpath_length; - - if(filename_len==query_file_len && - !strncasecmp(rtp.directory, list[i].localpath, query_file_len)) - { - - SSVAL(referal_offset, 0, 2); /* version */ - SSVAL(referal_offset, 2, 22); /* size */ - - if (rtp.type==3) - SSVAL(referal_offset, 4, 1); /* type SMB server*/ - else - SSVAL(referal_offset, 4, 0); /* type unknown */ - SSVAL(referal_offset, 6, 1); /* flags */ - SIVAL(referal_offset, 8, list[i].proximity); /* proximity */ - SIVAL(referal_offset, 12, 300); /* ttl */ - SSVAL(referal_offset, 16, localstring_offset-referal_offset); - SSVAL(referal_offset, 18, mangledstring_offset-referal_offset); - SSVAL(referal_offset, 20, sharename_offset-referal_offset); - -#ifdef UNICODE_DFS - for(j=0; j<list[i].sharename_length; j++) - { - SSVAL(sharename_offset, 2*j, (uint16) list[i].sharename[j]); - } - SSVAL(sharename_offset, 2*j, 0); - - sharename_offset=sharename_offset + 2*(1+list[i].sharename_length); -#else - for(j=0; j<list[i].sharename_length; j++) - { - sharename_offset[j]=list[i].sharename[j]; - } - sharename_offset[j]=0; - - sharename_offset=sharename_offset + (1+list[i].sharename_length); -#endif - - referal_offset=referal_offset+22; - } - } - - DEBUGADD(0,("call_trans2getdfsreferral:5\n")); - - send_trans2_replies(outbuf, bufsize, params, 0, *ppdata, bytesreq+22); - -/* send_trans2_replies(outbuf, bufsize, *ppdata, bytesreq, params, 0);*/ - DEBUGADD(0,("call_trans2getdfsreferral:6\n")); - - return(-1); -} - - -/**************************************************************************** -reply to a TRANS2_REPORT_DFS_INCONSISTANCY -****************************************************************************/ -static int call_trans2reportdfsinconsistancy(connection_struct *conn, - char *inbuf, char *outbuf, int length, - int bufsize, - char **pparams, char **ppdata) -{ - char *params = *pparams; - - DEBUG(4,("call_trans2reportdfsinconsistancy\n")); - send_trans2_replies(outbuf, bufsize, params, 4, *ppdata, 0); - return(-1); -} - /**************************************************************************** reply to a SMBtranss2 - just ignore it! @@ -2410,104 +2272,73 @@ int reply_trans2(connection_struct *conn, } /* Now we must call the relevant TRANS2 function */ - switch(tran_call) - { - case TRANSACT2_OPEN: - { - outsize = call_trans2open(conn, + switch(tran_call) { + case TRANSACT2_OPEN: + outsize = call_trans2open(conn, inbuf, outbuf, bufsize, ¶ms, &data); - break; - } - case TRANSACT2_FINDFIRST: - { - outsize = call_trans2findfirst(conn, inbuf, outbuf, - bufsize, ¶ms, &data); - break; - } - case TRANSACT2_FINDNEXT: - { - outsize = call_trans2findnext(conn, inbuf, outbuf, - length, bufsize, - ¶ms, &data); - break; - } - case TRANSACT2_QFSINFO: - { - outsize = call_trans2qfsinfo(conn, inbuf, outbuf, - length, bufsize, ¶ms, - &data); - break; - } - case TRANSACT2_SETFSINFO: - { - outsize = call_trans2setfsinfo(conn, inbuf, outbuf, - length, bufsize, - ¶ms, &data); - break; - } - case TRANSACT2_QPATHINFO: - case TRANSACT2_QFILEINFO: - { - outsize = call_trans2qfilepathinfo(conn, inbuf, outbuf, - length, bufsize, - ¶ms, &data, total_data); - break; - } - case TRANSACT2_SETPATHINFO: - case TRANSACT2_SETFILEINFO: - { - outsize = call_trans2setfilepathinfo(conn, inbuf, outbuf, - length, bufsize, - ¶ms, &data, - total_data); - break; - } - case TRANSACT2_FINDNOTIFYFIRST: - { - outsize = call_trans2findnotifyfirst(conn, inbuf, outbuf, - length, bufsize, - ¶ms, &data); - break; - } - case TRANSACT2_FINDNOTIFYNEXT: - { - outsize = call_trans2findnotifynext(conn, inbuf, outbuf, - length, bufsize, - ¶ms, &data); - break; - } - case TRANSACT2_MKDIR: - { - outsize = call_trans2mkdir(conn, inbuf, outbuf, length, - bufsize, ¶ms, &data); - break; - } - case TRANSACT2_GET_DFS_REFERRAL: - { - outsize = call_trans2getdfsreferral(conn, inbuf, outbuf, - length, bufsize, ¶ms, - &data, total_data); - break; - } - case TRANSACT2_REPORT_DFS_INCONSISTANCY: - { - outsize = call_trans2reportdfsinconsistancy(conn, inbuf, outbuf, - length, bufsize, - ¶ms, &data); - break; - } - default: - { - /* Error in request */ - DEBUG(2,("Unknown request %d in trans2 call\n", - tran_call)); - if(params) - free(params); - if(data) - free(data); - return (ERROR(ERRSRV,ERRerror)); - } + break; + + case TRANSACT2_FINDFIRST: + outsize = call_trans2findfirst(conn, inbuf, outbuf, + bufsize, ¶ms, &data); + break; + + case TRANSACT2_FINDNEXT: + outsize = call_trans2findnext(conn, inbuf, outbuf, + length, bufsize, + ¶ms, &data); + break; + + case TRANSACT2_QFSINFO: + outsize = call_trans2qfsinfo(conn, inbuf, outbuf, + length, bufsize, ¶ms, + &data); + break; + + case TRANSACT2_SETFSINFO: + outsize = call_trans2setfsinfo(conn, inbuf, outbuf, + length, bufsize, + ¶ms, &data); + break; + + case TRANSACT2_QPATHINFO: + case TRANSACT2_QFILEINFO: + outsize = call_trans2qfilepathinfo(conn, inbuf, outbuf, + length, bufsize, + ¶ms, &data, total_data); + break; + case TRANSACT2_SETPATHINFO: + case TRANSACT2_SETFILEINFO: + outsize = call_trans2setfilepathinfo(conn, inbuf, outbuf, + length, bufsize, + ¶ms, &data, + total_data); + break; + + case TRANSACT2_FINDNOTIFYFIRST: + outsize = call_trans2findnotifyfirst(conn, inbuf, outbuf, + length, bufsize, + ¶ms, &data); + break; + + case TRANSACT2_FINDNOTIFYNEXT: + outsize = call_trans2findnotifynext(conn, inbuf, outbuf, + length, bufsize, + ¶ms, &data); + break; + case TRANSACT2_MKDIR: + outsize = call_trans2mkdir(conn, inbuf, outbuf, length, + bufsize, ¶ms, &data); + break; + default: + /* Error in request */ + DEBUG(2,("Unknown request %d in trans2 call\n", tran_call)); + if(params) + free(params); + if(data) + free(data); + return (ERROR(ERRSRV,ERRerror)); } /* As we do not know how many data packets will need to be @@ -2525,4 +2356,3 @@ int reply_trans2(connection_struct *conn, call_trans2xxx calls have already sent it. If outsize != -1 then it is returning */ } - diff --git a/source3/smbd/uid.c b/source3/smbd/uid.c index 3501879d5f..ce0631e418 100644 --- a/source3/smbd/uid.c +++ b/source3/smbd/uid.c @@ -23,53 +23,36 @@ extern int DEBUGLEVEL; -static uid_t initial_uid; -static gid_t initial_gid; -static struct uid_cache vcache; - /* what user is current? */ extern struct current_user current_user; pstring OriginalDir; /**************************************************************************** -initialise the uid routines + Initialise the uid routines. ****************************************************************************/ + void init_uid(void) { - initial_uid = current_user.uid = geteuid(); - initial_gid = current_user.gid = getegid(); - - if (initial_gid != 0 && initial_uid == 0) { -#ifdef HAVE_SETRESUID - setresgid(0,0,0); -#else - setgid(0); - setegid(0); -#endif - } + current_user.uid = geteuid(); + current_user.gid = getegid(); - initial_uid = geteuid(); - initial_gid = getegid(); + if (current_user.gid != 0 && current_user.uid == 0) { + gain_root_group_privilege(); + } current_user.conn = NULL; current_user.vuid = UID_FIELD_INVALID; - vcache.entries = 0; - dos_ChDir(OriginalDir); } - /**************************************************************************** - become the specified uid + Become the specified uid. ****************************************************************************/ + static BOOL become_uid(uid_t uid) { - if (initial_uid != 0) { - return(True); - } - if (uid == (uid_t)-1 || ((sizeof(uid_t) == 2) && (uid == (uid_t)65535))) { static int done; if (!done) { @@ -78,98 +61,53 @@ static BOOL become_uid(uid_t uid) } } -#ifdef HAVE_TRAPDOOR_UID -#ifdef HAVE_SETUIDX - /* AIX3 has setuidx which is NOT a trapoor function (tridge) */ - if (setuidx(ID_EFFECTIVE, uid) != 0) { - if (seteuid(uid) != 0) { - DEBUG(1,("Can't set uid %d (setuidx)\n", (int)uid)); - return False; - } - } -#endif -#endif - -#ifdef HAVE_SETRESUID - if (setresuid(-1,uid,-1) != 0) -#else - if ((seteuid(uid) != 0) && - (setuid(uid) != 0)) -#endif - { - DEBUG(0,("Couldn't set uid %d currently set to (%d,%d)\n", - (int)uid,(int)getuid(), (int)geteuid())); - if (uid > (uid_t)32000) { - DEBUG(0,("Looks like your OS doesn't like high uid values - try using a different account\n")); - } - return(False); - } - - if (((uid == (uid_t)-1) || ((sizeof(uid_t) == 2) && (uid == 65535))) && (geteuid() != uid)) { - DEBUG(0,("Invalid uid -1. perhaps you have a account with uid 65535?\n")); - return(False); - } + set_effective_uid(uid); - current_user.uid = uid; + current_user.uid = uid; #ifdef WITH_PROFILE - profile_p->uid_changes++; + profile_p->uid_changes++; #endif - return(True); + return(True); } /**************************************************************************** - become the specified gid + Become the specified gid. ****************************************************************************/ + static BOOL become_gid(gid_t gid) { - if (initial_uid != 0) - return(True); - - if (gid == (gid_t)-1 || ((sizeof(gid_t) == 2) && (gid == (gid_t)65535))) { - DEBUG(1,("WARNING: using gid %d is a security risk\n",(int)gid)); - } - -#ifdef HAVE_SETRESUID - if (setresgid(-1,gid,-1) != 0) -#else - if (setgid(gid) != 0) -#endif - { - DEBUG(0,("Couldn't set gid %d currently set to (%d,%d)\n", - (int)gid,(int)getgid(),(int)getegid())); - if (gid > 32000) { - DEBUG(0,("Looks like your OS doesn't like high gid values - try using a different account\n")); + if (gid == (gid_t)-1 || ((sizeof(gid_t) == 2) && (gid == (gid_t)65535))) { + DEBUG(1,("WARNING: using gid %d is a security risk\n",(int)gid)); } - return(False); - } - - current_user.gid = gid; - - return(True); + + set_effective_gid(gid); + + current_user.gid = gid; + + return(True); } /**************************************************************************** - become the specified uid and gid + Become the specified uid and gid. ****************************************************************************/ + static BOOL become_id(uid_t uid,gid_t gid) { return(become_gid(gid) && become_uid(uid)); } /**************************************************************************** -become the guest user + Become the guest user. ****************************************************************************/ + BOOL become_guest(void) { BOOL ret; - static const struct passwd *pass=NULL; - - if (initial_uid != 0) - return(True); + static struct passwd *pass=NULL; if (!pass) pass = Get_Pwnam(lp_guestaccount(-1),True); @@ -193,138 +131,43 @@ BOOL become_guest(void) } /******************************************************************* -check if a username is OK + Check if a username is OK. ********************************************************************/ -static BOOL check_vuser_ok(struct uid_cache *cache, user_struct *vuser,int snum) + +static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum) { int i; - for (i=0;i<cache->entries;i++) - if (cache->list[i] == vuser->uid) return(True); + for (i=0;i<conn->uid_cache.entries;i++) + if (conn->uid_cache.list[i] == vuser->uid) return(True); if (!user_ok(vuser->name,snum)) return(False); - i = cache->entries % UID_CACHE_SIZE; - cache->list[i] = vuser->uid; + i = conn->uid_cache.entries % UID_CACHE_SIZE; + conn->uid_cache.list[i] = vuser->uid; - if (cache->entries < UID_CACHE_SIZE) - cache->entries++; + if (conn->uid_cache.entries < UID_CACHE_SIZE) + conn->uid_cache.entries++; return(True); } /**************************************************************************** - become the user of a connection number -****************************************************************************/ -BOOL become_vuser(uint16 vuid) -{ - user_struct *vuser = get_valid_user_struct(vuid); - gid_t gid; - uid_t uid; - - unbecome_vuser(); - - if((vuser != NULL) && !check_vuser_ok(&vcache, vuser, -1)) - return False; - - if ( vuser != 0 && - current_user.vuid == vuid && - current_user.uid == vuser->uid) - { - DEBUG(4,("Skipping become_vuser - already user\n")); - return(True); - } - uid = vuser->uid; - gid = vuser->gid; - current_user.ngroups = vuser->n_groups; - current_user.groups = vuser->groups; - - if (initial_uid == 0) - { - if (!become_gid(gid)) return(False); - -#ifdef HAVE_SETGROUPS - /* groups stuff added by ih/wreu */ - if (current_user.ngroups > 0) - { - if (setgroups(current_user.ngroups, - current_user.groups)<0) { - DEBUG(0,("setgroups call failed!\n")); - } - } -#endif - - if (!become_uid(uid)) return(False); - } - - current_user.conn = NULL; - current_user.vuid = vuid; - - DEBUG(5,("become_vuser uid=(%d,%d) gid=(%d,%d)\n", - (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid())); - - return(True); -} - -/**************************************************************************** - unbecome a user + Become the user of a connection number. ****************************************************************************/ -BOOL unbecome_vuser(void) -{ - dos_ChDir(OriginalDir); - - if (initial_uid == 0) - { -#ifdef HAVE_SETRESUID - setresuid(-1,getuid(),-1); - setresgid(-1,getgid(),-1); -#else - if (seteuid(initial_uid) != 0) - setuid(initial_uid); - setgid(initial_gid); -#endif - } - -#ifdef NO_EID - if (initial_uid == 0) - DEBUG(2,("Running with no EID\n")); - initial_uid = getuid(); - initial_gid = getgid(); -#else - if (geteuid() != initial_uid) { - DEBUG(0,("Warning: You appear to have a trapdoor uid system\n")); - initial_uid = geteuid(); - } - if (getegid() != initial_gid) { - DEBUG(0,("Warning: You appear to have a trapdoor gid system\n")); - initial_gid = getegid(); - } -#endif - current_user.uid = initial_uid; - current_user.gid = initial_gid; - - if (dos_ChDir(OriginalDir) != 0) - DEBUG( 0, ( "chdir(%s) failed in unbecome_vuser\n", OriginalDir ) ); - - DEBUG(5,("unbecome_vuser now uid=(%d,%d) gid=(%d,%d)\n", - (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid())); - - current_user.conn = NULL; - current_user.vuid = UID_FIELD_INVALID; - - return(True); -} - -/**************************************************************************** - become the user of a connection number -****************************************************************************/ BOOL become_user(connection_struct *conn, uint16 vuid) { user_struct *vuser = get_valid_user_struct(vuid); int snum; - gid_t gid; + gid_t gid; uid_t uid; + char group_c; + + if (!conn) { + DEBUG(2,("Connection not open\n")); + return(False); + } /* * We need a separate check in security=share mode due to vuid @@ -346,14 +189,9 @@ BOOL become_user(connection_struct *conn, uint16 vuid) unbecome_user(); - if (!conn) { - DEBUG(2,("Connection not open\n")); - return(False); - } - snum = SNUM(conn); - if((vuser != NULL) && !check_vuser_ok(&conn->uid_cache, vuser, snum)) + if((vuser != NULL) && !check_user_ok(conn, vuser, snum)) return False; if (conn->force_user || @@ -369,32 +207,55 @@ BOOL become_user(connection_struct *conn, uint16 vuid) return(False); } uid = vuser->uid; - if(!*lp_force_group(snum)) { - gid = vuser->gid; + gid = vuser->gid; + current_user.ngroups = vuser->n_groups; + current_user.groups = vuser->groups; + } + + /* + * See if we should force group for this service. + * If so this overrides any group set in the force + * user code. + */ + + if((group_c = *lp_force_group(snum))) { + if(group_c == '+') { + + /* + * Only force group if the user is a member of + * the service group. Check the group memberships for + * this user (we already have this) to + * see if we should force the group. + */ + + int i; + for (i = 0; i < current_user.ngroups; i++) { + if (current_user.groups[i] == conn->gid) { + gid = conn->gid; + break; + } + } } else { gid = conn->gid; } - current_user.ngroups = vuser->n_groups; - current_user.groups = vuser->groups; } - if (initial_uid == 0) { - if (!become_gid(gid)) return(False); + if (!become_gid(gid)) + return(False); #ifdef HAVE_SETGROUPS - if (!(conn && conn->ipc)) { - /* groups stuff added by ih/wreu */ - if (current_user.ngroups > 0) - if (setgroups(current_user.ngroups, - current_user.groups)<0) { - DEBUG(0,("setgroups call failed!\n")); - } - } + if (!(conn && conn->ipc)) { + /* groups stuff added by ih/wreu */ + if (current_user.ngroups > 0) + if (sys_setgroups(current_user.ngroups, + current_user.groups)<0) { + DEBUG(0,("sys_setgroups call failed!\n")); + } + } #endif - if (!conn->admin_user && !become_uid(uid)) - return(False); - } + if (!conn->admin_user && !become_uid(uid)) + return(False); current_user.conn = conn; current_user.vuid = vuid; @@ -406,56 +267,83 @@ BOOL become_user(connection_struct *conn, uint16 vuid) } /**************************************************************************** - unbecome the user of a connection number + Unbecome the user of a connection number. ****************************************************************************/ + BOOL unbecome_user(void ) { - if (!current_user.conn) - return(False); - - dos_ChDir(OriginalDir); - - if (initial_uid == 0) - { -#ifdef HAVE_SETRESUID - setresuid(-1,getuid(),-1); - setresgid(-1,getgid(),-1); -#else - if (seteuid(initial_uid) != 0) - setuid(initial_uid); - setgid(initial_gid); -#endif - } - -#ifdef NO_EID - if (initial_uid == 0) - DEBUG(2,("Running with no EID\n")); - initial_uid = getuid(); - initial_gid = getgid(); -#else - if (geteuid() != initial_uid) { - DEBUG(0,("Warning: You appear to have a trapdoor uid system\n")); - initial_uid = geteuid(); - } - if (getegid() != initial_gid) { - DEBUG(0,("Warning: You appear to have a trapdoor gid system\n")); - initial_gid = getegid(); - } -#endif + if (!current_user.conn) + return(False); + + dos_ChDir(OriginalDir); - current_user.uid = initial_uid; - current_user.gid = initial_gid; + set_effective_uid(0); + set_effective_gid(0); + + if (geteuid() != 0) { + DEBUG(0,("Warning: You appear to have a trapdoor uid system\n")); + } + if (getegid() != 0) { + DEBUG(0,("Warning: You appear to have a trapdoor gid system\n")); + } + + current_user.uid = 0; + current_user.gid = 0; - if (dos_ChDir(OriginalDir) != 0) - DEBUG( 0, ( "chdir(%s) failed in unbecome_user\n", OriginalDir ) ); + if (dos_ChDir(OriginalDir) != 0) + DEBUG( 0, ( "chdir(%s) failed in unbecome_user\n", OriginalDir ) ); - DEBUG(5,("unbecome_user now uid=(%d,%d) gid=(%d,%d)\n", - (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid())); + DEBUG(5,("unbecome_user now uid=(%d,%d) gid=(%d,%d)\n", + (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid())); - current_user.conn = NULL; - current_user.vuid = UID_FIELD_INVALID; + current_user.conn = NULL; + current_user.vuid = UID_FIELD_INVALID; - return(True); + return(True); +} + +/**************************************************************************** + Become the user of an authenticated connected named pipe. + When this is called we are currently running as the connection + user. +****************************************************************************/ + +BOOL become_authenticated_pipe_user(pipes_struct *p) +{ + /* + * Go back to root. + */ + + if(!unbecome_user()) + return False; + + /* + * Now become the authenticated user stored in the pipe struct. + */ + + if(!become_id(p->uid, p->gid)) { + /* Go back to the connection user. */ + become_user(p->conn, p->vuid); + return False; + } + + return True; +} + +/**************************************************************************** + Unbecome the user of an authenticated connected named pipe. + When this is called we are running as the authenticated pipe + user and need to go back to being the connection user. +****************************************************************************/ + +BOOL unbecome_authenticated_pipe_user(pipes_struct *p) +{ + if(!become_id(0,0)) { + DEBUG(0,("unbecome_authenticated_pipe_user: Unable to go back to root.\n")); + return False; + } + + return become_user(p->conn, p->vuid); } static struct current_user current_user_saved; @@ -463,13 +351,14 @@ static int become_root_depth; static pstring become_root_dir; /**************************************************************************** -This is used when we need to do a privilaged operation (such as mucking +This is used when we need to do a privileged operation (such as mucking with share mode files) and temporarily need root access to do it. This call should always be paired with an unbecome_root() call immediately after the operation Set save_dir if you also need to save/restore the CWD ****************************************************************************/ + void become_root(BOOL save_dir) { if (become_root_depth) { @@ -486,10 +375,11 @@ void become_root(BOOL save_dir) } /**************************************************************************** -When the privilaged operation is over call this +When the privileged operation is over call this Set save_dir if you also need to save/restore the CWD ****************************************************************************/ + void unbecome_root(BOOL restore_dir) { if (become_root_depth != 1) { @@ -512,9 +402,9 @@ void unbecome_root(BOOL restore_dir) #ifdef HAVE_SETGROUPS if (current_user_saved.ngroups > 0) { - if (setgroups(current_user_saved.ngroups, + if (sys_setgroups(current_user_saved.ngroups, current_user_saved.groups)<0) - DEBUG(0,("ERROR: setgroups call failed!\n")); + DEBUG(0,("ERROR: sys_setgroups call failed!\n")); } #endif @@ -531,5 +421,3 @@ void unbecome_root(BOOL restore_dir) become_root_depth = 0; } - - |