1 /*
2    Unix SMB/CIFS implementation.
3    service (connection) handling
4    Copyright (C) Andrew Tridgell 1992-2003
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "includes.h"
21 #include "smb_server/smb_server.h"
22 #include "smbd/service_stream.h"
23 #include "ntvfs/ntvfs.h"
24 #include "param/param.h"
25 
26 /****************************************************************************
27   Make a connection, given the snum to connect to, and the vuser of the
28   connecting user if appropriate.
29   Does note invoke the NTVFS connection hook
30 ****************************************************************************/
make_connection_scfg(struct smbsrv_request * req,struct share_config * scfg,enum ntvfs_type type,DATA_BLOB password,const char * dev)31 static NTSTATUS make_connection_scfg(struct smbsrv_request *req,
32 				     struct share_config *scfg,
33 				     enum ntvfs_type type,
34 				     DATA_BLOB password,
35 				     const char *dev)
36 {
37 	struct smbsrv_tcon *tcon;
38 	NTSTATUS status;
39 	uint64_t ntvfs_caps = 0;
40 
41 	tcon = smbsrv_smb_tcon_new(req->smb_conn, scfg->name);
42 	if (!tcon) {
43 		DEBUG(0,("Couldn't find free connection.\n"));
44 		return NT_STATUS_INSUFFICIENT_RESOURCES;
45 	}
46 	req->tcon = tcon;
47 
48 	if (req->smb_conn->negotiate.client_caps & CAP_LEVEL_II_OPLOCKS) {
49 		ntvfs_caps |= NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS;
50 	}
51 
52 	/* init ntvfs function pointers */
53 	status = ntvfs_init_connection(tcon, scfg, type,
54 				       req->smb_conn->negotiate.protocol,
55 				       ntvfs_caps,
56 				       req->smb_conn->connection->event.ctx,
57 				       req->smb_conn->connection->msg_ctx,
58 				       req->smb_conn->lp_ctx,
59 				       req->smb_conn->connection->server_id,
60 				       &tcon->ntvfs);
61 	if (!NT_STATUS_IS_OK(status)) {
62 		DEBUG(0, ("make_connection_scfg: connection failed for service %s\n",
63 			  scfg->name));
64 		goto failed;
65 	}
66 
67 	status = ntvfs_set_oplock_handler(tcon->ntvfs, smbsrv_send_oplock_break, tcon);
68 	if (!NT_STATUS_IS_OK(status)) {
69 		DEBUG(0,("make_connection: NTVFS failed to set the oplock handler!\n"));
70 		goto failed;
71 	}
72 
73 	status = ntvfs_set_addresses(tcon->ntvfs,
74 				     req->smb_conn->connection->local_address,
75 				     req->smb_conn->connection->remote_address);
76 	if (!NT_STATUS_IS_OK(status)) {
77 		DEBUG(0,("make_connection: NTVFS failed to set the addresses!\n"));
78 		goto failed;
79 	}
80 
81 	status = ntvfs_set_handle_callbacks(tcon->ntvfs,
82 					    smbsrv_handle_create_new,
83 					    smbsrv_handle_make_valid,
84 					    smbsrv_handle_destroy,
85 					    smbsrv_handle_search_by_wire_key,
86 					    smbsrv_handle_get_wire_key,
87 					    tcon);
88 	if (!NT_STATUS_IS_OK(status)) {
89 		DEBUG(0,("make_connection: NTVFS failed to set the handle callbacks!\n"));
90 		goto failed;
91 	}
92 
93 	return NT_STATUS_OK;
94 
95 failed:
96 	req->tcon = NULL;
97 	talloc_free(tcon);
98 	return status;
99 }
100 
101 /****************************************************************************
102  Make a connection to a service.
103  *
104  * @param service
105 ****************************************************************************/
make_connection(struct smbsrv_request * req,const char * service,DATA_BLOB password,const char * dev)106 static NTSTATUS make_connection(struct smbsrv_request *req,
107 				const char *service, DATA_BLOB password,
108 				const char *dev)
109 {
110 	NTSTATUS status;
111 	enum ntvfs_type type;
112 	const char *type_str;
113 	struct share_config *scfg;
114 	char *sharetype;
115 
116 	/* the service might be of the form \\SERVER\SHARE. Should we put
117 	   the server name we get from this somewhere? */
118 	if (strncmp(service, "\\\\", 2) == 0) {
119 		char *p = strchr(service+2, '\\');
120 		if (p) {
121 			service = p + 1;
122 		}
123 	}
124 
125 	status = share_get_config(req, req->smb_conn->share_context, service, &scfg);
126 	if (!NT_STATUS_IS_OK(status)) {
127 		DEBUG(0,("make_connection: couldn't find service %s: %s\n", service, nt_errstr(status)));
128 		return NT_STATUS_BAD_NETWORK_NAME;
129 	}
130 
131 	/* TODO: check the password, when it's share level security! */
132 
133 	if (!socket_check_access(req->smb_conn->connection->socket,
134 				 scfg->name,
135 				 share_string_list_option(req, scfg, SHARE_HOSTS_ALLOW),
136 				 share_string_list_option(req, scfg, SHARE_HOSTS_DENY))) {
137 		return NT_STATUS_ACCESS_DENIED;
138 	}
139 
140 	/* work out what sort of connection this is */
141 	sharetype = share_string_option(req, scfg, "type", "DISK");
142 	if (sharetype && strcmp(sharetype, "IPC") == 0) {
143 		type = NTVFS_IPC;
144 		type_str = "IPC";
145 	} else if (sharetype && strcmp(sharetype, "PRINTER") == 0) {
146 		type = NTVFS_PRINT;
147 		type_str = "LPT:";
148 	} else {
149 		type = NTVFS_DISK;
150 		type_str = "A:";
151 	}
152 	TALLOC_FREE(sharetype);
153 
154 	if (strcmp(dev, "?????") != 0 && strcasecmp(type_str, dev) != 0) {
155 		/* the client gave us the wrong device type */
156 		return NT_STATUS_BAD_DEVICE_TYPE;
157 	}
158 
159 	return make_connection_scfg(req, scfg, type, password, dev);
160 }
161 
162 /*
163   backend for tree connect call, in preparation for calling ntvfs_connect()
164 */
smbsrv_tcon_backend(struct smbsrv_request * req,union smb_tcon * con)165 NTSTATUS smbsrv_tcon_backend(struct smbsrv_request *req, union smb_tcon *con)
166 {
167 	NTSTATUS status;
168 
169 	if (con->generic.level == RAW_TCON_TCON) {
170 		DATA_BLOB password;
171 		password = data_blob_string_const(con->tcon.in.password);
172 
173 		status = make_connection(req, con->tcon.in.service, password, con->tcon.in.dev);
174 
175 		if (!NT_STATUS_IS_OK(status)) {
176 			return status;
177 		}
178 
179 		con->tcon.out.max_xmit = req->smb_conn->negotiate.max_recv;
180 		con->tcon.out.tid = req->tcon->tid;
181 
182 		return status;
183 	}
184 
185 	/* TODO: take a look at tconx.in.flags! */
186 
187 	status = make_connection(req, con->tconx.in.path, con->tconx.in.password,
188 				 con->tconx.in.device);
189 	if (!NT_STATUS_IS_OK(status)) {
190 		return status;
191 	}
192 
193 	con->tconx.out.tid = req->tcon->tid;
194 	con->tconx.out.options = SMB_SUPPORT_SEARCH_BITS | (share_int_option(req->tcon->ntvfs->config, SHARE_CSC_POLICY, SHARE_CSC_POLICY_DEFAULT) << 2);
195 	if (share_bool_option(req->tcon->ntvfs->config, SHARE_MSDFS_ROOT, SHARE_MSDFS_ROOT_DEFAULT) && lpcfg_host_msdfs(req->smb_conn->lp_ctx)) {
196 		con->tconx.out.options |= SMB_SHARE_IN_DFS;
197 	}
198 
199 	return status;
200 }
201