diff options
Diffstat (limited to 'source4/smb_server/smb/service.c')
-rw-r--r-- | source4/smb_server/smb/service.c | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/source4/smb_server/smb/service.c b/source4/smb_server/smb/service.c new file mode 100644 index 0000000000..213cf6726b --- /dev/null +++ b/source4/smb_server/smb/service.c @@ -0,0 +1,194 @@ +/* + Unix SMB/CIFS implementation. + service (connection) handling + Copyright (C) Andrew Tridgell 1992-2003 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "smb_server/smb_server.h" +#include "smbd/service_stream.h" + + +/** + * Find a service entry. service is always in dos codepage. + * + * @param service is modified (to canonical form??) + **/ +static int find_service(const char *service) +{ + int iService; + + iService = lp_servicenumber(service); + + if (iService >= 0 && !lp_snum_ok(iService)) { + DEBUG(0,("Invalid snum %d for %s\n",iService, service)); + iService = -1; + } + + if (iService == -1) { + DEBUG(3,("find_service() failed to find service %s\n", service)); + } + + return iService; +} + + +/**************************************************************************** + Make a connection, given the snum to connect to, and the vuser of the + connecting user if appropriate. +****************************************************************************/ +static NTSTATUS make_connection_snum(struct smbsrv_request *req, + int snum, enum ntvfs_type type, + DATA_BLOB password, + const char *dev) +{ + struct smbsrv_tcon *tcon; + NTSTATUS status; + + if (!socket_check_access(req->smb_conn->connection->socket, + lp_servicename(snum), + lp_hostsallow(snum), + lp_hostsdeny(snum))) { + return NT_STATUS_ACCESS_DENIED; + } + + tcon = smbsrv_tcon_new(req->smb_conn); + if (!tcon) { + DEBUG(0,("Couldn't find free connection.\n")); + return NT_STATUS_INSUFFICIENT_RESOURCES; + } + req->tcon = tcon; + + tcon->service = snum; + + /* init ntvfs function pointers */ + status = ntvfs_init_connection(req, type); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("ntvfs_init_connection failed for service %s\n", + lp_servicename(tcon->service))); + req->tcon = NULL; + talloc_free(tcon); + return status; + } + + /* Invoke NTVFS connection hook */ + status = ntvfs_connect(req, lp_servicename(snum)); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("make_connection: NTVFS make connection failed!\n")); + req->tcon = NULL; + talloc_free(tcon); + return status; + } + + return NT_STATUS_OK; +} + +/**************************************************************************** + Make a connection to a service. + * + * @param service +****************************************************************************/ +static NTSTATUS make_connection(struct smbsrv_request *req, + const char *service, DATA_BLOB password, + const char *dev) +{ + int snum; + enum ntvfs_type type; + const char *type_str; + + /* TODO: check the password, when it's share level security! */ + + /* the service might be of the form \\SERVER\SHARE. Should we put + the server name we get from this somewhere? */ + if (strncmp(service, "\\\\", 2) == 0) { + char *p = strchr(service+2, '\\'); + if (p) { + service = p + 1; + } + } + + snum = find_service(service); + + if (snum == -1) { + DEBUG(0,("couldn't find service %s\n", service)); + return NT_STATUS_BAD_NETWORK_NAME; + } + + /* work out what sort of connection this is */ + if (strcmp(lp_fstype(snum), "IPC") == 0) { + type = NTVFS_IPC; + type_str = "IPC"; + } else if (lp_print_ok(snum)) { + type = NTVFS_PRINT; + type_str = "LPT:"; + } else { + type = NTVFS_DISK; + type_str = "A:"; + } + + if (strcmp(dev, "?????") != 0 && strcasecmp(type_str, dev) != 0) { + /* the client gave us the wrong device type */ + return NT_STATUS_BAD_DEVICE_TYPE; + } + + return make_connection_snum(req, snum, type, password, dev); +} + +/* + backend for tree connect call +*/ +NTSTATUS tcon_backend(struct smbsrv_request *req, union smb_tcon *con) +{ + NTSTATUS status; + + /* can only do bare tcon in share level security */ + if (!req->session && lp_security() != SEC_SHARE) { + return NT_STATUS_ACCESS_DENIED; + } + + if (con->generic.level == RAW_TCON_TCON) { + DATA_BLOB password; + password = data_blob(con->tcon.in.password, strlen(con->tcon.in.password) + 1); + + status = make_connection(req, con->tcon.in.service, password, con->tcon.in.dev); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + con->tcon.out.max_xmit = req->smb_conn->negotiate.max_recv; + con->tcon.out.tid = req->tcon->tid; + + return status; + } + + status = make_connection(req, con->tconx.in.path, con->tconx.in.password, + con->tconx.in.device); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + con->tconx.out.tid = req->tcon->tid; + con->tconx.out.dev_type = talloc_strdup(req, req->tcon->dev_type); + con->tconx.out.fs_type = talloc_strdup(req, req->tcon->fs_type); + con->tconx.out.options = SMB_SUPPORT_SEARCH_BITS | (lp_csc_policy(req->tcon->service) << 2); + if (lp_msdfs_root(req->tcon->service) && lp_host_msdfs()) { + con->tconx.out.options |= SMB_SHARE_IN_DFS; + } + + return status; +} |