diff options
Diffstat (limited to 'source/smbd')
-rw-r--r-- | source/smbd/blocking.c | 2 | ||||
-rw-r--r-- | source/smbd/close.c | 34 | ||||
-rw-r--r-- | source/smbd/conn.c | 2 | ||||
-rw-r--r-- | source/smbd/connection.c | 8 | ||||
-rw-r--r-- | source/smbd/dir.c | 3 | ||||
-rw-r--r-- | source/smbd/dosmode.c | 13 | ||||
-rw-r--r-- | source/smbd/error.c | 11 | ||||
-rw-r--r-- | source/smbd/filename.c | 27 | ||||
-rw-r--r-- | source/smbd/lanman.c | 4 | ||||
-rw-r--r-- | source/smbd/mangle_hash.c | 6 | ||||
-rw-r--r-- | source/smbd/msdfs.c | 4 | ||||
-rw-r--r-- | source/smbd/negprot.c | 19 | ||||
-rw-r--r-- | source/smbd/notify_hash.c | 2 | ||||
-rw-r--r-- | source/smbd/nttrans.c | 236 | ||||
-rw-r--r-- | source/smbd/open.c | 375 | ||||
-rw-r--r-- | source/smbd/oplock.c | 80 | ||||
-rw-r--r-- | source/smbd/posix_acls.c | 42 | ||||
-rw-r--r-- | source/smbd/process.c | 257 | ||||
-rw-r--r-- | source/smbd/quotas.c | 204 | ||||
-rw-r--r-- | source/smbd/reply.c | 187 | ||||
-rw-r--r-- | source/smbd/server.c | 10 | ||||
-rw-r--r-- | source/smbd/service.c | 41 | ||||
-rw-r--r-- | source/smbd/session.c | 4 | ||||
-rw-r--r-- | source/smbd/sesssetup.c | 4 | ||||
-rw-r--r-- | source/smbd/trans2.c | 77 | ||||
-rw-r--r-- | source/smbd/uid.c | 7 | ||||
-rw-r--r-- | source/smbd/utmp.c | 4 | ||||
-rw-r--r-- | source/smbd/vfs.c | 36 |
28 files changed, 1442 insertions, 257 deletions
diff --git a/source/smbd/blocking.c b/source/smbd/blocking.c index c0512d5539b..3983a4cbdfb 100644 --- a/source/smbd/blocking.c +++ b/source/smbd/blocking.c @@ -680,7 +680,7 @@ void process_blocking_lock_queue(time_t t) continue; } - if(!set_current_service(conn,True)) { + if(!set_current_service(conn,SVAL(blr->inbuf,smb_flg),True)) { DEBUG(0,("process_blocking_lock_queue: Unable to become service Error was %s.\n", strerror(errno) )); /* * Remove the entry and return an error to the client. diff --git a/source/smbd/close.c b/source/smbd/close.c index 8b3010c1b2e..6de27746442 100644 --- a/source/smbd/close.c +++ b/source/smbd/close.c @@ -2,6 +2,7 @@ Unix SMB/CIFS implementation. file closing Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 1992-2004. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -105,6 +106,36 @@ static int close_filestruct(files_struct *fsp) } /**************************************************************************** + If any deferred opens are waiting on this close, notify them. +****************************************************************************/ + +static void notify_deferred_opens(files_struct *fsp) +{ + deferred_open_entry *de_array = NULL; + int num_de_entries, i; + pid_t mypid = sys_getpid(); + + if (!lp_defer_sharing_violations()) { + return; + } + + num_de_entries = get_deferred_opens(fsp->conn, fsp->dev, fsp->inode, &de_array); + for (i = 0; i < num_de_entries; i++) { + deferred_open_entry *entry = &de_array[i]; + if (entry->pid == mypid) { + /* + * We need to notify ourself to retry the open. + * Do this by finding the queued SMB record, moving it + * to the head of the queue and changing the wait time to zero. + */ + schedule_sharing_violation_open_smb_message(entry->mid); + } else { + send_deferred_open_retry_message(entry); + } + } +} + +/**************************************************************************** Close a file. If normal_close is 1 then this came from a normal SMBclose (or equivalent) @@ -177,6 +208,9 @@ static int close_normal_file(files_struct *fsp, BOOL normal_close) SAFE_FREE(share_entry); + /* Notify any deferred opens waiting on this close. */ + notify_deferred_opens(fsp); + /* * NT can set delete_on_close of the last open * reference to a file. diff --git a/source/smbd/conn.c b/source/smbd/conn.c index e083e144263..34e19a3ca6b 100644 --- a/source/smbd/conn.c +++ b/source/smbd/conn.c @@ -161,7 +161,7 @@ void conn_close_all(void) connection_struct *conn, *next; for (conn=Connections;conn;conn=next) { next=conn->next; - set_current_service(conn, True); + set_current_service(conn, 0, True); close_cnum(conn, conn->vuid); } } diff --git a/source/smbd/connection.c b/source/smbd/connection.c index a9ab1424615..5bb76eb3bd8 100644 --- a/source/smbd/connection.c +++ b/source/smbd/connection.c @@ -29,8 +29,8 @@ static TDB_CONTEXT *tdb; TDB_CONTEXT *conn_tdb_ctx(void) { if (!tdb) - tdb = tdb_open_ex(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT, - O_RDWR | O_CREAT, 0644, smbd_tdb_log); + tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT, + O_RDWR | O_CREAT, 0644); return tdb; } @@ -131,8 +131,8 @@ BOOL claim_connection(connection_struct *conn, const char *name,int max_connecti TDB_DATA kbuf, dbuf; if (!tdb) - tdb = tdb_open_ex(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT, - O_RDWR | O_CREAT, 0644, smbd_tdb_log); + tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT, + O_RDWR | O_CREAT, 0644); if (!tdb) return False; diff --git a/source/smbd/dir.c b/source/smbd/dir.c index 06ef23ab8cd..b88f687766d 100644 --- a/source/smbd/dir.c +++ b/source/smbd/dir.c @@ -763,7 +763,8 @@ static BOOL user_can_write_file(connection_struct *conn, char *name, SMB_STRUCT_ return True; else fsp = open_file_shared1(conn, name, pst, FILE_WRITE_ATTRIBUTES, SET_DENY_MODE(DENY_NONE), - (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &smb_action); + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, INTERNAL_OPEN_ONLY, + &access_mode, &smb_action); if (!fsp) return False; diff --git a/source/smbd/dosmode.c b/source/smbd/dosmode.c index d7dc63bb2fd..33c75fffd53 100644 --- a/source/smbd/dosmode.c +++ b/source/smbd/dosmode.c @@ -182,6 +182,7 @@ static BOOL get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_S if ((errno != ENOTSUP) && (errno != ENOATTR) && (errno != EACCES)) { DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n", path, strerror(errno) )); + set_store_dos_attributes(SNUM(conn), False); } #endif return False; @@ -224,9 +225,21 @@ static BOOL set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_ files_struct *fsp = NULL; BOOL ret = False; + if (!lp_store_dos_attributes(SNUM(conn))) { + return False; + } + snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK); if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) { if((errno != EPERM) && (errno != EACCES)) { + if (errno == ENOSYS +#if defined(ENOTSUP) + || errno == ENOTSUP) { +#else + ) { +#endif + set_store_dos_attributes(SNUM(conn), False); + } return False; } diff --git a/source/smbd/error.c b/source/smbd/error.c index 9c81d465e7a..d611e0ef873 100644 --- a/source/smbd/error.c +++ b/source/smbd/error.c @@ -29,6 +29,17 @@ NTSTATUS unix_ERR_ntstatus = NT_STATUS_OK; extern struct unix_error_map unix_dos_nt_errmap[]; /**************************************************************************** + Ensure we don't have any errors cached. +****************************************************************************/ + +void clear_cached_errors(void) +{ + unix_ERR_class = SMB_SUCCESS; + unix_ERR_code = 0; + unix_ERR_ntstatus = NT_STATUS_OK; +} + +/**************************************************************************** Create an error packet from a cached error. ****************************************************************************/ diff --git a/source/smbd/filename.c b/source/smbd/filename.c index 5e5f5726913..ab75d9c06ae 100644 --- a/source/smbd/filename.c +++ b/source/smbd/filename.c @@ -135,7 +135,7 @@ BOOL unix_convert(pstring name,connection_struct *conn,char *saved_last_componen if (SMB_VFS_STAT(conn,name,&st) == 0) { *pst = st; } - DEBUG(5,("conversion finished %s -> %s\n",orig_path, name)); + DEBUG(5,("conversion finished \"\" -> %s\n",name)); return(True); } @@ -237,6 +237,15 @@ BOOL unix_convert(pstring name,connection_struct *conn,char *saved_last_componen */ DEBUG(5,("Not a dir %s\n",start)); *end = '/'; + /* + * We need to return the fact that the intermediate + * name resolution failed. This is used to return an + * error of ERRbadpath rather than ERRbadfile. Some + * Windows applications depend on the difference between + * these two errors. + */ + errno = ENOTDIR; + *bad_path = True; return(False); } @@ -265,6 +274,9 @@ BOOL unix_convert(pstring name,connection_struct *conn,char *saved_last_componen if (end) pstrcpy(rest,end+1); + /* Reset errno so we can detect directory open errors. */ + errno = 0; + /* * Try to find this part of the path in the directory. */ @@ -292,6 +304,11 @@ BOOL unix_convert(pstring name,connection_struct *conn,char *saved_last_componen return(False); } + if (errno == ENOTDIR) { + *bad_path = True; + return(False); + } + /* * Just the last part of the name doesn't exist. * We may need to strupper() or strlower() it in case @@ -392,12 +409,11 @@ BOOL check_name(pstring name,connection_struct *conn) { BOOL ret = True; - errno = 0; - if (IS_VETO_PATH(conn, name)) { /* Is it not dot or dot dot. */ if (!((name[0] == '.') && (!name[1] || (name[1] == '.' && !name[2])))) { DEBUG(5,("file path name %s vetoed\n",name)); + errno = ENOENT; return False; } } @@ -416,13 +432,15 @@ BOOL check_name(pstring name,connection_struct *conn) if ( (SMB_VFS_LSTAT(conn,name,&statbuf) != -1) && (S_ISLNK(statbuf.st_mode)) ) { DEBUG(3,("check_name: denied: file path name %s is a symlink\n",name)); + errno = EACCES; ret = False; } } #endif - if (!ret) + if (!ret) { DEBUG(5,("check_name on %s failed\n",name)); + } return(ret); } @@ -496,5 +514,6 @@ static BOOL scan_directory(const char *path, char *name, size_t maxlength, } CloseDir(cur_dir); + errno = ENOENT; return(False); } diff --git a/source/smbd/lanman.c b/source/smbd/lanman.c index c4df84e76c7..dd9708356e7 100644 --- a/source/smbd/lanman.c +++ b/source/smbd/lanman.c @@ -1121,11 +1121,11 @@ static int fill_srv_info(struct srv_info_struct *service, switch (uLevel) { case 0: - push_ascii(p,service->name, 15, STR_TERMINATE); + push_ascii(p,service->name, MAX_NETBIOSNAME_LEN, STR_TERMINATE); break; case 1: - push_ascii(p,service->name,15, STR_TERMINATE); + push_ascii(p,service->name,MAX_NETBIOSNAME_LEN, STR_TERMINATE); SIVAL(p,18,service->type); SIVAL(p,22,PTR_DIFF(p2,baseaddr)); len += CopyAndAdvance(&p2,service->comment,&l2); diff --git a/source/smbd/mangle_hash.c b/source/smbd/mangle_hash.c index 7b8cbdbddba..d7239b82a79 100644 --- a/source/smbd/mangle_hash.c +++ b/source/smbd/mangle_hash.c @@ -546,8 +546,10 @@ static void cache_mangled_name( char *mangled_name, char *raw_name ) /* Fill the new cache entry, and add it to the cache. */ s1 = (char *)(new_entry + 1); s2 = (char *)&(s1[mangled_len + 1]); - safe_strcpy( s1, mangled_name, mangled_len ); - safe_strcpy( s2, raw_name, raw_len ); + memcpy( s1, mangled_name, mangled_len ); + s1[mangled_len] = '\0'; + memcpy( s2, raw_name, raw_len ); + s2[raw_len] = '\0'; ubi_cachePut( mangled_cache, i, new_entry, s1 ); } diff --git a/source/smbd/msdfs.c b/source/smbd/msdfs.c index c66f0477a84..6c132897f98 100644 --- a/source/smbd/msdfs.c +++ b/source/smbd/msdfs.c @@ -65,7 +65,7 @@ static BOOL parse_dfs_path(char* pathname, struct dfs_path* pdp) DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename)); /* rest is reqpath */ - check_path_syntax(pdp->reqpath, p+1); + check_path_syntax(pdp->reqpath, p+1,True); DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath)); return True; @@ -111,7 +111,7 @@ static BOOL parse_processed_dfs_path(char* pathname, struct dfs_path* pdp) DEBUG(10,("parse_processed_dfs_path: servicename: %s\n",pdp->servicename)); /* rest is reqpath */ - check_path_syntax(pdp->reqpath, p+1); + check_path_syntax(pdp->reqpath, p+1,True); DEBUG(10,("parse_processed_dfs_path: rest of the path: %s\n",pdp->reqpath)); return True; diff --git a/source/smbd/negprot.c b/source/smbd/negprot.c index 1843c174bb5..5ff53f63007 100644 --- a/source/smbd/negprot.c +++ b/source/smbd/negprot.c @@ -401,8 +401,9 @@ protocol [LANMAN2.1] #define ARCH_WIN2K 0xC /* Win2K is like NT */ #define ARCH_OS2 0x14 /* Again OS/2 is like NT */ #define ARCH_SAMBA 0x20 +#define ARCH_CIFSFS 0x40 -#define ARCH_ALL 0x3F +#define ARCH_ALL 0x7F /* List of supported protocols, most desired first */ static const struct { @@ -413,6 +414,7 @@ static const struct { } supported_protocols[] = { {"NT LANMAN 1.0", "NT1", reply_nt1, PROTOCOL_NT1}, {"NT LM 0.12", "NT1", reply_nt1, PROTOCOL_NT1}, + {"POSIX 2", "NT1", reply_nt1, PROTOCOL_NT1}, {"LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2}, {"Samba", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2}, {"DOS LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2}, @@ -460,7 +462,7 @@ int reply_negprot(connection_struct *conn, 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_WIN2K ); + arch &= ( ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K | ARCH_CIFSFS); else if (strcsequal(p,"LANMAN2.1")) arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 ); else if (strcsequal(p,"LM1.2X002")) @@ -472,12 +474,23 @@ int reply_negprot(connection_struct *conn, else if (strcsequal(p,"Samba")) { arch = ARCH_SAMBA; break; + } else if (strcsequal(p,"POSIX 2")) { + arch = ARCH_CIFSFS; + break; } p += strlen(p) + 2; } - + + /* CIFSFS can send one arch only, NT LM 0.12. */ + if (Index == 1 && (arch & ARCH_CIFSFS)) { + arch = ARCH_CIFSFS; + } + switch ( arch ) { + case ARCH_CIFSFS: + set_remote_arch(RA_CIFSFS); + break; case ARCH_SAMBA: set_remote_arch(RA_SAMBA); break; diff --git a/source/smbd/notify_hash.c b/source/smbd/notify_hash.c index ec414454f9e..843580f6edf 100644 --- a/source/smbd/notify_hash.c +++ b/source/smbd/notify_hash.c @@ -164,7 +164,7 @@ static BOOL hash_check_notify(connection_struct *conn, uint16 vuid, char *path, if (!change_to_user(conn,vuid)) return True; - if (!set_current_service(conn,True)) { + if (!set_current_service(conn,FLAG_CASELESS_PATHNAMES,True)) { change_to_root_user(); return True; } diff --git a/source/smbd/nttrans.c b/source/smbd/nttrans.c index 26be4434fdb..f717efac63e 100644 --- a/source/smbd/nttrans.c +++ b/source/smbd/nttrans.c @@ -651,7 +651,7 @@ create_options = 0x%x root_dir_fid = 0x%x\n", flags, desired_access, file_attrib if(!dir_fsp->is_directory) { - srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status); + srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status,False); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBntcreateX); return ERROR_NT(status); @@ -693,14 +693,14 @@ create_options = 0x%x root_dir_fid = 0x%x\n", flags, desired_access, file_attrib dir_name_len++; } - srvstr_get_path(inbuf, rel_fname, smb_buf(inbuf), sizeof(rel_fname), 0, STR_TERMINATE, &status); + srvstr_get_path(inbuf, rel_fname, smb_buf(inbuf), sizeof(rel_fname), 0, STR_TERMINATE, &status,False); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBntcreateX); return ERROR_NT(status); } pstrcat(fname, rel_fname); } else { - srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status); + srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status,False); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBntcreateX); return ERROR_NT(status); @@ -762,7 +762,18 @@ create_options = 0x%x root_dir_fid = 0x%x\n", flags, desired_access, file_attrib set_posix_case_semantics(conn, file_attributes); unix_convert(fname,conn,0,&bad_path,&sbuf); - + if (bad_path) { + restore_case_semantics(conn, file_attributes); + END_PROFILE(SMBntcreateX); + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } + /* All file access must go through check_name() */ + if (!check_name(fname,conn)) { + restore_case_semantics(conn, file_attributes); + END_PROFILE(SMBntcreateX); + return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath); + } + /* * If it's a request for a directory open, deal with it separately. */ @@ -863,6 +874,11 @@ create_options = 0x%x root_dir_fid = 0x%x\n", flags, desired_access, file_attrib restore_case_semantics(conn, file_attributes); END_PROFILE(SMBntcreateX); + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + clear_cached_errors(); + return -1; + } return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess); } } @@ -1010,7 +1026,7 @@ static int do_nt_transact_create_pipe( connection_struct *conn, char *inbuf, cha return ERROR_DOS(ERRDOS,ERRnoaccess); } - srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status); + srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } @@ -1213,7 +1229,7 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o return ERROR_DOS(ERRDOS,ERRbadfid); if(!dir_fsp->is_directory) { - srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status); + srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } @@ -1246,14 +1262,14 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o { pstring tmpname; - srvstr_get_path(inbuf, tmpname, params+53, sizeof(tmpname), parameter_count-53, STR_TERMINATE, &status); + srvstr_get_path(inbuf, tmpname, params+53, sizeof(tmpname), parameter_count-53, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } pstrcat(fname, tmpname); } } else { - srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status); + srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } @@ -1287,6 +1303,15 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); unix_convert(fname,conn,0,&bad_path,&sbuf); + if (bad_path) { + restore_case_semantics(conn, file_attributes); + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } + /* All file access must go through check_name() */ + if (!check_name(fname,conn)) { + restore_case_semantics(conn, file_attributes); + return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath); + } /* * If it's a request for a directory open, deal with it separately. @@ -1347,6 +1372,11 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o } } else { restore_case_semantics(conn, file_attributes); + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + clear_cached_errors(); + return -1; + } return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess); } } @@ -1491,6 +1521,155 @@ int reply_ntcancel(connection_struct *conn, } /**************************************************************************** + Copy a file. +****************************************************************************/ + +static NTSTATUS copy_internals(connection_struct *conn, char *oldname, char *newname, uint16 attrs) +{ + BOOL bad_path_oldname = False; + BOOL bad_path_newname = False; + SMB_STRUCT_STAT sbuf1, sbuf2; + pstring last_component_oldname; + pstring last_component_newname; + files_struct *fsp1,*fsp2; + uint16 fmode; + int access_mode; + int smb_action; + SMB_OFF_T ret=-1; + int close_ret; + NTSTATUS status = NT_STATUS_OK; + + ZERO_STRUCT(sbuf1); + ZERO_STRUCT(sbuf2); + + /* No wildcards. */ + if (ms_has_wild(newname) || ms_has_wild(oldname)) { + return NT_STATUS_OBJECT_PATH_SYNTAX_BAD; + } + + if (!CAN_WRITE(conn)) + return NT_STATUS_MEDIA_WRITE_PROTECTED; + + unix_convert(oldname,conn,last_component_oldname,&bad_path_oldname,&sbuf1); + if (bad_path_oldname) { + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + + /* Quick check for "." and ".." */ + if (last_component_oldname[0] == '.') { + if (!last_component_oldname[1] || (last_component_oldname[1] == '.' && !last_component_oldname[2])) { + return NT_STATUS_OBJECT_NAME_INVALID; + } + } + + /* Source must already exist. */ + if (!VALID_STAT(sbuf1)) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + if (!check_name(oldname,conn)) { + return NT_STATUS_ACCESS_DENIED; + } + + /* Ensure attributes match. */ + fmode = dos_mode(conn,oldname,&sbuf1); + if ((fmode & ~attrs) & (aHIDDEN | aSYSTEM)) + return NT_STATUS_NO_SUCH_FILE; + + unix_convert(newname,conn,last_component_newname,&bad_path_newname,&sbuf2); + if (bad_path_newname) { + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + + /* Quick check for "." and ".." */ + if (last_component_newname[0] == '.') { + if (!last_component_newname[1] || (last_component_newname[1] == '.' && !last_component_newname[2])) { + return NT_STATUS_OBJECT_NAME_INVALID; + } + } + + /* Disallow if newname already exists. */ + if (VALID_STAT(sbuf2)) { + return NT_STATUS_OBJECT_NAME_COLLISION; + } + + if (!check_name(newname,conn)) { + return NT_STATUS_ACCESS_DENIED; + } + + /* No links from a directory. */ + if (S_ISDIR(sbuf1.st_mode)) { + return NT_STATUS_FILE_IS_A_DIRECTORY; + } + + /* Ensure this is within the share. */ + if (!reduce_name(conn, oldname) != 0) { + return NT_STATUS_ACCESS_DENIED; + } + + DEBUG(10,("copy_internals: doing file copy %s to %s\n", oldname, newname)); + + fsp1 = open_file_shared1(conn,oldname,&sbuf1,FILE_READ_DATA,SET_DENY_MODE(DENY_ALL)|SET_OPEN_MODE(DOS_OPEN_RDONLY), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),FILE_ATTRIBUTE_NORMAL,0, + &access_mode,&smb_action); + + if (!fsp1) { + status = NT_STATUS_ACCESS_DENIED; + if (unix_ERR_class == ERRDOS && unix_ERR_code == ERRbadshare) + status = NT_STATUS_SHARING_VIOLATION; + unix_ERR_class = 0; + unix_ERR_code = 0; + unix_ERR_ntstatus = NT_STATUS_OK; + return status; + } + + fsp2 = open_file_shared1(conn,newname,&sbuf2,FILE_WRITE_DATA,SET_DENY_MODE(DENY_ALL)|SET_OPEN_MODE(DOS_OPEN_WRONLY), + (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_FAIL),fmode,INTERNAL_OPEN_ONLY, + &access_mode,&smb_action); + + if (!fsp2) { + status = NT_STATUS_ACCESS_DENIED; + if (unix_ERR_class == ERRDOS && unix_ERR_code == ERRbadshare) + status = NT_STATUS_SHARING_VIOLATION; + unix_ERR_class = 0; + unix_ERR_code = 0; + unix_ERR_ntstatus = NT_STATUS_OK; + close_file(fsp1,False); + return status; + } + + if (sbuf1.st_size) + ret = vfs_transfer_file(fsp1, fsp2, sbuf1.st_size); + + /* + * 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. + */ + close_file(fsp1,False); + + /* Ensure the modtime is set correctly on the destination file. */ + fsp2->pending_modtime = sbuf1.st_mtime; + + close_ret = close_file(fsp2,False); + + /* Grrr. We have to do this as open_file_shared1 adds aARCH when it + creates the file. This isn't the correct thing to do in the copy case. JRA */ + file_set_dosmode(conn, newname, fmode, &sbuf2); + + if (ret < (SMB_OFF_T)sbuf1.st_size) { + return NT_STATUS_DISK_FULL; + } + + if (close_ret != 0) { + status = map_nt_error_from_unix(close_ret); + DEBUG(3,("copy_internals: Error %s copy file %s to %s\n", + nt_errstr(status), oldname, newname)); + } + return status; +} + +/**************************************************************************** Reply to a NT rename request. ****************************************************************************/ @@ -1507,13 +1686,8 @@ int reply_ntrename(connection_struct *conn, START_PROFILE(SMBntrename); - if ((rename_type != RENAME_FLAG_RENAME) && (rename_type != RENAME_FLAG_HARD_LINK)) { - END_PROFILE(SMBntrename); - return ERROR_NT(NT_STATUS_ACCESS_DENIED); - } - p = smb_buf(inbuf) + 1; - p += srvstr_get_path(inbuf, oldname, p, sizeof(oldname), 0, STR_TERMINATE, &status); + p += srvstr_get_path(inbuf, oldname, p, sizeof(oldname), 0, STR_TERMINATE, &status, True); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBntrename); return ERROR_NT(status); @@ -1525,8 +1699,13 @@ int reply_ntrename(connection_struct *conn, return ERROR_NT(NT_STATUS_ACCESS_DENIED); } + if (ms_has_wild(oldname)) { + END_PROFILE(SMBntrename); + return ERROR_NT(NT_STATUS_OBJECT_PATH_SYNTAX_BAD); + } + p++; - p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status); + p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBntrename); return ERROR_NT(status); @@ -1537,14 +1716,31 @@ int reply_ntrename(connection_struct *conn, DEBUG(3,("reply_ntrename : %s -> %s\n",oldname,newname)); - if (rename_type == RENAME_FLAG_RENAME) { - status = rename_internals(conn, oldname, newname, attrs, False); - } else { - status = hardlink_internals(conn, oldname, newname); + switch(rename_type) { + case RENAME_FLAG_RENAME: + status = rename_internals(conn, oldname, newname, attrs, False); + break; + case RENAME_FLAG_HARD_LINK: + status = hardlink_internals(conn, oldname, newname); + break; + case RENAME_FLAG_COPY: + status = copy_internals(conn, oldname, newname, attrs); + break; + case RENAME_FLAG_MOVE_CLUSTER_INFORMATION: + status = NT_STATUS_INVALID_PARAMETER; + break; + default: + status = NT_STATUS_ACCESS_DENIED; /* Default error. */ + break; } if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBntrename); + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + clear_cached_errors(); + return -1; + } return ERROR_NT(status); } @@ -1630,7 +1826,7 @@ static int call_nt_transact_rename(connection_struct *conn, char *inbuf, char *o fsp = file_fsp(params, 0); replace_if_exists = (SVAL(params,2) & RENAME_REPLACE_IF_EXISTS) ? True : False; CHECK_FSP(fsp, conn); - srvstr_get_path(inbuf, new_name, params+4, sizeof(new_name), -1, STR_TERMINATE, &status); + srvstr_get_path(inbuf, new_name, params+4, sizeof(new_name), -1, STR_TERMINATE, &status, True); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } diff --git a/source/smbd/open.c b/source/smbd/open.c index 3b4f50b0656..65500c65cee 100644 --- a/source/smbd/open.c +++ b/source/smbd/open.c @@ -2,7 +2,7 @@ Unix SMB/CIFS implementation. file opening and share modes Copyright (C) Andrew Tridgell 1992-1998 - Copyright (C) Jeremy Allison 2001 + Copyright (C) Jeremy Allison 2001-2004 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,6 +26,11 @@ extern uint16 global_oplock_port; extern uint16 global_smbpid; extern BOOL global_client_failed_oplock_break; +struct dev_inode_bundle { + SMB_DEV_T dev; + SMB_INO_T inode; +}; + /**************************************************************************** fd support routines - attempt to do a dos_open. ****************************************************************************/ @@ -239,11 +244,12 @@ static BOOL open_file(files_struct *fsp,connection_struct *conn, BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write), conn->num_files_open + 1)); + errno = 0; return True; } /******************************************************************* -return True if the filename is one of the special executable types + Return True if the filename is one of the special executable types. ********************************************************************/ static BOOL is_executable(const char *fname) @@ -262,12 +268,13 @@ static BOOL is_executable(const char *fname) enum {AFAIL,AREAD,AWRITE,AALL}; /******************************************************************* -reproduce the share mode access table -this is horrendoously complex, and really can't be justified on any -rational grounds except that this is _exactly_ what NT does. See -the DENY1 and DENY2 tests in smbtorture for a comprehensive set of -test routines. + Reproduce the share mode access table. + This is horrendoously complex, and really can't be justified on any + rational grounds except that this is _exactly_ what NT does. See + the DENY1 and DENY2 tests in smbtorture for a comprehensive set of + test routines. ********************************************************************/ + static int access_table(int new_deny,int old_deny,int old_mode, BOOL same_pid, BOOL isexe) { @@ -353,9 +360,8 @@ 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 + Check if we can open a file with a share mode. ****************************************************************************/ static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, int share_mode, uint32 desired_access, @@ -364,6 +370,8 @@ static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, i int deny_mode = GET_DENY_MODE(share_mode); int old_open_mode = GET_OPEN_MODE(share->share_mode); int old_deny_mode = GET_DENY_MODE(share->share_mode); + BOOL non_io_open_request; + BOOL non_io_open_existing; /* * share modes = false means don't bother to check for @@ -373,6 +381,18 @@ static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, i if(!lp_share_modes(SNUM(conn))) return True; + if (desired_access & ~(SYNCHRONIZE_ACCESS|READ_CONTROL_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES)) { + non_io_open_request = False; + } else { + non_io_open_request = True; + } + + if (share->desired_access & ~(SYNCHRONIZE_ACCESS|READ_CONTROL_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES)) { + non_io_open_existing = False; + } else { + non_io_open_existing = True; + } + /* * Don't allow any opens once the delete on close flag has been * set. @@ -411,8 +431,7 @@ static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, i * and the existing desired_acces then share modes don't conflict. */ - if ( !(desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) && - !(share->desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ) { + if (non_io_open_request && non_io_open_existing) { /* * Wrinkle discovered by smbtorture.... @@ -436,6 +455,13 @@ static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, i and existing desired access (0x%x) are non-data opens\n", fname, (unsigned int)desired_access, (unsigned int)share->desired_access )); return True; + } else if (non_io_open_request || non_io_open_existing) { + /* + * If either are non-io opens then share modes don't conflict. + */ + DEBUG(5,("check_share_mode: One non-io open. Allowing open on file %s as desired access (0x%x) doesn't conflict with\ +existing desired access (0x%x).\n", fname, (unsigned int)desired_access, (unsigned int)share->desired_access )); + return True; } /* @@ -537,6 +563,20 @@ static void validate_my_share_entries(int num, share_mode_entry *share_entry) } #endif +struct share_mode_entry_list { + struct share_mode_entry_list *next, *prev; + share_mode_entry entry; +}; + +static void free_broken_entry_list(struct share_mode_entry_list *broken_entry_list) +{ + while (broken_entry_list) { + struct share_mode_entry_list *broken_entry = broken_entry_list; + DLIST_REMOVE(broken_entry_list, broken_entry); + SAFE_FREE(broken_entry); + } +} + /**************************************************************************** Deal with open deny mode and oplock break processing. Invarient: Share mode must be locked on entry and exit. @@ -554,8 +594,8 @@ static int open_mode_check(connection_struct *conn, const char *fname, SMB_DEV_T int oplock_contention_count = 0; share_mode_entry *old_shares = 0; BOOL fcbopen = False; - BOOL broke_oplock; - + BOOL broke_oplock; + if(GET_OPEN_MODE(share_mode) == DOS_OPEN_FCB) fcbopen = True; @@ -564,17 +604,25 @@ static int open_mode_check(connection_struct *conn, const char *fname, SMB_DEV_T if(num_share_modes == 0) return 0; + if (desired_access && ((desired_access & ~(SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES))==0) && + ((desired_access & (SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES)) != 0)) { + /* Stat open that doesn't trigger oplock breaks or share mode checks... ! JRA. */ + return num_share_modes; + } + /* * Check if the share modes will give us access. */ do { - share_mode_entry broken_entry; - + struct share_mode_entry_list *broken_entry_list = NULL; + struct share_mode_entry_list *broken_entry = NULL; + broke_oplock = False; *p_all_current_opens_are_level_II = True; for(i = 0; i < num_share_modes; i++) { + BOOL cause_oplock_break = False; share_mode_entry *share_entry = &old_shares[i]; #if defined(DEVELOPER) @@ -589,9 +637,17 @@ static int open_mode_check(connection_struct *conn, const char *fname, SMB_DEV_T * it before continuing. */ - if((*p_oplock_request && EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) || + /* Was this a delete this file request ? */ + if (!*p_oplock_request && desired_access == DELETE_ACCESS && + !BATCH_OPLOCK_TYPE(share_entry->op_type)) { + /* Don't break the oplock in this case. */ + cause_oplock_break = False; + } else if((*p_oplock_request && EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) || (!*p_oplock_request && (share_entry->op_type != NO_OPLOCK))) { - + cause_oplock_break = True; + } + + if(cause_oplock_break) { BOOL opb_ret; DEBUG(5,("open_mode_check: oplock_request = %d, breaking oplock (%x) on file %s, \ @@ -622,50 +678,65 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou return -1; } + broken_entry = malloc(sizeof(struct share_mode_entry_list)); + if (!broken_entry) { + smb_panic("open_mode_check: malloc fail.\n"); + } + broken_entry->entry = *share_entry; + DLIST_ADD(broken_entry_list, broken_entry); broke_oplock = True; - broken_entry = *share_entry; - break; } else if (!LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) { *p_all_current_opens_are_level_II = False; } - + } /* end for */ + + if (broke_oplock) { + /* Update the current open table. */ + SAFE_FREE(old_shares); + num_share_modes = get_share_modes(conn, dev, inode, &old_shares); + } + + /* Now we check the share modes, after any oplock breaks. */ + for(i = 0; i < num_share_modes; i++) { + share_mode_entry *share_entry = &old_shares[i]; + /* someone else has a share lock on it, check to see if we can too */ if (!check_share_mode(conn, share_entry, share_mode, desired_access, fname, fcbopen, p_flags)) { SAFE_FREE(old_shares); + free_broken_entry_list(broken_entry_list); errno = EACCES; return -1; } - - } /* end for */ - - if(broke_oplock) { - SAFE_FREE(old_shares); - num_share_modes = get_share_modes(conn, dev, inode, &old_shares); + } + + for(broken_entry = broken_entry_list; broken_entry; broken_entry = broken_entry->next) { oplock_contention_count++; /* Paranoia check that this is no longer an exlusive entry. */ for(i = 0; i < num_share_modes; i++) { share_mode_entry *share_entry = &old_shares[i]; - if (share_modes_identical(&broken_entry, share_entry) && - EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type) ) { + if (share_modes_identical(&broken_entry->entry, share_entry) && + EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type) ) { /* * This should not happen. The target left this oplock * as exlusive.... The process *must* be dead.... */ - DEBUG(0,("open_mode_check: exlusive oplock left by process %d after break ! For file %s, \ -dev = %x, inode = %.0f. Deleting it to continue...\n", (int)broken_entry.pid, fname, (unsigned int)dev, (double)inode)); + DEBUG(0,("open_mode_check: exlusive oplock left by process %d \ +after break ! For file %s, dev = %x, inode = %.0f. Deleting it to continue...\n", + (int)broken_entry->entry.pid, fname, (unsigned int)dev, (double)inode)); - if (process_exists(broken_entry.pid)) { + if (process_exists(broken_entry->entry.pid)) { DEBUG(0,("open_mode_check: Existent process %lu left active oplock.\n", - (unsigned long)broken_entry.pid )); + (unsigned long)broken_entry->entry.pid )); } - if (del_share_entry(dev, inode, &broken_entry, NULL) == -1) { + if (del_share_entry(dev, inode, &broken_entry->entry, NULL) == -1) { + free_broken_entry_list(broken_entry_list); errno = EACCES; unix_ERR_class = ERRDOS; unix_ERR_code = ERRbadshare; @@ -683,8 +754,8 @@ dev = %x, inode = %.0f. Deleting it to continue...\n", (int)broken_entry.pid, fn break; } } /* end for paranoia... */ - } /* end if broke_oplock */ - + } /* end for broken_entry */ + free_broken_entry_list(broken_entry_list); } while(broke_oplock); if(old_shares != 0) @@ -705,9 +776,119 @@ dev = %x, inode = %.0f. Deleting it to continue...\n", (int)broken_entry.pid, fn } /**************************************************************************** -set a kernel flock on a file for NFS interoperability -this requires a patch to Linux + Delete the record for a handled deferred open entry. ****************************************************************************/ + +static void delete_defered_open_entry_record(connection_struct *conn, SMB_DEV_T dev, SMB_INO_T inode) +{ + uint16 mid = get_current_mid(); + pid_t mypid = sys_getpid(); + deferred_open_entry *de_array = NULL; + int num_de_entries, i; + + if (!lp_defer_sharing_violations()) { + return; + } + + num_de_entries = get_deferred_opens(conn, dev, inode, &de_array); + for (i = 0; i < num_de_entries; i++) { + deferred_open_entry *entry = &de_array[i]; + if (entry->pid == mypid && entry->mid == mid && entry->dev == dev && + entry->inode == inode) { + + /* Remove the deferred open entry from the array. */ + delete_deferred_open_entry(entry); + SAFE_FREE(de_array); + return; + } + } + SAFE_FREE(de_array); +} + +/**************************************************************************** + Handle the 1 second delay in returning a SHARING_VIOLATION error. +****************************************************************************/ + +void defer_open_sharing_error(connection_struct *conn, struct timeval *ptv, + char *fname, SMB_DEV_T dev, SMB_INO_T inode) +{ + uint16 mid = get_current_mid(); + pid_t mypid = sys_getpid(); + deferred_open_entry *de_array = NULL; + int num_de_entries, i; + struct dev_inode_bundle dib; + + if (!lp_defer_sharing_violations()) { + return; + } + + dib.dev = dev; + dib.inode = inode; + + num_de_entries = get_deferred_opens(conn, dev, inode, &de_array); + for (i = 0; i < num_de_entries; i++) { + deferred_open_entry *entry = &de_array[i]; + if (entry->pid == mypid && entry->mid == mid) { + /* + * Check if a 1 second timeout has expired. + */ + if (usec_time_diff(ptv, &entry->time) > SHARING_VIOLATION_USEC_WAIT) { + DEBUG(10,("defer_open_sharing_error: Deleting deferred open entry for mid %u, \ +file %s\n", + (unsigned int)mid, fname )); + + /* Expired, return a real error. */ + /* Remove the deferred open entry from the array. */ + + delete_deferred_open_entry(entry); + SAFE_FREE(de_array); + return; + } + /* + * If the timeout hasn't expired yet and we still have a sharing violation, + * just leave the entry in the deferred open array alone. We do need to + * reschedule this open call though (with the original created time). + */ + DEBUG(10,("defer_open_sharing_error: time [%u.%06u] updating \ +deferred open entry for mid %u, file %s\n", + (unsigned int)entry->time.tv_sec, + (unsigned int)entry->time.tv_usec, + (unsigned int)mid, fname )); + + push_sharing_violation_open_smb_message(&entry->time, (char *)&dib, sizeof(dib)); + SAFE_FREE(de_array); + return; + } + } + + DEBUG(10,("defer_open_sharing_error: time [%u.%06u] adding deferred open entry for mid %u, file %s\n", + (unsigned int)ptv->tv_sec, (unsigned int)ptv->tv_usec, (unsigned int)mid, fname )); + + if (!push_sharing_violation_open_smb_message(ptv, (char *)&dib, sizeof(dib))) { + SAFE_FREE(de_array); + return; + } + if (!add_deferred_open(mid, ptv, dev, inode, global_oplock_port, fname)) { + remove_sharing_violation_open_smb_message(mid); + } + + /* + * Push the MID of this packet on the signing queue. + * We only do this once, the first time we push the packet + * onto the deferred open queue, as this has a side effect + * of incrementing the response sequence number. + */ + + srv_defer_sign_response(mid); + + SAFE_FREE(de_array); +} + +/**************************************************************************** + Set a kernel flock on a file for NFS interoperability. + This requires a patch to Linux. +****************************************************************************/ + static void kernel_flock(files_struct *fsp, int deny_mode) { #if HAVE_KERNEL_SHARE_MODES @@ -781,6 +962,8 @@ files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_ BOOL file_existed = VALID_STAT(*psbuf); BOOL fcbopen = False; BOOL def_acl = False; + BOOL add_share_mode = True; + BOOL internal_only_open = False; SMB_DEV_T dev = 0; SMB_INO_T inode = 0; int num_share_modes = 0; @@ -792,9 +975,50 @@ files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_ mode_t new_mode = (mode_t)0; int action; uint32 existing_dos_mode = 0; + struct pending_message_list *pml = NULL; + uint16 mid = get_current_mid(); /* We add aARCH to this as this mode is only used if the file is created new. */ mode_t mode = unix_mode(conn,new_dos_mode | aARCH,fname); + if (oplock_request == INTERNAL_OPEN_ONLY) { + internal_only_open = True; + oplock_request = 0; + } + + if ((pml = get_open_deferred_message(mid)) != NULL) { + struct dev_inode_bundle dib; + + memcpy(&dib, pml->private_data.data, sizeof(dib)); + + /* There could be a race condition where the dev/inode pair + has changed since we deferred the message. If so, just + remove the deferred open entry and return sharing violation. */ + + /* If the timeout value is non-zero, we need to just + return sharing violation. Don't retry the open + as we were not notified of a close and we don't want to + trigger another spurious oplock break. */ + + if (!file_existed || dib.dev != psbuf->st_dev || dib.inode != psbuf->st_ino || + pml->msg_time.tv_sec || pml->msg_time.tv_usec) { + /* Ensure we don't reprocess this message. */ + remove_sharing_violation_open_smb_message(mid); + + /* Now remove the deferred open entry under lock. */ + lock_share_entry(conn, dib.dev, dib.inode); + delete_defered_open_entry_record(conn, dib.dev, dib.inode); + unlock_share_entry(conn, dib.dev, dib.inode); + + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadshare; + unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION; + return NULL; + } + /* Ensure we don't reprocess this message. */ + remove_sharing_violation_open_smb_message(mid); + + } + if (conn->printer) { /* printers are handled completely differently. Most of the passed parameters are ignored */ @@ -805,25 +1029,6 @@ files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_ return print_fsp_open(conn, fname); } - if (desired_access && ((desired_access & ~(SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES))==0) && - ((desired_access & (SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES)) != 0)) { - /* Stat open that doesn't trigger oplock breaks or share mode checks... ! JRA. */ - oplock_request = 0; - fsp = open_file_stat(conn, fname, psbuf); - if (!fsp) - return NULL; - - fsp->desired_access = desired_access; - if (Access) - *Access = DOS_OPEN_RDONLY; - if (paction) - *paction = FILE_WAS_OPENED; - - DEBUG(10,("open_file_shared: stat open for fname = %s share_mode = %x\n", - fname, share_mode )); - return fsp; - } - fsp = file_new(conn); if(!fsp) return NULL; @@ -947,6 +1152,17 @@ files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_ return NULL; } + if (desired_access && ((desired_access & ~(SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES))==0) && + ((desired_access & (SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES)) != 0)) { + /* Stat open that doesn't trigger oplock breaks or share mode checks... ! JRA. */ + deny_mode = DENY_NONE; + if (file_existed) { + oplock_request = 0; + add_share_mode = False; + flags2 &= ~O_CREAT; + } + } + if (file_existed) { dev = psbuf->st_dev; @@ -985,17 +1201,28 @@ flags=0x%X flags2=0x%X mode=0%o returned %d\n", unix_ERR_ntstatus = NT_STATUS_ACCESS_DENIED; } + /* + * If we're returning a share violation, ensure we cope with + * the braindead 1 second delay. + */ + + if (!internal_only_open && NT_STATUS_EQUAL(unix_ERR_ntstatus,NT_STATUS_SHARING_VIOLATION)) { + /* The fsp->open_time here represents the current time of day. */ + defer_open_sharing_error(conn, &fsp->open_time, fname, dev, inode); + } + unlock_share_entry(conn, dev, inode); - if (fsp_open) + if (fsp_open) { fd_close(conn, fsp); + /* + * We have detected a sharing violation here + * so return the correct error code + */ + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadshare; + unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION; + } file_free(fsp); - /* - * We have detected a sharing violation here - * so return the correct error code - */ - unix_ERR_class = ERRDOS; - unix_ERR_code = ERRbadshare; - unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION; return NULL; } @@ -1060,6 +1287,16 @@ flags=0x%X flags2=0x%X mode=0%o returned %d\n", &flags, &oplock_request, &all_current_opens_are_level_II); if(num_share_modes == -1) { + /* + * If we're returning a share violation, ensure we cope with + * the braindead 1 second delay. + */ + + if (!internal_only_open && NT_STATUS_EQUAL(unix_ERR_ntstatus,NT_STATUS_SHARING_VIOLATION)) { + /* The fsp->open_time here represents the current time of day. */ + defer_open_sharing_error(conn, &fsp->open_time, fname, dev, inode); + } + unlock_share_entry_fsp(fsp); fd_close(conn,fsp); file_free(fsp); @@ -1166,14 +1403,18 @@ flags=0x%X flags2=0x%X mode=0%o returned %d\n", oplock_request = 0; } - set_share_mode(fsp, port, oplock_request); + if (add_share_mode) { + set_share_mode(fsp, port, oplock_request); + } if (delete_on_close) { NTSTATUS result = set_delete_on_close_internal(fsp, delete_on_close); if (NT_STATUS_V(result) != NT_STATUS_V(NT_STATUS_OK)) { /* Remember to delete the mode we just added. */ - del_share_mode(fsp, NULL); + if (add_share_mode) { + del_share_mode(fsp, NULL); + } unlock_share_entry_fsp(fsp); fd_close(conn,fsp); file_free(fsp); @@ -1224,6 +1465,8 @@ flags=0x%X flags2=0x%X mode=0%o returned %d\n", fname, (int)new_mode)); } + /* If this is a successful open, we must remove any deferred open records. */ + delete_defered_open_entry_record(conn, fsp->dev, fsp->inode); unlock_share_entry_fsp(fsp); conn->num_files_open++; diff --git a/source/smbd/oplock.c b/source/smbd/oplock.c index 19e6956d9ef..1ffc798b1fc 100644 --- a/source/smbd/oplock.c +++ b/source/smbd/oplock.c @@ -388,6 +388,30 @@ pid %d, port %d, dev = %x, inode = %.0f, file_id = %lu\n", (int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id)); break; + case RETRY_DEFERRED_OPEN_CMD: + + /* Request to retry and open that would return SHARING_VIOLATION. */ + if (msg_len != DEFERRED_OPEN_MSG_LEN) { + DEBUG(0,("process_local_message: incorrect length for RETRY_DEFERRED_OPEN_CMD (was %d, should be %d).\n", + (int)msg_len, (int)DEFERRED_OPEN_MSG_LEN)); + return False; + } + { + uint16 mid; + + memcpy((char *)&remotepid, msg_start+DEFERRED_OPEN_PID_OFFSET,sizeof(remotepid)); + memcpy((char *)&inode, msg_start+DEFERRED_OPEN_INODE_OFFSET,sizeof(inode)); + memcpy((char *)&dev, msg_start+DEFERRED_OPEN_DEV_OFFSET,sizeof(dev)); + memcpy((char *)&mid, msg_start+DEFERRED_OPEN_MID_OFFSET,sizeof(mid)); + + DEBUG(5,("process_local_message: RETRY_DEFERRED_OPEN from \ +pid %d, port %d, dev = %x, inode = %.0f, mid = %u\n", + (int)remotepid, from_port, (unsigned int)dev, (double)inode, (unsigned int)mid)); + + schedule_sharing_violation_open_smb_message(mid); + } + return True; + /* * Keep this as a debug case - eventually we can remove it. */ @@ -592,6 +616,8 @@ BOOL oplock_break_level2(files_struct *fsp, BOOL local_request, int token) */ if (global_client_caps & CAP_LEVEL_II_OPLOCKS) { + BOOL sign_state; + /* * If we are sending an oplock break due to an SMB sent * by our own client we ensure that we wait at leat @@ -603,10 +629,16 @@ BOOL oplock_break_level2(files_struct *fsp, BOOL local_request, int token) wait_before_sending_break(local_request); /* Prepare the SMBlockingX message. */ - prepare_break_message( outbuf, fsp, False); + + /* Save the server smb signing state. */ + sign_state = srv_oplock_set_signing(False); + if (!send_smb(smbd_server_fd(), outbuf)) exit_server("oplock_break_level2: send_smb failed."); + + /* Restore the sign state to what it was. */ + srv_oplock_set_signing(sign_state); } /* @@ -1215,7 +1247,51 @@ void release_level_2_oplocks_on_change(files_struct *fsp) } /**************************************************************************** -setup oplocks for this process + Send a 'retry your open' message to a process with a deferred open entry. +****************************************************************************/ + +BOOL send_deferred_open_retry_message(deferred_open_entry *entry) +{ + char de_msg[DEFERRED_OPEN_MSG_LEN]; + struct sockaddr_in addr_out; + pid_t pid = sys_getpid(); + + memset(de_msg, '\0', DEFERRED_OPEN_MSG_LEN); + SSVAL(de_msg,DEFERRED_OPEN_CMD_OFFSET,RETRY_DEFERRED_OPEN_CMD); + memcpy(de_msg+DEFERRED_OPEN_PID_OFFSET,(char *)&pid,sizeof(pid)); + memcpy(de_msg+DEFERRED_OPEN_DEV_OFFSET,(char *)&entry->dev,sizeof(entry->dev)); + memcpy(de_msg+DEFERRED_OPEN_INODE_OFFSET,(char *)&entry->inode,sizeof(entry->inode)); + memcpy(de_msg+DEFERRED_OPEN_MID_OFFSET,(char *)&entry->mid,sizeof(entry->mid)); + + /* Set the address and port. */ + memset((char *)&addr_out,'\0',sizeof(addr_out)); + addr_out.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr_out.sin_port = htons( entry->port ); + addr_out.sin_family = AF_INET; + + if( DEBUGLVL( 3 ) ) { + dbgtext( "send_deferred_open_retry_message: sending a message to "); + dbgtext( "pid %d on port %d ", (int)entry->pid, entry->port ); + dbgtext( "for dev = %x, inode = %.0f, mid = %u\n", + (unsigned int)entry->dev, (double)entry->inode, (unsigned int)entry->mid ); + } + + if(sys_sendto(oplock_sock,de_msg,DEFERRED_OPEN_MSG_LEN,0, + (struct sockaddr *)&addr_out,sizeof(addr_out)) < 0) { + if( DEBUGLVL( 0 ) ) { + dbgtext( "send_deferred_open_retry_message: failed sending a message to "); + dbgtext( "pid %d on port %d ", (int)entry->pid, entry->port ); + dbgtext( "for dev = %x, inode = %.0f, mid = %u\n", + (unsigned int)entry->dev, (double)entry->inode, (unsigned int)entry->mid ); + dbgtext( "Error was %s\n", strerror(errno) ); + } + return False; + } + return True; +} + +/**************************************************************************** + Setup oplocks for this process. ****************************************************************************/ BOOL init_oplocks(void) diff --git a/source/smbd/posix_acls.c b/source/smbd/posix_acls.c index 584164e9309..2d9591e6baa 100644 --- a/source/smbd/posix_acls.c +++ b/source/smbd/posix_acls.c @@ -880,7 +880,7 @@ static mode_t map_nt_perms( SEC_ACCESS sec_access, int type) Unpack a SEC_DESC into a UNIX owner and group. ****************************************************************************/ -static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd) +static BOOL unpack_nt_owners(int snum, SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd) { DOM_SID owner_sid; DOM_SID grp_sid; @@ -910,15 +910,17 @@ static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, if (security_info_sent & OWNER_SECURITY_INFORMATION) { sid_copy(&owner_sid, psd->owner_sid); if (!NT_STATUS_IS_OK(sid_to_uid(&owner_sid, puser))) { -#if ACL_FORCE_UNMAPPABLE - /* this allows take ownership to work reasonably */ - extern struct current_user current_user; - *puser = current_user.uid; -#else - DEBUG(3,("unpack_nt_owners: unable to validate owner sid for %s\n", - sid_string_static(&owner_sid))); - return False; -#endif + if (lp_force_unknown_acl_user(snum)) { + /* this allows take ownership to work + * reasonably */ + extern struct current_user current_user; + *puser = current_user.uid; + } else { + DEBUG(3,("unpack_nt_owners: unable to validate" + " owner sid for %s\n", + sid_string_static(&owner_sid))); + return False; + } } } @@ -930,14 +932,16 @@ static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, if (security_info_sent & GROUP_SECURITY_INFORMATION) { sid_copy(&grp_sid, psd->grp_sid); if (!NT_STATUS_IS_OK(sid_to_gid( &grp_sid, pgrp))) { -#if ACL_FORCE_UNMAPPABLE - /* this allows take group ownership to work reasonably */ - extern struct current_user current_user; - *pgrp = current_user.gid; -#else - DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n")); - return False; -#endif + if (lp_force_unknown_acl_user(snum)) { + /* this allows take group ownership to work + * reasonably */ + extern struct current_user current_user; + *pgrp = current_user.gid; + } else { + DEBUG(3,("unpack_nt_owners: unable to validate" + " group sid.\n")); + return False; + } } } @@ -3005,7 +3009,7 @@ BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd) * Unpack the user/group/world id's. */ - if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd)) + if (!unpack_nt_owners( SNUM(conn), &sbuf, &user, &grp, security_info_sent, psd)) return False; /* diff --git a/source/smbd/process.c b/source/smbd/process.c index 12fd809b784..60ce1499e8d 100644 --- a/source/smbd/process.c +++ b/source/smbd/process.c @@ -61,55 +61,215 @@ uint16 get_current_mid(void) for processing. ****************************************************************************/ -typedef struct { - ubi_slNode msg_next; - char *msg_buf; - int msg_len; -} pending_message_list; +static struct pending_message_list *smb_oplock_queue; +static struct pending_message_list *smb_sharing_violation_queue; -static ubi_slList smb_oplock_queue = { NULL, (ubi_slNodePtr)&smb_oplock_queue, 0}; +enum q_type { OPLOCK_QUEUE, SHARE_VIOLATION_QUEUE }; + +/**************************************************************************** + Free up a message. +****************************************************************************/ + +static void free_queued_message(struct pending_message_list *msg) +{ + data_blob_free(&msg->buf); + data_blob_free(&msg->private_data); + SAFE_FREE(msg); +} /**************************************************************************** Function to push a message onto the tail of a linked list of smb messages ready for processing. ****************************************************************************/ -static BOOL push_message(ubi_slList *list_head, char *buf, int msg_len) +static BOOL push_queued_message(enum q_type qt, char *buf, int msg_len, struct timeval *ptv, char *private, size_t private_len) { - pending_message_list *msg = (pending_message_list *) - malloc(sizeof(pending_message_list)); + struct pending_message_list *tmp_msg; + struct pending_message_list *msg = (struct pending_message_list *) + malloc(sizeof(struct pending_message_list)); if(msg == NULL) { DEBUG(0,("push_message: malloc fail (1)\n")); return False; } - msg->msg_buf = (char *)malloc(msg_len); - if(msg->msg_buf == NULL) { + memset(msg,'\0',sizeof(*msg)); + + msg->buf = data_blob(buf, msg_len); + if(msg->buf.data == NULL) { DEBUG(0,("push_message: malloc fail (2)\n")); SAFE_FREE(msg); return False; } - memcpy(msg->msg_buf, buf, msg_len); - msg->msg_len = msg_len; + if (ptv) { + msg->msg_time = *ptv; + } + + if (private) { + msg->private_data = data_blob(private, private_len); + if (msg->private_data.data == NULL) { + DEBUG(0,("push_message: malloc fail (3)\n")); + data_blob_free(&msg->buf); + SAFE_FREE(msg); + } + } - ubi_slAddTail( list_head, msg); + if (qt == OPLOCK_QUEUE) { + DLIST_ADD_END(smb_oplock_queue, msg, tmp_msg); + } else { + DLIST_ADD_END(smb_sharing_violation_queue, msg, tmp_msg); + } - /* Push the MID of this packet on the signing queue. */ - srv_defer_sign_response(SVAL(buf,smb_mid)); + DEBUG(10,("push_message: pushed message length %u on queue %s\n", + (unsigned int)msg_len, + qt == OPLOCK_QUEUE ? "smb_oplock_queue" : "smb_sharing_violation_queue" )); return True; } /**************************************************************************** - Function to push a smb message onto a linked list of local smb messages ready + Function to push an oplock smb message onto a linked list of local smb messages ready for processing. ****************************************************************************/ BOOL push_oplock_pending_smb_message(char *buf, int msg_len) { - return push_message(&smb_oplock_queue, buf, msg_len); + BOOL ret = push_queued_message(OPLOCK_QUEUE, buf, msg_len, NULL, NULL, 0); + if (ret) { + /* Push the MID of this packet on the signing queue. */ + srv_defer_sign_response(SVAL(buf,smb_mid)); + } + return ret; +} + +/**************************************************************************** + Function to delete a sharing violation open message by mid. +****************************************************************************/ + +void remove_sharing_violation_open_smb_message(uint16 mid) +{ + struct pending_message_list *pml; + + if (!lp_defer_sharing_violations()) { + return; + } + + for (pml = smb_sharing_violation_queue; pml; pml = pml->next) { + if (mid == SVAL(pml->buf.data,smb_mid)) { + DEBUG(10,("remove_sharing_violation_open_smb_message: deleting mid %u len %u\n", + (unsigned int)mid, (unsigned int)pml->buf.length )); + DLIST_REMOVE(smb_sharing_violation_queue, pml); + free_queued_message(pml); + return; + } + } +} + +/**************************************************************************** + Move a sharing violation open retry message to the front of the list and + schedule it for immediate processing. +****************************************************************************/ + +void schedule_sharing_violation_open_smb_message(uint16 mid) +{ + struct pending_message_list *pml; + int i = 0; + + if (!lp_defer_sharing_violations()) { + return; + } + + for (pml = smb_sharing_violation_queue; pml; pml = pml->next) { + uint16 msg_mid = SVAL(pml->buf.data,smb_mid); + DEBUG(10,("schedule_sharing_violation_open_smb_message: [%d] msg_mid = %u\n", i++, + (unsigned int)msg_mid )); + if (mid == msg_mid) { + DEBUG(10,("schedule_sharing_violation_open_smb_message: scheduling mid %u\n", + mid )); + pml->msg_time.tv_sec = 0; + pml->msg_time.tv_usec = 0; + DLIST_PROMOTE(smb_sharing_violation_queue, pml); + return; + } + } + + DEBUG(10,("schedule_sharing_violation_open_smb_message: failed to find message mid %u\n", + mid )); +} + +/**************************************************************************** + Return true if this mid is on the deferred queue. +****************************************************************************/ + +BOOL open_was_deferred(uint16 mid) +{ + struct pending_message_list *pml; + + if (!lp_defer_sharing_violations()) { + return False; + } + + for (pml = smb_sharing_violation_queue; pml; pml = pml->next) { + if (SVAL(pml->buf.data,smb_mid) == mid) { + return True; + } + } + return False; +} + +/**************************************************************************** + Return the message queued by this mid. +****************************************************************************/ + +struct pending_message_list *get_open_deferred_message(uint16 mid) +{ + struct pending_message_list *pml; + + if (!lp_defer_sharing_violations()) { + return NULL; + } + + for (pml = smb_sharing_violation_queue; pml; pml = pml->next) { + if (SVAL(pml->buf.data,smb_mid) == mid) { + return pml; + } + } + return NULL; +} + +/**************************************************************************** + Function to push a sharing violation open smb message onto a linked list of local smb messages ready + for processing. +****************************************************************************/ + +BOOL push_sharing_violation_open_smb_message(struct timeval *ptv, char *private, size_t priv_len) +{ + uint16 mid = SVAL(InBuffer,smb_mid); + struct timeval tv; + SMB_BIG_INT tdif; + + if (!lp_defer_sharing_violations()) { + return True; + } + + tv = *ptv; + tdif = tv.tv_sec; + tdif *= 1000000; + tdif += tv.tv_usec; + + /* Add on the timeout. */ + tdif += SHARING_VIOLATION_USEC_WAIT; + + tv.tv_sec = tdif / 1000000; + tv.tv_usec = tdif % 1000000; + + DEBUG(10,("push_sharing_violation_open_smb_message: pushing message len %u mid %u\ + timeout time [%u.%06u]\n", (unsigned int) smb_len(InBuffer)+4, (unsigned int)mid, + (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec)); + + return push_queued_message(SHARE_VIOLATION_QUEUE, InBuffer, + smb_len(InBuffer)+4, &tv, private, priv_len); } /**************************************************************************** @@ -168,12 +328,17 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout) fd_set fds; int selrtn; struct timeval to; + struct timeval *pto; int maxfd; smb_read_error = 0; again: + to.tv_sec = timeout / 1000; + to.tv_usec = (timeout % 1000) * 1000; + pto = timeout > 0 ? &to : NULL; + /* * Note that this call must be before processing any SMB * messages as we need to synchronously process any messages @@ -185,18 +350,57 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout) * Check to see if we already have a message on the smb queue. * If so - copy and return it. */ - if(ubi_slCount(&smb_oplock_queue) != 0) { - pending_message_list *msg = (pending_message_list *)ubi_slRemHead(&smb_oplock_queue); - memcpy(buffer, msg->msg_buf, MIN(buffer_len, msg->msg_len)); + if(smb_oplock_queue != NULL) { + struct pending_message_list *msg = smb_oplock_queue; + memcpy(buffer, msg->buf.data, MIN(buffer_len, msg->buf.length)); /* Free the message we just copied. */ - SAFE_FREE(msg->msg_buf); - SAFE_FREE(msg); + DLIST_REMOVE(smb_oplock_queue, msg); + free_queued_message(msg); DEBUG(5,("receive_message_or_smb: returning queued smb message.\n")); return True; } + /* + * Check to see if we already have a message on the deferred open queue + * and it's time to schedule. + */ + if(smb_sharing_violation_queue != NULL) { + BOOL pop_message = False; + struct pending_message_list *msg = smb_sharing_violation_queue; + + if (msg->msg_time.tv_sec == 0 && msg->msg_time.tv_usec == 0) { + pop_message = True; + } else { + struct timeval tv; + SMB_BIG_INT tdif; + + GetTimeOfDay(&tv); + tdif = usec_time_diff(&msg->msg_time, &tv); + if (tdif <= 0) { + /* Timed out. Schedule...*/ + pop_message = True; + DEBUG(10,("receive_message_or_smb: queued message timed out.\n")); + } else { + /* Make a more accurate select timeout. */ + to.tv_sec = tdif / 1000000; + to.tv_usec = tdif % 1000000; + pto = &to; + DEBUG(10,("receive_message_or_smb: select with timeout of [%u.%06u]\n", + (unsigned int)pto->tv_sec, (unsigned int)pto->tv_usec )); + } + } + + if (pop_message) { + memcpy(buffer, msg->buf.data, MIN(buffer_len, msg->buf.length)); + + /* We leave this message on the queue so the open code can + know this is a retry. */ + DEBUG(5,("receive_message_or_smb: returning deferred open smb message.\n")); + return True; + } + } /* * Setup the select read fd set. @@ -227,10 +431,7 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout) FD_SET(smbd_server_fd(),&fds); maxfd = setup_oplock_select_set(&fds); - to.tv_sec = timeout / 1000; - to.tv_usec = (timeout % 1000) * 1000; - - selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,NULL,NULL,timeout>0?&to:NULL); + selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,NULL,NULL,pto); /* if we get EINTR then maybe we have received an oplock signal - treat this as select returning 1. This is ugly, but @@ -755,7 +956,7 @@ static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize return(ERROR_DOS(ERRSRV,ERRaccess)); /* load service specific parameters */ - if (conn && !set_current_service(conn,(flags & (AS_USER|DO_CHDIR)?True:False))) + if (conn && !set_current_service(conn,SVAL(inbuf,smb_flg),(flags & (AS_USER|DO_CHDIR)?True:False))) return(ERROR_DOS(ERRSRV,ERRaccess)); /* does this protocol need to be run as guest? */ diff --git a/source/smbd/quotas.c b/source/smbd/quotas.c index e439c1e571a..3c4d4319f63 100644 --- a/source/smbd/quotas.c +++ b/source/smbd/quotas.c @@ -933,6 +933,176 @@ BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB #include <devnm.h> #endif +#if defined(__FreeBSD__) + +#include <rpc/rpc.h> +#include <rpc/types.h> +#include <rpcsvc/rquota.h> +#include <rpc/nettype.h> +#include <rpc/xdr.h> + +static int quotastat; + +static int my_xdr_getquota_args(XDR *xdrsp, struct getquota_args *args) +{ + if (!xdr_string(xdrsp, &args->gqa_pathp, RQ_PATHLEN )) + return(0); + if (!xdr_int(xdrsp, &args->gqa_uid)) + return(0); + return (1); +} + +static int my_xdr_getquota_rslt(XDR *xdrsp, struct getquota_rslt *gqr) +{ + if (!xdr_int(xdrsp, "astat)) { + DEBUG(6,("nfs_quotas: Status bad or zero\n")); + return 0; + } + if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsize)) { + DEBUG(6,("nfs_quotas: Block size bad or zero\n")); + return 0; + } + if (!xdr_bool(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_active)) { + DEBUG(6,("nfs_quotas: Active bad or zero\n")); + return 0; + } + if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bhardlimit)) { + DEBUG(6,("nfs_quotas: Hardlimit bad or zero\n")); + return 0; + } + if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsoftlimit)) { + DEBUG(6,("nfs_quotas: Softlimit bad or zero\n")); + return 0; + } + if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_curblocks)) { + DEBUG(6,("nfs_quotas: Currentblocks bad or zero\n")); + return 0; + } + return (1); +} + +/* Works on FreeBSD, too. :-) */ +static BOOL nfs_quotas(char *nfspath, uid_t euser_id, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) +{ + uid_t uid = euser_id; + struct dqblk D; + char *mnttype = nfspath; + CLIENT *clnt; + struct getquota_rslt gqr; + struct getquota_args args; + char *cutstr, *pathname, *host, *testpath; + int len; + static struct timeval timeout = {2,0}; + enum clnt_stat clnt_stat; + BOOL ret = True; + + *bsize = *dfree = *dsize = (SMB_BIG_UINT)0; + + len=strcspn(mnttype, ":"); + pathname=strstr(mnttype, ":"); + cutstr = (char *) malloc(len+1); + if (!cutstr) + return False; + + memset(cutstr, '\0', len+1); + host = strncat(cutstr,mnttype, sizeof(char) * len ); + DEBUG(5,("nfs_quotas: looking for mount on \"%s\"\n", cutstr)); + DEBUG(5,("nfs_quotas: of path \"%s\"\n", mnttype)); + testpath=strchr_m(mnttype, ':'); + args.gqa_pathp = testpath+1; + args.gqa_uid = uid; + + DEBUG(5,("nfs_quotas: Asking for host \"%s\" rpcprog \"%i\" rpcvers \"%i\" network \"%s\"\n", host, RQUOTAPROG, RQUOTAVERS, "udp")); + + if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) == NULL) { + ret = False; + goto out; + } + + clnt->cl_auth = authunix_create_default(); + DEBUG(9,("nfs_quotas: auth_success\n")); + + clnt_stat=clnt_call(clnt, RQUOTAPROC_GETQUOTA, (const xdrproc_t) my_xdr_getquota_args, (caddr_t)&args, (const xdrproc_t) my_xdr_getquota_rslt, (caddr_t)&gqr, timeout); + + if (clnt_stat != RPC_SUCCESS) { + DEBUG(9,("nfs_quotas: clnt_call fail\n")); + ret = False; + goto out; + } + + /* + * quotastat returns 0 if the rpc call fails, 1 if quotas exist, 2 if there is + * no quota set, and 3 if no permission to get the quota. If 0 or 3 return + * something sensible. + */ + + switch ( quotastat ) { + case 0: + DEBUG(9,("nfs_quotas: Remote Quotas Failed! Error \"%i\" \n", quotastat )); + ret = False; + goto out; + + case 1: + DEBUG(9,("nfs_quotas: Good quota data\n")); + D.dqb_bsoftlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit; + D.dqb_bhardlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit; + D.dqb_curblocks = gqr.getquota_rslt_u.gqr_rquota.rq_curblocks; + break; + + case 2: + case 3: + D.dqb_bsoftlimit = 1; + D.dqb_curblocks = 1; + DEBUG(9,("nfs_quotas: Remote Quotas returned \"%i\" \n", quotastat )); + break; + + default: + DEBUG(9,("nfs_quotas: Remote Quotas Questionable! Error \"%i\" \n", quotastat )); + break; + } + + DEBUG(10,("nfs_quotas: Let`s look at D a bit closer... status \"%i\" bsize \"%i\" active? \"%i\" bhard \"%i\" bsoft \"%i\" curb \"%i\" \n", + quotastat, + gqr.getquota_rslt_u.gqr_rquota.rq_bsize, + gqr.getquota_rslt_u.gqr_rquota.rq_active, + gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit, + gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit, + gqr.getquota_rslt_u.gqr_rquota.rq_curblocks)); + + if (D.dqb_bsoftlimit == 0) + D.dqb_bsoftlimit = D.dqb_bhardlimit; + if (D.dqb_bsoftlimit == 0) + return False; + + *bsize = gqr.getquota_rslt_u.gqr_rquota.rq_bsize; + *dsize = D.dqb_bsoftlimit; + + if (D.dqb_curblocks == D.dqb_curblocks == 1) + *bsize = DEV_BSIZE; + + if (D.dqb_curblocks > D.dqb_bsoftlimit) { + *dfree = 0; + *dsize = D.dqb_curblocks; + } else + *dfree = D.dqb_bsoftlimit - D.dqb_curblocks; + + out: + + if (clnt) { + if (clnt->cl_auth) + auth_destroy(clnt->cl_auth); + clnt_destroy(clnt); + } + + DEBUG(5,("nfs_quotas: For path \"%s\" returning bsize %.0f, dfree %.0f, dsize %.0f\n",args.gqa_pathp,(double)*bsize,(double)*dfree,(double)*dsize)); + + SAFE_FREE(cutstr); + DEBUG(10,("nfs_quotas: End of nfs_quotas\n" )); + return ret; +} + +#endif + /**************************************************************************** try to get the disk space from disk quotas - default version ****************************************************************************/ @@ -976,10 +1146,42 @@ BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB { /* FreeBSD patches from Marty Moll <martym@arbor.edu> */ gid_t egrp_id; - +#if defined(__FreeBSD__) + SMB_DEV_T devno; + struct statfs *mnts; + SMB_STRUCT_STAT st; + int mntsize, i; + + if (sys_stat(path,&st) < 0) + return False; + devno = st.st_dev; + + mntsize = getmntinfo(&mnts,MNT_NOWAIT); + if (mntsize <= 0) + return False; + + for (i = 0; i < mntsize; i++) { + if (sys_stat(mnts[i].f_mntonname,&st) < 0) + return False; + if (st.st_dev == devno) + break; + } + if (i == mntsize) + return False; +#endif + save_re_uid(); set_effective_uid(0); +#if defined(__FreeBSD__) + if (strcmp(mnts[i].f_fstypename,"nfs") == 0) { + BOOL retval; + retval = nfs_quotas(mnts[i].f_mntfromname,euser_id,bsize,dfree,dsize); + restore_re_uid(); + return retval; + } +#endif + egrp_id = getegid(); r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D); diff --git a/source/smbd/reply.c b/source/smbd/reply.c index 560208ae157..71efb793af0 100644 --- a/source/smbd/reply.c +++ b/source/smbd/reply.c @@ -43,7 +43,7 @@ extern BOOL global_encrypted_passwords_negotiated; set. ****************************************************************************/ -NTSTATUS check_path_syntax(pstring destname, const pstring srcname) +NTSTATUS check_path_syntax(pstring destname, const pstring srcname, BOOL allow_wcard_names) { char *d = destname; const char *s = srcname; @@ -119,7 +119,21 @@ NTSTATUS check_path_syntax(pstring destname, const pstring srcname) s++; } else { if (!(*s & 0x80)) { - *d++ = *s++; + if (allow_wcard_names) { + *d++ = *s++; + } else { + switch (*s) { + case '*': + case '?': + case '<': + case '>': + case '"': + return NT_STATUS_OBJECT_NAME_INVALID; + default: + *d++ = *s++; + break; + } + } } else { switch(next_mb_char_size(s)) { case 4: @@ -147,7 +161,7 @@ NTSTATUS check_path_syntax(pstring destname, const pstring srcname) Pull a string and check the path - provide for error return. ****************************************************************************/ -size_t srvstr_get_path(char *inbuf, char *dest, const char *src, size_t dest_len, size_t src_len, int flags, NTSTATUS *err) +size_t srvstr_get_path(char *inbuf, char *dest, const char *src, size_t dest_len, size_t src_len, int flags, NTSTATUS *err, BOOL allow_wcard_names) { pstring tmppath; char *tmppath_ptr = tmppath; @@ -161,7 +175,7 @@ size_t srvstr_get_path(char *inbuf, char *dest, const char *src, size_t dest_len } else { ret = srvstr_pull( inbuf, tmppath_ptr, src, dest_len, src_len, flags); } - *err = check_path_syntax(dest, tmppath); + *err = check_path_syntax(dest, tmppath, allow_wcard_names); return ret; } @@ -516,7 +530,7 @@ int reply_chkpth(connection_struct *conn, char *inbuf,char *outbuf, int dum_size START_PROFILE(SMBchkpth); - srvstr_get_path(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), 0, STR_TERMINATE, &status); + srvstr_get_path(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), 0, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBchkpth); return ERROR_NT(status); @@ -525,6 +539,10 @@ int reply_chkpth(connection_struct *conn, char *inbuf,char *outbuf, int dum_size RESOLVE_DFSPATH(name, conn, inbuf, outbuf); unix_convert(name,conn,0,&bad_path,&sbuf); + if (bad_path) { + END_PROFILE(SMBchkpth); + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } mode = SVAL(inbuf,smb_vwv0); @@ -548,18 +566,11 @@ int reply_chkpth(connection_struct *conn, char *inbuf,char *outbuf, int dum_size * the parent directory is valid but not the * last component - it returns NT_STATUS_OBJECT_NAME_NOT_FOUND * for that case and NT_STATUS_OBJECT_PATH_NOT_FOUND - * if the path is invalid. + * if the path is invalid. This is different from set_bad_path_error() + * in the non-NT error case. */ - if (bad_path) { - END_PROFILE(SMBchkpth); - return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); - } else { - END_PROFILE(SMBchkpth); - return ERROR_NT(NT_STATUS_OBJECT_NAME_NOT_FOUND); - } - } else if (errno == ENOTDIR) { END_PROFILE(SMBchkpth); - return ERROR_NT(NT_STATUS_NOT_A_DIRECTORY); + return ERROR_BOTH(NT_STATUS_OBJECT_NAME_NOT_FOUND,ERRDOS,ERRbadpath); } END_PROFILE(SMBchkpth); @@ -594,7 +605,7 @@ int reply_getatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size START_PROFILE(SMBgetatr); p = smb_buf(inbuf) + 1; - p += srvstr_get_path(inbuf, fname, p, sizeof(fname), 0, STR_TERMINATE, &status); + p += srvstr_get_path(inbuf, fname, p, sizeof(fname), 0, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBgetatr); return ERROR_NT(status); @@ -613,6 +624,10 @@ int reply_getatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size ok = True; } else { unix_convert(fname,conn,0,&bad_path,&sbuf); + if (bad_path) { + END_PROFILE(SMBgetatr); + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } if (check_name(fname,conn)) { if (VALID_STAT(sbuf) || SMB_VFS_STAT(conn,fname,&sbuf) == 0) { mode = dos_mode(conn,fname,&sbuf); @@ -669,13 +684,17 @@ int reply_setatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size START_PROFILE(SMBsetatr); p = smb_buf(inbuf) + 1; - p += srvstr_get_path(inbuf, fname, p, sizeof(fname), 0, STR_TERMINATE, &status); + p += srvstr_get_path(inbuf, fname, p, sizeof(fname), 0, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBsetatr); return ERROR_NT(status); } unix_convert(fname,conn,0,&bad_path,&sbuf); + if (bad_path) { + END_PROFILE(SMBsetatr); + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } mode = SVAL(inbuf,smb_vwv0); mtime = make_unix_date3(inbuf+smb_vwv1); @@ -798,7 +817,7 @@ int reply_search(connection_struct *conn, char *inbuf,char *outbuf, int dum_size maxentries = SVAL(inbuf,smb_vwv0); dirtype = SVAL(inbuf,smb_vwv1); p = smb_buf(inbuf) + 1; - p += srvstr_get_path(inbuf, path, p, sizeof(path), 0, STR_TERMINATE, &nt_status); + p += srvstr_get_path(inbuf, path, p, sizeof(path), 0, STR_TERMINATE, &nt_status, True); if (!NT_STATUS_IS_OK(nt_status)) { END_PROFILE(SMBsearch); return ERROR_NT(nt_status); @@ -976,7 +995,7 @@ int reply_fclose(connection_struct *conn, char *inbuf,char *outbuf, int dum_size outsize = set_message(outbuf,1,0,True); p = smb_buf(inbuf) + 1; - p += srvstr_get_path(inbuf, path, p, sizeof(path), 0, STR_TERMINATE, &err); + p += srvstr_get_path(inbuf, path, p, sizeof(path), 0, STR_TERMINATE, &err, True); if (!NT_STATUS_IS_OK(err)) { END_PROFILE(SMBfclose); return ERROR_NT(err); @@ -1028,7 +1047,7 @@ int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, share_mode = SVAL(inbuf,smb_vwv0); - srvstr_get_path(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), 0, STR_TERMINATE, &status); + srvstr_get_path(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), 0, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBopen); return ERROR_NT(status); @@ -1037,12 +1056,21 @@ int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); unix_convert(fname,conn,0,&bad_path,&sbuf); + if (bad_path) { + END_PROFILE(SMBopen); + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } fsp = open_file_shared(conn,fname,&sbuf,share_mode,(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), (uint32)dos_attr, oplock_request,&rmode,NULL); if (!fsp) { END_PROFILE(SMBopen); + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + clear_cached_errors(); + return -1; + } return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess); } @@ -1117,7 +1145,7 @@ int reply_open_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt } /* XXXX we need to handle passed times, sattr and flags */ - srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status); + srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBopenX); return ERROR_NT(status); @@ -1126,12 +1154,21 @@ int reply_open_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); unix_convert(fname,conn,0,&bad_path,&sbuf); + if (bad_path) { + END_PROFILE(SMBopenX); + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } fsp = open_file_shared(conn,fname,&sbuf,smb_mode,smb_ofun,(uint32)smb_attr, oplock_request, &rmode,&smb_action); if (!fsp) { END_PROFILE(SMBopenX); + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + clear_cached_errors(); + return -1; + } return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess); } @@ -1230,7 +1267,7 @@ int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, com = SVAL(inbuf,smb_com); createmode = SVAL(inbuf,smb_vwv0); - srvstr_get_path(inbuf, fname, smb_buf(inbuf) + 1, sizeof(fname), 0, STR_TERMINATE, &status); + srvstr_get_path(inbuf, fname, smb_buf(inbuf) + 1, sizeof(fname), 0, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBcreate); return ERROR_NT(status); @@ -1239,6 +1276,10 @@ int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); unix_convert(fname,conn,0,&bad_path,&sbuf); + if (bad_path) { + END_PROFILE(SMBcreate); + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } if (createmode & aVOLID) DEBUG(0,("Attempt to create file (%s) with volid set - please report this\n",fname)); @@ -1257,6 +1298,11 @@ int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, if (!fsp) { END_PROFILE(SMBcreate); + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + clear_cached_errors(); + return -1; + } return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess); } @@ -1297,7 +1343,7 @@ int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, START_PROFILE(SMBctemp); createattr = SVAL(inbuf,smb_vwv0); - srvstr_get_path(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), 0, STR_TERMINATE, &status); + srvstr_get_path(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), 0, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBctemp); return ERROR_NT(status); @@ -1311,6 +1357,10 @@ int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); unix_convert(fname,conn,0,&bad_path,&sbuf); + if (bad_path) { + END_PROFILE(SMBctemp); + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } tmpfd = smb_mkstemp(fname); if (tmpfd == -1) { @@ -1332,6 +1382,11 @@ int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, if (!fsp) { END_PROFILE(SMBctemp); + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + clear_cached_errors(); + return -1; + } return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess); } @@ -1373,15 +1428,20 @@ int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, Check if a user is allowed to rename a file. ********************************************************************/ -static NTSTATUS can_rename(char *fname,connection_struct *conn, SMB_STRUCT_STAT *pst) +static NTSTATUS can_rename(char *fname,connection_struct *conn, uint16 dirtype, SMB_STRUCT_STAT *pst) { int smb_action; int access_mode; files_struct *fsp; + uint16 fmode; if (!CAN_WRITE(conn)) return NT_STATUS_MEDIA_WRITE_PROTECTED; - + + fmode = dos_mode(conn,fname,pst); + if ((fmode & ~dirtype) & (aHIDDEN | aSYSTEM)) + return NT_STATUS_NO_SUCH_FILE; + if (S_ISDIR(pst->st_mode)) return NT_STATUS_OK; @@ -1561,7 +1621,7 @@ NTSTATUS unlink_internals(connection_struct *conn, int dirtype, char *name) /* Quick check for "." and ".." */ if (fname[0] == '.') { if (!fname[1] || (fname[1] == '.' && !fname[2])) { - if ((dirtype & aDIR)) { + if ((dirtype & FILE_ATTRIBUTE_DIRECTORY) && (dirtype & FILE_ATTRIBUTE_SYSTEM)) { sys_direntry = True; } else { continue; @@ -1574,6 +1634,8 @@ NTSTATUS unlink_internals(connection_struct *conn, int dirtype, char *name) if (sys_direntry) { error = NT_STATUS_OBJECT_NAME_INVALID; + DEBUG(3,("unlink_internals: system directory delete denied [%s] mask [%s]\n", + fname, mask)); break; } @@ -1612,7 +1674,7 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size dirtype = SVAL(inbuf,smb_vwv0); - srvstr_get_path(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), 0, STR_TERMINATE, &status); + srvstr_get_path(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), 0, STR_TERMINATE, &status, True); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBunlink); return ERROR_NT(status); @@ -1623,8 +1685,14 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size DEBUG(3,("reply_unlink : %s\n",name)); status = unlink_internals(conn, dirtype, name); - if (!NT_STATUS_IS_OK(status)) + if (!NT_STATUS_IS_OK(status)) { + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + clear_cached_errors(); + return -1; + } return ERROR_NT(status); + } /* * Win2k needs a changenotify request response before it will @@ -1661,12 +1729,13 @@ void send_file_readbraw(connection_struct *conn, files_struct *fsp, SMB_OFF_T st #if defined(WITH_SENDFILE) /* - * We can only use sendfile on a non-chained packet and on a file - * that is exclusively oplocked. reply_readbraw has already checked the length. + * We can only use sendfile on a non-chained packet + * but we can use on a non-oplocked file. tridge proved this + * on a train in Germany :-). JRA. + * reply_readbraw has already checked the length. */ - if ((nread > 0) && (lp_write_cache_size(SNUM(conn)) == 0) && - EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && lp_use_sendfile(SNUM(conn)) ) { + if ((nread > 0) && (lp_write_cache_size(SNUM(conn)) == 0) && lp_use_sendfile(SNUM(conn)) ) { DATA_BLOB header; _smb_setlen(outbuf,nread); @@ -1999,12 +2068,13 @@ int send_file_readX(connection_struct *conn, char *inbuf,char *outbuf,int length #if defined(WITH_SENDFILE) /* - * We can only use sendfile on a non-chained packet and on a file - * that is exclusively oplocked. + * We can only use sendfile on a non-chained packet + * but we can use on a non-oplocked file. tridge proved this + * on a train in Germany :-). JRA. */ - if ((CVAL(inbuf,smb_vwv0) == 0xFF) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && - lp_use_sendfile(SNUM(conn)) && (lp_write_cache_size(SNUM(conn)) == 0) ) { + if ((CVAL(inbuf,smb_vwv0) == 0xFF) && lp_use_sendfile(SNUM(conn)) && + (lp_write_cache_size(SNUM(conn)) == 0) ) { SMB_STRUCT_STAT sbuf; DATA_BLOB header; @@ -3138,15 +3208,16 @@ NTSTATUS mkdir_internal(connection_struct *conn, pstring directory) return NT_STATUS_OBJECT_NAME_INVALID; } + if (bad_path) { + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + if (check_name(directory, conn)) ret = vfs_MkDir(conn,directory,unix_mode(conn,aDIR,directory)); if (ret == -1) { if(errno == ENOENT) { - if (bad_path) - return NT_STATUS_OBJECT_PATH_NOT_FOUND; - else - return NT_STATUS_OBJECT_NAME_NOT_FOUND; + return NT_STATUS_OBJECT_NAME_NOT_FOUND; } return map_nt_error_from_unix(errno); } @@ -3165,7 +3236,7 @@ int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, NTSTATUS status; START_PROFILE(SMBmkdir); - srvstr_get_path(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), 0, STR_TERMINATE, &status); + srvstr_get_path(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), 0, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBmkdir); return ERROR_NT(status); @@ -3335,7 +3406,7 @@ int reply_rmdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, NTSTATUS status; START_PROFILE(SMBrmdir); - srvstr_get_path(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), 0, STR_TERMINATE, &status); + srvstr_get_path(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), 0, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBrmdir); return ERROR_NT(status); @@ -3344,6 +3415,10 @@ int reply_rmdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, RESOLVE_DFSPATH(directory, conn, inbuf, outbuf) unix_convert(directory,conn, NULL,&bad_path,&sbuf); + if (bad_path) { + END_PROFILE(SMBrmdir); + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } if (check_name(directory,conn)) { dptr_closepath(directory,SVAL(inbuf,smb_pid)); @@ -3475,7 +3550,7 @@ static void rename_open_files(connection_struct *conn, SMB_DEV_T dev, SMB_INO_T Rename an open file - given an fsp. ****************************************************************************/ -NTSTATUS rename_internals_fsp(connection_struct *conn, files_struct *fsp, char *newname, BOOL replace_if_exists) +NTSTATUS rename_internals_fsp(connection_struct *conn, files_struct *fsp, char *newname, uint16 attrs, BOOL replace_if_exists) { SMB_STRUCT_STAT sbuf; BOOL bad_path = False; @@ -3556,7 +3631,7 @@ NTSTATUS rename_internals_fsp(connection_struct *conn, files_struct *fsp, char * return NT_STATUS_OBJECT_NAME_COLLISION; } - error = can_rename(newname,conn,&sbuf); + error = can_rename(newname,conn,attrs,&sbuf); if (dest_exists && !NT_STATUS_IS_OK(error)) { DEBUG(3,("rename_internals: Error %s rename %s -> %s\n", @@ -3762,7 +3837,7 @@ directory = %s, newname = %s, last_component_dest = %s, is_8_3 = %d\n", return NT_STATUS_OBJECT_PATH_NOT_FOUND; } - error = can_rename(directory,conn,&sbuf1); + error = can_rename(directory,conn,attrs,&sbuf1); if (!NT_STATUS_IS_OK(error)) { DEBUG(3,("rename_internals: Error %s rename %s -> %s\n", @@ -3853,7 +3928,7 @@ directory = %s, newname = %s, last_component_dest = %s, is_8_3 = %d\n", DEBUG(6,("rename %s failed. Error %s\n", fname, nt_errstr(error))); continue; } - error = can_rename(fname,conn,&sbuf1); + error = can_rename(fname,conn,attrs,&sbuf1); if (!NT_STATUS_IS_OK(error)) { DEBUG(6,("rename %s refused\n", fname)); continue; @@ -3924,13 +3999,13 @@ int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, START_PROFILE(SMBmv); p = smb_buf(inbuf) + 1; - p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status); + p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status, True); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBmv); return ERROR_NT(status); } p++; - p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status); + p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status, True); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBmv); return ERROR_NT(status); @@ -3944,6 +4019,11 @@ int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, status = rename_internals(conn, name, newname, attrs, False); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBmv); + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + clear_cached_errors(); + return -1; + } return ERROR_NT(status); } @@ -3989,7 +4069,8 @@ static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun, return(False); fsp1 = open_file_shared(conn,src,&src_sbuf,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY), - (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),FILE_ATTRIBUTE_NORMAL,0,&Access,&action); + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),FILE_ATTRIBUTE_NORMAL,INTERNAL_OPEN_ONLY, + &Access,&action); if (!fsp1) return(False); @@ -4002,7 +4083,7 @@ static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun, ZERO_STRUCTP(&sbuf2); fsp2 = open_file_shared(conn,dest,&sbuf2,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_WRONLY), - ofun,dosattrs,0,&Access,&action); + ofun,dosattrs,INTERNAL_OPEN_ONLY,&Access,&action); if (!fsp2) { close_file(fsp1,False); @@ -4070,12 +4151,12 @@ int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, *directory = *mask = 0; p = smb_buf(inbuf); - p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status); + p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status, True); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBcopy); return ERROR_NT(status); } - p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status); + p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status, True); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBcopy); return ERROR_NT(status); @@ -4235,7 +4316,7 @@ int reply_setdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size return ERROR_DOS(ERRDOS,ERRnoaccess); } - srvstr_get_path(inbuf, newdir, smb_buf(inbuf) + 1, sizeof(newdir), 0, STR_TERMINATE, &status); + srvstr_get_path(inbuf, newdir, smb_buf(inbuf) + 1, sizeof(newdir), 0, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(pathworks_setdir); return ERROR_NT(status); diff --git a/source/smbd/server.c b/source/smbd/server.c index 343a835be8a..c3e0da542e2 100644 --- a/source/smbd/server.c +++ b/source/smbd/server.c @@ -494,18 +494,16 @@ BOOL reload_services(BOOL test) load_interfaces(); - { - if (smbd_server_fd() != -1) { - set_socket_options(smbd_server_fd(),"SO_KEEPALIVE"); - set_socket_options(smbd_server_fd(), user_socket_options); - } + if (smbd_server_fd() != -1) { + set_socket_options(smbd_server_fd(),"SO_KEEPALIVE"); + set_socket_options(smbd_server_fd(), user_socket_options); } mangle_reset_cache(); reset_stat_cache(); /* this forces service parameters to be flushed */ - set_current_service(NULL,True); + set_current_service(NULL,0,True); return(ret); } diff --git a/source/smbd/service.c b/source/smbd/service.c index c74537c299e..3b499d5cc1d 100644 --- a/source/smbd/service.c +++ b/source/smbd/service.c @@ -28,10 +28,11 @@ extern userdom_struct current_user_info; Load parameters specific to a connection/service. ****************************************************************************/ -BOOL set_current_service(connection_struct *conn,BOOL do_chdir) +BOOL set_current_service(connection_struct *conn, uint16 flags, BOOL do_chdir) { extern char magic_char; static connection_struct *last_conn; + static uint16 last_flags; int snum; if (!conn) { @@ -51,10 +52,24 @@ BOOL set_current_service(connection_struct *conn,BOOL do_chdir) return(False); } - if (conn == last_conn) + if ((conn == last_conn) && (last_flags == flags)) { return(True); + } last_conn = conn; + last_flags = flags; + + /* Obey the client case sensitivity requests - only for clients that support it. */ + if (lp_casesensitive(snum) == Auto) { + /* We need this uglyness due to DOS/Win9x clients that lie about case insensitivity. */ + enum remote_arch_types ra_type = get_remote_arch(); + if ((ra_type != RA_SAMBA) && (ra_type != RA_CIFSFS)) { + /* Client can't support per-packet case sensitive pathnames. */ + conn->case_sensitive = False; + } else { + conn->case_sensitive = !(flags & FLAG_CASELESS_PATHNAMES); + } + } magic_char = lp_magicchar(snum); return(True); @@ -347,7 +362,13 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, conn->dirptr = NULL; /* Case options for the share. */ - conn->case_sensitive = lp_casesensitive(snum); + if (lp_casesensitive(snum) == Auto) { + /* We will be setting this per packet. Set to be case insensitive for now. */ + conn->case_sensitive = False; + } else { + conn->case_sensitive = (BOOL)lp_casesensitive(snum); + } + conn->case_preserve = lp_preservecase(snum); conn->short_case_preserve = lp_shortpreservecase(snum); @@ -499,6 +520,20 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, return NULL; } + /* + * If widelinks are disallowed we need to canonicalise the + * connect path here to ensure we don't have any symlinks in + * the connectpath. We will be checking all paths on this + * connection are below this directory. We must do this after + * the VFS init as we depend on the realpath() pointer in the vfs table. JRA. + */ + if (!lp_widelinks(snum)) { + pstring s; + pstrcpy(s,conn->connectpath); + canonicalize_path(conn, s); + string_set(&conn->connectpath,s); + } + /* ROOT Activities: */ /* check number of connections */ if (!claim_connection(conn, diff --git a/source/smbd/session.c b/source/smbd/session.c index 61118f13dd9..91ebaeb830b 100644 --- a/source/smbd/session.c +++ b/source/smbd/session.c @@ -34,8 +34,8 @@ BOOL session_init(void) if (tdb) return True; - tdb = tdb_open_ex(lock_path("sessionid.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT, - O_RDWR | O_CREAT, 0644, smbd_tdb_log); + tdb = tdb_open_log(lock_path("sessionid.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT, + O_RDWR | O_CREAT, 0644); if (!tdb) { DEBUG(1,("session_init: failed to open sessionid tdb\n")); return False; diff --git a/source/smbd/sesssetup.c b/source/smbd/sesssetup.c index 8a56478929f..0122b662ebf 100644 --- a/source/smbd/sesssetup.c +++ b/source/smbd/sesssetup.c @@ -152,7 +152,6 @@ static int reply_spnego_kerberos(connection_struct *conn, auth_serversupplied_info *server_info = NULL; DATA_BLOB session_key = data_blob(NULL, 0); uint8 tok_id[2]; - BOOL foreign = False; DATA_BLOB nullblob = data_blob(NULL, 0); fstring real_username; @@ -197,7 +196,6 @@ static int reply_spnego_kerberos(connection_struct *conn, SAFE_FREE(client); return ERROR_NT(NT_STATUS_LOGON_FAILURE); } - foreign = True; } /* this gives a fully qualified user name (ie. with full realm). @@ -244,6 +242,8 @@ static int reply_spnego_kerberos(connection_struct *conn, /* lookup the passwd struct, create a new user if necessary */ + map_username( user ); + pw = smb_getpwnam( user, real_username, True ); if (!pw) { diff --git a/source/smbd/trans2.c b/source/smbd/trans2.c index 738d12e020f..a7db9daf7d6 100644 --- a/source/smbd/trans2.c +++ b/source/smbd/trans2.c @@ -607,7 +607,7 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, i if (IS_IPC(conn)) return(ERROR_DOS(ERRSRV,ERRaccess)); - srvstr_get_path(inbuf, fname, pname, sizeof(fname), -1, STR_TERMINATE, &status); + srvstr_get_path(inbuf, fname, pname, sizeof(fname), -1, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } @@ -618,6 +618,9 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, i /* XXXX we need to handle passed times, sattr and flags */ unix_convert(fname,conn,0,&bad_path,&sbuf); + if (bad_path) { + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } if (!check_name(fname,conn)) { return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess); @@ -627,6 +630,11 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, i oplock_request, &rmode,&smb_action); if (!fsp) { + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + clear_cached_errors(); + return -1; + } return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess); } @@ -1371,7 +1379,7 @@ close_if_end = %d requires_resume_key = %d level = 0x%x, max_data_bytes = %d\n", return(ERROR_DOS(ERRDOS,ERRunknownlevel)); } - srvstr_get_path(inbuf, directory, params+12, sizeof(directory), -1, STR_TERMINATE, &ntstatus); + srvstr_get_path(inbuf, directory, params+12, sizeof(directory), -1, STR_TERMINATE, &ntstatus, True); if (!NT_STATUS_IS_OK(ntstatus)) { return ERROR_NT(ntstatus); } @@ -1379,6 +1387,9 @@ close_if_end = %d requires_resume_key = %d level = 0x%x, max_data_bytes = %d\n", RESOLVE_FINDFIRST_DFSPATH(directory, conn, inbuf, outbuf); unix_convert(directory,conn,0,&bad_path,&sbuf); + if (bad_path) { + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } if(!check_name(directory,conn)) { return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath); } @@ -1564,7 +1575,7 @@ static int call_trans2findnext(connection_struct *conn, char *inbuf, char *outbu *mask = *directory = *resume_name = 0; - srvstr_get_path(inbuf, resume_name, params+12, sizeof(resume_name), -1, STR_TERMINATE, &ntstatus); + srvstr_get_path(inbuf, resume_name, params+12, sizeof(resume_name), -1, STR_TERMINATE, &ntstatus, True); if (!NT_STATUS_IS_OK(ntstatus)) { return ERROR_NT(ntstatus); } @@ -2257,6 +2268,8 @@ static int call_trans2qfilepathinfo(connection_struct *conn, if (!params) return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + ZERO_STRUCT(sbuf); + if (tran_call == TRANSACT2_QFILEINFO) { if (total_params < 4) return(ERROR_DOS(ERRDOS,ERRinvalidparam)); @@ -2272,11 +2285,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn, */ pstrcpy(fname, fsp->fsp_name); - unix_convert(fname,conn,0,&bad_path,&sbuf); - if (!check_name(fname,conn)) { - DEBUG(3,("call_trans2qfilepathinfo: fileinfo of %s failed for fake_file(%s)\n",fname,strerror(errno))); - return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath); - } + /* We know this name is ok, it's already passed the checks. */ } else if(fsp && (fsp->is_directory || fsp->fd == -1)) { /* @@ -2284,12 +2293,8 @@ static int call_trans2qfilepathinfo(connection_struct *conn, * handle (returned from an NT SMB). NT5.0 seems * to do this call. JRA. */ + /* We know this name is ok, it's already passed the checks. */ pstrcpy(fname, fsp->fsp_name); - unix_convert(fname,conn,0,&bad_path,&sbuf); - if (!check_name(fname,conn)) { - DEBUG(3,("call_trans2qfilepathinfo: fileinfo of %s failed (%s)\n",fname,strerror(errno))); - return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath); - } if (INFO_LEVEL_IS_UNIX(info_level)) { /* Always do lstat for UNIX calls. */ @@ -2297,7 +2302,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn, DEBUG(3,("call_trans2qfilepathinfo: SMB_VFS_LSTAT of %s failed (%s)\n",fname,strerror(errno))); return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath); } - } else if (!VALID_STAT(sbuf) && SMB_VFS_STAT(conn,fname,&sbuf)) { + } else if (SMB_VFS_STAT(conn,fname,&sbuf)) { DEBUG(3,("call_trans2qfilepathinfo: SMB_VFS_STAT of %s failed (%s)\n",fname,strerror(errno))); return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath); } @@ -2329,7 +2334,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn, DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QPATHINFO: level = %d\n", info_level)); - srvstr_get_path(inbuf, fname, ¶ms[6], sizeof(fname), -1, STR_TERMINATE, &status); + srvstr_get_path(inbuf, fname, ¶ms[6], sizeof(fname), -1, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } @@ -2337,6 +2342,9 @@ static int call_trans2qfilepathinfo(connection_struct *conn, RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); unix_convert(fname,conn,0,&bad_path,&sbuf); + if (bad_path) { + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } if (!check_name(fname,conn)) { DEBUG(3,("call_trans2qfilepathinfo: fileinfo of %s failed (%s)\n",fname,strerror(errno))); return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath); @@ -2871,7 +2879,6 @@ NTSTATUS hardlink_internals(connection_struct *conn, char *oldname, char *newnam BOOL bad_path_oldname = False; BOOL bad_path_newname = False; SMB_STRUCT_STAT sbuf1, sbuf2; - BOOL rc, rcdest; pstring last_component_oldname; pstring last_component_newname; NTSTATUS status = NT_STATUS_OK; @@ -2884,8 +2891,8 @@ NTSTATUS hardlink_internals(connection_struct *conn, char *oldname, char *newnam return NT_STATUS_OBJECT_PATH_SYNTAX_BAD; } - rc = unix_convert(oldname,conn,last_component_oldname,&bad_path_oldname,&sbuf1); - if (!rc && bad_path_oldname) { + unix_convert(oldname,conn,last_component_oldname,&bad_path_oldname,&sbuf1); + if (bad_path_oldname) { return NT_STATUS_OBJECT_PATH_NOT_FOUND; } @@ -2905,8 +2912,8 @@ NTSTATUS hardlink_internals(connection_struct *conn, char *oldname, char *newnam return NT_STATUS_ACCESS_DENIED; } - rcdest = unix_convert(newname,conn,last_component_newname,&bad_path_newname,&sbuf2); - if (!rcdest && bad_path_newname) { + unix_convert(newname,conn,last_component_newname,&bad_path_newname,&sbuf2); + if (bad_path_newname) { return NT_STATUS_OBJECT_PATH_NOT_FOUND; } @@ -2974,6 +2981,8 @@ static int call_trans2setfilepathinfo(connection_struct *conn, if (!params) return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + ZERO_STRUCT(sbuf); + if (tran_call == TRANSACT2_SETFILEINFO) { if (total_params < 4) return(ERROR_DOS(ERRDOS,ERRinvalidparam)); @@ -2988,8 +2997,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, * to do this call. JRA. */ pstrcpy(fname, fsp->fsp_name); - unix_convert(fname,conn,0,&bad_path,&sbuf); - if (!check_name(fname,conn) || (!VALID_STAT(sbuf))) { + if (SMB_VFS_STAT(conn,fname,&sbuf) != 0) { DEBUG(3,("call_trans2setfilepathinfo: fileinfo of %s failed (%s)\n",fname,strerror(errno))); return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRbadpath); } @@ -3027,11 +3035,14 @@ static int call_trans2setfilepathinfo(connection_struct *conn, return(ERROR_DOS(ERRDOS,ERRinvalidparam)); info_level = SVAL(params,0); - srvstr_get_path(inbuf, fname, ¶ms[6], sizeof(fname), -1, STR_TERMINATE, &status); + srvstr_get_path(inbuf, fname, ¶ms[6], sizeof(fname), -1, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } unix_convert(fname,conn,0,&bad_path,&sbuf); + if (bad_path) { + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } /* * For CIFS UNIX extensions the target name may not exist. @@ -3205,7 +3216,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, SET_OPEN_MODE(DOS_OPEN_RDWR), (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, - 0, &access_mode, &action); + INTERNAL_OPEN_ONLY, &access_mode, &action); if (new_fsp == NULL) return(UNIXERROR(ERRDOS,ERRbadpath)); @@ -3374,14 +3385,15 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", return(ERROR_DOS(ERRDOS,ERRnoaccess)); if (file_type != UNIX_TYPE_CHARDEV && file_type != UNIX_TYPE_BLKDEV && - file_type != UNIX_TYPE_FIFO) + file_type != UNIX_TYPE_FIFO && + file_type != UNIX_TYPE_SOCKET) return(ERROR_DOS(ERRDOS,ERRnoaccess)); DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_BASIC doing mknod dev %.0f mode \ 0%o for file %s\n", (double)dev, unixmode, fname )); /* Ok - do the mknod. */ - if (SMB_VFS_MKNOD(conn,dos_to_unix_static(fname), unixmode, dev) != 0) + if (SMB_VFS_MKNOD(conn,fname, unixmode, dev) != 0) return(UNIXERROR(ERRDOS,ERRnoaccess)); inherit_access_acl(conn, fname, unixmode); @@ -3482,7 +3494,7 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", char *newname = fname; /* Set a hard link. */ - srvstr_get_path(inbuf, oldname, pdata, sizeof(oldname), -1, STR_TERMINATE, &status); + srvstr_get_path(inbuf, oldname, pdata, sizeof(oldname), -1, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } @@ -3515,7 +3527,7 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", overwrite = (CVAL(pdata,0) ? True : False); root_fid = IVAL(pdata,4); len = IVAL(pdata,8); - srvstr_get_path(inbuf, newname, &pdata[12], sizeof(newname), len, 0, &status); + srvstr_get_path(inbuf, newname, &pdata[12], sizeof(newname), len, 0, &status, False); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } @@ -3538,7 +3550,7 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", if (fsp) { DEBUG(10,("call_trans2setfilepathinfo: SMB_FILE_RENAME_INFORMATION (fnum %d) %s -> %s\n", fsp->fnum, fsp->fsp_name, base_name )); - status = rename_internals_fsp(conn, fsp, base_name, overwrite); + status = rename_internals_fsp(conn, fsp, base_name, 0, overwrite); } else { DEBUG(10,("call_trans2setfilepathinfo: SMB_FILE_RENAME_INFORMATION %s -> %s\n", fname, newname )); @@ -3654,7 +3666,7 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", SET_OPEN_MODE(DOS_OPEN_RDWR), (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, - 0, &access_mode, &action); + INTERNAL_OPEN_ONLY, &access_mode, &action); if (new_fsp == NULL) return(UNIXERROR(ERRDOS,ERRbadpath)); @@ -3695,7 +3707,7 @@ static int call_trans2mkdir(connection_struct *conn, if (total_params < 4) return(ERROR_DOS(ERRDOS,ERRinvalidparam)); - srvstr_get_path(inbuf, directory, ¶ms[4], sizeof(directory), -1, STR_TERMINATE, &status); + srvstr_get_path(inbuf, directory, ¶ms[4], sizeof(directory), -1, STR_TERMINATE, &status, False); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } @@ -3703,6 +3715,9 @@ static int call_trans2mkdir(connection_struct *conn, DEBUG(3,("call_trans2mkdir : name = %s\n", directory)); unix_convert(directory,conn,0,&bad_path,&sbuf); + if (bad_path) { + return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND); + } if (check_name(directory,conn)) ret = vfs_MkDir(conn,directory,unix_mode(conn,aDIR,directory)); diff --git a/source/smbd/uid.c b/source/smbd/uid.c index e1864c74caa..de2f96450fc 100644 --- a/source/smbd/uid.c +++ b/source/smbd/uid.c @@ -125,6 +125,13 @@ static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum) readonly_share = is_share_read_only_for_user(conn, vuser); + if (!readonly_share && + !share_access_check(conn, snum, vuser, FILE_WRITE_DATA)) { + /* smb.conf allows r/w, but the security descriptor denies + * write. Fall back to looking at readonly. */ + readonly_share = True; + } + if (!share_access_check(conn, snum, vuser, readonly_share ? FILE_READ_DATA : FILE_WRITE_DATA)) { return False; } diff --git a/source/smbd/utmp.c b/source/smbd/utmp.c index a521d0113d4..b1735dfcc49 100644 --- a/source/smbd/utmp.c +++ b/source/smbd/utmp.c @@ -514,10 +514,10 @@ static BOOL sys_utmp_fill(struct utmp *u, * But note that we do the more precise ut_tv as the final assignment. */ #if defined(HAVE_UT_UT_TIME) - gettimeofday(&timeval, NULL); + GetTimeOfDay(&timeval); u->ut_time = timeval.tv_sec; #elif defined(HAVE_UT_UT_TV) - gettimeofday(&timeval, NULL); + GetTimeOfDay(&timeval); u->ut_tv = timeval; #else #error "with-utmp must have UT_TIME or UT_TV" diff --git a/source/smbd/vfs.c b/source/smbd/vfs.c index a415e0470e2..13cfdac0f35 100644 --- a/source/smbd/vfs.c +++ b/source/smbd/vfs.c @@ -784,6 +784,31 @@ char *vfs_GetWd(connection_struct *conn, char *path) return (path); } +BOOL canonicalize_path(connection_struct *conn, pstring path) +{ +#ifdef REALPATH_TAKES_NULL + char *resolved_name = SMB_VFS_REALPATH(conn,path,NULL); + if (!resolved_name) { + return False; + } + pstrcpy(path, resolved_name); + SAFE_FREE(resolved_name); + return True; +#else +#ifdef PATH_MAX + char resolved_name_buf[PATH_MAX+1]; +#else + pstring resolved_name_buf; +#endif + char *resolved_name = SMB_VFS_REALPATH(conn,path,resolved_name_buf); + if (!resolved_name) { + return False; + } + pstrcpy(path, resolved_name); + return True; +#endif /* REALPATH_TAKES_NULL */ +} + /******************************************************************* Reduce a file name, removing .. elements and checking that it is below dir in the heirachy. This uses realpath. @@ -804,6 +829,7 @@ BOOL reduce_name(connection_struct *conn, pstring fname) char *resolved_name = NULL; size_t con_path_len = strlen(conn->connectpath); char *p = NULL; + int saved_errno = errno; DEBUG(3,("reduce_name [%s] [%s]\n", fname, conn->connectpath)); @@ -817,6 +843,7 @@ BOOL reduce_name(connection_struct *conn, pstring fname) switch (errno) { case ENOTDIR: DEBUG(3,("reduce_name: Component not a directory in getting realpath for %s\n", fname)); + errno = saved_errno; return False; case ENOENT: { @@ -841,6 +868,7 @@ BOOL reduce_name(connection_struct *conn, pstring fname) #endif if (!resolved_name) { DEBUG(3,("reduce_name: couldn't get realpath for %s\n", fname)); + errno = saved_errno; return False; } pstrcpy(tmp_fname, resolved_name); @@ -851,6 +879,7 @@ BOOL reduce_name(connection_struct *conn, pstring fname) resolved_name = strdup(tmp_fname); if (!resolved_name) { DEBUG(0,("reduce_name: malloc fail for %s\n", tmp_fname)); + errno = saved_errno; return False; } #else @@ -865,6 +894,7 @@ BOOL reduce_name(connection_struct *conn, pstring fname) } default: DEBUG(1,("reduce_name: couldn't get realpath for %s\n", fname)); + errno = saved_errno; return False; } } @@ -875,13 +905,15 @@ BOOL reduce_name(connection_struct *conn, pstring fname) DEBUG(0,("reduce_name: realpath doesn't return absolute paths !\n")); if (free_resolved_name) SAFE_FREE(resolved_name); + errno = saved_errno; return False; } if (strncmp(conn->connectpath, resolved_name, con_path_len) != 0) { - DEBUG(2, ("reduce_name: Bad access attemt: %s is a symlink outside the share path", fname)); + DEBUG(2, ("reduce_name: Bad access attempt: %s is a symlink outside the share path", fname)); if (free_resolved_name) SAFE_FREE(resolved_name); + errno = EACCES; return False; } @@ -900,11 +932,13 @@ BOOL reduce_name(connection_struct *conn, pstring fname) DEBUG(3,("reduce_name: denied: file path name %s is a symlink\n",fname)); if (free_resolved_name) SAFE_FREE(resolved_name); + errno = EACCES; return False; } DEBUG(3,("reduce_name: %s reduced to %s\n", fname, p)); if (free_resolved_name) SAFE_FREE(resolved_name); + errno = saved_errno; return(True); } |