mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
56ac1efc70
Signed-off-by: David Mulder <dmulder@suse.com> Reviewed-by: Jeremy Allison <jra@samba.org>
893 lines
22 KiB
C
893 lines
22 KiB
C
/*
|
|
Unix SMB/CIFS implementation.
|
|
Main SMB reply routines
|
|
Copyright (C) Andrew Tridgell 1992-1998
|
|
Copyright (C) Andrew Bartlett 2001
|
|
Copyright (C) Jeremy Allison 1992-2007.
|
|
Copyright (C) Volker Lendecke 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
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
/*
|
|
This file handles most of the reply_ calls that the server
|
|
makes to handle specific protocols
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "libsmb/namequery.h"
|
|
#include "system/filesys.h"
|
|
#include "printing.h"
|
|
#include "locking/share_mode_lock.h"
|
|
#include "smbd/smbd.h"
|
|
#include "smbd/globals.h"
|
|
#include "smbd/smbXsrv_open.h"
|
|
#include "fake_file.h"
|
|
#include "rpc_client/rpc_client.h"
|
|
#include "../librpc/gen_ndr/ndr_spoolss_c.h"
|
|
#include "rpc_client/cli_spoolss.h"
|
|
#include "rpc_client/init_spoolss.h"
|
|
#include "rpc_server/rpc_ncacn_np.h"
|
|
#include "libcli/security/security.h"
|
|
#include "libsmb/nmblib.h"
|
|
#include "auth.h"
|
|
#include "smbprofile.h"
|
|
#include "../lib/tsocket/tsocket.h"
|
|
#include "lib/util/tevent_ntstatus.h"
|
|
#include "libcli/smb/smb_signing.h"
|
|
#include "lib/util/sys_rw_data.h"
|
|
#include "librpc/gen_ndr/open_files.h"
|
|
#include "smb1_utils.h"
|
|
#include "libcli/smb/smb2_posix.h"
|
|
#include "lib/util/string_wrappers.h"
|
|
#include "source3/printing/rap_jobid.h"
|
|
#include "source3/lib/substitute.h"
|
|
|
|
/****************************************************************************
|
|
Ensure we check the path in *exactly* the same way as W2K for a findfirst/findnext
|
|
path or anything including wildcards.
|
|
We're assuming here that '/' is not the second byte in any multibyte char
|
|
set (a safe assumption). '\\' *may* be the second byte in a multibyte char
|
|
set.
|
|
****************************************************************************/
|
|
|
|
/* Custom version for processing POSIX paths. */
|
|
#define IS_PATH_SEP(c,posix_only) ((c) == '/' || (!(posix_only) && (c) == '\\'))
|
|
|
|
static NTSTATUS check_path_syntax_internal(char *path,
|
|
bool posix_path)
|
|
{
|
|
char *d = path;
|
|
const char *s = path;
|
|
NTSTATUS ret = NT_STATUS_OK;
|
|
bool start_of_name_component = True;
|
|
bool stream_started = false;
|
|
bool last_component_contains_wcard = false;
|
|
|
|
while (*s) {
|
|
if (stream_started) {
|
|
switch (*s) {
|
|
case '/':
|
|
case '\\':
|
|
return NT_STATUS_OBJECT_NAME_INVALID;
|
|
case ':':
|
|
if (s[1] == '\0') {
|
|
return NT_STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
if (strchr_m(&s[1], ':')) {
|
|
return NT_STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((*s == ':') && !posix_path && !stream_started) {
|
|
if (last_component_contains_wcard) {
|
|
return NT_STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
/* Stream names allow more characters than file names.
|
|
We're overloading posix_path here to allow a wider
|
|
range of characters. If stream_started is true this
|
|
is still a Windows path even if posix_path is true.
|
|
JRA.
|
|
*/
|
|
stream_started = true;
|
|
start_of_name_component = false;
|
|
posix_path = true;
|
|
|
|
if (s[1] == '\0') {
|
|
return NT_STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
}
|
|
|
|
if (!stream_started && 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_PATH_SEP(*s,posix_path)) {
|
|
s++;
|
|
}
|
|
if ((d != path) && (*s != '\0')) {
|
|
/* We only care about non-leading or trailing '/' or '\\' */
|
|
*d++ = '/';
|
|
}
|
|
|
|
start_of_name_component = True;
|
|
/* New component. */
|
|
last_component_contains_wcard = false;
|
|
continue;
|
|
}
|
|
|
|
if (start_of_name_component) {
|
|
if ((s[0] == '.') && (s[1] == '.') && (IS_PATH_SEP(s[2],posix_path) || s[2] == '\0')) {
|
|
/* Uh oh - "/../" or "\\..\\" or "/..\0" or "\\..\0" ! */
|
|
|
|
/*
|
|
* No mb char starts with '.' so we're safe checking the directory separator here.
|
|
*/
|
|
|
|
/* If we just added a '/' - delete it */
|
|
if ((d > path) && (*(d-1) == '/')) {
|
|
*(d-1) = '\0';
|
|
d--;
|
|
}
|
|
|
|
/* Are we at the start ? Can't go back further if so. */
|
|
if (d <= path) {
|
|
ret = NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
|
|
break;
|
|
}
|
|
/* Go back one level... */
|
|
/* We know this is safe as '/' cannot be part of a mb sequence. */
|
|
/* NOTE - if this assumption is invalid we are not in good shape... */
|
|
/* Decrement d first as d points to the *next* char to write into. */
|
|
for (d--; d > path; d--) {
|
|
if (*d == '/')
|
|
break;
|
|
}
|
|
s += 2; /* Else go past the .. */
|
|
/* We're still at the start of a name component, just the previous one. */
|
|
continue;
|
|
|
|
} else if ((s[0] == '.') && ((s[1] == '\0') || IS_PATH_SEP(s[1],posix_path))) {
|
|
if (posix_path) {
|
|
/* Eat the '.' */
|
|
s++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (!(*s & 0x80)) {
|
|
if (!posix_path) {
|
|
if (*s <= 0x1f || *s == '|') {
|
|
return NT_STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
switch (*s) {
|
|
case '*':
|
|
case '?':
|
|
case '<':
|
|
case '>':
|
|
case '"':
|
|
last_component_contains_wcard = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
*d++ = *s++;
|
|
} else {
|
|
size_t siz;
|
|
/* Get the size of the next MB character. */
|
|
next_codepoint(s,&siz);
|
|
switch(siz) {
|
|
case 5:
|
|
*d++ = *s++;
|
|
FALL_THROUGH;
|
|
case 4:
|
|
*d++ = *s++;
|
|
FALL_THROUGH;
|
|
case 3:
|
|
*d++ = *s++;
|
|
FALL_THROUGH;
|
|
case 2:
|
|
*d++ = *s++;
|
|
FALL_THROUGH;
|
|
case 1:
|
|
*d++ = *s++;
|
|
break;
|
|
default:
|
|
DEBUG(0,("check_path_syntax_internal: character length assumptions invalid !\n"));
|
|
*d = '\0';
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
start_of_name_component = False;
|
|
}
|
|
|
|
*d = '\0';
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Ensure we check the path in *exactly* the same way as W2K for regular pathnames.
|
|
No wildcards allowed.
|
|
****************************************************************************/
|
|
|
|
NTSTATUS check_path_syntax(char *path)
|
|
{
|
|
return check_path_syntax_internal(path, false);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Check the path for a POSIX client.
|
|
We're assuming here that '/' is not the second byte in any multibyte char
|
|
set (a safe assumption).
|
|
****************************************************************************/
|
|
|
|
NTSTATUS check_path_syntax_posix(char *path)
|
|
{
|
|
return check_path_syntax_internal(path, true);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Pull a string and check the path allowing a wildcard - provide for error return.
|
|
Passes in posix flag.
|
|
****************************************************************************/
|
|
|
|
static size_t srvstr_get_path_internal(TALLOC_CTX *ctx,
|
|
const char *base_ptr,
|
|
uint16_t smb_flags2,
|
|
char **pp_dest,
|
|
const char *src,
|
|
size_t src_len,
|
|
int flags,
|
|
bool posix_pathnames,
|
|
NTSTATUS *err)
|
|
{
|
|
size_t ret;
|
|
|
|
*pp_dest = NULL;
|
|
|
|
ret = srvstr_pull_talloc(ctx, base_ptr, smb_flags2, pp_dest, src,
|
|
src_len, flags);
|
|
|
|
if (!*pp_dest) {
|
|
*err = NT_STATUS_INVALID_PARAMETER;
|
|
return ret;
|
|
}
|
|
|
|
if (smb_flags2 & FLAGS2_DFS_PATHNAMES) {
|
|
/*
|
|
* For a DFS path the function parse_dfs_path()
|
|
* will do the path processing, just make a copy.
|
|
*/
|
|
*err = NT_STATUS_OK;
|
|
return ret;
|
|
}
|
|
|
|
if (posix_pathnames) {
|
|
*err = check_path_syntax_posix(*pp_dest);
|
|
} else {
|
|
*err = check_path_syntax(*pp_dest);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Pull a string and check the path - provide for error return.
|
|
****************************************************************************/
|
|
|
|
size_t srvstr_get_path(TALLOC_CTX *ctx,
|
|
const char *base_ptr,
|
|
uint16_t smb_flags2,
|
|
char **pp_dest,
|
|
const char *src,
|
|
size_t src_len,
|
|
int flags,
|
|
NTSTATUS *err)
|
|
{
|
|
return srvstr_get_path_internal(ctx,
|
|
base_ptr,
|
|
smb_flags2,
|
|
pp_dest,
|
|
src,
|
|
src_len,
|
|
flags,
|
|
false,
|
|
err);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Pull a string and check the path - provide for error return.
|
|
posix_pathnames version.
|
|
****************************************************************************/
|
|
|
|
size_t srvstr_get_path_posix(TALLOC_CTX *ctx,
|
|
const char *base_ptr,
|
|
uint16_t smb_flags2,
|
|
char **pp_dest,
|
|
const char *src,
|
|
size_t src_len,
|
|
int flags,
|
|
NTSTATUS *err)
|
|
{
|
|
return srvstr_get_path_internal(ctx,
|
|
base_ptr,
|
|
smb_flags2,
|
|
pp_dest,
|
|
src,
|
|
src_len,
|
|
flags,
|
|
true,
|
|
err);
|
|
}
|
|
|
|
|
|
size_t srvstr_get_path_req(TALLOC_CTX *mem_ctx, struct smb_request *req,
|
|
char **pp_dest, const char *src, int flags,
|
|
NTSTATUS *err)
|
|
{
|
|
ssize_t bufrem = smbreq_bufrem(req, src);
|
|
|
|
if (bufrem < 0) {
|
|
*err = NT_STATUS_INVALID_PARAMETER;
|
|
return 0;
|
|
}
|
|
|
|
if (req->posix_pathnames) {
|
|
return srvstr_get_path_internal(mem_ctx,
|
|
(const char *)req->inbuf,
|
|
req->flags2,
|
|
pp_dest,
|
|
src,
|
|
bufrem,
|
|
flags,
|
|
true,
|
|
err);
|
|
} else {
|
|
return srvstr_get_path_internal(mem_ctx,
|
|
(const char *)req->inbuf,
|
|
req->flags2,
|
|
pp_dest,
|
|
src,
|
|
bufrem,
|
|
flags,
|
|
false,
|
|
err);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* pull a string from the smb_buf part of a packet. In this case the
|
|
* string can either be null terminated or it can be terminated by the
|
|
* end of the smbbuf area
|
|
*/
|
|
size_t srvstr_pull_req_talloc(TALLOC_CTX *ctx, struct smb_request *req,
|
|
char **dest, const uint8_t *src, int flags)
|
|
{
|
|
ssize_t bufrem = smbreq_bufrem(req, src);
|
|
|
|
if (bufrem < 0) {
|
|
return 0;
|
|
}
|
|
|
|
return pull_string_talloc(ctx, req->inbuf, req->flags2, dest, src,
|
|
bufrem, flags);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Check if we have a correct fsp pointing to a file. Basic check for open fsp.
|
|
****************************************************************************/
|
|
|
|
bool check_fsp_open(connection_struct *conn, struct smb_request *req,
|
|
files_struct *fsp)
|
|
{
|
|
if ((fsp == NULL) || (conn == NULL)) {
|
|
reply_nterror(req, NT_STATUS_INVALID_HANDLE);
|
|
return False;
|
|
}
|
|
if ((conn != fsp->conn) || (req->vuid != fsp->vuid)) {
|
|
reply_nterror(req, NT_STATUS_INVALID_HANDLE);
|
|
return False;
|
|
}
|
|
return True;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Check if we have a correct fsp pointing to a file.
|
|
****************************************************************************/
|
|
|
|
bool check_fsp(connection_struct *conn, struct smb_request *req,
|
|
files_struct *fsp)
|
|
{
|
|
if (!check_fsp_open(conn, req, fsp)) {
|
|
return False;
|
|
}
|
|
if (fsp->fsp_flags.is_directory) {
|
|
reply_nterror(req, NT_STATUS_INVALID_DEVICE_REQUEST);
|
|
return False;
|
|
}
|
|
if (fsp_get_pathref_fd(fsp) == -1) {
|
|
reply_nterror(req, NT_STATUS_ACCESS_DENIED);
|
|
return False;
|
|
}
|
|
fsp->num_smb_operations++;
|
|
return True;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Check if we have a correct fsp pointing to a quota fake file. Replacement for
|
|
the CHECK_NTQUOTA_HANDLE_OK macro.
|
|
****************************************************************************/
|
|
|
|
bool check_fsp_ntquota_handle(connection_struct *conn, struct smb_request *req,
|
|
files_struct *fsp)
|
|
{
|
|
if (!check_fsp_open(conn, req, fsp)) {
|
|
return false;
|
|
}
|
|
|
|
if (fsp->fsp_flags.is_directory) {
|
|
return false;
|
|
}
|
|
|
|
if (fsp->fake_file_handle == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (fsp->fake_file_handle->type != FAKE_FILE_TYPE_QUOTA) {
|
|
return false;
|
|
}
|
|
|
|
if (fsp->fake_file_handle->private_data == NULL) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Return the port number we've bound to on a socket.
|
|
****************************************************************************/
|
|
|
|
static int get_socket_port(int fd)
|
|
{
|
|
struct samba_sockaddr saddr = {
|
|
.sa_socklen = sizeof(struct sockaddr_storage),
|
|
};
|
|
|
|
if (fd == -1) {
|
|
return -1;
|
|
}
|
|
|
|
if (getsockname(fd, &saddr.u.sa, &saddr.sa_socklen) < 0) {
|
|
int level = (errno == ENOTCONN) ? 2 : 0;
|
|
DEBUG(level, ("getsockname failed. Error was %s\n",
|
|
strerror(errno)));
|
|
return -1;
|
|
}
|
|
|
|
#if defined(HAVE_IPV6)
|
|
if (saddr.u.sa.sa_family == AF_INET6) {
|
|
return ntohs(saddr.u.in6.sin6_port);
|
|
}
|
|
#endif
|
|
if (saddr.u.sa.sa_family == AF_INET) {
|
|
return ntohs(saddr.u.in.sin_port);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static bool netbios_session_retarget(struct smbXsrv_connection *xconn,
|
|
const char *name, int name_type)
|
|
{
|
|
char *trim_name;
|
|
char *trim_name_type;
|
|
const char *retarget_parm;
|
|
char *retarget;
|
|
char *p;
|
|
int retarget_type = 0x20;
|
|
int retarget_port = NBT_SMB_PORT;
|
|
struct sockaddr_storage retarget_addr;
|
|
struct sockaddr_in *in_addr;
|
|
bool ret = false;
|
|
uint8_t outbuf[10];
|
|
|
|
if (get_socket_port(xconn->transport.sock) != NBT_SMB_PORT) {
|
|
return false;
|
|
}
|
|
|
|
trim_name = talloc_strdup(talloc_tos(), name);
|
|
if (trim_name == NULL) {
|
|
goto fail;
|
|
}
|
|
trim_char(trim_name, ' ', ' ');
|
|
|
|
trim_name_type = talloc_asprintf(trim_name, "%s#%2.2x", trim_name,
|
|
name_type);
|
|
if (trim_name_type == NULL) {
|
|
goto fail;
|
|
}
|
|
|
|
retarget_parm = lp_parm_const_string(-1, "netbios retarget",
|
|
trim_name_type, NULL);
|
|
if (retarget_parm == NULL) {
|
|
retarget_parm = lp_parm_const_string(-1, "netbios retarget",
|
|
trim_name, NULL);
|
|
}
|
|
if (retarget_parm == NULL) {
|
|
goto fail;
|
|
}
|
|
|
|
retarget = talloc_strdup(trim_name, retarget_parm);
|
|
if (retarget == NULL) {
|
|
goto fail;
|
|
}
|
|
|
|
DEBUG(10, ("retargeting %s to %s\n", trim_name_type, retarget));
|
|
|
|
p = strchr(retarget, ':');
|
|
if (p != NULL) {
|
|
*p++ = '\0';
|
|
retarget_port = atoi(p);
|
|
}
|
|
|
|
p = strchr_m(retarget, '#');
|
|
if (p != NULL) {
|
|
*p++ = '\0';
|
|
if (sscanf(p, "%x", &retarget_type) != 1) {
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
ret = resolve_name(retarget, &retarget_addr, retarget_type, false);
|
|
if (!ret) {
|
|
DEBUG(10, ("could not resolve %s\n", retarget));
|
|
goto fail;
|
|
}
|
|
|
|
if (retarget_addr.ss_family != AF_INET) {
|
|
DEBUG(10, ("Retarget target not an IPv4 addr\n"));
|
|
goto fail;
|
|
}
|
|
|
|
in_addr = (struct sockaddr_in *)(void *)&retarget_addr;
|
|
|
|
_smb_setlen(outbuf, 6);
|
|
SCVAL(outbuf, 0, 0x84);
|
|
*(uint32_t *)(outbuf+4) = in_addr->sin_addr.s_addr;
|
|
*(uint16_t *)(outbuf+8) = htons(retarget_port);
|
|
|
|
if (!srv_send_smb(xconn, (char *)outbuf, false, 0, false,
|
|
NULL)) {
|
|
exit_server_cleanly("netbios_session_retarget: srv_send_smb "
|
|
"failed.");
|
|
}
|
|
|
|
ret = true;
|
|
fail:
|
|
TALLOC_FREE(trim_name);
|
|
return ret;
|
|
}
|
|
|
|
static void reply_called_name_not_present(char *outbuf)
|
|
{
|
|
smb_setlen(outbuf, 1);
|
|
SCVAL(outbuf, 0, 0x83);
|
|
SCVAL(outbuf, 4, 0x82);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Reply to a (netbios-level) special message.
|
|
****************************************************************************/
|
|
|
|
void reply_special(struct smbXsrv_connection *xconn, char *inbuf, size_t inbuf_size)
|
|
{
|
|
struct smbd_server_connection *sconn = xconn->client->sconn;
|
|
int msg_type = CVAL(inbuf,0);
|
|
int msg_flags = CVAL(inbuf,1);
|
|
/*
|
|
* We only really use 4 bytes of the outbuf, but for the smb_setlen
|
|
* calculation & friends (srv_send_smb uses that) we need the full smb
|
|
* header.
|
|
*/
|
|
char outbuf[smb_size];
|
|
|
|
memset(outbuf, '\0', sizeof(outbuf));
|
|
|
|
smb_setlen(outbuf,0);
|
|
|
|
switch (msg_type) {
|
|
case NBSSrequest: /* session request */
|
|
{
|
|
/* inbuf_size is guarenteed to be at least 4. */
|
|
fstring name1,name2;
|
|
int name_type1, name_type2;
|
|
int name_len1, name_len2;
|
|
|
|
*name1 = *name2 = 0;
|
|
|
|
if (xconn->transport.nbt.got_session) {
|
|
exit_server_cleanly("multiple session request not permitted");
|
|
}
|
|
|
|
SCVAL(outbuf,0,NBSSpositive);
|
|
SCVAL(outbuf,3,0);
|
|
|
|
/* inbuf_size is guaranteed to be at least 4. */
|
|
name_len1 = name_len((unsigned char *)(inbuf+4),inbuf_size - 4);
|
|
if (name_len1 <= 0 || name_len1 > inbuf_size - 4) {
|
|
DEBUG(0,("Invalid name length in session request\n"));
|
|
reply_called_name_not_present(outbuf);
|
|
break;
|
|
}
|
|
name_len2 = name_len((unsigned char *)(inbuf+4+name_len1),inbuf_size - 4 - name_len1);
|
|
if (name_len2 <= 0 || name_len2 > inbuf_size - 4 - name_len1) {
|
|
DEBUG(0,("Invalid name length in session request\n"));
|
|
reply_called_name_not_present(outbuf);
|
|
break;
|
|
}
|
|
|
|
name_type1 = name_extract((unsigned char *)inbuf,
|
|
inbuf_size,(unsigned int)4,name1);
|
|
name_type2 = name_extract((unsigned char *)inbuf,
|
|
inbuf_size,(unsigned int)(4 + name_len1),name2);
|
|
|
|
if (name_type1 == -1 || name_type2 == -1) {
|
|
DEBUG(0,("Invalid name type in session request\n"));
|
|
reply_called_name_not_present(outbuf);
|
|
break;
|
|
}
|
|
|
|
DEBUG(2,("netbios connect: name1=%s0x%x name2=%s0x%x\n",
|
|
name1, name_type1, name2, name_type2));
|
|
|
|
if (netbios_session_retarget(xconn, name1, name_type1)) {
|
|
exit_server_cleanly("retargeted client");
|
|
}
|
|
|
|
/*
|
|
* Windows NT/2k uses "*SMBSERVER" and XP uses
|
|
* "*SMBSERV" arrggg!!!
|
|
*/
|
|
if (strequal(name1, "*SMBSERVER ")
|
|
|| strequal(name1, "*SMBSERV ")) {
|
|
char *raddr;
|
|
|
|
raddr = tsocket_address_inet_addr_string(sconn->remote_address,
|
|
talloc_tos());
|
|
if (raddr == NULL) {
|
|
exit_server_cleanly("could not allocate raddr");
|
|
}
|
|
|
|
fstrcpy(name1, raddr);
|
|
}
|
|
|
|
set_local_machine_name(name1, True);
|
|
set_remote_machine_name(name2, True);
|
|
|
|
if (is_ipaddress(sconn->remote_hostname)) {
|
|
char *p = discard_const_p(char, sconn->remote_hostname);
|
|
|
|
talloc_free(p);
|
|
|
|
sconn->remote_hostname = talloc_strdup(sconn,
|
|
get_remote_machine_name());
|
|
if (sconn->remote_hostname == NULL) {
|
|
exit_server_cleanly("could not copy remote name");
|
|
}
|
|
xconn->remote_hostname = sconn->remote_hostname;
|
|
}
|
|
|
|
DEBUG(2,("netbios connect: local=%s remote=%s, name type = %x\n",
|
|
get_local_machine_name(), get_remote_machine_name(),
|
|
name_type2));
|
|
|
|
if (name_type2 == 'R') {
|
|
/* We are being asked for a pathworks session ---
|
|
no thanks! */
|
|
reply_called_name_not_present(outbuf);
|
|
break;
|
|
}
|
|
|
|
reload_services(sconn, conn_snum_used, true);
|
|
reopen_logs();
|
|
|
|
xconn->transport.nbt.got_session = true;
|
|
break;
|
|
}
|
|
|
|
case 0x89: /* session keepalive request
|
|
(some old clients produce this?) */
|
|
SCVAL(outbuf,0,NBSSkeepalive);
|
|
SCVAL(outbuf,3,0);
|
|
break;
|
|
|
|
case NBSSpositive: /* positive session response */
|
|
case NBSSnegative: /* negative session response */
|
|
case NBSSretarget: /* retarget session response */
|
|
DEBUG(0,("Unexpected session response\n"));
|
|
break;
|
|
|
|
case NBSSkeepalive: /* session keepalive */
|
|
default:
|
|
return;
|
|
}
|
|
|
|
DEBUG(5,("init msg_type=0x%x msg_flags=0x%x\n",
|
|
msg_type, msg_flags));
|
|
|
|
if (!srv_send_smb(xconn, outbuf, false, 0, false, NULL)) {
|
|
exit_server_cleanly("reply_special: srv_send_smb failed.");
|
|
}
|
|
|
|
if (CVAL(outbuf, 0) != 0x82) {
|
|
exit_server_cleanly("invalid netbios session");
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*******************************************************************
|
|
* unlink a file with all relevant access checks
|
|
*******************************************************************/
|
|
|
|
NTSTATUS unlink_internals(connection_struct *conn,
|
|
struct smb_request *req,
|
|
uint32_t dirtype,
|
|
struct smb_filename *smb_fname)
|
|
{
|
|
uint32_t fattr;
|
|
files_struct *fsp;
|
|
uint32_t dirtype_orig = dirtype;
|
|
NTSTATUS status;
|
|
int ret;
|
|
struct smb2_create_blobs *posx = NULL;
|
|
|
|
if (dirtype == 0) {
|
|
dirtype = FILE_ATTRIBUTE_NORMAL;
|
|
}
|
|
|
|
DBG_DEBUG("%s, dirtype = %d\n",
|
|
smb_fname_str_dbg(smb_fname),
|
|
dirtype);
|
|
|
|
if (!CAN_WRITE(conn)) {
|
|
return NT_STATUS_MEDIA_WRITE_PROTECTED;
|
|
}
|
|
|
|
ret = vfs_stat(conn, smb_fname);
|
|
if (ret != 0) {
|
|
return map_nt_error_from_unix(errno);
|
|
}
|
|
|
|
fattr = fdos_mode(smb_fname->fsp);
|
|
|
|
if (dirtype & FILE_ATTRIBUTE_NORMAL) {
|
|
dirtype = FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY;
|
|
}
|
|
|
|
dirtype &= (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM);
|
|
if (!dirtype) {
|
|
return NT_STATUS_NO_SUCH_FILE;
|
|
}
|
|
|
|
if (!dir_check_ftype(fattr, dirtype)) {
|
|
if (fattr & FILE_ATTRIBUTE_DIRECTORY) {
|
|
return NT_STATUS_FILE_IS_A_DIRECTORY;
|
|
}
|
|
return NT_STATUS_NO_SUCH_FILE;
|
|
}
|
|
|
|
if (dirtype_orig & 0x8000) {
|
|
/* These will never be set for POSIX. */
|
|
return NT_STATUS_NO_SUCH_FILE;
|
|
}
|
|
|
|
#if 0
|
|
if ((fattr & dirtype) & FILE_ATTRIBUTE_DIRECTORY) {
|
|
return NT_STATUS_FILE_IS_A_DIRECTORY;
|
|
}
|
|
|
|
if ((fattr & ~dirtype) & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)) {
|
|
return NT_STATUS_NO_SUCH_FILE;
|
|
}
|
|
|
|
if (dirtype & 0xFF00) {
|
|
/* These will never be set for POSIX. */
|
|
return NT_STATUS_NO_SUCH_FILE;
|
|
}
|
|
|
|
dirtype &= 0xFF;
|
|
if (!dirtype) {
|
|
return NT_STATUS_NO_SUCH_FILE;
|
|
}
|
|
|
|
/* Can't delete a directory. */
|
|
if (fattr & FILE_ATTRIBUTE_DIRECTORY) {
|
|
return NT_STATUS_FILE_IS_A_DIRECTORY;
|
|
}
|
|
#endif
|
|
|
|
#if 0 /* JRATEST */
|
|
else if (dirtype & FILE_ATTRIBUTE_DIRECTORY) /* Asked for a directory and it isn't. */
|
|
return NT_STATUS_OBJECT_NAME_INVALID;
|
|
#endif /* JRATEST */
|
|
|
|
if (smb_fname->flags & SMB_FILENAME_POSIX_PATH) {
|
|
status = make_smb2_posix_create_ctx(
|
|
talloc_tos(), &posx, 0777);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DBG_WARNING("make_smb2_posix_create_ctx failed: %s\n",
|
|
nt_errstr(status));
|
|
return status;
|
|
}
|
|
}
|
|
|
|
/* On open checks the open itself will check the share mode, so
|
|
don't do it here as we'll get it wrong. */
|
|
|
|
status = SMB_VFS_CREATE_FILE
|
|
(conn, /* conn */
|
|
req, /* req */
|
|
smb_fname, /* fname */
|
|
DELETE_ACCESS, /* access_mask */
|
|
FILE_SHARE_NONE, /* share_access */
|
|
FILE_OPEN, /* create_disposition*/
|
|
FILE_NON_DIRECTORY_FILE, /* create_options */
|
|
FILE_ATTRIBUTE_NORMAL, /* file_attributes */
|
|
0, /* oplock_request */
|
|
NULL, /* lease */
|
|
0, /* allocation_size */
|
|
0, /* private_flags */
|
|
NULL, /* sd */
|
|
NULL, /* ea_list */
|
|
&fsp, /* result */
|
|
NULL, /* pinfo */
|
|
posx, /* in_context_blobs */
|
|
NULL); /* out_context_blobs */
|
|
|
|
TALLOC_FREE(posx);
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DBG_DEBUG("SMB_VFS_CREATEFILE failed: %s\n",
|
|
nt_errstr(status));
|
|
return status;
|
|
}
|
|
|
|
status = can_set_delete_on_close(fsp, fattr);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DBG_DEBUG("can_set_delete_on_close for file %s - "
|
|
"(%s)\n",
|
|
smb_fname_str_dbg(smb_fname),
|
|
nt_errstr(status));
|
|
close_file_free(req, &fsp, NORMAL_CLOSE);
|
|
return status;
|
|
}
|
|
|
|
/* The set is across all open files on this dev/inode pair. */
|
|
if (!set_delete_on_close(fsp, True,
|
|
conn->session_info->security_token,
|
|
conn->session_info->unix_token)) {
|
|
close_file_free(req, &fsp, NORMAL_CLOSE);
|
|
return NT_STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
return close_file_free(req, &fsp, NORMAL_CLOSE);
|
|
}
|