From fba5a722497c1e4577aa463921a0fec5f6d5fe55 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 3 Mar 2004 20:55:59 +0000 Subject: Use a common function to parse all pathnames from the wire. This allows much closer emulation of Win2k3 error return codes. Jeremy. (This used to be commit c9f31fafeda6ad79e590276f36e03ecd2e93f818) --- source3/smbd/filename.c | 27 ++---- source3/smbd/nttrans.c | 69 +++++++------ source3/smbd/reply.c | 253 ++++++++++++++++++++++++++++++++++++------------ source3/smbd/trans2.c | 69 ++++++++++--- 4 files changed, 290 insertions(+), 128 deletions(-) (limited to 'source3/smbd') diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index 643e315c06..a71e8a806c 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -114,29 +114,30 @@ BOOL unix_convert(pstring name,connection_struct *conn,char *saved_last_componen DEBUG(5, ("unix_convert called on file \"%s\"\n", name)); /* - * Convert to basic unix format - removing \ chars and cleaning it up. + * Conversion to basic unix format is already done in check_path_syntax(). */ - unix_format(name); - unix_clean_name(name); - /* - * Names must be relative to the root of the service - trim any leading /. - * also trim trailing /'s. + * Names must be relative to the root of the service - any leading /. + * and trailing /'s should have been trimmed by check_path_syntax(). */ - trim_char(name,'/','/'); +#ifdef DEVELOPER + SMB_ASSERT(*name != '/'); +#endif /* * If we trimmed down to a single '\0' character * then we should use the "." directory to avoid * searching the cache, but not if we are in a * printing share. + * As we know this is valid we can return true here. */ if (!*name) { name[0] = '.'; name[1] = '\0'; + return(True); } /* @@ -154,19 +155,7 @@ BOOL unix_convert(pstring name,connection_struct *conn,char *saved_last_componen if (!case_sensitive && (!case_preserve || (mangle_is_8_3(name, False) && !short_case_preserve))) strnorm(name); - /* - * If we trimmed down to a single '\0' character - * then we will be using the "." directory. - * As we know this is valid we can return true here. - */ - - if(!*name) - return(True); - start = name; - while (start[0] == '.' && start[1] == '/') - start += 2; - pstrcpy(orig_path, name); if(!case_sensitive && stat_cache_lookup(conn, name, dirpath, &start, &st)) { diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index 7b02c60001..9a0063481a 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -537,7 +537,7 @@ static int do_ntcreate_pipe_open(connection_struct *conn, char *p = NULL; NTSTATUS status; - srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), STR_TERMINATE,&status); + srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) return ERROR_NT(status); @@ -658,7 +658,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), STR_TERMINATE,&status); + srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBntcreateX); return ERROR_NT(status); @@ -700,13 +700,13 @@ create_options = 0x%x root_dir_fid = 0x%x\n", flags, desired_access, file_attrib dir_name_len++; } - srvstr_get_path(inbuf, &fname[dir_name_len], smb_buf(inbuf), sizeof(fname)-dir_name_len, STR_TERMINATE,&status); + srvstr_get_path(inbuf, &fname[dir_name_len], smb_buf(inbuf), sizeof(fname)-dir_name_len, 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBntcreateX); return ERROR_NT(status); } } else { - srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), STR_TERMINATE,&status); + srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBntcreateX); return ERROR_NT(status); @@ -1018,8 +1018,7 @@ static int do_nt_transact_create_pipe( connection_struct *conn, char *inbuf, cha return ERROR_DOS(ERRDOS,ERRnoaccess); } - srvstr_pull(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE); - status = check_path_syntax(fname); + srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } @@ -1160,7 +1159,7 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o int smb_open_mode; int smb_attr; time_t c_time; - NTSTATUS nt_status; + NTSTATUS status; DEBUG(5,("call_nt_transact_create\n")); @@ -1226,11 +1225,9 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o return ERROR_DOS(ERRDOS,ERRbadfid); if(!dir_fsp->is_directory) { - - srvstr_pull(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE); - nt_status = check_path_syntax(fname); - if (!NT_STATUS_IS_OK(nt_status)) { - return ERROR_NT(nt_status); + srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); } /* @@ -1259,17 +1256,18 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o dir_name_len++; } - srvstr_pull(inbuf, &fname[dir_name_len], params+53, sizeof(fname)-dir_name_len, - parameter_count-53, STR_TERMINATE); - nt_status = check_path_syntax(fname); - if (!NT_STATUS_IS_OK(nt_status)) { - return ERROR_NT(nt_status); + { + pstring tmpname; + srvstr_get_path(inbuf, tmpname, params+53, sizeof(tmpname), parameter_count-53, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); + } + pstrcat(fname, tmpname); } } else { - srvstr_pull(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE); - nt_status = check_path_syntax(fname); - if (!NT_STATUS_IS_OK(nt_status)) { - return ERROR_NT(nt_status); + srvstr_get_path(inbuf, fname, params+53, sizeof(fname), parameter_count-53, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); } /* @@ -1395,10 +1393,10 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o * Now try and apply the desired SD. */ - if (sd_len && !NT_STATUS_IS_OK(nt_status = set_sd( fsp, data, sd_len, ALL_SECURITY_INFORMATION))) { + if (sd_len && !NT_STATUS_IS_OK(status = set_sd( fsp, data, sd_len, ALL_SECURITY_INFORMATION))) { close_file(fsp,False); restore_case_semantics(file_attributes); - return ERROR_NT(nt_status); + return ERROR_NT(status); } restore_case_semantics(file_attributes); @@ -1518,17 +1516,31 @@ int reply_ntrename(connection_struct *conn, pstring newname; char *p; NTSTATUS status; + uint16 attrs = SVAL(inbuf,smb_vwv0); + uint16 rename_type = SVAL(inbuf,smb_vwv1); START_PROFILE(SMBntrename); + if (rename_type != RENAME_FLAG_RENAME) { + END_PROFILE(SMBntrename); + return ERROR_NT(NT_STATUS_ACCESS_DENIED); + } + p = smb_buf(inbuf) + 1; - p += srvstr_get_path(inbuf, name, p, sizeof(name), STR_TERMINATE,&status); + p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBntrename); return ERROR_NT(status); } + + if( strchr_m(name, ':')) { + /* Can't rename a stream. */ + END_PROFILE(SMBntrename); + return ERROR_NT(NT_STATUS_ACCESS_DENIED); + } + p++; - p += srvstr_get_path(inbuf, newname, p, sizeof(newname), STR_TERMINATE,&status); + p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBntrename); return ERROR_NT(status); @@ -1539,7 +1551,7 @@ int reply_ntrename(connection_struct *conn, DEBUG(3,("reply_ntrename : %s -> %s\n",name,newname)); - status = rename_internals(conn, name, newname, False); + status = rename_internals(conn, name, newname, attrs, False); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBntrename); return ERROR_NT(status); @@ -1627,14 +1639,13 @@ 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_pull(inbuf, new_name, params+4, sizeof(new_name), -1, STR_TERMINATE); - status = check_path_syntax(new_name); + srvstr_get_path(inbuf, new_name, params+4, sizeof(new_name), -1, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } status = rename_internals(conn, fsp->fsp_name, - new_name, replace_if_exists); + new_name, 0, replace_if_exists); if (!NT_STATUS_IS_OK(status)) return ERROR_NT(status); diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index 836f95e179..051aaafb45 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -40,28 +40,117 @@ extern BOOL global_encrypted_passwords_negotiated; /**************************************************************************** Ensure we check the path in *exactly* the same way as W2K. + We're assuming here that '/' is not the second byte in any multibyte char + set (a safe assumption). '\\' *may* be the second byte in a multibyte char + set. ****************************************************************************/ -NTSTATUS check_path_syntax(const char *name) +static NTSTATUS check_path_syntax(pstring destname, const pstring srcname) { - while (*name == '\\' || *name == '/') - name++; - if (name[0] == '.' && name[1] == '\0') - return NT_STATUS_OBJECT_NAME_INVALID; - else if (name[0] == '.' && name[1] == '.' && - (name[2] == '\\' || name [2] == '/' || name[2] == '\0')) - return NT_STATUS_OBJECT_PATH_SYNTAX_BAD; - return NT_STATUS_OK; + char *d = destname; + const char *s = srcname; + NTSTATUS ret = NT_STATUS_OK; + + while (*s) { + if (IS_DIRECTORY_SEP(*s)) { + /* + * Safe to assume is not the second part of a mb char as this is handled below. + */ + /* Eat multiple '/' or '\\' */ + while (IS_DIRECTORY_SEP(*s)) { + s++; + } + if ((d != destname) && (*s != '\0')) { + /* We only care about non-leading or trailing '/' or '\\' */ + *d++ = '/'; + } + } else if ((s[0] == '.') && (s[1] == '.') && (IS_DIRECTORY_SEP(s[2]) || s[2] == '\0')) { + /* Uh oh - "../" or "..\\" or "..\0" ! */ + + /* + * No mb char starts with '.' so we're safe checking the directory separator here. + */ + + /* If we just added a '/', delete it. */ + + if ((d > destname) && (*(d-1) == '/')) { + *(d-1) = '\0'; + if (d == (destname + 1)) { + d--; + } else { + d -= 2; + } + } + /* Are we at the start ? Can't go back further if so. */ + if (d == destname) { + return NT_STATUS_OBJECT_PATH_SYNTAX_BAD; + } + /* Go back one level... */ + while (d > destname) { + if (*d == '/') + break; + d--; + } + s += 3; + } else if ((s[0] == '.') && IS_DIRECTORY_SEP(s[1])) { + + /* + * No mb char starts with '.' so we're safe checking the directory separator here. + */ + + /* "./" or ".\\" fails with a different error depending on where it is... */ + + if (s == srcname) { + ret = NT_STATUS_OBJECT_NAME_INVALID; + } else { + if (s[2] == '\0') { + return NT_STATUS_INVALID_PARAMETER; + } + ret = NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + s++; + } else { + if ((*s & 0x80) && IS_DIRECTORY_SEP(s[1])) { + /* + * Potential mb char with second char a directory separator. + * All the encodings we care about are 2 byte only, so do a + * conversion to unicode. If the 2 byte char won't convert then + * it's probably a one byte char with a real directory separator + * following, so only copy one byte. If it will convert then + * copy both bytes. + */ + uint16 ucs2_val; + if (convert_string(CH_UNIX, CH_UCS2, s, 2, &ucs2_val, 2) == 2) { + *d++ = *s++; + } + } + /* Just copy the char (or the second byte of the mb char). */ + *d++ = *s++; + } + } + *d = '\0'; + return ret; } /**************************************************************************** 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, 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) { - size_t ret = srvstr_pull_buf( inbuf, dest, src, dest_len, flags); - *err = check_path_syntax(dest); + pstring tmppath; + char *tmppath_ptr = tmppath; +#ifdef DEVELOPER + SMB_ASSERT(dest_len == sizeof(pstring)); +#endif + size_t ret; + + if (src_len == 0) { + ret = srvstr_pull_buf( inbuf, tmppath_ptr, src, dest_len, flags); + } else { + ret = srvstr_pull( inbuf, tmppath_ptr, src, dest_len, src_len, flags); + } + *err = check_path_syntax(dest, tmppath); return ret; } @@ -414,7 +503,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), STR_TERMINATE, &status); + srvstr_get_path(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBchkpth); return ERROR_NT(status); @@ -492,7 +581,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), STR_TERMINATE,&status); + p += srvstr_get_path(inbuf, fname, p, sizeof(fname), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBgetatr); return ERROR_NT(status); @@ -567,7 +656,7 @@ 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), STR_TERMINATE,&status); + p += srvstr_get_path(inbuf, fname, p, sizeof(fname), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBsetatr); return ERROR_NT(status); @@ -695,7 +784,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), STR_TERMINATE,&nt_status); + p += srvstr_get_path(inbuf, path, p, sizeof(path), 0, STR_TERMINATE, &nt_status); if (!NT_STATUS_IS_OK(nt_status)) { END_PROFILE(SMBsearch); return ERROR_NT(nt_status); @@ -873,7 +962,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), STR_TERMINATE,&err); + p += srvstr_get_path(inbuf, path, p, sizeof(path), 0, STR_TERMINATE, &err); if (!NT_STATUS_IS_OK(err)) { END_PROFILE(SMBfclose); return ERROR_NT(err); @@ -925,7 +1014,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), STR_TERMINATE,&status); + srvstr_get_path(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBopen); return ERROR_NT(status); @@ -1017,7 +1106,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), STR_TERMINATE,&status); + srvstr_get_path(inbuf, fname, smb_buf(inbuf), sizeof(fname), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBopenX); return ERROR_NT(status); @@ -1133,7 +1222,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), STR_TERMINATE,&status); + srvstr_get_path(inbuf, fname, smb_buf(inbuf) + 1, sizeof(fname), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBcreate); return ERROR_NT(status); @@ -1202,7 +1291,7 @@ int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, START_PROFILE(SMBctemp); createmode = SVAL(inbuf,smb_vwv0); - srvstr_get_path(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), STR_TERMINATE,&status); + srvstr_get_path(inbuf, fname, smb_buf(inbuf)+1, sizeof(fname), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBctemp); return ERROR_NT(status); @@ -1456,17 +1545,27 @@ NTSTATUS unlink_internals(connection_struct *conn, int dirtype, char *name) while ((dname = ReadDirName(dirptr))) { pstring fname; pstrcpy(fname,dname); + BOOL sys_direntry = False; /* Quick check for "." and ".." */ if (fname[0] == '.') { if (!fname[1] || (fname[1] == '.' && !fname[2])) { - continue; + if ((dirtype & aDIR)) { + sys_direntry = True; + } else { + continue; + } } } if(!mask_match(fname, mask, case_sensitive)) continue; + if (sys_direntry) { + error = NT_STATUS_OBJECT_NAME_INVALID; + continue; + } + slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname); error = can_delete(fname,conn,dirtype,bad_path); if (!NT_STATUS_IS_OK(error)) @@ -1501,7 +1600,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), STR_TERMINATE,&status); + srvstr_get_path(inbuf, name, smb_buf(inbuf) + 1, sizeof(name), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBunlink); return ERROR_NT(status); @@ -3054,7 +3153,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), STR_TERMINATE,&status); + srvstr_get_path(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBmkdir); return ERROR_NT(status); @@ -3224,7 +3323,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), STR_TERMINATE,&status); + srvstr_get_path(inbuf, directory, smb_buf(inbuf) + 1, sizeof(directory), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBrmdir); return ERROR_NT(status); @@ -3464,15 +3563,16 @@ NTSTATUS rename_internals_fsp(connection_struct *conn, files_struct *fsp, char * code. ****************************************************************************/ -NTSTATUS rename_internals(connection_struct *conn, char *name, char *newname, BOOL replace_if_exists) +NTSTATUS rename_internals(connection_struct *conn, char *name, char *newname, uint16 attrs, BOOL replace_if_exists) { pstring directory; pstring mask; - pstring newname_last_component; + pstring last_component_src; + pstring last_component_dest; char *p; BOOL has_wild; - BOOL bad_path1 = False; - BOOL bad_path2 = False; + BOOL bad_path_src = False; + BOOL bad_path_dest = False; int count=0; NTSTATUS error = NT_STATUS_OK; BOOL rc = True; @@ -3483,8 +3583,29 @@ NTSTATUS rename_internals(connection_struct *conn, char *name, char *newname, BO ZERO_STRUCT(sbuf1); ZERO_STRUCT(sbuf2); - rc = unix_convert(name,conn,0,&bad_path1,&sbuf1); - rcdest = unix_convert(newname,conn,newname_last_component,&bad_path2,&sbuf2); + + rc = unix_convert(name,conn,last_component_src,&bad_path_src,&sbuf1); + if (!rc && bad_path_src) { + if (ms_has_wild(last_component_src)) + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + + /* Quick check for "." and ".." */ + if (last_component_src[0] == '.') { + if (!last_component_src[1] || (last_component_src[1] == '.' && !last_component_src[2])) { + return NT_STATUS_OBJECT_NAME_INVALID; + } + } + + rcdest = unix_convert(newname,conn,last_component_dest,&bad_path_dest,&sbuf2); + + /* Quick check for "." and ".." */ + if (last_component_dest[0] == '.') { + if (!last_component_dest[1] || (last_component_dest[1] == '.' && !last_component_dest[2])) { + return NT_STATUS_OBJECT_NAME_INVALID; + } + } /* * Split the old name into directory and last component @@ -3540,9 +3661,9 @@ NTSTATUS rename_internals(connection_struct *conn, char *name, char *newname, BO } DEBUG(3,("rename_internals: case_sensitive = %d, case_preserve = %d, short case preserve = %d, \ -directory = %s, newname = %s, newname_last_component = %s, is_8_3 = %d\n", +directory = %s, newname = %s, last_component_dest = %s, is_8_3 = %d\n", case_sensitive, case_preserve, short_case_preserve, directory, - newname, newname_last_component, is_short_name)); + newname, last_component_dest, is_short_name)); /* * Check for special case with case preserving and not @@ -3558,7 +3679,7 @@ directory = %s, newname = %s, newname_last_component = %s, is_8_3 = %d\n", ((short_case_preserve == True) && (is_short_name == True))) && strcsequal(directory, newname)) { - pstring newname_modified_last_component; + pstring modified_last_component; /* * Get the last component of the modified name. @@ -3566,15 +3687,15 @@ directory = %s, newname = %s, newname_last_component = %s, is_8_3 = %d\n", * character above. */ p = strrchr_m(newname,'/'); - pstrcpy(newname_modified_last_component,p+1); + pstrcpy(modified_last_component,p+1); - if(strcsequal(newname_modified_last_component, - newname_last_component) == False) { + if(strcsequal(modified_last_component, + last_component_dest) == False) { /* * Replace the modified last component with * the original. */ - pstrcpy(p+1, newname_last_component); + pstrcpy(p+1, last_component_dest); } } @@ -3609,14 +3730,9 @@ directory = %s, newname = %s, newname_last_component = %s, is_8_3 = %d\n", return error; } - /* Quick check for "." and ".." */ - if (!bad_path2 && newname_last_component[0] == '.') { - if (!newname_last_component[1] || (newname_last_component[1] == '.' && !newname_last_component[2])) { - DEBUG(10,("rename_internals: newname_last_component = '.' or '..'\n")); - return NT_STATUS_ACCESS_DENIED; - } - } - if (!rcdest && bad_path2) { + if (!rcdest && bad_path_dest) { + if (ms_has_wild(last_component_dest)) + return NT_STATUS_OBJECT_NAME_INVALID; return NT_STATUS_OBJECT_PATH_NOT_FOUND; } @@ -3669,17 +3785,6 @@ directory = %s, newname = %s, newname_last_component = %s, is_8_3 = %d\n", const char *dname; pstring destname; - /* Quick check for "." and ".." */ - if (!bad_path2 && newname_last_component[0] == '.') { - if (!newname_last_component[1] || (newname_last_component[1] == '.' && !newname_last_component[2])) { - DEBUG(10,("rename_internals: newname_last_component = '.' or '..'\n")); - return NT_STATUS_ACCESS_DENIED; - } - } - if (!rcdest && bad_path2) { - return NT_STATUS_OBJECT_PATH_NOT_FOUND; - } - if (check_name(directory,conn)) dirptr = OpenDir(conn, directory, True); @@ -3692,19 +3797,29 @@ directory = %s, newname = %s, newname_last_component = %s, is_8_3 = %d\n", while ((dname = ReadDirName(dirptr))) { pstring fname; + BOOL sysdir_entry = False; pstrcpy(fname,dname); /* Quick check for "." and ".." */ if (fname[0] == '.') { if (!fname[1] || (fname[1] == '.' && !fname[2])) { - continue; + if (attrs & aDIR) { + sysdir_entry = True; + } else { + continue; + } } } if(!mask_match(fname, mask, case_sensitive)) continue; + if (sysdir_entry) { + error = NT_STATUS_OBJECT_NAME_INVALID; + continue; + } + error = NT_STATUS_ACCESS_DENIED; slprintf(fname,sizeof(fname)-1,"%s/%s",directory,dname); if (!vfs_object_exist(conn, fname, &sbuf1)) { @@ -3735,11 +3850,20 @@ directory = %s, newname = %s, newname_last_component = %s, is_8_3 = %d\n", if (!SMB_VFS_RENAME(conn,fname,destname)) { rename_open_files(conn, sbuf1.st_dev, sbuf1.st_ino, newname); count++; + error = NT_STATUS_OK; } DEBUG(3,("rename_internals: doing rename on %s -> %s\n",fname,destname)); } CloseDir(dirptr); } + + if (!NT_STATUS_EQUAL(error,NT_STATUS_NO_SUCH_FILE)) { + if (!rcdest && bad_path_dest) { + if (ms_has_wild(last_component_dest)) + return NT_STATUS_OBJECT_NAME_INVALID; + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + } } if (count == 0 && NT_STATUS_IS_OK(error)) { @@ -3760,18 +3884,19 @@ int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, pstring name; pstring newname; char *p; + uint16 attrs = SVAL(inbuf,smb_vwv0); NTSTATUS status; START_PROFILE(SMBmv); p = smb_buf(inbuf) + 1; - p += srvstr_get_path(inbuf, name, p, sizeof(name), STR_TERMINATE,&status); + p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBmv); return ERROR_NT(status); } p++; - p += srvstr_get_path(inbuf, newname, p, sizeof(newname), STR_TERMINATE,&status); + p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBmv); return ERROR_NT(status); @@ -3782,7 +3907,7 @@ int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, DEBUG(3,("reply_mv : %s -> %s\n",name,newname)); - status = rename_internals(conn, name, newname, False); + status = rename_internals(conn, name, newname, attrs, False); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBmv); return ERROR_NT(status); @@ -3909,12 +4034,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), STR_TERMINATE,&status); + p += srvstr_get_path(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBcopy); return ERROR_NT(status); } - p += srvstr_get_path(inbuf, newname, p, sizeof(newname), STR_TERMINATE,&status); + p += srvstr_get_path(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBcopy); return ERROR_NT(status); @@ -4074,7 +4199,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), STR_TERMINATE,&status); + srvstr_get_path(inbuf, newdir, smb_buf(inbuf) + 1, sizeof(newdir), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(pathworks_setdir); return ERROR_NT(status); diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 5fbc5ce6ee..1d580e6f5b 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -228,6 +228,7 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, i int smb_action = 0; BOOL bad_path = False; files_struct *fsp; + NTSTATUS status; /* * Ensure we have enough parameters to perform the operation. @@ -248,7 +249,10 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, i open_size = IVAL(params,14); pname = ¶ms[28]; - srvstr_pull(inbuf, fname, pname, sizeof(fname), -1, STR_TERMINATE); + srvstr_get_path(inbuf, fname, pname, sizeof(fname), -1, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); + } DEBUG(3,("trans2open %s mode=%d attr=%d ofun=%d size=%d\n", fname,open_mode, open_attr, open_ofun, open_size)); @@ -939,6 +943,7 @@ static int call_trans2findfirst(connection_struct *conn, char *inbuf, char *outb int space_remaining; BOOL bad_path = False; SMB_STRUCT_STAT sbuf; + NTSTATUS ntstatus = NT_STATUS_OK; if (total_params < 12) return(ERROR_DOS(ERRDOS,ERRinvalidparam)); @@ -968,7 +973,10 @@ close_if_end = %d requires_resume_key = %d level = %d, max_data_bytes = %d\n", return(ERROR_DOS(ERRDOS,ERRunknownlevel)); } - srvstr_pull(inbuf, directory, params+12, sizeof(directory), -1, STR_TERMINATE); + srvstr_get_path(inbuf, directory, params+12, sizeof(directory), -1, STR_TERMINATE, &ntstatus); + if (!NT_STATUS_IS_OK(ntstatus)) { + return ERROR_NT(ntstatus); + } RESOLVE_FINDFIRST_DFSPATH(directory, conn, inbuf, outbuf); @@ -1151,13 +1159,17 @@ static int call_trans2findnext(connection_struct *conn, char *inbuf, char *outbu BOOL dont_descend = False; BOOL out_of_space = False; int space_remaining; + NTSTATUS ntstatus = NT_STATUS_OK; if (total_params < 12) return(ERROR_DOS(ERRDOS,ERRinvalidparam)); *mask = *directory = *resume_name = 0; - srvstr_pull(inbuf, resume_name, params+12, sizeof(resume_name), -1, STR_TERMINATE); + srvstr_get_path(inbuf, resume_name, params+12, sizeof(resume_name), -1, STR_TERMINATE, &ntstatus); + if (!NT_STATUS_IS_OK(ntstatus)) { + return ERROR_NT(ntstatus); + } DEBUG(3,("call_trans2findnext: dirhandle = %d, max_data_bytes = %d, maxentries = %d, \ close_after_request=%d, close_if_end = %d requires_resume_key = %d \ @@ -1909,6 +1921,8 @@ static int call_trans2qfilepathinfo(connection_struct *conn, desired_access = fsp->desired_access; } } else { + NTSTATUS status = NT_STATUS_OK; + /* qpathinfo */ if (total_params < 6) return(ERROR_DOS(ERRDOS,ERRinvalidparam)); @@ -1917,7 +1931,10 @@ static int call_trans2qfilepathinfo(connection_struct *conn, DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QPATHINFO: level = %d\n", info_level)); - srvstr_pull(inbuf, fname, ¶ms[6], sizeof(fname), -1, STR_TERMINATE); + srvstr_get_path(inbuf, fname, ¶ms[6], sizeof(fname), -1, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); + } RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); @@ -2483,6 +2500,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, uid_t set_owner = (uid_t)SMB_UID_NO_CHANGE; gid_t set_grp = (uid_t)SMB_GID_NO_CHANGE; mode_t unixmode = 0; + NTSTATUS status = NT_STATUS_OK; if (!params) return ERROR_NT(NT_STATUS_INVALID_PARAMETER); @@ -2540,7 +2558,10 @@ static int call_trans2setfilepathinfo(connection_struct *conn, return(ERROR_DOS(ERRDOS,ERRinvalidparam)); info_level = SVAL(params,0); - srvstr_pull(inbuf, fname, ¶ms[6], sizeof(fname), -1, STR_TERMINATE); + srvstr_get_path(inbuf, fname, ¶ms[6], sizeof(fname), -1, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); + } unix_convert(fname,conn,0,&bad_path,&sbuf); /* @@ -2761,7 +2782,6 @@ static int call_trans2setfilepathinfo(connection_struct *conn, case SMB_SET_FILE_DISPOSITION_INFO: /* Set delete on close for open file. */ { BOOL delete_on_close; - NTSTATUS status; if (total_data < 1) return(ERROR_DOS(ERRDOS,ERRinvalidparam)); @@ -2948,7 +2968,10 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", if (VALID_STAT(sbuf)) return(ERROR_DOS(ERRDOS,ERRbadpath)); - srvstr_pull(inbuf, link_dest, pdata, sizeof(link_dest), -1, STR_TERMINATE); + srvstr_get_path(inbuf, link_dest, pdata, sizeof(link_dest), -1, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); + } if (ensure_link_is_safe(conn, link_dest, link_dest) != 0) return(UNIXERROR(ERRDOS,ERRnoaccess)); @@ -2973,7 +2996,10 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", if (VALID_STAT(sbuf)) return(ERROR_DOS(ERRDOS,ERRbadpath)); - srvstr_pull(inbuf, link_dest, pdata, sizeof(link_dest), -1, STR_TERMINATE); + srvstr_get_path(inbuf, link_dest, pdata, sizeof(link_dest), -1, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); + } if (ensure_link_is_safe(conn, link_dest, link_dest) != 0) return(UNIXERROR(ERRDOS,ERRnoaccess)); @@ -2996,7 +3022,6 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", pstring newname; pstring base_name; char *p; - NTSTATUS status; if (total_data < 12) return(ERROR_DOS(ERRDOS,ERRinvalidparam)); @@ -3004,10 +3029,13 @@ 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_pull(inbuf, newname, &pdata[12], sizeof(newname), len, 0); + srvstr_get_path(inbuf, newname, &pdata[12], sizeof(newname), len, 0, &status); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); + } - /* Check the new name has no '\' characters. */ - if (strchr_m(newname, '\\') || strchr_m(newname, '/')) + /* Check the new name has no '/' characters. */ + if (strchr_m(newname, '/')) return ERROR_NT(NT_STATUS_NOT_SUPPORTED); RESOLVE_DFSPATH(newname, conn, inbuf, outbuf); @@ -3028,10 +3056,11 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", } else { DEBUG(10,("call_trans2setfilepathinfo: SMB_FILE_RENAME_INFORMATION %s -> %s\n", fname, newname )); - status = rename_internals(conn, fname, base_name, overwrite); + status = rename_internals(conn, fname, base_name, 0, overwrite); } - if (!NT_STATUS_IS_OK(status)) + if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); + } process_pending_change_notify_queue((time_t)0); SSVAL(params,0,0); send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0); @@ -3171,6 +3200,7 @@ static int call_trans2mkdir(connection_struct *conn, int ret = -1; SMB_STRUCT_STAT sbuf; BOOL bad_path = False; + NTSTATUS status = NT_STATUS_OK; if (!CAN_WRITE(conn)) return ERROR_DOS(ERRSRV,ERRaccess); @@ -3178,7 +3208,10 @@ static int call_trans2mkdir(connection_struct *conn, if (total_params < 4) return(ERROR_DOS(ERRDOS,ERRinvalidparam)); - srvstr_pull(inbuf, directory, ¶ms[4], sizeof(directory), -1, STR_TERMINATE); + srvstr_get_path(inbuf, directory, ¶ms[4], sizeof(directory), -1, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); + } DEBUG(3,("call_trans2mkdir : name = %s\n", directory)); @@ -3290,6 +3323,7 @@ static int call_trans2getdfsreferral(connection_struct *conn, char* inbuf, pstring pathname; int reply_size = 0; int max_referral_level; + NTSTATUS status = NT_STATUS_OK; DEBUG(10,("call_trans2getdfsreferral\n")); @@ -3301,7 +3335,10 @@ static int call_trans2getdfsreferral(connection_struct *conn, char* inbuf, if(!lp_host_msdfs()) return ERROR_DOS(ERRDOS,ERRbadfunc); - srvstr_pull(inbuf, pathname, ¶ms[2], sizeof(pathname), -1, STR_TERMINATE); + srvstr_get_path(inbuf, pathname, ¶ms[2], sizeof(pathname), -1, STR_TERMINATE, &status); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); + } if((reply_size = setup_dfs_referral(conn, pathname,max_referral_level,ppdata)) < 0) return UNIXERROR(ERRDOS,ERRbadfile); -- cgit