diff options
Diffstat (limited to 'source/smbd')
-rw-r--r-- | source/smbd/close.c | 4 | ||||
-rw-r--r-- | source/smbd/dir.c | 6 | ||||
-rw-r--r-- | source/smbd/dosmode.c | 36 | ||||
-rw-r--r-- | source/smbd/fileio.c | 4 | ||||
-rw-r--r-- | source/smbd/files.c | 6 | ||||
-rw-r--r-- | source/smbd/msdfs.c | 841 | ||||
-rw-r--r-- | source/smbd/negprot.c | 4 | ||||
-rw-r--r-- | source/smbd/notify_inotify.c | 4 | ||||
-rw-r--r-- | source/smbd/nttrans.c | 138 | ||||
-rw-r--r-- | source/smbd/open.c | 3 | ||||
-rw-r--r-- | source/smbd/pipes.c | 2 | ||||
-rw-r--r-- | source/smbd/process.c | 23 | ||||
-rw-r--r-- | source/smbd/reply.c | 678 | ||||
-rw-r--r-- | source/smbd/sesssetup.c | 236 | ||||
-rw-r--r-- | source/smbd/trans2.c | 711 |
15 files changed, 1844 insertions, 852 deletions
diff --git a/source/smbd/close.c b/source/smbd/close.c index b826cd622af..a123a542fd2 100644 --- a/source/smbd/close.c +++ b/source/smbd/close.c @@ -373,9 +373,9 @@ static NTSTATUS close_normal_file(files_struct *fsp, enum file_close_type close_ * Ensure pending modtime is set after close. */ - if(fsp->pending_modtime && fsp->pending_modtime_owner) { + if (fsp->pending_modtime_owner && !null_timespec(fsp->pending_modtime)) { set_filetime(conn, fsp->fsp_name, fsp->pending_modtime); - } else if (fsp->last_write_time) { + } else if (!null_timespec(fsp->last_write_time)) { set_filetime(conn, fsp->fsp_name, fsp->last_write_time); } diff --git a/source/smbd/dir.c b/source/smbd/dir.c index 2795b2a24b2..db3e155ae47 100644 --- a/source/smbd/dir.c +++ b/source/smbd/dir.c @@ -1017,6 +1017,7 @@ BOOL is_visible_file(connection_struct *conn, const char *dir_path, const char * } if (hide_unreadable || hide_unwriteable || hide_special) { + pstring link_target; char *entry = NULL; if (asprintf(&entry, "%s/%s", dir_path, name) == -1) { @@ -1026,10 +1027,7 @@ BOOL is_visible_file(connection_struct *conn, const char *dir_path, const char * /* If it's a dfs symlink, ignore _hide xxxx_ options */ if (lp_host_msdfs() && lp_msdfs_root(SNUM(conn)) && - /* We get away with NULL talloc ctx here as - we're not interested in the link contents - so we have nothing to free. */ - is_msdfs_link(NULL, conn, entry, NULL, NULL, NULL)) { + is_msdfs_link(conn, entry, link_target, NULL)) { SAFE_FREE(entry); return True; } diff --git a/source/smbd/dosmode.c b/source/smbd/dosmode.c index ad79bbacddf..71d4fa179d4 100644 --- a/source/smbd/dosmode.c +++ b/source/smbd/dosmode.c @@ -282,7 +282,7 @@ static BOOL set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_ } /* We want DOS semantics, ie allow non owner with write permission to change the - bits on a file. Just like file_utime below. + bits on a file. Just like file_ntimes below. */ /* Check if we have write access. */ @@ -504,7 +504,7 @@ int file_set_dosmode(connection_struct *conn, const char *fname, return -1; /* We want DOS semantics, ie allow non owner with write permission to change the - bits on a file. Just like file_utime below. + bits on a file. Just like file_ntimes below. */ /* Check if we have write access. */ @@ -532,11 +532,11 @@ int file_set_dosmode(connection_struct *conn, const char *fname, } /******************************************************************* - Wrapper around dos_utime that possibly allows DOS semantics rather + Wrapper around the VFS ntimes that possibly allows DOS semantics rather than POSIX. *******************************************************************/ -int file_utime(connection_struct *conn, const char *fname, struct utimbuf *times) +int file_ntimes(connection_struct *conn, const char *fname, const struct timespec ts[2]) { SMB_STRUCT_STAT sbuf; int ret = -1; @@ -555,14 +555,17 @@ int file_utime(connection_struct *conn, const char *fname, struct utimbuf *times return 0; } - if(SMB_VFS_UTIME(conn,fname, times) == 0) + if(SMB_VFS_NTIMES(conn, fname, ts) == 0) { return 0; + } - if((errno != EPERM) && (errno != EACCES)) + if((errno != EPERM) && (errno != EACCES)) { return -1; + } - if(!lp_dos_filetimes(SNUM(conn))) + if(!lp_dos_filetimes(SNUM(conn))) { return -1; + } /* We have permission (given by the Samba admin) to break POSIX semantics and allow a user to change @@ -574,7 +577,7 @@ int file_utime(connection_struct *conn, const char *fname, struct utimbuf *times if (can_write_to_file(conn, fname, &sbuf)) { /* We are allowed to become root and change the filetime. */ become_root(); - ret = SMB_VFS_UTIME(conn,fname, times); + ret = SMB_VFS_NTIMES(conn, fname, ts); unbecome_root(); } @@ -585,16 +588,19 @@ int file_utime(connection_struct *conn, const char *fname, struct utimbuf *times Change a filetime - possibly allowing DOS semantics. *******************************************************************/ -BOOL set_filetime(connection_struct *conn, const char *fname, time_t mtime) +BOOL set_filetime(connection_struct *conn, const char *fname, + const struct timespec mtime) { - struct utimbuf times; + struct timespec ts[2]; - if (null_mtime(mtime)) + if (null_timespec(mtime)) { return(True); + } - times.modtime = times.actime = mtime; + ts[1] = mtime; /* mtime. */ + ts[0] = ts[1]; /* atime. */ - if (file_utime(conn, fname, ×)) { + if (file_ntimes(conn, fname, ts)) { DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno))); return False; } @@ -602,5 +608,5 @@ BOOL set_filetime(connection_struct *conn, const char *fname, time_t mtime) notify_fname(conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_LAST_WRITE, fname); - return(True); -} + return True; +} diff --git a/source/smbd/fileio.c b/source/smbd/fileio.c index e0945be8893..65238c0e9ee 100644 --- a/source/smbd/fileio.c +++ b/source/smbd/fileio.c @@ -149,13 +149,13 @@ static ssize_t real_write_file(files_struct *fsp,const char *data, SMB_OFF_T pos * The 99% solution will hopefully be good enough in this case. JRA. */ - if (fsp->pending_modtime) { + if (!null_timespec(fsp->pending_modtime)) { set_filetime(fsp->conn, fsp->fsp_name, fsp->pending_modtime); /* If we didn't get the "set modtime" call ourselves, we must store the last write time to restore on close. JRA. */ if (!fsp->pending_modtime_owner) { - fsp->last_write_time = time(NULL); + fsp->last_write_time = timespec_current(); } } diff --git a/source/smbd/files.c b/source/smbd/files.c index 66ef37bb0fa..23fd47671b8 100644 --- a/source/smbd/files.c +++ b/source/smbd/files.c @@ -383,11 +383,11 @@ files_struct *file_find_print(void) Record the owner of that modtime. ****************************************************************************/ -void fsp_set_pending_modtime(files_struct *tfsp, time_t pmod) +void fsp_set_pending_modtime(files_struct *tfsp, const struct timespec mod) { files_struct *fsp; - if (null_mtime(pmod)) { + if (null_timespec(mod)) { return; } @@ -395,7 +395,7 @@ void fsp_set_pending_modtime(files_struct *tfsp, time_t pmod) if ( fsp->fh->fd != -1 && fsp->dev == tfsp->dev && fsp->inode == tfsp->inode ) { - fsp->pending_modtime = pmod; + fsp->pending_modtime = mod; fsp->pending_modtime_owner = False; } } diff --git a/source/smbd/msdfs.c b/source/smbd/msdfs.c index 2a19d6fb418..284061331bf 100644 --- a/source/smbd/msdfs.c +++ b/source/smbd/msdfs.c @@ -1,8 +1,9 @@ /* Unix SMB/Netbios implementation. Version 3.0 - MSDfs services for Samba + MSDFS services for Samba Copyright (C) Shirish Kalele 2000 + Copyright (C) Jeremy Allison 2007 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,108 +27,142 @@ extern uint32 global_client_caps; /********************************************************************** - Parse the pathname of the form \hostname\service\reqpath - into the dfs_path structure - **********************************************************************/ + Parse a DFS pathname of the form \hostname\service\reqpath + into the dfs_path structure. + If POSIX pathnames is true, the pathname may also be of the + form /hostname/service/reqpath. + We cope with either here. + + Unfortunately, due to broken clients who might set the + SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES bit and then + send a local path, we have to cope with that too.... -static BOOL parse_dfs_path(const char *pathname, struct dfs_path *pdp) + JRA. +**********************************************************************/ + +static NTSTATUS parse_dfs_path(const char *pathname, + BOOL allow_wcards, + struct dfs_path *pdp, + BOOL *ppath_contains_wcard) { pstring pathname_local; - char *p, *temp; + char *p,*temp; + NTSTATUS status = NT_STATUS_OK; + char sepchar; + + ZERO_STRUCTP(pdp); pstrcpy(pathname_local,pathname); p = temp = pathname_local; - ZERO_STRUCTP(pdp); + pdp->posix_path = (lp_posix_pathnames() && *pathname == '/'); - trim_char(temp,'\\','\\'); - DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp)); + sepchar = pdp->posix_path ? '/' : '\\'; - /* now tokenize */ - /* parse out hostname */ - p = strchr_m(temp,'\\'); - if(p == NULL) { - return False; - } - *p = '\0'; - pstrcpy(pdp->hostname,temp); - DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname)); + if (*pathname != sepchar) { + DEBUG(10,("parse_dfs_path: path %s doesn't start with %c\n", + pathname, sepchar )); + /* + * Possibly client sent a local path by mistake. + * Try and convert to a local path. + */ - /* parse out servicename */ - temp = p+1; - p = strchr_m(temp,'\\'); - if(p == NULL) { - pstrcpy(pdp->servicename,temp); - pdp->reqpath[0] = '\0'; - return True; + pdp->hostname[0] = '\0'; + pdp->servicename[0] = '\0'; + + /* We've got no info about separators. */ + pdp->posix_path = lp_posix_pathnames(); + p = temp; + DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n", + temp)); + goto local_path; } - *p = '\0'; - pstrcpy(pdp->servicename,temp); - DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename)); - /* rest is reqpath */ - check_path_syntax(pdp->reqpath, p+1); + trim_char(temp,sepchar,sepchar); - DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath)); - return True; -} + DEBUG(10,("parse_dfs_path: temp = |%s| after trimming %c's\n", + temp, sepchar)); -/********************************************************************** - Parse the pathname of the form /hostname/service/reqpath - into the dfs_path structure - This code is dependent on the fact that check_path_syntax() will - convert '\\' characters to '/'. - When POSIX pathnames have been selected this doesn't happen, so we - must look for the unaltered separator of '\\' instead of the modified '/'. - JRA. - **********************************************************************/ + /* Now tokenize. */ + /* Parse out hostname. */ + p = strchr_m(temp,sepchar); + if(p == NULL) { + DEBUG(10,("parse_dfs_path: can't parse hostname from path %s\n", + temp)); + /* + * Possibly client sent a local path by mistake. + * Try and convert to a local path. + */ -static BOOL parse_processed_dfs_path(char* pathname, struct dfs_path *pdp, BOOL allow_wcards) -{ - pstring pathname_local; - char *p,*temp; - const char sepchar = lp_posix_pathnames() ? '\\' : '/'; + pdp->hostname[0] = '\0'; + pdp->servicename[0] = '\0'; - pstrcpy(pathname_local,pathname); - p = temp = pathname_local; + p = temp; + DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n", + temp)); + goto local_path; + } + *p = '\0'; + fstrcpy(pdp->hostname,temp); + DEBUG(10,("parse_dfs_path: hostname: %s\n",pdp->hostname)); - ZERO_STRUCTP(pdp); + /* If we got a hostname, is it ours (or an IP address) ? */ + if (!is_myname_or_ipaddr(pdp->hostname)) { + /* Repair path. */ + *p = sepchar; + DEBUG(10,("parse_dfs_path: hostname %s isn't ours. Try local path from path %s\n", + pdp->hostname, temp)); + /* + * Possibly client sent a local path by mistake. + * Try and convert to a local path. + */ - trim_char(temp,sepchar,sepchar); - DEBUG(10,("temp in parse_processed_dfs_path: .%s. after trimming \\'s\n",temp)); + pdp->hostname[0] = '\0'; + pdp->servicename[0] = '\0'; - /* now tokenize */ - /* parse out hostname */ - p = strchr_m(temp,sepchar); - if(p == NULL) { - return False; + p = temp; + DEBUG(10,("parse_dfs_path: trying to convert %s to a local path\n", + temp)); + goto local_path; } - *p = '\0'; - pstrcpy(pdp->hostname,temp); - DEBUG(10,("parse_processed_dfs_path: hostname: %s\n",pdp->hostname)); - /* parse out servicename */ + /* Parse out servicename. */ temp = p+1; p = strchr_m(temp,sepchar); if(p == NULL) { - pstrcpy(pdp->servicename,temp); + fstrcpy(pdp->servicename,temp); pdp->reqpath[0] = '\0'; - return True; + return NT_STATUS_OK; } *p = '\0'; - pstrcpy(pdp->servicename,temp); - DEBUG(10,("parse_processed_dfs_path: servicename: %s\n",pdp->servicename)); + fstrcpy(pdp->servicename,temp); + DEBUG(10,("parse_dfs_path: servicename: %s\n",pdp->servicename)); + + p++; + + local_path: + + *ppath_contains_wcard = False; - /* rest is reqpath */ - if (allow_wcards) { - BOOL path_contains_wcard; - check_path_syntax_wcard(pdp->reqpath, p+1, &path_contains_wcard); + /* Rest is reqpath. */ + if (pdp->posix_path) { + status = check_path_syntax_posix(pdp->reqpath, p); } else { - check_path_syntax(pdp->reqpath, p+1); + if (allow_wcards) { + status = check_path_syntax_wcard(pdp->reqpath, p, ppath_contains_wcard); + } else { + status = check_path_syntax(pdp->reqpath, p); + } } - DEBUG(10,("parse_processed_dfs_path: rest of the path: %s\n",pdp->reqpath)); - return True; + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10,("parse_dfs_path: '%s' failed with %s\n", + p, nt_errstr(status) )); + return status; + } + + DEBUG(10,("parse_dfs_path: rest of the path: %s\n",pdp->reqpath)); + return NT_STATUS_OK; } /******************************************************** @@ -135,7 +170,7 @@ static BOOL parse_processed_dfs_path(char* pathname, struct dfs_path *pdp, BOOL Note this CHANGES CWD !!!! JRA. *********************************************************/ -static BOOL create_conn_struct(connection_struct *conn, int snum, char *path) +static BOOL create_conn_struct(connection_struct *conn, int snum, const char *path) { pstring connpath; @@ -145,19 +180,19 @@ static BOOL create_conn_struct(connection_struct *conn, int snum, char *path) pstring_sub(connpath , "%S", lp_servicename(snum)); /* needed for smbd_vfs_init() */ - - if ( (conn->mem_ctx=talloc_init("connection_struct")) == NULL ) { - DEBUG(0,("talloc_init(connection_struct) failed!\n")); - return False; - } - if (!(conn->params = TALLOC_P(conn->mem_ctx, struct share_params))) { + if ((conn->mem_ctx=talloc_init("connection_struct")) == NULL) { + DEBUG(0,("talloc_init(connection_struct) failed!\n")); + return False; + } + + if (!(conn->params = TALLOC_ZERO_P(conn->mem_ctx, struct share_params))) { DEBUG(0, ("TALLOC failed\n")); return False; } - + conn->params->service = snum; - + set_conn_connectpath(conn, connpath); if (!smbd_vfs_init(conn)) { @@ -184,11 +219,27 @@ static BOOL create_conn_struct(connection_struct *conn, int snum, char *path) /********************************************************************** Parse the contents of a symlink to verify if it is an msdfs referral - A valid referral is of the form: msdfs:server1\share1,server2\share2 - talloc CTX can be NULL here if preflist and refcount pointers are null. + A valid referral is of the form: + + msdfs:server1\share1,server2\share2 + msdfs:server1\share1\pathname,server2\share2\pathname + msdfs:server1/share1,server2/share2 + msdfs:server1/share1/pathname,server2/share2/pathname. + + Note that the alternate paths returned here must be of the canonicalized + form: + + \server\share or + \server\share\path\to\file, + + even in posix path mode. This is because we have no knowledge if the + server we're referring to understands posix paths. **********************************************************************/ -static BOOL parse_symlink(TALLOC_CTX *ctx, char *buf, struct referral **preflist, int *refcount) +static BOOL parse_msdfs_symlink(TALLOC_CTX *ctx, + char *target, + struct referral **preflist, + int *refcount) { pstring temp; char *prot; @@ -196,45 +247,28 @@ static BOOL parse_symlink(TALLOC_CTX *ctx, char *buf, struct referral **preflist int count = 0, i; struct referral *reflist; - pstrcpy(temp,buf); - + pstrcpy(temp,target); prot = strtok(temp,":"); - if (!strequal(prot, "msdfs")) { - return False; - } - - /* No referral list requested. Just yes/no. */ - if (!preflist) { - return True; - } - - if (!ctx) { - DEBUG(0,("parse_symlink: logic error. TALLOC_CTX should not be null.\n")); - return True; - } - /* parse out the alternate paths */ while((count<MAX_REFERRAL_COUNT) && ((alt_path[count] = strtok(NULL,",")) != NULL)) { count++; } - DEBUG(10,("parse_symlink: count=%d\n", count)); + DEBUG(10,("parse_msdfs_symlink: count=%d\n", count)); - reflist = *preflist = TALLOC_ARRAY(ctx, struct referral, count); + reflist = *preflist = TALLOC_ZERO_ARRAY(ctx, struct referral, count); if(reflist == NULL) { - DEBUG(0,("parse_symlink: talloc failed!\n")); + DEBUG(0,("parse_msdfs_symlink: talloc failed!\n")); return False; } for(i=0;i<count;i++) { char *p; - /* replace all /'s in the alternate path by a \ */ - for(p = alt_path[i]; *p && ((p = strchr_m(p,'/'))!=NULL); p++) { - *p = '\\'; - } + /* Canonicalize link target. Replace all /'s in the path by a \ */ + string_replace(alt_path[i], '/', '\\'); /* Remove leading '\\'s */ p = alt_path[i]; @@ -244,35 +278,29 @@ static BOOL parse_symlink(TALLOC_CTX *ctx, char *buf, struct referral **preflist pstrcpy(reflist[i].alternate_path, "\\"); pstrcat(reflist[i].alternate_path, p); + reflist[i].proximity = 0; reflist[i].ttl = REFERRAL_TTL; - DEBUG(10, ("parse_symlink: Created alt path: %s\n", reflist[i].alternate_path)); - } - - if(refcount) { - *refcount = count; + DEBUG(10, ("parse_msdfs_symlink: Created alt path: %s\n", reflist[i].alternate_path)); + *refcount += 1; } return True; } /********************************************************************** - Returns true if the unix path is a valid msdfs symlink - talloc CTX can be NULL here if reflistp and refcnt pointers are null. - **********************************************************************/ + Returns true if the unix path is a valid msdfs symlink and also + returns the target string from inside the link. +**********************************************************************/ -BOOL is_msdfs_link(TALLOC_CTX *ctx, connection_struct *conn, const char *path, - struct referral **reflistp, int *refcnt, - SMB_STRUCT_STAT *sbufp) +BOOL is_msdfs_link(connection_struct *conn, + const char *path, + pstring link_target, + SMB_STRUCT_STAT *sbufp) { SMB_STRUCT_STAT st; - pstring referral; int referral_len = 0; - if (!path || !conn) { - return False; - } - if (sbufp == NULL) { sbufp = &st; } @@ -282,76 +310,59 @@ BOOL is_msdfs_link(TALLOC_CTX *ctx, connection_struct *conn, const char *path, return False; } - if (S_ISLNK(sbufp->st_mode)) { - /* open the link and read it */ - referral_len = SMB_VFS_READLINK(conn, path, referral, sizeof(pstring)-1); - if (referral_len == -1) { - DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n", path, strerror(errno))); - return False; - } + if (!S_ISLNK(sbufp->st_mode)) { + DEBUG(5,("is_msdfs_link: %s is not a link.\n",path)); + return False; + } - referral[referral_len] = '\0'; - DEBUG(5,("is_msdfs_link: %s -> %s\n",path,referral)); - if (parse_symlink(ctx, referral, reflistp, refcnt)) { - return True; - } + /* open the link and read it */ + referral_len = SMB_VFS_READLINK(conn, path, link_target, sizeof(pstring)-1); + if (referral_len == -1) { + DEBUG(0,("is_msdfs_link: Error reading msdfs link %s: %s\n", + path, strerror(errno))); + return False; + } + link_target[referral_len] = '\0'; + + DEBUG(5,("is_msdfs_link: %s -> %s\n",path, link_target)); + + if (!strnequal(link_target, "msdfs:", 6)) { + return False; } - return False; + return True; } /***************************************************************** Used by other functions to decide if a dfs path is remote, -and to get the list of referred locations for that remote path. + and to get the list of referred locations for that remote path. -findfirst_flag: For findfirsts, dfs links themselves are not -redirected, but paths beyond the links are. For normal smb calls, -even dfs links need to be redirected. + search_flag: For findfirsts, dfs links themselves are not + redirected, but paths beyond the links are. For normal smb calls, + even dfs links need to be redirected. -self_referralp: clients expect a dfs referral for the same share when -they request referrals for dfs roots on a server. + consumedcntp: how much of the dfs path is being redirected. the client + should try the remaining path on the redirected server. -consumedcntp: how much of the dfs path is being redirected. the client -should try the remaining path on the redirected server. - -TALLOC_CTX can be NULL here if struct referral **reflistpp, int *refcntp -are also NULL. + If this returns NT_STATUS_PATH_NOT_COVERED the contents of the msdfs + link redirect are in targetpath. *****************************************************************/ -static BOOL resolve_dfs_path(TALLOC_CTX *ctx, - const char *dfspath, - struct dfs_path *dp, - connection_struct *conn, - BOOL search_flag, - struct referral **reflistpp, - int *refcntp, - BOOL *self_referralp, - int *consumedcntp) +static NTSTATUS dfs_path_lookup(connection_struct *conn, + const char *dfspath, /* Incoming complete dfs path */ + const struct dfs_path *pdp, /* Parsed out server+share+extrapath. */ + BOOL search_flag, /* Called from a findfirst ? */ + int *consumedcntp, + pstring targetpath) { - pstring localpath; - int consumed_level = 1; - char *p; + char *p = NULL; + char *q = NULL; SMB_STRUCT_STAT sbuf; NTSTATUS status; - pstring reqpath; - - if (!dp || !conn) { - DEBUG(1,("resolve_dfs_path: NULL dfs_path* or NULL connection_struct*!\n")); - return False; - } - - if (!ctx && (reflistpp || refcntp)) { - DEBUG(0,("resolve_dfs_path: logic error. TALLOC_CTX must not be NULL.\n")); - } - - if (dp->reqpath[0] == '\0') { - if (self_referralp) { - DEBUG(6,("resolve_dfs_path: self-referral. returning False\n")); - *self_referralp = True; - } - return False; - } + pstring localpath; + pstring canon_dfspath; /* Canonicalized dfs path. (only '/' components). */ - DEBUG(10,("resolve_dfs_path: Conn path = %s req_path = %s\n", conn->connectpath, dp->reqpath)); + DEBUG(10,("dfs_path_lookup: Conn path = %s reqpath = %s\n", + conn->connectpath, pdp->reqpath)); /* * Note the unix path conversion here we're doing we can @@ -365,144 +376,189 @@ static BOOL resolve_dfs_path(TALLOC_CTX *ctx, * think this is needed. JRA. */ - pstrcpy(localpath, dp->reqpath); - - status = unix_convert(conn, localpath, False, NULL, &sbuf); + pstrcpy(localpath, pdp->reqpath); + status = unix_convert(conn, localpath, search_flag, NULL, &sbuf); if (!NT_STATUS_IS_OK(status)) { - return False; + return status; } - /* check if need to redirect */ - if (is_msdfs_link(ctx, conn, localpath, reflistpp, refcntp, NULL)) { - if ( search_flag ) { - DEBUG(6,("resolve_dfs_path (FindFirst) No redirection " + /* Optimization - check if we can redirect the whole path. */ + + if (is_msdfs_link(conn, localpath, targetpath, NULL)) { + if (search_flag) { + DEBUG(6,("dfs_path_lookup (FindFirst) No redirection " "for dfs link %s.\n", dfspath)); - return False; + return NT_STATUS_OK; } - DEBUG(6,("resolve_dfs_path: %s resolves to a valid Dfs link.\n", dfspath)); + DEBUG(6,("dfs_path_lookup: %s resolves to a " + "valid dfs link %s.\n", dfspath, targetpath)); + if (consumedcntp) { *consumedcntp = strlen(dfspath); } - return True; + return NT_STATUS_PATH_NOT_COVERED; + } + + /* Prepare to test only for '/' components in the given path, + * so if a Windows path replace all '\\' characters with '/'. + * For a POSIX DFS path we know all separators are already '/'. */ + + pstrcpy(canon_dfspath, dfspath); + if (!pdp->posix_path) { + string_replace(canon_dfspath, '\\', '/'); + } + + /* + * Redirect if any component in the path is a link. + * We do this by walking backwards through the + * local path, chopping off the last component + * in both the local path and the canonicalized + * DFS path. If we hit a DFS link then we're done. + */ + + p = strrchr_m(localpath, '/'); + if (consumedcntp) { + q = strrchr_m(canon_dfspath, '/'); } - /* redirect if any component in the path is a link */ - pstrcpy(reqpath, localpath); - p = strrchr_m(reqpath, '/'); while (p) { *p = '\0'; - pstrcpy(localpath, reqpath); - if (is_msdfs_link(ctx, conn, localpath, reflistpp, refcntp, NULL)) { - DEBUG(4, ("resolve_dfs_path: Redirecting %s because parent %s is dfs link\n", dfspath, localpath)); - - /* To find the path consumed, we truncate the original - DFS pathname passed to use to remove the last - component. The length of the resulting string is - the path consumed - */ - + if (q) { + *q = '\0'; + } + + if (is_msdfs_link(conn, localpath, targetpath, NULL)) { + DEBUG(4, ("dfs_path_lookup: Redirecting %s because " + "parent %s is dfs link\n", dfspath, localpath)); + if (consumedcntp) { - char *q; - pstring buf; - pstrcpy(buf, dfspath); - trim_char(buf, '\0', '\\'); - for (; consumed_level; consumed_level--) { - q = strrchr_m(buf, '\\'); - if (q) { - *q = 0; - } - } - *consumedcntp = strlen(buf); - DEBUG(10, ("resolve_dfs_path: Path consumed: %s (%d)\n", buf, *consumedcntp)); + *consumedcntp = strlen(canon_dfspath); + DEBUG(10, ("dfs_path_lookup: Path consumed: %s " + "(%d)\n", canon_dfspath, *consumedcntp)); } - - return True; + + return NT_STATUS_PATH_NOT_COVERED; + } + + /* Step back on the filesystem. */ + p = strrchr_m(localpath, '/'); + + if (consumedcntp) { + /* And in the canonicalized dfs path. */ + q = strrchr_m(canon_dfspath, '/'); } - p = strrchr_m(reqpath, '/'); - consumed_level++; } - return False; + return NT_STATUS_OK; } /***************************************************************** - Decides if a dfs pathname should be redirected or not. - If not, the pathname is converted to a tcon-relative local unix path - - search_wcard_flag: this flag performs 2 functions bother related - to searches. See resolve_dfs_path() and parse_processed_dfs_path() - for details. + Decides if a dfs pathname should be redirected or not. + If not, the pathname is converted to a tcon-relative local unix path + + search_wcard_flag: this flag performs 2 functions bother related + to searches. See resolve_dfs_path() and parse_dfs_path_XX() + for details. + + This function can return NT_STATUS_OK, meaning use the returned path as-is + (mapped into a local path). + or NT_STATUS_NOT_COVERED meaning return a DFS redirect, or + any other NT_STATUS error which is a genuine error to be + returned to the client. *****************************************************************/ -BOOL dfs_redirect( pstring pathname, connection_struct *conn, BOOL search_wcard_flag ) +static NTSTATUS dfs_redirect( connection_struct *conn, + pstring dfs_path, + BOOL search_wcard_flag, + BOOL *ppath_contains_wcard) { + NTSTATUS status; struct dfs_path dp; + pstring targetpath; - if (!conn || !pathname) { - return False; + status = parse_dfs_path(dfs_path, search_wcard_flag, &dp, ppath_contains_wcard); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (dp.reqpath[0] == '\0') { + pstrcpy(dfs_path, dp.reqpath); + DEBUG(5,("dfs_redirect: self-referral.\n")); + return NT_STATUS_OK; } - parse_processed_dfs_path(pathname, &dp, search_wcard_flag); + /* If dfs pathname for a non-dfs share, convert to tcon-relative + path and return OK */ - /* if dfs pathname for a non-dfs share, convert to tcon-relative - path and return false */ if (!lp_msdfs_root(SNUM(conn))) { - pstrcpy(pathname, dp.reqpath); - return False; + pstrcpy(dfs_path, dp.reqpath); + return NT_STATUS_OK; } - - if ( !( strequal(dp.servicename, lp_servicename(SNUM(conn))) - || ( strequal(dp.servicename, HOMES_NAME) - && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) ) - { - return False; + + /* If it looked like a local path (zero hostname/servicename) + * just treat as a tcon-relative path. */ + + if (dp.hostname[0] == '\0' && dp.servicename[0] == '\0') { + pstrcpy(dfs_path, dp.reqpath); + return NT_STATUS_OK; } - if (resolve_dfs_path(NULL, pathname, &dp, conn, search_wcard_flag, - NULL, NULL, NULL, NULL)) { - DEBUG(3,("dfs_redirect: Redirecting %s\n", pathname)); - return True; - } else { - DEBUG(3,("dfs_redirect: Not redirecting %s.\n", pathname)); + if (!( strequal(dp.servicename, lp_servicename(SNUM(conn))) + || (strequal(dp.servicename, HOMES_NAME) + && strequal(lp_servicename(SNUM(conn)), get_current_username()) )) ) { - /* Form non-dfs tcon-relative path */ - pstrcpy(pathname, dp.reqpath); + /* The given sharename doesn't match this connection. */ - DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", pathname)); - return False; + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + + status = dfs_path_lookup(conn, dfs_path, &dp, + search_wcard_flag, NULL, targetpath); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) { + DEBUG(3,("dfs_redirect: Redirecting %s\n", dfs_path)); + } else { + DEBUG(10,("dfs_redirect: dfs_path_lookup failed for %s with %s\n", + dfs_path, nt_errstr(status) )); + } + return status; } - /* never reached */ + DEBUG(3,("dfs_redirect: Not redirecting %s.\n", dfs_path)); + + /* Form non-dfs tcon-relative path */ + pstrcpy(dfs_path, dp.reqpath); + + DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", dfs_path)); + return NT_STATUS_OK; } /********************************************************************** Return a self referral. **********************************************************************/ -static BOOL self_ref(TALLOC_CTX *ctx, const char *pathname, struct junction_map *jucn, - int *consumedcntp, BOOL *self_referralp) +static BOOL self_ref(TALLOC_CTX *ctx, + const char *dfs_path, + struct junction_map *jucn, + int *consumedcntp, + BOOL *self_referralp) { struct referral *ref; - if (self_referralp != NULL) { - *self_referralp = True; - } + *self_referralp = True; jucn->referral_count = 1; - if((ref = TALLOC_P(ctx, struct referral)) == NULL) { - DEBUG(0,("self_ref: malloc failed for referral\n")); + if((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) { + DEBUG(0,("self_ref: talloc failed for referral\n")); return False; } - pstrcpy(ref->alternate_path,pathname); + pstrcpy(ref->alternate_path,dfs_path); ref->proximity = 0; ref->ttl = REFERRAL_TTL; jucn->referral_list = ref; - if (consumedcntp) { - *consumedcntp = strlen(pathname); - } - + *consumedcntp = strlen(dfs_path); return True; } @@ -511,40 +567,39 @@ static BOOL self_ref(TALLOC_CTX *ctx, const char *pathname, struct junction_map junction_map structure. **********************************************************************/ -BOOL get_referred_path(TALLOC_CTX *ctx, const char *pathname, struct junction_map *jucn, - int *consumedcntp, BOOL *self_referralp) +BOOL get_referred_path(TALLOC_CTX *ctx, + const char *dfs_path, + struct junction_map *jucn, + int *consumedcntp, + BOOL *self_referralp) { - struct dfs_path dp; - struct connection_struct conns; struct connection_struct *conn = &conns; + struct dfs_path dp; pstring conn_path; + pstring targetpath; int snum; + NTSTATUS status; BOOL ret = False; - BOOL self_referral = False; - - if (!pathname || !jucn) { - return False; - } + BOOL dummy; ZERO_STRUCT(conns); - if (self_referralp) { - *self_referralp = False; - } else { - self_referralp = &self_referral; - } + *self_referralp = False; - parse_dfs_path(pathname, &dp); + status = parse_dfs_path(dfs_path, False, &dp, &dummy); + if (!NT_STATUS_IS_OK(status)) { + return False; + } /* Verify hostname in path */ if (!is_myname_or_ipaddr(dp.hostname)) { DEBUG(3, ("get_referred_path: Invalid hostname %s in path %s\n", - dp.hostname, pathname)); + dp.hostname, dfs_path)); return False; } - pstrcpy(jucn->service_name, dp.servicename); + fstrcpy(jucn->service_name, dp.servicename); pstrcpy(jucn->volume_name, dp.reqpath); /* Verify the share is a dfs root */ @@ -556,8 +611,8 @@ BOOL get_referred_path(TALLOC_CTX *ctx, const char *pathname, struct junction_ma } if (!lp_msdfs_root(snum)) { - DEBUG(3,("get_referred_path: .%s. in dfs path %s is not a dfs root.\n", - dp.servicename, pathname)); + DEBUG(3,("get_referred_path: |%s| in dfs path %s is not a dfs root.\n", + dp.servicename, dfs_path)); goto out; } @@ -569,15 +624,23 @@ BOOL get_referred_path(TALLOC_CTX *ctx, const char *pathname, struct junction_ma */ if (dp.reqpath[0] == '\0') { - - struct referral* ref; + struct referral *ref; if (*lp_msdfs_proxy(snum) == '\0') { - return self_ref(ctx, pathname, jucn, consumedcntp, self_referralp); + return self_ref(ctx, + dfs_path, + jucn, + consumedcntp, + self_referralp); } + /* + * It's an msdfs proxy share. Redirect to + * the configured target share. + */ + jucn->referral_count = 1; - if ((ref = TALLOC_P(ctx, struct referral)) == NULL) { + if ((ref = TALLOC_ZERO_P(ctx, struct referral)) == NULL) { DEBUG(0, ("malloc failed for referral\n")); goto out; } @@ -589,9 +652,7 @@ BOOL get_referred_path(TALLOC_CTX *ctx, const char *pathname, struct junction_ma ref->proximity = 0; ref->ttl = REFERRAL_TTL; jucn->referral_list = ref; - if (consumedcntp) { - *consumedcntp = strlen(pathname); - } + *consumedcntp = strlen(dfs_path); ret = True; goto out; } @@ -601,23 +662,27 @@ BOOL get_referred_path(TALLOC_CTX *ctx, const char *pathname, struct junction_ma return False; } - /* If not remote & not a self referral, return False */ - if (!resolve_dfs_path(ctx, pathname, &dp, conn, False, - &jucn->referral_list, &jucn->referral_count, - self_referralp, consumedcntp)) { - if (!*self_referralp) { - DEBUG(3,("get_referred_path: No valid referrals for path %s\n", pathname)); - goto out; - } + /* If this is a DFS path dfs_lookup should return + * NT_STATUS_PATH_NOT_COVERED. */ + + status = dfs_path_lookup(conn, dfs_path, &dp, + False, consumedcntp, targetpath); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) { + DEBUG(3,("get_referred_path: No valid referrals for path %s\n", + dfs_path)); + goto out; } - - /* if self_referral, fill up the junction map */ - if (*self_referralp) { - if (self_ref(ctx, pathname, jucn, consumedcntp, self_referralp) == False) { - goto out; - } + + /* We know this is a valid dfs link. Parse the targetpath. */ + if (!parse_msdfs_symlink(ctx, targetpath, + &jucn->referral_list, + &jucn->referral_count)) { + DEBUG(3,("get_referred_path: failed to parse symlink " + "target %s\n", targetpath )); + goto out; } - + ret = True; out: @@ -626,10 +691,11 @@ out: return ret; } -static int setup_ver2_dfs_referral(char *pathname, char **ppdata, - struct junction_map *junction, - int consumedcnt, - BOOL self_referral) +static int setup_ver2_dfs_referral(const char *pathname, + char **ppdata, + struct junction_map *junction, + int consumedcnt, + BOOL self_referral) { char* pdata = *ppdata; @@ -641,7 +707,7 @@ static int setup_ver2_dfs_referral(char *pathname, char **ppdata, int reply_size = 0; int i=0; - DEBUG(10,("setting up version2 referral\nRequested path:\n")); + DEBUG(10,("Setting up version2 referral\nRequested path:\n")); requestedpathlen = rpcstr_push(uni_requestedpath, pathname, sizeof(pstring), STR_TERMINATE); @@ -675,7 +741,7 @@ static int setup_ver2_dfs_referral(char *pathname, char **ppdata, pdata = (char *)SMB_REALLOC(pdata,reply_size); if(pdata == NULL) { - DEBUG(0,("malloc failed for Realloc!\n")); + DEBUG(0,("Realloc failed!\n")); return -1; } *ppdata = pdata; @@ -726,10 +792,11 @@ static int setup_ver2_dfs_referral(char *pathname, char **ppdata, return reply_size; } -static int setup_ver3_dfs_referral(char *pathname, char **ppdata, - struct junction_map *junction, - int consumedcnt, - BOOL self_referral) +static int setup_ver3_dfs_referral(const char *pathname, + char **ppdata, + struct junction_map *junction, + int consumedcnt, + BOOL self_referral) { char* pdata = *ppdata; @@ -810,17 +877,23 @@ static int setup_ver3_dfs_referral(char *pathname, char **ppdata, } /****************************************************************** - Set up the Dfs referral for the dfs pathname + Set up the DFS referral for the dfs pathname. This call returns + the amount of the path covered by this server, and where the + client should be redirected to. This is the meat of the + TRANS2_GET_DFS_REFERRAL call. ******************************************************************/ -int setup_dfs_referral(connection_struct *orig_conn, char *pathname, int max_referral_level, char **ppdata) +int setup_dfs_referral(connection_struct *orig_conn, + const char *dfs_path, + int max_referral_level, + char **ppdata) { struct junction_map junction; - int consumedcnt; + int consumedcnt = 0; BOOL self_referral = False; - pstring buf; int reply_size = 0; - char *pathnamep = pathname; + char *pathnamep = NULL; + pstring local_dfs_path; TALLOC_CTX *ctx; if (!(ctx=talloc_init("setup_dfs_referral"))) { @@ -830,21 +903,24 @@ int setup_dfs_referral(connection_struct *orig_conn, char *pathname, int max_ref ZERO_STRUCT(junction); /* get the junction entry */ - if (!pathnamep) { + if (!dfs_path) { talloc_destroy(ctx); return -1; } - /* Trim pathname sent by client so it begins with only one backslash. - Two backslashes confuse some dfs clients + /* + * Trim pathname sent by client so it begins with only one backslash. + * Two backslashes confuse some dfs clients */ - while (pathnamep[0] == '\\' && pathnamep[1] == '\\') { + + pstrcpy(local_dfs_path, dfs_path); + pathnamep = local_dfs_path; + while (IS_DIRECTORY_SEP(pathnamep[0]) && IS_DIRECTORY_SEP(pathnamep[1])) { pathnamep++; } - pstrcpy(buf, pathnamep); /* The following call can change cwd. */ - if (!get_referred_path(ctx, buf, &junction, &consumedcnt, &self_referral)) { + if (!get_referred_path(ctx, pathnamep, &junction, &consumedcnt, &self_referral)) { vfs_ChDir(orig_conn,orig_conn->connectpath); talloc_destroy(ctx); return -1; @@ -902,31 +978,40 @@ int setup_dfs_referral(connection_struct *orig_conn, char *pathname, int max_ref **********************************************************************/ /********************************************************************* - Creates a junction structure from a Dfs pathname + Creates a junction structure from a DFS pathname **********************************************************************/ -BOOL create_junction(const char *pathname, struct junction_map *jucn) +BOOL create_junction(const char *dfs_path, struct junction_map *jucn) { - struct dfs_path dp; + int snum; + BOOL dummy; + struct dfs_path dp; - parse_dfs_path(pathname,&dp); + NTSTATUS status = parse_dfs_path(dfs_path, False, &dp, &dummy); + + if (!NT_STATUS_IS_OK(status)) { + return False; + } - /* check if path is dfs : validate first token */ + /* check if path is dfs : validate first token */ if (!is_myname_or_ipaddr(dp.hostname)) { DEBUG(4,("create_junction: Invalid hostname %s in dfs path %s\n", - dp.hostname, pathname)); + dp.hostname, dfs_path)); return False; } /* Check for a non-DFS share */ - if(!lp_msdfs_root(lp_servicenumber(dp.servicename))) { - DEBUG(4,("create_junction: %s is not an msdfs root.\n", dp.servicename)); + snum = lp_servicenumber(dp.servicename); + + if(snum < 0 || !lp_msdfs_root(snum)) { + DEBUG(4,("create_junction: %s is not an msdfs root.\n", + dp.servicename)); return False; } - pstrcpy(jucn->service_name,dp.servicename); + fstrcpy(jucn->service_name,dp.servicename); pstrcpy(jucn->volume_name,dp.reqpath); - pstrcpy(jucn->comment, lp_comment(lp_servicenumber(dp.servicename))); + pstrcpy(jucn->comment, lp_comment(snum)); return True; } @@ -934,16 +1019,14 @@ BOOL create_junction(const char *pathname, struct junction_map *jucn) Forms a valid Unix pathname from the junction **********************************************************************/ -static BOOL junction_to_local_path(struct junction_map *jucn, char *path, - int max_pathlen, connection_struct *conn) +static BOOL junction_to_local_path(struct junction_map *jucn, + char *path, + int max_pathlen, + connection_struct *conn_out) { int snum; pstring conn_path; - if(!path || !jucn) { - return False; - } - snum = lp_servicenumber(jucn->service_name); if(snum < 0) { return False; @@ -954,7 +1037,7 @@ static BOOL junction_to_local_path(struct junction_map *jucn, char *path, safe_strcat(path, jucn->volume_name, max_pathlen-1); pstrcpy(conn_path, lp_pathname(snum)); - if (!create_conn_struct(conn, snum, conn_path)) { + if (!create_conn_struct(conn_out, snum, conn_path)) { return False; } @@ -977,11 +1060,12 @@ BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists) return False; } - /* form the msdfs_link contents */ + /* Form the msdfs_link contents */ pstrcpy(msdfs_link, "msdfs:"); for(i=0; i<jucn->referral_count; i++) { char* refpath = jucn->referral_list[i].alternate_path; + /* Alternate paths always use Windows separators. */ trim_char(refpath, '\\', '\\'); if(*refpath == '\0') { if (i == 0) { @@ -999,7 +1083,8 @@ BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists) } } - DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", path, msdfs_link)); + DEBUG(5,("create_msdfs_link: Creating new msdfs link: %s -> %s\n", + path, msdfs_link)); if(exists) { if(SMB_VFS_UNLINK(conn,path)!=0) { @@ -1015,7 +1100,7 @@ BOOL create_msdfs_link(struct junction_map *jucn, BOOL exists) ret = True; - + out: conn_free_internal(conn); @@ -1042,13 +1127,16 @@ BOOL remove_msdfs_link(struct junction_map *jucn) return ret; } -static int form_junctions(TALLOC_CTX *ctx, int snum, struct junction_map *jucn, int jn_remain) +static int form_junctions(TALLOC_CTX *ctx, + int snum, + struct junction_map *jucn, + int jn_remain) { int cnt = 0; SMB_STRUCT_DIR *dirp; - char* dname; + char *dname; pstring connect_path; - char* service_name = lp_servicename(snum); + char *service_name = lp_servicename(snum); connection_struct conn; struct referral *ref = NULL; @@ -1076,13 +1164,13 @@ static int form_junctions(TALLOC_CTX *ctx, int snum, struct junction_map *jucn, DO NOT REMOVE THIS: NT clients will not work with us if this is not present */ - pstrcpy(jucn[cnt].service_name, service_name); + fstrcpy(jucn[cnt].service_name, service_name); jucn[cnt].volume_name[0] = '\0'; jucn[cnt].referral_count = 1; - ref = jucn[cnt].referral_list = TALLOC_P(ctx, struct referral); + ref = jucn[cnt].referral_list = TALLOC_ZERO_P(ctx, struct referral); if (jucn[cnt].referral_list == NULL) { - DEBUG(0, ("Malloc failed!\n")); + DEBUG(0, ("talloc failed!\n")); goto out; } @@ -1093,9 +1181,10 @@ static int form_junctions(TALLOC_CTX *ctx, int snum, struct junction_map *jucn, cnt++; goto out; } - - slprintf(ref->alternate_path, sizeof(pstring)-1, - "\\\\%s\\%s", get_local_machine_name(), service_name); + + pstr_sprintf(ref->alternate_path, "\\\\%s\\%s", + get_local_machine_name(), + service_name); cnt++; /* Now enumerate all dfs links */ @@ -1105,16 +1194,22 @@ static int form_junctions(TALLOC_CTX *ctx, int snum, struct junction_map *jucn, } while ((dname = vfs_readdirname(&conn, dirp)) != NULL) { + pstring link_target; if (cnt >= jn_remain) { SMB_VFS_CLOSEDIR(&conn,dirp); DEBUG(2, ("ran out of MSDFS junction slots")); goto out; } - if (is_msdfs_link(ctx, &conn, dname, &jucn[cnt].referral_list, - &jucn[cnt].referral_count, NULL)) { - pstrcpy(jucn[cnt].service_name, service_name); - pstrcpy(jucn[cnt].volume_name, dname); - cnt++; + if (is_msdfs_link(&conn, dname, link_target, NULL)) { + if (parse_msdfs_symlink(ctx, + link_target, + &jucn[cnt].referral_list, + &jucn[cnt].referral_count)) { + + fstrcpy(jucn[cnt].service_name, service_name); + pstrcpy(jucn[cnt].volume_name, dname); + cnt++; + } } } @@ -1148,3 +1243,33 @@ int enum_msdfs_links(TALLOC_CTX *ctx, struct junction_map *jucn, int jn_max) } return jn_count; } + +/****************************************************************************** + Core function to resolve a dfs pathname. +******************************************************************************/ + +NTSTATUS resolve_dfspath(connection_struct *conn, BOOL dfs_pathnames, pstring name) +{ + NTSTATUS status = NT_STATUS_OK; + BOOL dummy; + if (dfs_pathnames && lp_host_msdfs() && lp_msdfs_root(SNUM(conn))) { + status = dfs_redirect(conn, name, False, &dummy); + } + return status; +} + +/****************************************************************************** + Core function to resolve a dfs pathname possibly containing a wildcard. + This function is identical to the above except for the BOOL param to + dfs_redirect but I need this to be separate so it's really clear when + we're allowing wildcards and when we're not. JRA. +******************************************************************************/ + +NTSTATUS resolve_dfspath_wcard(connection_struct *conn, BOOL dfs_pathnames, pstring name, BOOL *ppath_contains_wcard) +{ + NTSTATUS status = NT_STATUS_OK; + if (dfs_pathnames && lp_host_msdfs() && lp_msdfs_root(SNUM(conn))) { + status = dfs_redirect(conn, name, True, ppath_contains_wcard); + } + return status; +} diff --git a/source/smbd/negprot.c b/source/smbd/negprot.c index fb5610b20bb..bc7c75aab4c 100644 --- a/source/smbd/negprot.c +++ b/source/smbd/negprot.c @@ -259,7 +259,9 @@ static int reply_nt1(char *inbuf, char *outbuf) if ( (SVAL(inbuf, smb_flg2) & FLAGS2_EXTENDED_SECURITY) && ((SVAL(inbuf, smb_flg2) & FLAGS2_UNKNOWN_BIT4) == 0) ) { - set_remote_arch( RA_VISTA ); + if (get_remote_arch() != RA_SAMBA) { + set_remote_arch( RA_VISTA ); + } } /* do spnego in user level security if the client diff --git a/source/smbd/notify_inotify.c b/source/smbd/notify_inotify.c index 5fb414de4cc..ff17d455f39 100644 --- a/source/smbd/notify_inotify.c +++ b/source/smbd/notify_inotify.c @@ -26,6 +26,10 @@ #ifdef HAVE_INOTIFY +#ifdef HAVE_ASM_TYPES_H +#include <asm/types.h> +#endif + #include <linux/inotify.h> #include <asm/unistd.h> diff --git a/source/smbd/nttrans.c b/source/smbd/nttrans.c index 19710d1dcdd..2a49e1f4a64 100644 --- a/source/smbd/nttrans.c +++ b/source/smbd/nttrans.c @@ -353,7 +353,7 @@ static int nt_open_pipe(char *fname, connection_struct *conn, return(ERROR_DOS(ERRSRV,ERRnofids)); } - /* TODO: Add pipe to db */ + /* Add pipe to db */ if ( !store_pipe_opendb( p ) ) { DEBUG(3,("nt_open_pipe: failed to store %s pipe open.\n", fname)); @@ -469,7 +469,7 @@ int reply_ntcreate_and_X(connection_struct *conn, SMB_OFF_T file_len = 0; SMB_STRUCT_STAT sbuf; int info = 0; - files_struct *fsp=NULL; + files_struct *fsp = NULL; char *p = NULL; struct timespec c_timespec; struct timespec a_timespec; @@ -491,7 +491,9 @@ int reply_ntcreate_and_X(connection_struct *conn, (unsigned int)create_options, (unsigned int)root_dir_fid )); - /* If it's an IPC, use the pipe handler. */ + /* + * If it's an IPC, use the pipe handler. + */ if (IS_IPC(conn)) { if (lp_nt_pipe_support()) { @@ -502,7 +504,7 @@ int reply_ntcreate_and_X(connection_struct *conn, return(ERROR_DOS(ERRDOS,ERRnoaccess)); } } - + if (create_options & FILE_OPEN_BY_FILE_ID) { END_PROFILE(SMBntcreateX); return ERROR_NT(NT_STATUS_NOT_SUPPORTED); @@ -522,7 +524,7 @@ int reply_ntcreate_and_X(connection_struct *conn, if(!dir_fsp) { END_PROFILE(SMBntcreateX); - return(ERROR_DOS(ERRDOS,ERRbadfid)); + return ERROR_DOS(ERRDOS,ERRbadfid); } if(!dir_fsp->is_directory) { @@ -533,7 +535,7 @@ int reply_ntcreate_and_X(connection_struct *conn, return ERROR_NT(status); } - /* + /* * Check to see if this is a mac fork of some kind. */ @@ -564,7 +566,7 @@ int reply_ntcreate_and_X(connection_struct *conn, * Ensure it ends in a '\'. */ - if(fname[dir_name_len-1] != '\\' && fname[dir_name_len-1] != '/') { + if((fname[dir_name_len-1] != '\\') && (fname[dir_name_len-1] != '/')) { pstrcat(fname, "/"); dir_name_len++; } @@ -582,7 +584,7 @@ int reply_ntcreate_and_X(connection_struct *conn, return ERROR_NT(status); } - /* + /* * Check to see if this is a mac fork of some kind. */ @@ -613,7 +615,14 @@ int reply_ntcreate_and_X(connection_struct *conn, * Now contruct the smb_open_mode value from the filename, * desired access and the share access. */ - RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + status = resolve_dfspath(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, fname); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBntcreateX); + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0; if (oplock_request) { @@ -664,19 +673,19 @@ int reply_ntcreate_and_X(connection_struct *conn, } } - /* + /* * If it's a request for a directory open, deal with it separately. */ if(create_options & FILE_DIRECTORY_FILE) { - oplock_request = 0; - + /* Can't open a temp directory. IFS kit test. */ if (file_attributes & FILE_ATTRIBUTE_TEMPORARY) { END_PROFILE(SMBntcreateX); return ERROR_NT(NT_STATUS_INVALID_PARAMETER); } + oplock_request = 0; status = open_directory(conn, fname, &sbuf, access_mask, share_access, @@ -695,7 +704,9 @@ int reply_ntcreate_and_X(connection_struct *conn, END_PROFILE(SMBntcreateX); return ERROR_NT(status); } + } else { + /* * Ordinary file case. */ @@ -721,6 +732,7 @@ int reply_ntcreate_and_X(connection_struct *conn, new_file_attributes, oplock_request, &info, &fsp); + if (!NT_STATUS_IS_OK(status)) { /* We cheat here. There are two cases we * care about. One is a directory rename, @@ -773,7 +785,6 @@ int reply_ntcreate_and_X(connection_struct *conn, return ERROR_NT(status); } } else { - restore_case_semantics(conn, file_attributes); END_PROFILE(SMBntcreateX); if (open_was_deferred(SVAL(inbuf,smb_mid))) { @@ -786,7 +797,7 @@ int reply_ntcreate_and_X(connection_struct *conn, } restore_case_semantics(conn, file_attributes); - + file_len = sbuf.st_size; fattr = dos_mode(conn,fname,&sbuf); if(fattr == 0) { @@ -827,11 +838,11 @@ int reply_ntcreate_and_X(connection_struct *conn, * and we granted one (by whatever means) - set the * correct bit for extended oplock reply. */ - + if (oplock_request && lp_fake_oplocks(SNUM(conn))) { extended_oplock_granted = True; } - + if(oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { extended_oplock_granted = True; } @@ -871,8 +882,8 @@ int reply_ntcreate_and_X(connection_struct *conn, SIVAL(p,0,info); } p += 4; - - /* Create time. */ + + /* Create time. */ c_timespec = get_create_timespec(&sbuf,lp_fake_dir_create_times(SNUM(conn))); a_timespec = get_atimespec(&sbuf); m_timespec = get_mtimespec(&sbuf); @@ -883,7 +894,7 @@ int reply_ntcreate_and_X(connection_struct *conn, dos_filetime_timespec(&m_timespec); } - put_long_date_timespec(p, c_timespec); + put_long_date_timespec(p, c_timespec); /* create time. */ p += 8; put_long_date_timespec(p, a_timespec); /* access time */ p += 8; @@ -1252,15 +1263,27 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o } oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0; - oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0; + if (oplock_request) { + oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0; + } /* + * Ordinary file or directory. + */ + + /* * Check if POSIX semantics are wanted. */ - + new_file_attributes = set_posix_case_semantics(conn, file_attributes); - RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + status = resolve_dfspath(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, fname); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } status = unix_convert(conn, fname, False, NULL, &sbuf); if (!NT_STATUS_IS_OK(status)) { @@ -1273,7 +1296,7 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o restore_case_semantics(conn, file_attributes); return ERROR_NT(status); } - + /* This is the correct thing to do (check every time) but can_delete is expensive (it may have to read the parent directory permissions). So for now we're not doing it unless we have a strong hint the client @@ -1317,14 +1340,13 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o return ERROR_NT(NT_STATUS_INVALID_PARAMETER); } - oplock_request = 0; - /* * We will get a create directory here if the Win32 * app specified a security descriptor in the * CreateDirectory() call. */ + oplock_request = 0; status = open_directory(conn, fname, &sbuf, access_mask, share_access, @@ -1464,11 +1486,11 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o * and we granted one (by whatever means) - set the * correct bit for extended oplock reply. */ - + if (oplock_request && lp_fake_oplocks(SNUM(conn))) { extended_oplock_granted = True; } - + if(oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { extended_oplock_granted = True; } @@ -1592,15 +1614,15 @@ static NTSTATUS copy_internals(connection_struct *conn, char *oldname, char *new return status; } - /* Source must already exist. */ - if (!VALID_STAT(sbuf1)) { - return NT_STATUS_OBJECT_NAME_NOT_FOUND; - } status = check_name(conn, oldname); if (!NT_STATUS_IS_OK(status)) { return status; } + /* Source must already exist. */ + if (!VALID_STAT(sbuf1)) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } /* Ensure attributes match. */ fattr = dos_mode(conn,oldname,&sbuf1); if ((fattr & ~attrs) & (aHIDDEN | aSYSTEM)) { @@ -1612,16 +1634,16 @@ static NTSTATUS copy_internals(connection_struct *conn, char *oldname, char *new return status; } - /* Disallow if newname already exists. */ - if (VALID_STAT(sbuf2)) { - return NT_STATUS_OBJECT_NAME_COLLISION; - } - status = check_name(conn, newname); if (!NT_STATUS_IS_OK(status)) { return status; } + /* Disallow if newname already exists. */ + if (VALID_STAT(sbuf2)) { + return NT_STATUS_OBJECT_NAME_COLLISION; + } + /* No links from a directory. */ if (S_ISDIR(sbuf1.st_mode)) { return NT_STATUS_FILE_IS_A_DIRECTORY; @@ -1675,7 +1697,7 @@ static NTSTATUS copy_internals(connection_struct *conn, char *oldname, char *new close_file(fsp1,NORMAL_CLOSE); /* Ensure the modtime is set correctly on the destination file. */ - fsp_set_pending_modtime(fsp2, sbuf1.st_mtime); + fsp_set_pending_modtime(fsp2, get_mtimespec(&sbuf1)); status = close_file(fsp2,NORMAL_CLOSE); @@ -1708,15 +1730,15 @@ int reply_ntrename(connection_struct *conn, pstring newname; char *p; NTSTATUS status; - BOOL path1_contains_wcard = False; - BOOL path2_contains_wcard = False; + BOOL src_has_wcard = False; + BOOL dest_has_wcard = False; uint32 attrs = SVAL(inbuf,smb_vwv0); uint16 rename_type = SVAL(inbuf,smb_vwv1); START_PROFILE(SMBntrename); p = smb_buf(inbuf) + 1; - p += srvstr_get_path_wcard(inbuf, oldname, p, sizeof(oldname), 0, STR_TERMINATE, &status, &path1_contains_wcard); + p += srvstr_get_path_wcard(inbuf, oldname, p, sizeof(oldname), 0, STR_TERMINATE, &status, &src_has_wcard); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBntrename); return ERROR_NT(status); @@ -1734,23 +1756,38 @@ int reply_ntrename(connection_struct *conn, } p++; - p += srvstr_get_path_wcard(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status, &path2_contains_wcard); + p += srvstr_get_path_wcard(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status, &dest_has_wcard); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBntrename); return ERROR_NT(status); } - RESOLVE_DFSPATH(oldname, conn, inbuf, outbuf); - RESOLVE_DFSPATH(newname, conn, inbuf, outbuf); - + status = resolve_dfspath(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, oldname); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBntrename); + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } + + status = resolve_dfspath(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, newname); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBntrename); + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } + DEBUG(3,("reply_ntrename : %s -> %s\n",oldname,newname)); switch(rename_type) { case RENAME_FLAG_RENAME: - status = rename_internals(conn, oldname, newname, attrs, False, path1_contains_wcard); + status = rename_internals(conn, oldname, newname, attrs, False, src_has_wcard, dest_has_wcard); break; case RENAME_FLAG_HARD_LINK: - if (path1_contains_wcard || path2_contains_wcard) { + if (src_has_wcard || dest_has_wcard) { /* No wildcards. */ status = NT_STATUS_OBJECT_PATH_SYNTAX_BAD; } else { @@ -1758,7 +1795,7 @@ int reply_ntrename(connection_struct *conn, } break; case RENAME_FLAG_COPY: - if (path1_contains_wcard || path2_contains_wcard) { + if (src_has_wcard || dest_has_wcard) { /* No wildcards. */ status = NT_STATUS_OBJECT_PATH_SYNTAX_BAD; } else { @@ -1899,7 +1936,7 @@ static int call_nt_transact_rename(connection_struct *conn, char *inbuf, char *o pstring new_name; files_struct *fsp = NULL; BOOL replace_if_exists = False; - BOOL path_contains_wcard = False; + BOOL dest_has_wcard = False; NTSTATUS status; if(parameter_count < 5) { @@ -1909,13 +1946,14 @@ 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_wcard(inbuf, new_name, params+4, sizeof(new_name), parameter_count - 4, STR_TERMINATE, &status, &path_contains_wcard); + srvstr_get_path_wcard(inbuf, new_name, params+4, sizeof(new_name), parameter_count - 4, + STR_TERMINATE, &status, &dest_has_wcard); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } status = rename_internals(conn, fsp->fsp_name, - new_name, 0, replace_if_exists, path_contains_wcard); + new_name, 0, replace_if_exists, False, dest_has_wcard); if (!NT_STATUS_IS_OK(status)) { if (open_was_deferred(SVAL(inbuf,smb_mid))) { diff --git a/source/smbd/open.c b/source/smbd/open.c index 13ec9f952fe..c7a7086894e 100644 --- a/source/smbd/open.c +++ b/source/smbd/open.c @@ -1903,7 +1903,6 @@ static NTSTATUS mkdir_internal(connection_struct *conn, uint32 file_attributes, SMB_STRUCT_STAT *psbuf) { - int ret= -1; mode_t mode; char *parent_dir; const char *dirname; @@ -1931,7 +1930,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn, mode = unix_mode(conn, aDIR, name, parent_dir); } - if ((ret=SMB_VFS_MKDIR(conn, name, mode)) != 0) { + if (SMB_VFS_MKDIR(conn, name, mode) != 0) { return map_nt_error_from_unix(errno); } diff --git a/source/smbd/pipes.c b/source/smbd/pipes.c index 52660da2ffe..58756a0b5a6 100644 --- a/source/smbd/pipes.c +++ b/source/smbd/pipes.c @@ -300,7 +300,5 @@ int reply_pipe_close(connection_struct *conn, char *inbuf,char *outbuf) return ERROR_DOS(ERRDOS,ERRbadfid); } - /* TODO: REMOVE PIPE FROM DB */ - return(outsize); } diff --git a/source/smbd/process.c b/source/smbd/process.c index 14941e64990..836801ba82c 100644 --- a/source/smbd/process.c +++ b/source/smbd/process.c @@ -1148,6 +1148,7 @@ int chain_reply(char *inbuf,char *outbuf,int size,int bufsize) unsigned smb_off2 = SVAL(inbuf,smb_vwv1); char *inbuf2, *outbuf2; int outsize2; + int new_size; char inbuf_saved[smb_wct]; char outbuf_saved[smb_wct]; int outsize = smb_len(outbuf) + 4; @@ -1198,6 +1199,20 @@ int chain_reply(char *inbuf,char *outbuf,int size,int bufsize) /* create the in buffer */ SCVAL(inbuf2,smb_com,smb_com2); + /* work out the new size for the in buffer. */ + new_size = size - (inbuf2 - inbuf); + if (new_size < 0) { + DEBUG(0,("chain_reply: chain packet size incorrect (orig size = %d, " + "offset = %d)\n", + size, + (inbuf2 - inbuf) )); + exit_server_cleanly("Bad chained packet"); + return(-1); + } + + /* And set it in the header. */ + smb_setlen(inbuf2, new_size); + /* create the out buffer */ construct_reply_common(inbuf2, outbuf2); @@ -1205,7 +1220,7 @@ int chain_reply(char *inbuf,char *outbuf,int size,int bufsize) show_msg(inbuf2); /* process the request */ - outsize2 = switch_message(smb_com2,inbuf2,outbuf2,size-chain_size, + outsize2 = switch_message(smb_com2,inbuf2,outbuf2,new_size, bufsize-chain_size); /* copy the new reply and request headers over the old ones, but @@ -1219,8 +1234,10 @@ int chain_reply(char *inbuf,char *outbuf,int size,int bufsize) { int ofs = smb_wct - PTR_DIFF(outbuf2,orig_outbuf); - if (ofs < 0) ofs = 0; - memmove(outbuf2+ofs,outbuf_saved+ofs,smb_wct-ofs); + if (ofs < 0) { + ofs = 0; + } + memmove(outbuf2+ofs,outbuf_saved+ofs,smb_wct-ofs); } return outsize2; diff --git a/source/smbd/reply.c b/source/smbd/reply.c index c48bebb0c6c..14b16e1ae23 100644 --- a/source/smbd/reply.c +++ b/source/smbd/reply.c @@ -44,9 +44,12 @@ extern BOOL global_encrypted_passwords_negotiated; set. ****************************************************************************/ +/* Custom version for processing POSIX paths. */ +#define IS_PATH_SEP(c,posix_only) ((c) == '/' || (!(posix_only) && (c) == '\\')) + NTSTATUS check_path_syntax_internal(pstring destname, const pstring srcname, - BOOL windows_path, + BOOL posix_path, BOOL *p_last_component_contains_wcard) { char *d = destname; @@ -57,12 +60,12 @@ NTSTATUS check_path_syntax_internal(pstring destname, *p_last_component_contains_wcard = False; while (*s) { - if (IS_DIRECTORY_SEP(*s)) { + if (IS_PATH_SEP(*s,posix_path)) { /* * 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)) { + while (IS_PATH_SEP(*s,posix_path)) { s++; } if ((d != destname) && (*s != '\0')) { @@ -77,7 +80,7 @@ NTSTATUS check_path_syntax_internal(pstring destname, } if (start_of_name_component) { - if ((s[0] == '.') && (s[1] == '.') && (IS_DIRECTORY_SEP(s[2]) || s[2] == '\0')) { + if ((s[0] == '.') && (s[1] == '.') && (IS_PATH_SEP(s[2],posix_path) || s[2] == '\0')) { /* Uh oh - "/../" or "\\..\\" or "/..\0" or "\\..\0" ! */ /* @@ -107,8 +110,8 @@ NTSTATUS check_path_syntax_internal(pstring destname, /* We're still at the start of a name component, just the previous one. */ continue; - } else if ((s[0] == '.') && ((s[1] == '\0') || IS_DIRECTORY_SEP(s[1]))) { - if (!windows_path) { + } else if ((s[0] == '.') && ((s[1] == '\0') || IS_PATH_SEP(s[1],posix_path))) { + if (posix_path) { /* Eat the '.' */ s++; continue; @@ -118,7 +121,7 @@ NTSTATUS check_path_syntax_internal(pstring destname, } if (!(*s & 0x80)) { - if (windows_path) { + if (!posix_path) { if (*s <= 0x1f) { return NT_STATUS_OBJECT_NAME_INVALID; } @@ -176,7 +179,7 @@ NTSTATUS check_path_syntax_internal(pstring destname, NTSTATUS check_path_syntax(pstring destname, const pstring srcname) { BOOL ignore; - return check_path_syntax_internal(destname, srcname, True, &ignore); + return check_path_syntax_internal(destname, srcname, False, &ignore); } /**************************************************************************** @@ -187,7 +190,7 @@ NTSTATUS check_path_syntax(pstring destname, const pstring srcname) NTSTATUS check_path_syntax_wcard(pstring destname, const pstring srcname, BOOL *p_contains_wcard) { - return check_path_syntax_internal(destname, srcname, True, p_contains_wcard); + return check_path_syntax_internal(destname, srcname, False, p_contains_wcard); } /**************************************************************************** @@ -196,10 +199,10 @@ NTSTATUS check_path_syntax_wcard(pstring destname, const pstring srcname, BOOL * set (a safe assumption). ****************************************************************************/ -static NTSTATUS check_path_syntax_posix(pstring destname, const pstring srcname) +NTSTATUS check_path_syntax_posix(pstring destname, const pstring srcname) { BOOL ignore; - return check_path_syntax_internal(destname, srcname, False, &ignore); + return check_path_syntax_internal(destname, srcname, True, &ignore); } /**************************************************************************** @@ -224,6 +227,16 @@ size_t srvstr_get_path_wcard(char *inbuf, char *dest, const char *src, size_t de *contains_wcard = False; + if (SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES) { + /* + * For a DFS path the function parse_dfs_path() + * will do the path processing, just make a copy. + */ + pstrcpy(dest, tmppath); + *err = NT_STATUS_OK; + return ret; + } + if (lp_posix_pathnames()) { *err = check_path_syntax_posix(dest, tmppath); } else { @@ -251,6 +264,17 @@ 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); } + + if (SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES) { + /* + * For a DFS path the function parse_dfs_path() + * will do the path processing, just make a copy. + */ + pstrcpy(dest, tmppath); + *err = NT_STATUS_OK; + return ret; + } + if (lp_posix_pathnames()) { *err = check_path_syntax_posix(dest, tmppath); } else { @@ -631,7 +655,14 @@ int reply_checkpath(connection_struct *conn, char *inbuf,char *outbuf, int dum_s return ERROR_NT(status); } - RESOLVE_DFSPATH(name, conn, inbuf, outbuf); + status = resolve_dfspath(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, name); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + END_PROFILE(SMBcheckpath); + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + goto path_err; + } DEBUG(3,("reply_checkpath %s mode=%d\n", name, (int)SVAL(inbuf,smb_vwv0))); @@ -710,7 +741,14 @@ int reply_getatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size return ERROR_NT(status); } - RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + status = resolve_dfspath(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, fname); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBgetatr); + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } /* dos smetimes asks for a stat of "" - it returns a "hidden directory" under WfWg - weird! */ @@ -789,7 +827,14 @@ int reply_setatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size return ERROR_NT(status); } - RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + status = resolve_dfspath(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, fname); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBsetatr); + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } status = unix_convert(conn, fname, False, NULL, &sbuf); if (!NT_STATUS_IS_OK(status)) { @@ -797,6 +842,12 @@ int reply_setatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size return ERROR_NT(status); } + status = check_name(conn, fname); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBsetatr); + return ERROR_NT(status); + } + if (fname[0] == '.' && fname[1] == '\0') { /* * Not sure here is the right place to catch this @@ -806,12 +857,6 @@ int reply_setatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size return ERROR_NT(NT_STATUS_ACCESS_DENIED); } - status = check_name(conn, fname); - if (!NT_STATUS_IS_OK(status)) { - END_PROFILE(SMBsetatr); - return ERROR_NT(status); - } - mode = SVAL(inbuf,smb_vwv0); mtime = srv_make_unix_date3(inbuf+smb_vwv1); @@ -827,7 +872,7 @@ int reply_setatr(connection_struct *conn, char *inbuf,char *outbuf, int dum_size } } - if (!set_filetime(conn,fname,mtime)) { + if (!set_filetime(conn,fname,convert_time_t_to_timespec(mtime))) { END_PROFILE(SMBsetatr); return UNIXERROR(ERRDOS, ERRnoaccess); } @@ -944,7 +989,14 @@ int reply_search(connection_struct *conn, char *inbuf,char *outbuf, int dum_size return ERROR_NT(nt_status); } - RESOLVE_DFSPATH_WCARD(path, conn, inbuf, outbuf); + nt_status = resolve_dfspath_wcard(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, path, &mask_contains_wcard); + if (!NT_STATUS_IS_OK(nt_status)) { + END_PROFILE(SMBsearch); + if (NT_STATUS_EQUAL(nt_status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(nt_status); + } p++; status_len = SVAL(p, 0); @@ -954,16 +1006,13 @@ int reply_search(connection_struct *conn, char *inbuf,char *outbuf, int dum_size if (status_len == 0) { SMB_STRUCT_STAT sbuf; - pstring dir2; pstrcpy(directory,path); - pstrcpy(dir2,path); nt_status = unix_convert(conn, directory, True, NULL, &sbuf); if (!NT_STATUS_IS_OK(nt_status)) { END_PROFILE(SMBsearch); return ERROR_NT(nt_status); } - unix_format(dir2); nt_status = check_name(conn, directory); if (!NT_STATUS_IS_OK(nt_status)) { @@ -971,23 +1020,16 @@ int reply_search(connection_struct *conn, char *inbuf,char *outbuf, int dum_size return ERROR_NT(nt_status); } - p = strrchr_m(dir2,'/'); - if (p == NULL) { - pstrcpy(mask,dir2); - *dir2 = 0; - } else { - *p = 0; - pstrcpy(mask,p+1); - } - p = strrchr_m(directory,'/'); if (!p) { - *directory = 0; + pstrcpy(mask,directory); + pstrcpy(directory,"."); } else { *p = 0; + pstrcpy(mask,p+1); } - if (strlen(directory) == 0) { + if (*directory == '\0') { pstrcpy(directory,"."); } memset((char *)status,'\0',21); @@ -1007,6 +1049,11 @@ int reply_search(connection_struct *conn, char *inbuf,char *outbuf, int dum_size } string_set(&conn->dirpath,dptr_path(dptr_num)); pstrcpy(mask, dptr_wcard(dptr_num)); + /* + * For a 'continue' search we have no string. So + * check from the initial saved string. + */ + mask_contains_wcard = ms_has_wild(mask); } p = smb_buf(outbuf) + 3; @@ -1202,7 +1249,14 @@ int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, return ERROR_NT(status); } - RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + status = resolve_dfspath(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, fname); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBopen); + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } status = unix_convert(conn, fname, False, NULL, &sbuf); if (!NT_STATUS_IS_OK(status)) { @@ -1210,6 +1264,12 @@ int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, return ERROR_NT(status); } + status = check_name(conn, fname); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBopen); + return ERROR_NT(status); + } + if (!map_open_params_to_ntcreate(fname, deny_mode, OPENX_FILE_EXISTS_OPEN, &access_mask, &share_mode, &create_disposition, &create_options)) { END_PROFILE(SMBopen); @@ -1320,7 +1380,14 @@ int reply_open_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt return ERROR_NT(status); } - RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + status = resolve_dfspath(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, fname); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBopenX); + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } status = unix_convert(conn, fname, False, NULL, &sbuf); if (!NT_STATUS_IS_OK(status)) { @@ -1328,6 +1395,12 @@ int reply_open_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt return ERROR_NT(status); } + status = check_name(conn, fname); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBopenX); + return ERROR_NT(status); + } + if (!map_open_params_to_ntcreate(fname, deny_mode, smb_ofun, &access_mask, &share_mode, @@ -1470,7 +1543,7 @@ int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int com; int outsize = 0; uint32 fattr = SVAL(inbuf,smb_vwv0); - struct utimbuf times; + struct timespec ts[2]; files_struct *fsp; int oplock_request = CORE_OPLOCK_REQUEST(inbuf); SMB_STRUCT_STAT sbuf; @@ -1484,7 +1557,7 @@ int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, com = SVAL(inbuf,smb_com); - times.modtime = srv_make_unix_date3(inbuf + smb_vwv1); + ts[1] = convert_time_t_to_timespec(srv_make_unix_date3(inbuf + smb_vwv1)); /* mtime. */ srvstr_get_path(inbuf, fname, smb_buf(inbuf) + 1, sizeof(fname), 0, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { @@ -1492,7 +1565,14 @@ int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, return ERROR_NT(status); } - RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + status = resolve_dfspath(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, fname); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBcreate); + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } status = unix_convert(conn, fname, False, NULL, &sbuf); if (!NT_STATUS_IS_OK(status)) { @@ -1500,6 +1580,12 @@ int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, return ERROR_NT(status); } + status = check_name(conn, fname); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBcreate); + return ERROR_NT(status); + } + if (fattr & aVOLID) { DEBUG(0,("Attempt to create file (%s) with volid set - please report this\n",fname)); } @@ -1531,8 +1617,8 @@ int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, return ERROR_NT(status); } - times.actime = sbuf.st_atime; - file_utime(conn, fname, ×); + ts[0] = get_atimespec(&sbuf); /* atime. */ + file_ntimes(conn, fname, ts); outsize = set_message(outbuf,1,0,True); SSVAL(outbuf,smb_vwv0,fsp->fnum); @@ -1582,13 +1668,26 @@ int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, pstrcat(fname,"TMXXXXXX"); } - RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + status = resolve_dfspath(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, fname); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBctemp); + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } status = unix_convert(conn, fname, False, NULL, &sbuf); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBctemp); return ERROR_NT(status); } + + status = check_name(conn, fname); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBctemp); + return ERROR_NT(status); + } tmpfd = smb_mkstemp(fname); if (tmpfd == -1) { @@ -1858,6 +1957,12 @@ NTSTATUS unlink_internals(connection_struct *conn, uint32 dirtype, if (dirtype == 0) { dirtype = FILE_ATTRIBUTE_NORMAL; } + + status = check_name(conn, directory); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + status = can_delete(conn,directory,dirtype,can_defer); if (!NT_STATUS_IS_OK(status)) { return status; @@ -1920,6 +2025,13 @@ NTSTATUS unlink_internals(connection_struct *conn, uint32 dirtype, } slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname); + + status = check_name(conn, fname); + if (!NT_STATUS_IS_OK(status)) { + CloseDir(dir_hnd); + return status; + } + status = can_delete(conn, fname, dirtype, can_defer); if (!NT_STATUS_IS_OK(status)) { continue; @@ -1966,8 +2078,15 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size END_PROFILE(SMBunlink); return ERROR_NT(status); } - - RESOLVE_DFSPATH_WCARD(name, conn, inbuf, outbuf); + + status = resolve_dfspath_wcard(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, name, &path_contains_wcard); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBunlink); + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } DEBUG(3,("reply_unlink : %s\n",name)); @@ -3109,7 +3228,6 @@ int reply_close(connection_struct *conn, char *inbuf,char *outbuf, int size, { NTSTATUS status = NT_STATUS_OK; int outsize = 0; - time_t mtime; files_struct *fsp = NULL; START_PROFILE(SMBclose); @@ -3151,8 +3269,8 @@ int reply_close(connection_struct *conn, char *inbuf,char *outbuf, int size, * Take care of any time sent in the close. */ - mtime = srv_make_unix_date3(inbuf+smb_vwv1); - fsp_set_pending_modtime(fsp, mtime); + fsp_set_pending_modtime(fsp, + convert_time_t_to_timespec(srv_make_unix_date3(inbuf+smb_vwv1))); /* * close_file() returns the unix errno if an error @@ -3185,7 +3303,7 @@ int reply_writeclose(connection_struct *conn, NTSTATUS close_status = NT_STATUS_OK; SMB_OFF_T startpos; char *data; - time_t mtime; + struct timespec mtime; files_struct *fsp = file_fsp(inbuf,smb_vwv0); START_PROFILE(SMBwriteclose); @@ -3196,7 +3314,7 @@ int reply_writeclose(connection_struct *conn, numtowrite = SVAL(inbuf,smb_vwv1); startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv2); - mtime = srv_make_unix_date3(inbuf+smb_vwv4); + mtime = convert_time_t_to_timespec(srv_make_unix_date3(inbuf+smb_vwv4)); data = smb_buf(inbuf) + 1; if (numtowrite && is_locked(fsp,(uint32)SVAL(inbuf,smb_pid),(SMB_BIG_UINT)numtowrite,(SMB_BIG_UINT)startpos, WRITE_LOCK)) { @@ -3206,7 +3324,7 @@ int reply_writeclose(connection_struct *conn, nwritten = write_file(fsp,data,startpos,numtowrite); - set_filetime(conn, fsp->fsp_name,mtime); + set_filetime(conn, fsp->fsp_name, mtime); /* * More insanity. W2K only closes the file if writelen > 0. @@ -3598,7 +3716,14 @@ int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, return ERROR_NT(status); } - RESOLVE_DFSPATH(directory, conn, inbuf, outbuf); + status = resolve_dfspath(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, directory); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBmkdir); + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } status = unix_convert(conn, directory, False, NULL, &sbuf); if (!NT_STATUS_IS_OK(status)) { @@ -3606,6 +3731,12 @@ int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, return ERROR_NT(status); } + status = check_name(conn, directory); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBmkdir); + return ERROR_NT(status); + } + status = create_directory(conn, directory); DEBUG(5, ("create_directory returned %s\n", nt_errstr(status))); @@ -3810,7 +3941,14 @@ int reply_rmdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, return ERROR_NT(status); } - RESOLVE_DFSPATH(directory, conn, inbuf, outbuf) + status = resolve_dfspath(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, directory); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBrmdir); + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } status = unix_convert(conn, directory, False, NULL, &sbuf); if (!NT_STATUS_IS_OK(status)) { @@ -3854,7 +3992,6 @@ static BOOL resolve_wildcards(const char *name1, char *name2) char *p,*p2, *pname1, *pname2; int available_space, actual_space; - pname1 = strrchr_m(name1,'/'); pname2 = strrchr_m(name2,'/'); @@ -4008,6 +4145,11 @@ NTSTATUS rename_internals_fsp(connection_struct *conn, files_struct *fsp, pstrin return status; } + status = check_name(conn, newname); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + /* Ensure newname contains a '/' */ if(strrchr_m(newname,'/') == 0) { pstring tmpstr; @@ -4152,9 +4294,13 @@ static void notify_rename(connection_struct *conn, BOOL is_dir, code. ****************************************************************************/ -NTSTATUS rename_internals(connection_struct *conn, pstring name, - pstring newname, uint32 attrs, - BOOL replace_if_exists, BOOL has_wild) +NTSTATUS rename_internals(connection_struct *conn, + pstring name, + pstring newname, + uint32 attrs, + BOOL replace_if_exists, + BOOL src_has_wild, + BOOL dest_has_wild) { pstring directory; pstring mask; @@ -4165,20 +4311,22 @@ NTSTATUS rename_internals(connection_struct *conn, pstring name, NTSTATUS status = NT_STATUS_OK; SMB_STRUCT_STAT sbuf1, sbuf2; struct share_mode_lock *lck = NULL; + struct smb_Dir *dir_hnd = NULL; + const char *dname; + long offset = 0; + pstring destname; *directory = *mask = 0; ZERO_STRUCT(sbuf1); ZERO_STRUCT(sbuf2); - status = unix_convert(conn, name, has_wild, last_component_src, - &sbuf1); + status = unix_convert(conn, name, src_has_wild, last_component_src, &sbuf1); if (!NT_STATUS_IS_OK(status)) { return status; } - status = unix_convert(conn, newname, True, last_component_dest, - &sbuf2); + status = unix_convert(conn, newname, dest_has_wild, last_component_dest, &sbuf2); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -4212,10 +4360,11 @@ NTSTATUS rename_internals(connection_struct *conn, pstring name, * Tine Smukavec <valentin.smukavec@hermes.si>. */ - if (!VALID_STAT(sbuf1) && mangle_is_mangled(mask, conn->params)) + if (!VALID_STAT(sbuf1) && mangle_is_mangled(mask, conn->params)) { mangle_check_cache( mask, sizeof(pstring)-1, conn->params ); + } - if (!has_wild) { + if (!src_has_wild) { /* * No wildcards - just process the one file. */ @@ -4242,6 +4391,21 @@ NTSTATUS rename_internals(connection_struct *conn, pstring name, conn->short_case_preserve, directory, newname, last_component_dest, is_short_name)); + /* Ensure the source name is valid for us to access. */ + status = check_name(conn, directory); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* The dest name still may have wildcards. */ + if (dest_has_wild) { + if (!resolve_wildcards(directory,newname)) { + DEBUG(6, ("rename_internals: resolve_wildcards %s %s failed\n", + directory,newname)); + return NT_STATUS_NO_MEMORY; + } + } + /* * Check for special case with case preserving and not * case sensitive, if directory and newname are identical, @@ -4276,8 +4440,12 @@ NTSTATUS rename_internals(connection_struct *conn, pstring name, } } - resolve_wildcards(directory,newname); - + /* Ensure the dest name is valid for us to access. */ + status = check_name(conn, newname); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + /* * The source object must exist. */ @@ -4369,136 +4537,130 @@ NTSTATUS rename_internals(connection_struct *conn, pstring name, nt_errstr(status), directory,newname)); return status; - } else { - /* - * Wildcards - process each file that matches. - */ - struct smb_Dir *dir_hnd = NULL; - const char *dname; - long offset = 0; - pstring destname; - - if (strequal(mask,"????????.???")) - pstrcpy(mask,"*"); + } + + /* + * Wildcards - process each file that matches. + */ + if (strequal(mask,"????????.???")) { + pstrcpy(mask,"*"); + } - status = check_name(conn, directory); - if (!NT_STATUS_IS_OK(status)) { - return status; - } + status = check_name(conn, directory); + if (!NT_STATUS_IS_OK(status)) { + return status; + } - dir_hnd = OpenDir(conn, directory, mask, attrs); - if (dir_hnd == NULL) { - return map_nt_error_from_unix(errno); - } + dir_hnd = OpenDir(conn, directory, mask, attrs); + if (dir_hnd == NULL) { + return map_nt_error_from_unix(errno); + } - status = NT_STATUS_NO_SUCH_FILE; - /* - * Was status = NT_STATUS_OBJECT_NAME_NOT_FOUND; - * - gentest fix. JRA - */ + status = NT_STATUS_NO_SUCH_FILE; + /* + * Was status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + * - gentest fix. JRA + */ - while ((dname = ReadDirName(dir_hnd, &offset))) { - pstring fname; - BOOL sysdir_entry = False; + while ((dname = ReadDirName(dir_hnd, &offset))) { + pstring fname; + BOOL sysdir_entry = False; - pstrcpy(fname,dname); + pstrcpy(fname,dname); - /* Quick check for "." and ".." */ - if (fname[0] == '.') { - if (!fname[1] - || (fname[1] == '.' && !fname[2])) { - if (attrs & aDIR) { - sysdir_entry = True; - } else { - continue; - } + /* Quick check for "." and ".." */ + if (fname[0] == '.') { + if (!fname[1] || (fname[1] == '.' && !fname[2])) { + if (attrs & aDIR) { + sysdir_entry = True; + } else { + continue; } } + } - if (!is_visible_file(conn, directory, dname, &sbuf1, - False)) { - continue; - } + if (!is_visible_file(conn, directory, dname, &sbuf1, False)) { + continue; + } - if(!mask_match(fname, mask, conn->case_sensitive)) { - continue; - } + if(!mask_match(fname, mask, conn->case_sensitive)) { + continue; + } - if (sysdir_entry) { - status = NT_STATUS_OBJECT_NAME_INVALID; - break; - } + if (sysdir_entry) { + status = NT_STATUS_OBJECT_NAME_INVALID; + break; + } - status = NT_STATUS_ACCESS_DENIED; - slprintf(fname, sizeof(fname)-1, "%s/%s", directory, - dname); - if (!vfs_object_exist(conn, fname, &sbuf1)) { - status = NT_STATUS_OBJECT_NAME_NOT_FOUND; - DEBUG(6, ("rename %s failed. Error %s\n", - fname, nt_errstr(status))); - continue; - } - status = can_rename(conn,fname,attrs,&sbuf1); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(6, ("rename %s refused\n", fname)); - continue; - } - pstrcpy(destname,newname); + status = NT_STATUS_ACCESS_DENIED; + slprintf(fname, sizeof(fname)-1, "%s/%s", directory, dname); + + /* Ensure the source name is valid for us to access. */ + status = check_name(conn, fname); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (!vfs_object_exist(conn, fname, &sbuf1)) { + status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + DEBUG(6, ("rename %s failed. Error %s\n", + fname, nt_errstr(status))); + continue; + } + status = can_rename(conn,fname,attrs,&sbuf1); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(6, ("rename %s refused\n", fname)); + continue; + } + pstrcpy(destname,newname); - if (!resolve_wildcards(fname,destname)) { - DEBUG(6, ("resolve_wildcards %s %s failed\n", - fname, destname)); - continue; - } + if (!resolve_wildcards(fname,destname)) { + DEBUG(6, ("resolve_wildcards %s %s failed\n", + fname, destname)); + continue; + } - if (strcsequal(fname,destname)) { - rename_open_files(conn, NULL, sbuf1.st_dev, - sbuf1.st_ino, newname); - DEBUG(3,("rename_internals: identical names " - "in wildcard rename %s - success\n", - fname)); - count++; - status = NT_STATUS_OK; - continue; - } + /* Ensure the dest name is valid for us to access. */ + status = check_name(conn, destname); + if (!NT_STATUS_IS_OK(status)) { + return status; + } - if (!replace_if_exists - && vfs_file_exist(conn,destname, NULL)) { - DEBUG(6,("file_exist %s\n", destname)); - status = NT_STATUS_OBJECT_NAME_COLLISION; - continue; - } + if (strcsequal(fname,destname)) { + rename_open_files(conn, NULL, sbuf1.st_dev, + sbuf1.st_ino, newname); + DEBUG(3,("rename_internals: identical names " + "in wildcard rename %s - success\n", + fname)); + count++; + status = NT_STATUS_OK; + continue; + } + + if (!replace_if_exists && vfs_file_exist(conn,destname, NULL)) { + DEBUG(6,("file_exist %s\n", destname)); + status = NT_STATUS_OBJECT_NAME_COLLISION; + continue; + } - if (rename_path_prefix_equal(fname, destname)) { - return NT_STATUS_SHARING_VIOLATION; - } + if (rename_path_prefix_equal(fname, destname)) { + return NT_STATUS_SHARING_VIOLATION; + } - lck = get_share_mode_lock(NULL, sbuf1.st_dev, - sbuf1.st_ino, NULL, NULL); + lck = get_share_mode_lock(NULL, sbuf1.st_dev, + sbuf1.st_ino, NULL, NULL); - if (!SMB_VFS_RENAME(conn,fname,destname)) { - rename_open_files(conn, lck, sbuf1.st_dev, - sbuf1.st_ino, newname); - count++; - status = NT_STATUS_OK; - } - TALLOC_FREE(lck); - DEBUG(3,("rename_internals: doing rename on %s -> " - "%s\n",fname,destname)); + if (!SMB_VFS_RENAME(conn,fname,destname)) { + rename_open_files(conn, lck, sbuf1.st_dev, + sbuf1.st_ino, newname); + count++; + status = NT_STATUS_OK; } - CloseDir(dir_hnd); + TALLOC_FREE(lck); + DEBUG(3,("rename_internals: doing rename on %s -> " + "%s\n",fname,destname)); } - -#if 0 - /* Don't think needed any more - JRA. */ - 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; - } - } -#endif + CloseDir(dir_hnd); if (count == 0 && NT_STATUS_IS_OK(status)) { status = map_nt_error_from_unix(errno); @@ -4520,30 +4682,45 @@ int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, char *p; uint32 attrs = SVAL(inbuf,smb_vwv0); NTSTATUS status; - BOOL path1_contains_wcard = False; - BOOL path2_contains_wcard = False; + BOOL src_has_wcard = False; + BOOL dest_has_wcard = False; START_PROFILE(SMBmv); p = smb_buf(inbuf) + 1; - p += srvstr_get_path_wcard(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status, &path1_contains_wcard); + p += srvstr_get_path_wcard(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status, &src_has_wcard); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBmv); return ERROR_NT(status); } p++; - p += srvstr_get_path_wcard(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status, &path2_contains_wcard); + p += srvstr_get_path_wcard(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status, &dest_has_wcard); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBmv); return ERROR_NT(status); } - RESOLVE_DFSPATH_WCARD(name, conn, inbuf, outbuf); - RESOLVE_DFSPATH_WCARD(newname, conn, inbuf, outbuf); + status = resolve_dfspath_wcard(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, name, &src_has_wcard); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBmv); + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } + + status = resolve_dfspath_wcard(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, newname, &dest_has_wcard); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBmv); + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } DEBUG(3,("reply_mv : %s -> %s\n",name,newname)); - status = rename_internals(conn, name, newname, attrs, False, path1_contains_wcard); + status = rename_internals(conn, name, newname, attrs, False, src_has_wcard, dest_has_wcard); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBmv); if (open_was_deferred(SVAL(inbuf,smb_mid))) { @@ -4567,8 +4744,12 @@ int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, * TODO: check error codes on all callers */ -NTSTATUS copy_file(char *src, char *dest1,connection_struct *conn, int ofun, - int count, BOOL target_is_directory) +NTSTATUS copy_file(connection_struct *conn, + char *src, + char *dest1, + int ofun, + int count, + BOOL target_is_directory) { SMB_STRUCT_STAT src_sbuf, sbuf2; SMB_OFF_T ret=-1; @@ -4653,7 +4834,7 @@ NTSTATUS copy_file(char *src, char *dest1,connection_struct *conn, int ofun, close_file(fsp1,NORMAL_CLOSE); /* Ensure the modtime is set correctly on the destination file. */ - fsp_set_pending_modtime( fsp2, src_sbuf.st_mtime); + fsp_set_pending_modtime( fsp2, get_mtimespec(&src_sbuf)); /* * As we are opening fsp1 read-only we only expect @@ -4688,14 +4869,12 @@ int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, int count=0; int error = ERRnoaccess; int err = 0; - BOOL has_wild; - BOOL exists=False; int tid2 = SVAL(inbuf,smb_vwv0); int ofun = SVAL(inbuf,smb_vwv1); int flags = SVAL(inbuf,smb_vwv2); BOOL target_is_directory=False; - BOOL path_contains_wcard1 = False; - BOOL path_contains_wcard2 = False; + BOOL source_has_wild = False; + BOOL dest_has_wild = False; SMB_STRUCT_STAT sbuf1, sbuf2; NTSTATUS status; START_PROFILE(SMBcopy); @@ -4703,12 +4882,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_wcard(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status, &path_contains_wcard1); + p += srvstr_get_path_wcard(inbuf, name, p, sizeof(name), 0, STR_TERMINATE, &status, &source_has_wild); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBcopy); return ERROR_NT(status); } - p += srvstr_get_path_wcard(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status, &path_contains_wcard2); + p += srvstr_get_path_wcard(inbuf, newname, p, sizeof(newname), 0, STR_TERMINATE, &status, &dest_has_wild); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBcopy); return ERROR_NT(status); @@ -4723,16 +4902,31 @@ int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, return ERROR_DOS(ERRSRV,ERRinvdevice); } - RESOLVE_DFSPATH_WCARD(name, conn, inbuf, outbuf); - RESOLVE_DFSPATH_WCARD(newname, conn, inbuf, outbuf); + status = resolve_dfspath_wcard(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, name, &source_has_wild); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBcopy); + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } - status = unix_convert(conn, name, path_contains_wcard1, NULL, &sbuf1); + status = resolve_dfspath_wcard(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, newname, &dest_has_wild); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBcopy); + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } return ERROR_NT(status); } - status = unix_convert(conn, newname, path_contains_wcard2, NULL, &sbuf2); + status = unix_convert(conn, name, source_has_wild, NULL, &sbuf1); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBcopy); + return ERROR_NT(status); + } + + status = unix_convert(conn, newname, dest_has_wild, NULL, &sbuf2); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBcopy); return ERROR_NT(status); @@ -4776,25 +4970,38 @@ int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, * Tine Smukavec <valentin.smukavec@hermes.si>. */ - if (!VALID_STAT(sbuf1) && mangle_is_mangled(mask, conn->params)) + if (!VALID_STAT(sbuf1) && mangle_is_mangled(mask, conn->params)) { mangle_check_cache( mask, sizeof(pstring)-1, conn->params ); + } - has_wild = path_contains_wcard1; - - if (!has_wild) { + if (!source_has_wild) { pstrcat(directory,"/"); pstrcat(directory,mask); - if (resolve_wildcards(directory,newname) - && NT_STATUS_IS_OK(status = copy_file( - directory,newname,conn,ofun, - count,target_is_directory))) - count++; - if(!count && !NT_STATUS_IS_OK(status)) { - END_PROFILE(SMBcopy); + if (dest_has_wild) { + if (!resolve_wildcards(directory,newname)) { + END_PROFILE(SMBcopy); + return ERROR_NT(NT_STATUS_NO_MEMORY); + } + } + + status = check_name(conn, directory); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); + } + + status = check_name(conn, newname); + if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } - if (!count) { - exists = vfs_file_exist(conn,directory,NULL); + + status = copy_file(conn,directory,newname,ofun, + count,target_is_directory); + + if(!NT_STATUS_IS_OK(status)) { + END_PROFILE(SMBcopy); + return ERROR_NT(status); + } else { + count++; } } else { struct smb_Dir *dir_hnd = NULL; @@ -4833,13 +5040,27 @@ int reply_copy(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, error = ERRnoaccess; slprintf(fname,sizeof(fname)-1, "%s/%s",directory,dname); pstrcpy(destname,newname); - if (resolve_wildcards(fname,destname) && - NT_STATUS_IS_OK(status = copy_file( - fname,destname,conn,ofun, - count,target_is_directory))) { + if (!resolve_wildcards(fname,destname)) { + continue; + } + + status = check_name(conn, fname); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); + } + + status = check_name(conn, destname); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); + } + + DEBUG(3,("reply_copy : doing copy on %s -> %s\n",fname, destname)); + + status = copy_file(conn,fname,destname,ofun, + count,target_is_directory); + if (NT_STATUS_IS_OK(status)) { count++; } - DEBUG(3,("reply_copy : doing copy on %s -> %s\n",fname,destname)); } CloseDir(dir_hnd); } @@ -4888,7 +5109,14 @@ int reply_setdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size return ERROR_NT(status); } - RESOLVE_DFSPATH(newdir, conn, inbuf, outbuf); + status = resolve_dfspath(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, newdir); + if (!NT_STATUS_IS_OK(status)) { + END_PROFILE(pathworks_setdir); + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } if (strlen(newdir) != 0) { if (!vfs_directory_exist(conn,newdir,NULL)) { @@ -5440,7 +5668,7 @@ int reply_readbmpx(connection_struct *conn, char *inbuf,char *outbuf,int length, int reply_setattrE(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) { - struct utimbuf unix_times; + struct timespec ts[2]; int outsize = 0; files_struct *fsp = file_fsp(inbuf,smb_vwv0); START_PROFILE(SMBsetattrE); @@ -5457,15 +5685,15 @@ int reply_setattrE(connection_struct *conn, char *inbuf,char *outbuf, int size, * time as UNIX can't set this. */ - unix_times.actime = srv_make_unix_date2(inbuf+smb_vwv3); - unix_times.modtime = srv_make_unix_date2(inbuf+smb_vwv5); + ts[0] = convert_time_t_to_timespec(srv_make_unix_date2(inbuf+smb_vwv3)); /* atime. */ + ts[1] = convert_time_t_to_timespec(srv_make_unix_date2(inbuf+smb_vwv5)); /* mtime. */ /* * Patch from Ray Frush <frush@engr.colostate.edu> * Sometimes times are sent as zero - ignore them. */ - if (null_mtime(unix_times.actime) && null_mtime(unix_times.modtime)) { + if (null_timespec(ts[0]) && null_timespec(ts[1])) { /* Ignore request */ if( DEBUGLVL( 3 ) ) { dbgtext( "reply_setattrE fnum=%d ", fsp->fnum); @@ -5473,20 +5701,22 @@ int reply_setattrE(connection_struct *conn, char *inbuf,char *outbuf, int size, } END_PROFILE(SMBsetattrE); return(outsize); - } else if (!null_mtime(unix_times.actime) && null_mtime(unix_times.modtime)) { + } else if (!null_timespec(ts[0]) && null_timespec(ts[1])) { /* set modify time = to access time if modify time was unset */ - unix_times.modtime = unix_times.actime; + ts[1] = ts[0]; } /* Set the date on this file */ /* Should we set pending modtime here ? JRA */ - if(file_utime(conn, fsp->fsp_name, &unix_times)) { + if(file_ntimes(conn, fsp->fsp_name, ts)) { END_PROFILE(SMBsetattrE); return ERROR_DOS(ERRDOS,ERRnoaccess); } - DEBUG( 3, ( "reply_setattrE fnum=%d actime=%d modtime=%d\n", - fsp->fnum, (int)unix_times.actime, (int)unix_times.modtime ) ); + DEBUG( 3, ( "reply_setattrE fnum=%d actime=%u modtime=%u\n", + fsp->fnum, + (unsigned int)ts[0].tv_sec, + (unsigned int)ts[1].tv_sec)); END_PROFILE(SMBsetattrE); return(outsize); diff --git a/source/smbd/sesssetup.c b/source/smbd/sesssetup.c index 7a5f8be47ff..ff1b2821cca 100644 --- a/source/smbd/sesssetup.c +++ b/source/smbd/sesssetup.c @@ -158,13 +158,76 @@ static NTSTATUS check_guest_password(auth_serversupplied_info **server_info) #ifdef HAVE_KRB5 + +#if 0 +/* Experiment that failed. See "only happens with a KDC" comment below. */ +/**************************************************************************** + Cerate a clock skew error blob for a Windows client. +****************************************************************************/ + +static BOOL make_krb5_skew_error(DATA_BLOB *pblob_out) +{ + krb5_context context = NULL; + krb5_error_code kerr = 0; + krb5_data reply; + krb5_principal host_princ = NULL; + char *host_princ_s = NULL; + BOOL ret = False; + + *pblob_out = data_blob(NULL,0); + + initialize_krb5_error_table(); + kerr = krb5_init_context(&context); + if (kerr) { + return False; + } + /* Create server principal. */ + asprintf(&host_princ_s, "%s$@%s", global_myname(), lp_realm()); + if (!host_princ_s) { + goto out; + } + strlower_m(host_princ_s); + + kerr = smb_krb5_parse_name(context, host_princ_s, &host_princ); + if (kerr) { + DEBUG(10,("make_krb5_skew_error: smb_krb5_parse_name failed for name %s: Error %s\n", + host_princ_s, error_message(kerr) )); + goto out; + } + + kerr = smb_krb5_mk_error(context, KRB5KRB_AP_ERR_SKEW, host_princ, &reply); + if (kerr) { + DEBUG(10,("make_krb5_skew_error: smb_krb5_mk_error failed: Error %s\n", + error_message(kerr) )); + goto out; + } + + *pblob_out = data_blob(reply.data, reply.length); + kerberos_free_data_contents(context,&reply); + ret = True; + + out: + + if (host_princ_s) { + SAFE_FREE(host_princ_s); + } + if (host_princ) { + krb5_free_principal(context, host_princ); + } + krb5_free_context(context); + return ret; +} +#endif + /**************************************************************************** -reply to a session setup spnego negotiate packet for kerberos + Reply to a session setup spnego negotiate packet for kerberos. ****************************************************************************/ + static int reply_spnego_kerberos(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize, - DATA_BLOB *secblob) + DATA_BLOB *secblob, + BOOL *p_invalidate_vuid) { TALLOC_CTX *mem_ctx; DATA_BLOB ticket; @@ -191,9 +254,13 @@ static int reply_spnego_kerberos(connection_struct *conn, ZERO_STRUCT(ap_rep_wrapped); ZERO_STRUCT(response); + /* Normally we will always invalidate the intermediate vuid. */ + *p_invalidate_vuid = True; + mem_ctx = talloc_init("reply_spnego_kerberos"); - if (mem_ctx == NULL) + if (mem_ctx == NULL) { return ERROR_NT(nt_status_squash(NT_STATUS_NO_MEMORY)); + } if (!spnego_parse_krb5_wrap(*secblob, &ticket, tok_id)) { talloc_destroy(mem_ctx); @@ -205,9 +272,50 @@ static int reply_spnego_kerberos(connection_struct *conn, data_blob_free(&ticket); if (!NT_STATUS_IS_OK(ret)) { - DEBUG(1,("Failed to verify incoming ticket!\n")); +#if 0 + /* Experiment that failed. See "only happens with a KDC" comment below. */ + + if (NT_STATUS_EQUAL(ret, NT_STATUS_TIME_DIFFERENCE_AT_DC)) { + + /* + * Windows in this case returns NT_STATUS_MORE_PROCESSING_REQUIRED + * with a negTokenTarg blob containing an krb5_error struct ASN1 encoded + * containing KRB5KRB_AP_ERR_SKEW. The client then fixes its + * clock and continues rather than giving an error. JRA. + * -- Looks like this only happens with a KDC. JRA. + */ + + BOOL ok = make_krb5_skew_error(&ap_rep); + if (!ok) { + talloc_destroy(mem_ctx); + return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); + } + ap_rep_wrapped = spnego_gen_krb5_wrap(ap_rep, TOK_ID_KRB_ERROR); + response = spnego_gen_auth_response(&ap_rep_wrapped, ret, OID_KERBEROS5_OLD); + reply_sesssetup_blob(conn, outbuf, response, NT_STATUS_MORE_PROCESSING_REQUIRED); + + /* + * In this one case we don't invalidate the intermediate vuid. + * as we're expecting the client to re-use it for the next + * sessionsetupX packet. JRA. + */ + + *p_invalidate_vuid = False; + + data_blob_free(&ap_rep); + data_blob_free(&ap_rep_wrapped); + data_blob_free(&response); + talloc_destroy(mem_ctx); + return -1; /* already replied */ + } +#else + if (!NT_STATUS_EQUAL(ret, NT_STATUS_TIME_DIFFERENCE_AT_DC)) { + ret = NT_STATUS_LOGON_FAILURE; + } +#endif + DEBUG(1,("Failed to verify incoming ticket with error %s!\n", nt_errstr(ret))); talloc_destroy(mem_ctx); - return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); + return ERROR_NT(nt_status_squash(ret)); } DEBUG(3,("Ticket name is [%s]\n", client)); @@ -523,32 +631,19 @@ static BOOL reply_spnego_ntlmssp(connection_struct *conn, char *inbuf, char *out } /**************************************************************************** - Reply to a session setup spnego negotiate packet. + Is this a krb5 mechanism ? ****************************************************************************/ -static int reply_spnego_negotiate(connection_struct *conn, - char *inbuf, - char *outbuf, - uint16 vuid, - int length, int bufsize, - DATA_BLOB blob1, - AUTH_NTLMSSP_STATE **auth_ntlmssp_state) +static NTSTATUS parse_spnego_mechanisms(DATA_BLOB blob_in, DATA_BLOB *pblob_out, BOOL *p_is_krb5) { char *OIDs[ASN1_MAX_OIDS]; - DATA_BLOB secblob; int i; - DATA_BLOB chal; -#ifdef HAVE_KRB5 - BOOL got_kerberos_mechanism = False; -#endif - NTSTATUS nt_status; - /* parse out the OIDs and the first sec blob */ - if (!parse_negTokenTarg(blob1, OIDs, &secblob)) { - /* Kill the intermediate vuid */ - invalidate_vuid(vuid); + *p_is_krb5 = False; - return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); + /* parse out the OIDs and the first sec blob */ + if (!parse_negTokenTarg(blob_in, OIDs, pblob_out)) { + return NT_STATUS_LOGON_FAILURE; } /* only look at the first OID for determining the mechToken -- @@ -564,24 +659,53 @@ static int reply_spnego_negotiate(connection_struct *conn, #ifdef HAVE_KRB5 if (strcmp(OID_KERBEROS5, OIDs[0]) == 0 || strcmp(OID_KERBEROS5_OLD, OIDs[0]) == 0) { - got_kerberos_mechanism = True; + *p_is_krb5 = True; } #endif for (i=0;OIDs[i];i++) { - DEBUG(3,("Got OID %s\n", OIDs[i])); + DEBUG(5,("parse_spnego_mechanisms: Got OID %s\n", OIDs[i])); free(OIDs[i]); } - DEBUG(3,("Got secblob of size %lu\n", (unsigned long)secblob.length)); + return NT_STATUS_OK; +} + +/**************************************************************************** + Reply to a session setup spnego negotiate packet. +****************************************************************************/ + +static int reply_spnego_negotiate(connection_struct *conn, + char *inbuf, + char *outbuf, + uint16 vuid, + int length, int bufsize, + DATA_BLOB blob1, + AUTH_NTLMSSP_STATE **auth_ntlmssp_state) +{ + DATA_BLOB secblob; + DATA_BLOB chal; + BOOL got_kerberos_mechanism = False; + NTSTATUS status; + + status = parse_spnego_mechanisms(blob1, &secblob, &got_kerberos_mechanism); + if (!NT_STATUS_IS_OK(status)) { + /* Kill the intermediate vuid */ + invalidate_vuid(vuid); + return ERROR_NT(nt_status_squash(status)); + } + + DEBUG(3,("reply_spnego_negotiate: Got secblob of size %lu\n", (unsigned long)secblob.length)); #ifdef HAVE_KRB5 if ( got_kerberos_mechanism && ((lp_security()==SEC_ADS) || lp_use_kerberos_keytab()) ) { + BOOL destroy_vuid = True; int ret = reply_spnego_kerberos(conn, inbuf, outbuf, - length, bufsize, &secblob); + length, bufsize, &secblob, &destroy_vuid); data_blob_free(&secblob); - /* Kill the intermediate vuid */ - invalidate_vuid(vuid); - + if (destroy_vuid) { + /* Kill the intermediate vuid */ + invalidate_vuid(vuid); + } return ret; } #endif @@ -590,28 +714,27 @@ static int reply_spnego_negotiate(connection_struct *conn, auth_ntlmssp_end(auth_ntlmssp_state); } - nt_status = auth_ntlmssp_start(auth_ntlmssp_state); - if (!NT_STATUS_IS_OK(nt_status)) { + status = auth_ntlmssp_start(auth_ntlmssp_state); + if (!NT_STATUS_IS_OK(status)) { /* Kill the intermediate vuid */ invalidate_vuid(vuid); - - return ERROR_NT(nt_status_squash(nt_status)); + return ERROR_NT(nt_status_squash(status)); } - nt_status = auth_ntlmssp_update(*auth_ntlmssp_state, + status = auth_ntlmssp_update(*auth_ntlmssp_state, secblob, &chal); data_blob_free(&secblob); reply_spnego_ntlmssp(conn, inbuf, outbuf, vuid, auth_ntlmssp_state, - &chal, nt_status, True); + &chal, status, True); data_blob_free(&chal); /* already replied */ return -1; } - + /**************************************************************************** Reply to a session setup spnego auth packet. ****************************************************************************/ @@ -622,8 +745,10 @@ static int reply_spnego_auth(connection_struct *conn, char *inbuf, char *outbuf, DATA_BLOB blob1, AUTH_NTLMSSP_STATE **auth_ntlmssp_state) { - DATA_BLOB auth, auth_reply; - NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER; + DATA_BLOB auth = data_blob(NULL,0); + DATA_BLOB auth_reply = data_blob(NULL,0); + DATA_BLOB secblob = data_blob(NULL,0); + NTSTATUS status = NT_STATUS_INVALID_PARAMETER; if (!spnego_parse_auth(blob1, &auth)) { #if 0 @@ -634,6 +759,33 @@ static int reply_spnego_auth(connection_struct *conn, char *inbuf, char *outbuf, return ERROR_NT(nt_status_squash(NT_STATUS_INVALID_PARAMETER)); } + + if (auth.data[0] == ASN1_APPLICATION(0)) { + /* Might be a second negTokenTarg packet */ + + BOOL got_krb5_mechanism = False; + status = parse_spnego_mechanisms(auth, &secblob, &got_krb5_mechanism); + if (NT_STATUS_IS_OK(status)) { + DEBUG(3,("reply_spnego_auth: Got secblob of size %lu\n", (unsigned long)secblob.length)); +#ifdef HAVE_KRB5 + if ( got_krb5_mechanism && ((lp_security()==SEC_ADS) || lp_use_kerberos_keytab()) ) { + BOOL destroy_vuid = True; + int ret = reply_spnego_kerberos(conn, inbuf, outbuf, + length, bufsize, &secblob, &destroy_vuid); + data_blob_free(&secblob); + data_blob_free(&auth); + if (destroy_vuid) { + /* Kill the intermediate vuid */ + invalidate_vuid(vuid); + } + return ret; + } +#endif + } + } + + /* If we get here it wasn't a negTokenTarg auth packet. */ + data_blob_free(&secblob); if (!*auth_ntlmssp_state) { /* Kill the intermediate vuid */ @@ -643,14 +795,14 @@ static int reply_spnego_auth(connection_struct *conn, char *inbuf, char *outbuf, return ERROR_NT(nt_status_squash(NT_STATUS_INVALID_PARAMETER)); } - nt_status = auth_ntlmssp_update(*auth_ntlmssp_state, + status = auth_ntlmssp_update(*auth_ntlmssp_state, auth, &auth_reply); data_blob_free(&auth); reply_spnego_ntlmssp(conn, inbuf, outbuf, vuid, auth_ntlmssp_state, - &auth_reply, nt_status, True); + &auth_reply, status, True); data_blob_free(&auth_reply); diff --git a/source/smbd/trans2.c b/source/smbd/trans2.c index 5bbd618231b..deb5db1bafe 100644 --- a/source/smbd/trans2.c +++ b/source/smbd/trans2.c @@ -5,6 +5,7 @@ Copyright (C) Stefan (metze) Metzmacher 2003 Copyright (C) Volker Lendecke 2005 Copyright (C) Steve French 2005 + Copyright (C) James Peach 2007 Extensively modified by Andrew Tridgell, 1995 @@ -34,6 +35,16 @@ extern struct current_user current_user; #define get_file_size(sbuf) ((sbuf).st_size) #define DIR_ENTRY_SAFETY_MARGIN 4096 +static char *store_file_unix_basic(connection_struct *conn, + char *pdata, + files_struct *fsp, + const SMB_STRUCT_STAT *psbuf); + +static char *store_file_unix_basic_info2(connection_struct *conn, + char *pdata, + files_struct *fsp, + const SMB_STRUCT_STAT *psbuf); + /******************************************************************** Roundup a value to the nearest allocation roundup size boundary. Only do this for Windows clients. @@ -56,7 +67,7 @@ SMB_BIG_UINT smb_roundup(connection_struct *conn, SMB_BIG_UINT val) account sparse files. ********************************************************************/ -SMB_BIG_UINT get_allocation_size(connection_struct *conn, files_struct *fsp, SMB_STRUCT_STAT *sbuf) +SMB_BIG_UINT get_allocation_size(connection_struct *conn, files_struct *fsp, const SMB_STRUCT_STAT *sbuf) { SMB_BIG_UINT ret; @@ -1191,15 +1202,17 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn, continue; } } else if (!VALID_STAT(sbuf) && SMB_VFS_STAT(conn,pathreal,&sbuf) != 0) { + pstring link_target; /* Needed to show the msdfs symlinks as * directories */ if(lp_host_msdfs() && lp_msdfs_root(SNUM(conn)) && - ((ms_dfs_link = is_msdfs_link(NULL,conn, pathreal, NULL, NULL, &sbuf)) == True)) { - - DEBUG(5,("get_lanman2_dir_entry: Masquerading msdfs link %s as a directory\n", pathreal)); + ((ms_dfs_link = is_msdfs_link(conn, pathreal, link_target, &sbuf)) == True)) { + DEBUG(5,("get_lanman2_dir_entry: Masquerading msdfs link %s " + "as a directory\n", + pathreal)); sbuf.st_mode = (sbuf.st_mode & 0xFFF) | S_IFDIR; } else { @@ -1578,51 +1591,21 @@ static BOOL get_lanman2_dir_entry(connection_struct *conn, /* CIFS UNIX Extension. */ case SMB_FIND_FILE_UNIX: - DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_FILE_UNIX\n")); + case SMB_FIND_FILE_UNIX_INFO2: p+= 4; SIVAL(p,0,reskey); p+= 4; /* Used for continuing search. */ /* Begin of SMB_QUERY_FILE_UNIX_BASIC */ - SOFF_T(p,0,get_file_size(sbuf)); /* File size 64 Bit */ - p+= 8; - - SOFF_T(p,0,get_allocation_size(conn,NULL,&sbuf)); /* Number of bytes used on disk - 64 Bit */ - p+= 8; - - put_long_date_timespec(p,get_ctimespec(&sbuf)); /* Inode change Time 64 Bit */ - put_long_date_timespec(p+8,get_atimespec(&sbuf)); /* Last access time 64 Bit */ - put_long_date_timespec(p+16,get_mtimespec(&sbuf)); /* Last modification time 64 Bit */ - p+= 24; - - SIVAL(p,0,sbuf.st_uid); /* user id for the owner */ - SIVAL(p,4,0); - p+= 8; - - SIVAL(p,0,sbuf.st_gid); /* group id of owner */ - SIVAL(p,4,0); - p+= 8; - - SIVAL(p,0,unix_filetype(sbuf.st_mode)); - p+= 4; - - SIVAL(p,0,unix_dev_major(sbuf.st_rdev)); /* Major device number if type is device */ - SIVAL(p,4,0); - p+= 8; - - SIVAL(p,0,unix_dev_minor(sbuf.st_rdev)); /* Minor device number if type is device */ - SIVAL(p,4,0); - p+= 8; - - SINO_T_VAL(p,0,(SMB_INO_T)sbuf.st_ino); /* inode number */ - p+= 8; - SIVAL(p,0, unix_perms_to_wire(sbuf.st_mode)); /* Standard UNIX file permissions */ - SIVAL(p,4,0); - p+= 8; - - SIVAL(p,0,sbuf.st_nlink); /* number of hard links */ - SIVAL(p,4,0); - p+= 8; + if (info_level == SMB_FIND_FILE_UNIX) { + DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_FILE_UNIX\n")); + p = store_file_unix_basic(conn, p, + NULL, &sbuf); + } else { + DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_FILE_UNIX_INFO2\n")); + p = store_file_unix_basic_info2(conn, p, + NULL, &sbuf); + } len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE); p += len; @@ -1732,6 +1715,7 @@ close_if_end = %d requires_resume_key = %d level = 0x%x, max_data_bytes = %d\n", case SMB_FIND_ID_BOTH_DIRECTORY_INFO: break; case SMB_FIND_FILE_UNIX: + case SMB_FIND_FILE_UNIX_INFO2: if (!lp_unix_extensions()) { return ERROR_NT(NT_STATUS_INVALID_LEVEL); } @@ -1745,7 +1729,13 @@ close_if_end = %d requires_resume_key = %d level = 0x%x, max_data_bytes = %d\n", return ERROR_NT(ntstatus); } - RESOLVE_DFSPATH_WCARD(directory, conn, inbuf, outbuf); + ntstatus = resolve_dfspath_wcard(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, directory, &mask_contains_wcard); + if (!NT_STATUS_IS_OK(ntstatus)) { + if (NT_STATUS_EQUAL(ntstatus,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(ntstatus); + } ntstatus = unix_convert(conn, directory, True, NULL, &sbuf); if (!NT_STATUS_IS_OK(ntstatus)) { @@ -2037,6 +2027,7 @@ resume_key = %d resume name = %s continue=%d level = %d\n", case SMB_FIND_ID_BOTH_DIRECTORY_INFO: break; case SMB_FIND_FILE_UNIX: + case SMB_FIND_FILE_UNIX_INFO2: if (!lp_unix_extensions()) { return ERROR_NT(NT_STATUS_INVALID_LEVEL); } @@ -2232,7 +2223,7 @@ static int call_trans2qfsinfo(connection_struct *conn, char *inbuf, char *outbuf char **pparams, int total_params, char **ppdata, int total_data, unsigned int max_data_bytes) { - char *pdata = *ppdata; + char *pdata; char *params = *pparams; uint16 info_level; int data_len, len; @@ -2530,6 +2521,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned CIFS_UNIX_POSIX_ACLS_CAP| CIFS_UNIX_POSIX_PATHNAMES_CAP| CIFS_UNIX_FCNTL_LOCKS_CAP| + CIFS_UNIX_EXTATTR_CAP| CIFS_UNIX_POSIX_PATH_OPERATIONS_CAP))); break; @@ -2566,6 +2558,114 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned break; } + case SMB_QUERY_POSIX_WHOAMI: + { + uint32_t flags = 0; + uint32_t sid_bytes; + int i; + + if (!lp_unix_extensions()) { + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } + + if (max_data_bytes < 40) { + return ERROR_NT(NT_STATUS_BUFFER_TOO_SMALL); + } + + /* We ARE guest if global_sid_Builtin_Guests is + * in our list of SIDs. + */ + if (nt_token_check_sid(&global_sid_Builtin_Guests, + current_user.nt_user_token)) { + flags |= SMB_WHOAMI_GUEST; + } + + /* We are NOT guest if global_sid_Authenticated_Users + * is in our list of SIDs. + */ + if (nt_token_check_sid(&global_sid_Authenticated_Users, + current_user.nt_user_token)) { + flags &= ~SMB_WHOAMI_GUEST; + } + + /* NOTE: 8 bytes for UID/GID, irrespective of native + * platform size. This matches + * SMB_QUERY_FILE_UNIX_BASIC and friends. + */ + data_len = 4 /* flags */ + + 4 /* flag mask */ + + 8 /* uid */ + + 8 /* gid */ + + 4 /* ngroups */ + + 4 /* num_sids */ + + 4 /* SID bytes */ + + 4 /* pad/reserved */ + + (current_user.ut.ngroups * 8) + /* groups list */ + + (current_user.nt_user_token->num_sids * + SID_MAX_SIZE) + /* SID list */; + + SIVAL(pdata, 0, flags); + SIVAL(pdata, 4, SMB_WHOAMI_MASK); + SBIG_UINT(pdata, 8, (SMB_BIG_UINT)current_user.ut.uid); + SBIG_UINT(pdata, 16, (SMB_BIG_UINT)current_user.ut.gid); + + + if (data_len >= max_data_bytes) { + /* Potential overflow, skip the GIDs and SIDs. */ + + SIVAL(pdata, 24, 0); /* num_groups */ + SIVAL(pdata, 28, 0); /* num_sids */ + SIVAL(pdata, 32, 0); /* num_sid_bytes */ + SIVAL(pdata, 36, 0); /* reserved */ + + data_len = 40; + break; + } + + SIVAL(pdata, 24, current_user.ut.ngroups); + SIVAL(pdata, 28, + current_user.nt_user_token->num_sids); + + /* We walk the SID list twice, but this call is fairly + * infrequent, and I don't expect that it's performance + * sensitive -- jpeach + */ + for (i = 0, sid_bytes = 0; + i < current_user.nt_user_token->num_sids; ++i) { + sid_bytes += + sid_size(¤t_user.nt_user_token->user_sids[i]); + } + + /* SID list byte count */ + SIVAL(pdata, 32, sid_bytes); + + /* 4 bytes pad/reserved - must be zero */ + SIVAL(pdata, 36, 0); + data_len = 40; + + /* GID list */ + for (i = 0; i < current_user.ut.ngroups; ++i) { + SBIG_UINT(pdata, data_len, + (SMB_BIG_UINT)current_user.ut.groups[i]); + data_len += 8; + } + + /* SID list */ + for (i = 0; + i < current_user.nt_user_token->num_sids; ++i) { + int sid_len = + sid_size(¤t_user.nt_user_token->user_sids[i]); + + sid_linearize(pdata + data_len, sid_len, + ¤t_user.nt_user_token->user_sids[i]); + data_len += sid_len; + } + + break; + } + case SMB_MAC_QUERY_FS_INFO: /* * Thursby MAC extension... ONLY on NTFS filesystems @@ -2871,7 +2971,7 @@ static BOOL marshall_posix_acl(connection_struct *conn, char *pdata, SMB_STRUCT_ static char *store_file_unix_basic(connection_struct *conn, char *pdata, files_struct *fsp, - SMB_STRUCT_STAT *psbuf) + const SMB_STRUCT_STAT *psbuf) { DEBUG(10,("store_file_unix_basic: SMB_QUERY_FILE_UNIX_BASIC\n")); DEBUG(4,("store_file_unix_basic: st_mode=%o\n",(int)psbuf->st_mode)); @@ -2920,6 +3020,121 @@ static char *store_file_unix_basic(connection_struct *conn, return pdata; } +/* Forward and reverse mappings from the UNIX_INFO2 file flags field and + * the chflags(2) (or equivalent) flags. + * + * XXX: this really should be behind the VFS interface. To do this, we would + * need to alter SMB_STRUCT_STAT so that it included a flags and a mask field. + * Each VFS module could then implement it's own mapping as appropriate for the + * platform. We would then pass the SMB flags into SMB_VFS_CHFLAGS. + */ +static const struct {unsigned stat_fflag; unsigned smb_fflag;} + info2_flags_map[] = +{ +#ifdef UF_NODUMP + { UF_NODUMP, EXT_DO_NOT_BACKUP }, +#endif + +#ifdef UF_IMMUTABLE + { UF_IMMUTABLE, EXT_IMMUTABLE }, +#endif + +#ifdef UF_APPEND + { UF_APPEND, EXT_OPEN_APPEND_ONLY }, +#endif + +#ifdef UF_HIDDEN + { UF_HIDDEN, EXT_HIDDEN }, +#endif + + /* Do not remove. We need to guarantee that this array has at least one + * entry to build on HP-UX. + */ + { 0, 0 } + +}; + +static void map_info2_flags_from_sbuf(const SMB_STRUCT_STAT *psbuf, + uint32 *smb_fflags, uint32 *smb_fmask) +{ +#ifdef HAVE_STAT_ST_FLAGS + int i; + + for (i = 0; i < ARRAY_SIZE(info2_flags_map); ++i) { + *smb_fmask |= info2_flags_map[i].smb_fflag; + if (psbuf->st_flags & info2_flags_map[i].stat_fflag) { + *smb_fflags |= info2_flags_map[i].smb_fflag; + } + } +#endif /* HAVE_STAT_ST_FLAGS */ +} + +static BOOL map_info2_flags_to_sbuf(const SMB_STRUCT_STAT *psbuf, + const uint32 smb_fflags, + const uint32 smb_fmask, + int *stat_fflags) +{ +#ifdef HAVE_STAT_ST_FLAGS + uint32 max_fmask = 0; + int i; + + *stat_fflags = psbuf->st_flags; + + /* For each flags requested in smb_fmask, check the state of the + * corresponding flag in smb_fflags and set or clear the matching + * stat flag. + */ + + for (i = 0; i < ARRAY_SIZE(info2_flags_map); ++i) { + max_fmask |= info2_flags_map[i].smb_fflag; + if (smb_fmask & info2_flags_map[i].smb_fflag) { + if (smb_fflags & info2_flags_map[i].smb_fflag) { + *stat_fflags |= info2_flags_map[i].stat_fflag; + } else { + *stat_fflags &= ~info2_flags_map[i].stat_fflag; + } + } + } + + /* If smb_fmask is asking to set any bits that are not supported by + * our flag mappings, we should fail. + */ + if ((smb_fmask & max_fmask) != smb_fmask) { + return False; + } + + return True; +#else + return False; +#endif /* HAVE_STAT_ST_FLAGS */ +} + + +/* Just like SMB_QUERY_FILE_UNIX_BASIC, but with the addition + * of file flags and birth (create) time. + */ +static char *store_file_unix_basic_info2(connection_struct *conn, + char *pdata, + files_struct *fsp, + const SMB_STRUCT_STAT *psbuf) +{ + uint32 file_flags = 0; + uint32 flags_mask = 0; + + pdata = store_file_unix_basic(conn, pdata, fsp, psbuf); + + /* Create (birth) time 64 bit */ + put_long_date_timespec(pdata, get_create_timespec(psbuf, False)); + pdata += 8; + + map_info2_flags_from_sbuf(psbuf, &file_flags, &flags_mask); + SIVAL(pdata, 0, file_flags); /* flags */ + SIVAL(pdata, 4, flags_mask); /* mask */ + pdata += 8; + + return pdata; +} + /**************************************************************************** Reply to a TRANS2_QFILEPATHINFO or TRANSACT2_QFILEINFO (query file info by file name or file id). @@ -2970,6 +3185,10 @@ static int call_trans2qfilepathinfo(connection_struct *conn, char *inbuf, char * DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QFILEINFO: level = %d\n", info_level)); + if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions()) { + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } + if(fsp && (fsp->fake_file_handle)) { /* * This is actually for the QUOTA_FAKE_FILE --metze @@ -3026,12 +3245,22 @@ static int call_trans2qfilepathinfo(connection_struct *conn, char *inbuf, char * DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QPATHINFO: level = %d\n", info_level)); + if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions()) { + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } + srvstr_get_path(inbuf, fname, ¶ms[6], sizeof(fname), total_params - 6, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } - RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + status = resolve_dfspath(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, fname); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } status = unix_convert(conn, fname, False, NULL, &sbuf); if (!NT_STATUS_IS_OK(status)) { @@ -3175,18 +3404,16 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd allocation_size = get_allocation_size(conn,fsp,&sbuf); if (fsp) { - if (fsp->pending_modtime) { + if (!null_timespec(fsp->pending_modtime)) { /* the pending modtime overrides the current modtime */ - mtime_ts.tv_sec = fsp->pending_modtime; - mtime_ts.tv_nsec = 0; + mtime_ts = fsp->pending_modtime; } } else { /* Do we have this path open ? */ files_struct *fsp1 = file_find_di_first(sbuf.st_dev, sbuf.st_ino); - if (fsp1 && fsp1->pending_modtime) { + if (fsp1 && !null_timespec(fsp1->pending_modtime)) { /* the pending modtime overrides the current modtime */ - mtime_ts.tv_sec = fsp1->pending_modtime; - mtime_ts.tv_nsec = 0; + mtime_ts = fsp1->pending_modtime; } if (fsp1 && fsp1->initial_allocation_size) { allocation_size = get_allocation_size(conn, fsp1, &sbuf); @@ -3539,6 +3766,22 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd break; + case SMB_QUERY_FILE_UNIX_INFO2: + + pdata = store_file_unix_basic_info2(conn, pdata, fsp, &sbuf); + data_size = PTR_DIFF(pdata,(*ppdata)); + + { + int i; + DEBUG(4,("call_trans2qfilepathinfo: SMB_QUERY_FILE_UNIX_INFO2 ")); + + for (i=0; i<100; i++) + DEBUG(4,("%d=%x, ",i, (*ppdata)[i])); + DEBUG(4,("\n")); + } + + break; + case SMB_QUERY_FILE_UNIX_LINK: { pstring buffer; @@ -3743,17 +3986,22 @@ NTSTATUS hardlink_internals(connection_struct *conn, pstring oldname, pstring ne return status; } + status = check_name(conn, oldname); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + /* source must already exist. */ if (!VALID_STAT(sbuf1)) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } - status = check_name(conn, oldname); + status = unix_convert(conn, newname, False, last_component_newname, &sbuf2); if (!NT_STATUS_IS_OK(status)) { - return NT_STATUS_ACCESS_DENIED; + return status; } - status = unix_convert(conn, newname, False, last_component_newname, &sbuf2); + status = check_name(conn, newname); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -3763,11 +4011,6 @@ NTSTATUS hardlink_internals(connection_struct *conn, pstring oldname, pstring ne return NT_STATUS_OBJECT_NAME_COLLISION; } - status = check_name(conn, newname); - if (!NT_STATUS_IS_OK(status)) { - return NT_STATUS_ACCESS_DENIED; - } - /* No links from a directory. */ if (S_ISDIR(sbuf1.st_mode)) { return NT_STATUS_FILE_IS_A_DIRECTORY; @@ -3798,7 +4041,7 @@ static NTSTATUS smb_set_file_time(connection_struct *conn, files_struct *fsp, const char *fname, const SMB_STRUCT_STAT *psbuf, - struct utimbuf tvs) + struct timespec ts[2]) { uint32 action = FILE_NOTIFY_CHANGE_LAST_ACCESS @@ -3810,26 +4053,30 @@ static NTSTATUS smb_set_file_time(connection_struct *conn, } /* get some defaults (no modifications) if any info is zero or -1. */ - if (null_mtime(tvs.actime)) { - tvs.actime = psbuf->st_atime; + if (null_timespec(ts[0])) { + ts[0] = get_atimespec(psbuf); action &= ~FILE_NOTIFY_CHANGE_LAST_ACCESS; } - if (null_mtime(tvs.modtime)) { - tvs.modtime = psbuf->st_mtime; + if (null_timespec(ts[1])) { + ts[1] = get_mtimespec(psbuf); action &= ~FILE_NOTIFY_CHANGE_LAST_WRITE; } - DEBUG(6,("smb_set_file_time: actime: %s " , ctime(&tvs.actime))); - DEBUG(6,("smb_set_file_time: modtime: %s ", ctime(&tvs.modtime))); + DEBUG(6,("smb_set_file_time: actime: %s " , time_to_asc(convert_timespec_to_time_t(ts[0])) )); + DEBUG(6,("smb_set_file_time: modtime: %s ", time_to_asc(convert_timespec_to_time_t(ts[1])) )); /* * Try and set the times of this file if * they are different from the current values. */ - if (psbuf->st_mtime == tvs.modtime && psbuf->st_atime == tvs.actime) { - return NT_STATUS_OK; + { + struct timespec mts = get_mtimespec(psbuf); + struct timespec ats = get_atimespec(psbuf); + if ((timespec_compare(&ts[0], &ats) == 0) && (timespec_compare(&ts[1], &mts) == 0)) { + return NT_STATUS_OK; + } } if(fsp != NULL) { @@ -3843,15 +4090,16 @@ static NTSTATUS smb_set_file_time(connection_struct *conn, * away and will set it on file close and after a write. JRA. */ - if (tvs.modtime != (time_t)0 && tvs.modtime != (time_t)-1) { - DEBUG(10,("smb_set_file_time: setting pending modtime to %s\n", ctime(&tvs.modtime) )); - fsp_set_pending_modtime(fsp, tvs.modtime); + if (!null_timespec(ts[1])) { + DEBUG(10,("smb_set_file_time: setting pending modtime to %s\n", + time_to_asc(convert_timespec_to_time_t(ts[1])) )); + fsp_set_pending_modtime(fsp, ts[1]); } } DEBUG(10,("smb_set_file_time: setting utimes to modified values.\n")); - if(file_utime(conn, fname, &tvs)!=0) { + if(file_ntimes(conn, fname, ts)!=0) { return map_nt_error_from_unix(errno); } if (action != 0) { @@ -4030,6 +4278,12 @@ static NTSTATUS smb_set_file_disposition_info(connection_struct *conn, delete_on_close = (CVAL(pdata,0) ? True : False); dosmode = dos_mode(conn, fname, psbuf); + DEBUG(10,("smb_set_file_disposition_info: file %s, dosmode = %u, " + "delete_on_close = %u\n", + fsp->fsp_name, + (unsigned int)dosmode, + (unsigned int)delete_on_close )); + status = can_set_delete_on_close(fsp, delete_on_close, dosmode); if (!NT_STATUS_IS_OK(status)) { @@ -4132,7 +4386,6 @@ static NTSTATUS smb_set_file_unix_link(connection_struct *conn, pstring rel_name; char *last_dirp = NULL; - unix_format(link_target); if (*link_target == '/') { /* No absolute paths allowed. */ return NT_STATUS_ACCESS_DENIED; @@ -4186,7 +4439,10 @@ static NTSTATUS smb_set_file_unix_hlink(connection_struct *conn, return status; } - RESOLVE_DFSPATH_STATUS(oldname, conn, inbuf, outbuf); + status = resolve_dfspath(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, oldname); + if (!NT_STATUS_IS_OK(status)) { + return status; + } DEBUG(10,("smb_set_file_unix_hlink: SMB_SET_FILE_UNIX_LINK doing hard link %s -> %s\n", fname, oldname)); @@ -4211,6 +4467,7 @@ static NTSTATUS smb_file_rename_information(connection_struct *conn, uint32 len; pstring newname; pstring base_name; + BOOL dest_has_wcard = False; NTSTATUS status = NT_STATUS_OK; char *p; @@ -4226,12 +4483,15 @@ static NTSTATUS smb_file_rename_information(connection_struct *conn, return NT_STATUS_INVALID_PARAMETER; } - srvstr_get_path(inbuf, newname, &pdata[12], sizeof(newname), len, 0, &status); + srvstr_get_path_wcard(inbuf, newname, &pdata[12], sizeof(newname), len, 0, &status, &dest_has_wcard); if (!NT_STATUS_IS_OK(status)) { return status; } - RESOLVE_DFSPATH_STATUS(newname, conn, inbuf, outbuf); + status = resolve_dfspath_wcard(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, newname, &dest_has_wcard); + if (!NT_STATUS_IS_OK(status)) { + return status; + } /* Check the new name has no '/' characters. */ if (strchr_m(newname, '/')) { @@ -4255,7 +4515,7 @@ static NTSTATUS smb_file_rename_information(connection_struct *conn, } else { DEBUG(10,("smb_file_rename_information: SMB_FILE_RENAME_INFORMATION %s -> %s\n", fname, newname )); - status = rename_internals(conn, fname, base_name, 0, overwrite, False); + status = rename_internals(conn, fname, base_name, 0, overwrite, False, dest_has_wcard); } return status; @@ -4305,6 +4565,11 @@ static NTSTATUS smb_set_posix_acl(connection_struct *conn, return NT_STATUS_INVALID_PARAMETER; } + DEBUG(10,("smb_set_posix_acl: file %s num_file_acls = %u, num_def_acls = %u\n", + fname ? fname : fsp->fsp_name, + (unsigned int)num_file_acls, + (unsigned int)num_def_acls)); + if (valid_file_acls && !set_unix_posix_acl(conn, fsp, fname, num_file_acls, pdata + SMB_POSIX_ACL_HEADER_SIZE)) { return map_nt_error_from_unix(errno); @@ -4386,6 +4651,14 @@ static NTSTATUS smb_set_posix_lock(connection_struct *conn, count = (SMB_BIG_UINT)IVAL(pdata,POSIX_LOCK_LEN_OFFSET); #endif /* HAVE_LONGLONG */ + DEBUG(10,("smb_set_posix_lock: file %s, lock_type = %u," + "lock_pid = %u, count = %.0f, offset = %.0f\n", + fsp->fsp_name, + (unsigned int)lock_type, + (unsigned int)lock_pid, + (double)count, + (double)offset )); + if (lock_type == UNLOCK_LOCK) { status = do_unlock(fsp, lock_pid, @@ -4439,22 +4712,25 @@ static NTSTATUS smb_set_info_standard(connection_struct *conn, const char *fname, const SMB_STRUCT_STAT *psbuf) { - struct utimbuf tvs; + struct timespec ts[2]; if (total_data < 12) { return NT_STATUS_INVALID_PARAMETER; } /* access time */ - tvs.actime = srv_make_unix_date2(pdata+l1_fdateLastAccess); + ts[0] = convert_time_t_to_timespec(srv_make_unix_date2(pdata+l1_fdateLastAccess)); /* write time */ - tvs.modtime = srv_make_unix_date2(pdata+l1_fdateLastWrite); + ts[1] = convert_time_t_to_timespec(srv_make_unix_date2(pdata+l1_fdateLastWrite)); + + DEBUG(10,("smb_set_info_standard: file %s\n", + fname ? fname : fsp->fsp_name )); return smb_set_file_time(conn, fsp, fname, psbuf, - tvs); + ts); } /**************************************************************************** @@ -4469,10 +4745,10 @@ static NTSTATUS smb_set_file_basic_info(connection_struct *conn, SMB_STRUCT_STAT *psbuf) { /* Patch to do this correctly from Paul Eggert <eggert@twinsun.com>. */ - time_t write_time; - time_t changed_time; + struct timespec write_time; + struct timespec changed_time; uint32 dosmode = 0; - struct utimbuf tvs; + struct timespec ts[2]; NTSTATUS status = NT_STATUS_OK; if (total_data < 36) { @@ -4492,26 +4768,31 @@ static NTSTATUS smb_set_file_basic_info(connection_struct *conn, /* Ignore create time at offset pdata. */ /* access time */ - tvs.actime = convert_timespec_to_time_t(interpret_long_date(pdata+8)); + ts[0] = interpret_long_date(pdata+8); - write_time = convert_timespec_to_time_t(interpret_long_date(pdata+16)); - changed_time = convert_timespec_to_time_t(interpret_long_date(pdata+24)); + write_time = interpret_long_date(pdata+16); + changed_time = interpret_long_date(pdata+24); - tvs.modtime = MIN(write_time, changed_time); + /* mtime */ + ts[1] = timespec_min(&write_time, &changed_time); - if (write_time > tvs.modtime && write_time != (time_t)-1) { - tvs.modtime = write_time; + if ((timespec_compare(&write_time, &ts[1]) == 1) && !null_timespec(write_time)) { + ts[1] = write_time; } + /* Prefer a defined time to an undefined one. */ - if (null_mtime(tvs.modtime)) { - tvs.modtime = null_mtime(write_time) ? changed_time : write_time; + if (null_timespec(ts[1])) { + ts[1] = null_timespec(write_time) ? changed_time : write_time; } + DEBUG(10,("smb_set_file_basic_info: file %s\n", + fname ? fname : fsp->fsp_name )); + return smb_set_file_time(conn, fsp, fname, psbuf, - tvs); + ts); } /**************************************************************************** @@ -4725,7 +5006,7 @@ static NTSTATUS smb_set_file_unix_basic(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf) { - struct utimbuf tvs; + struct timespec ts[2]; uint32 raw_unixmode; mode_t unixmode; SMB_OFF_T size = 0; @@ -4752,8 +5033,8 @@ static NTSTATUS smb_set_file_unix_basic(connection_struct *conn, #endif /* LARGE_SMB_OFF_T */ } - tvs.actime = convert_timespec_to_time_t(interpret_long_date(pdata+24)); /* access_time */ - tvs.modtime = convert_timespec_to_time_t(interpret_long_date(pdata+32)); /* modification_time */ + ts[0] = interpret_long_date(pdata+24); /* access_time */ + ts[1] = interpret_long_date(pdata+32); /* modification_time */ set_owner = (uid_t)IVAL(pdata,40); set_grp = (gid_t)IVAL(pdata,48); raw_unixmode = IVAL(pdata,84); @@ -4796,8 +5077,8 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", /* Ensure we don't try and change anything else. */ raw_unixmode = SMB_MODE_NO_CHANGE; size = get_file_size(*psbuf); - tvs.modtime = psbuf->st_mtime; - tvs.actime = psbuf->st_atime; + ts[0] = get_atimespec(psbuf); + ts[1] = get_mtimespec(psbuf); /* * We continue here as we might want to change the * owner uid/gid. @@ -4876,7 +5157,68 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", fsp, fname, psbuf, - tvs); + ts); +} + +/**************************************************************************** + Deal with SMB_SET_FILE_UNIX_INFO2. +****************************************************************************/ + +static NTSTATUS smb_set_file_unix_info2(connection_struct *conn, + const char *pdata, + int total_data, + files_struct *fsp, + const char *fname, + SMB_STRUCT_STAT *psbuf) +{ + NTSTATUS status; + uint32 smb_fflags; + uint32 smb_fmask; + + if (total_data < 116) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* Start by setting all the fields that are common between UNIX_BASIC + * and UNIX_INFO2. + */ + status = smb_set_file_unix_basic(conn, pdata, total_data, + fsp, fname, psbuf); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + smb_fflags = IVAL(pdata, 108); + smb_fmask = IVAL(pdata, 112); + + /* NB: We should only attempt to alter the file flags if the client + * sends a non-zero mask. + */ + if (smb_fmask != 0) { + int stat_fflags = 0; + + if (!map_info2_flags_to_sbuf(psbuf, smb_fflags, smb_fmask, + &stat_fflags)) { + /* Client asked to alter a flag we don't understand. */ + return NT_STATUS_INVALID_PARAMETER; + } + + if (fsp && fsp->fh->fd != -1) { + /* XXX: we should be using SMB_VFS_FCHFLAGS here. */ + return NT_STATUS_NOT_SUPPORTED; + } else { + if (SMB_VFS_CHFLAGS(conn, fname, stat_fflags) != 0) { + return map_nt_error_from_unix(errno); + } + } + } + + /* XXX: need to add support for changing the create_time here. You + * can do this for paths on Darwin with setattrlist(2). The right way + * to hook this up is probably by extending the VFS utimes interface. + */ + + return NT_STATUS_OK; } /**************************************************************************** @@ -4896,13 +5238,16 @@ static NTSTATUS smb_posix_mkdir(connection_struct *conn, mode_t unixmode = (mode_t)0; files_struct *fsp = NULL; uint16 info_level_return = 0; + int info; char *pdata = *ppdata; - if (total_data < 10) { + if (total_data < 18) { return NT_STATUS_INVALID_PARAMETER; } raw_unixmode = IVAL(pdata,8); + /* Next 4 bytes are not yet defined. */ + status = unix_perms_from_wire(conn, psbuf, raw_unixmode, PERM_NEW_DIR, &unixmode); if (!NT_STATUS_IS_OK(status)) { return status; @@ -4910,6 +5255,9 @@ static NTSTATUS smb_posix_mkdir(connection_struct *conn, mod_unixmode = (uint32)unixmode | FILE_FLAG_POSIX_SEMANTICS; + DEBUG(10,("smb_posix_mkdir: file %s, mode 0%o\n", + fname, (unsigned int)unixmode )); + status = open_directory(conn, fname, psbuf, @@ -4918,19 +5266,21 @@ static NTSTATUS smb_posix_mkdir(connection_struct *conn, FILE_CREATE, 0, mod_unixmode, - NULL, + &info, &fsp); if (NT_STATUS_IS_OK(status)) { close_file(fsp, NORMAL_CLOSE); } - info_level_return = SVAL(pdata,12); + info_level_return = SVAL(pdata,16); if (info_level_return == SMB_QUERY_FILE_UNIX_BASIC) { - *pdata_return_size = 8 + SMB_FILE_UNIX_BASIC_SIZE; + *pdata_return_size = 12 + SMB_FILE_UNIX_BASIC_SIZE; + } else if (info_level_return == SMB_QUERY_FILE_UNIX_INFO2) { + *pdata_return_size = 12 + SMB_FILE_UNIX_INFO2_SIZE; } else { - *pdata_return_size = 8; + *pdata_return_size = 12; } /* Realloc the data size */ @@ -4941,15 +5291,21 @@ static NTSTATUS smb_posix_mkdir(connection_struct *conn, } SSVAL(pdata,0,NO_OPLOCK_RETURN); - SSVAL(pdata,2,0); - - if (info_level_return == SMB_QUERY_FILE_UNIX_BASIC) { - SSVAL(pdata,4,SMB_QUERY_FILE_UNIX_BASIC); - SSVAL(pdata,6,0); /* Padding. */ - store_file_unix_basic(conn, pdata + 8, fsp, psbuf); - } else { - SSVAL(pdata,4,SMB_NO_INFO_LEVEL_RETURNED); - SSVAL(pdata,6,0); /* Padding. */ + SSVAL(pdata,2,0); /* No fnum. */ + SIVAL(pdata,4,info); /* Was directory created. */ + + switch (info_level_return) { + case SMB_QUERY_FILE_UNIX_BASIC: + SSVAL(pdata,8,SMB_QUERY_FILE_UNIX_BASIC); + SSVAL(pdata,10,0); /* Padding. */ + store_file_unix_basic(conn, pdata + 12, fsp, psbuf); + case SMB_QUERY_FILE_UNIX_INFO2: + SSVAL(pdata,8,SMB_QUERY_FILE_UNIX_INFO2); + SSVAL(pdata,10,0); /* Padding. */ + store_file_unix_basic_info2(conn, pdata + 12, fsp, psbuf); + default: + SSVAL(pdata,8,SMB_NO_INFO_LEVEL_RETURNED); + SSVAL(pdata,10,0); /* Padding. */ } return status; @@ -4982,7 +5338,7 @@ static NTSTATUS smb_posix_open(connection_struct *conn, int info = 0; uint16 info_level_return = 0; - if (total_data < 14) { + if (total_data < 18) { return NT_STATUS_INVALID_PARAMETER; } @@ -5034,6 +5390,8 @@ static NTSTATUS smb_posix_open(connection_struct *conn, } raw_unixmode = IVAL(pdata,8); + /* Next 4 bytes are not yet defined. */ + status = unix_perms_from_wire(conn, psbuf, raw_unixmode, @@ -5056,6 +5414,11 @@ static NTSTATUS smb_posix_open(connection_struct *conn, mod_unixmode |= FILE_FLAG_NO_BUFFERING; } + DEBUG(10,("smb_posix_open: file %s, smb_posix_flags = %u, mode 0%o\n", + fname, + (unsigned int)wire_open_mode, + (unsigned int)unixmode )); + status = open_file_ntcreate(conn, fname, psbuf, @@ -5080,12 +5443,16 @@ static NTSTATUS smb_posix_open(connection_struct *conn, extended_oplock_granted = True; } - info_level_return = SVAL(pdata,12); + info_level_return = SVAL(pdata,16); + /* Allocate the correct return size. */ + if (info_level_return == SMB_QUERY_FILE_UNIX_BASIC) { - *pdata_return_size = 8 + SMB_FILE_UNIX_BASIC_SIZE; + *pdata_return_size = 12 + SMB_FILE_UNIX_BASIC_SIZE; + } else if (info_level_return == SMB_QUERY_FILE_UNIX_INFO2) { + *pdata_return_size = 12 + SMB_FILE_UNIX_INFO2_SIZE; } else { - *pdata_return_size = 8; + *pdata_return_size = 12; } /* Realloc the data size */ @@ -5109,13 +5476,20 @@ static NTSTATUS smb_posix_open(connection_struct *conn, } SSVAL(pdata,2,fsp->fnum); - if (info_level_return == SMB_QUERY_FILE_UNIX_BASIC) { - SSVAL(pdata,4,SMB_QUERY_FILE_UNIX_BASIC); - SSVAL(pdata,6,0); /* padding. */ - store_file_unix_basic(conn, pdata + 8, fsp, psbuf); - } else { - SSVAL(pdata,4,SMB_NO_INFO_LEVEL_RETURNED); - SSVAL(pdata,6,0); /* padding. */ + SIVAL(pdata,4,info); /* Was file created etc. */ + + switch (info_level_return) { + case SMB_QUERY_FILE_UNIX_BASIC: + SSVAL(pdata,8,SMB_QUERY_FILE_UNIX_BASIC); + SSVAL(pdata,10,0); /* padding. */ + store_file_unix_basic(conn, pdata + 12, fsp, psbuf); + case SMB_QUERY_FILE_UNIX_INFO2: + SSVAL(pdata,8,SMB_QUERY_FILE_UNIX_INFO2); + SSVAL(pdata,10,0); /* padding. */ + store_file_unix_basic_info2(conn, pdata + 12, fsp, psbuf); + default: + SSVAL(pdata,8,SMB_NO_INFO_LEVEL_RETURNED); + SSVAL(pdata,10,0); /* padding. */ } return NT_STATUS_OK; } @@ -5132,12 +5506,28 @@ static NTSTATUS smb_posix_unlink(connection_struct *conn, { NTSTATUS status = NT_STATUS_OK; files_struct *fsp = NULL; + uint16 flags = 0; int info = 0; + if (total_data < 2) { + return NT_STATUS_INVALID_PARAMETER; + } + + flags = SVAL(pdata,0); + if (!VALID_STAT(*psbuf)) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } + if ((flags == SMB_POSIX_UNLINK_DIRECTORY_TARGET) && + !VALID_STAT_OF_DIR(*psbuf)) { + return NT_STATUS_NOT_A_DIRECTORY; + } + + DEBUG(10,("smb_posix_unlink: %s %s\n", + (flags == SMB_POSIX_UNLINK_DIRECTORY_TARGET) ? "directory" : "file", + fname)); + if (VALID_STAT_OF_DIR(*psbuf)) { status = open_directory(conn, fname, @@ -5150,17 +5540,34 @@ static NTSTATUS smb_posix_unlink(connection_struct *conn, &info, &fsp); } else { + char del = 1; + status = open_file_ntcreate(conn, fname, psbuf, DELETE_ACCESS, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN, - FILE_DELETE_ON_CLOSE, + 0, FILE_FLAG_POSIX_SEMANTICS|0777, - INTERNAL_OPEN_ONLY, + 0, /* No oplock, but break existing ones. */ &info, &fsp); + /* + * For file opens we must set the delete on close + * after the open. + */ + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = smb_set_file_disposition_info(conn, + &del, + 1, + fsp, + fname, + psbuf); } if (!NT_STATUS_IS_OK(status)) { @@ -5251,13 +5658,24 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char return ERROR_NT(status); } - RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); + status = resolve_dfspath(conn, SVAL(inbuf,smb_flg2) & FLAGS2_DFS_PATHNAMES, fname); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + return ERROR_BOTH(NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); + } + return ERROR_NT(status); + } status = unix_convert(conn, fname, False, NULL, &sbuf); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } + status = check_name(conn, fname); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); + } + /* * For CIFS UNIX extensions the target name may not exist. */ @@ -5266,12 +5684,6 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char DEBUG(3,("call_trans2setfilepathinfo: stat of %s failed (%s)\n", fname, strerror(errno))); return UNIXERROR(ERRDOS,ERRbadpath); } - - status = check_name(conn, fname); - if (!NT_STATUS_IS_OK(status)) { - return ERROR_NT(status); - } - } if (!CAN_WRITE(conn)) { @@ -5294,9 +5706,9 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char SSVAL(params,0,0); - if (fsp && fsp->pending_modtime) { + if (fsp && !null_timespec(fsp->pending_modtime)) { /* the pending modtime overrides the current modtime */ - sbuf.st_mtime = fsp->pending_modtime; + set_mtimespec(&sbuf, fsp->pending_modtime); } switch (info_level) { @@ -5417,6 +5829,17 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char break; } + case SMB_SET_FILE_UNIX_INFO2: + { + status = smb_set_file_unix_info2(conn, + pdata, + total_data, + fsp, + fname, + &sbuf); + break; + } + case SMB_SET_FILE_UNIX_LINK: { if (tran_call != TRANSACT2_SETPATHINFO) { @@ -5576,6 +5999,12 @@ static int call_trans2mkdir(connection_struct *conn, char *inbuf, char *outbuf, return ERROR_NT(status); } + status = check_name(conn, directory); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(5,("call_trans2mkdir error (%s)\n", nt_errstr(status))); + return ERROR_NT(status); + } + /* Any data in this call is an EA list. */ if (total_data && (total_data != 4) && !lp_ea_support(SNUM(conn))) { return ERROR_NT(NT_STATUS_EAS_NOT_SUPPORTED); @@ -5607,12 +6036,6 @@ static int call_trans2mkdir(connection_struct *conn, char *inbuf, char *outbuf, return ERROR_NT(NT_STATUS_INVALID_PARAMETER); } - status = check_name(conn, directory); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(5,("call_trans2mkdir error (%s)\n", nt_errstr(status))); - return ERROR_NT(status); - } - status = create_directory(conn, directory); if (!NT_STATUS_IS_OK(status)) { |