r4232: added server support for multi-part SMBtrans requests, while
[samba.git] / source4 / smb_server / service.c
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 2 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, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22 #include "smb_server/smb_server.h"
23
24
25
26 /****************************************************************************
27  Add a home service. Returns the new service number or -1 if fail.
28 ****************************************************************************/
29 int add_home_service(const char *service, const char *username, const char *homedir)
30 {
31         int iHomeService;
32
33         if (!service || !homedir)
34                 return -1;
35
36         if ((iHomeService = lp_servicenumber(HOMES_NAME)) < 0)
37                 return -1;
38
39         /*
40          * If this is a winbindd provided username, remove
41          * the domain component before adding the service.
42          * Log a warning if the "path=" parameter does not
43          * include any macros.
44          */
45
46         {
47                 const char *p = strchr(service,*lp_winbind_separator());
48
49                 /* We only want the 'user' part of the string */
50                 if (p) {
51                         service = p + 1;
52                 }
53         }
54
55         if (!lp_add_home(service, iHomeService, username, homedir)) {
56                 return -1;
57         }
58         
59         return lp_servicenumber(service);
60
61 }
62
63
64 /**
65  * Find a service entry. service is always in dos codepage.
66  *
67  * @param service is modified (to canonical form??)
68  **/
69 static int find_service(const char *service)
70 {
71         int iService;
72
73         iService = lp_servicenumber(service);
74
75         /* If we still don't have a service, attempt to add it as a printer. */
76         if (iService == -1) {
77                 int iPrinterService;
78
79                 if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) >= 0) {
80                         const char *pszTemp;
81
82                         DEBUG(3,("checking whether %s is a valid printer name...\n", service));
83                         pszTemp = lp_printcapname();
84                         if ((pszTemp != NULL) && pcap_printername_ok(service, pszTemp)) {
85                                 DEBUG(3,("%s is a valid printer name\n", service));
86                                 DEBUG(3,("adding %s as a printer service\n", service));
87                                 lp_add_printer(service, iPrinterService);
88                                 iService = lp_servicenumber(service);
89                                 if (iService < 0)
90                                         DEBUG(0,("failed to add %s as a printer service!\n", service));
91                         } else {
92                                 DEBUG(3,("%s is not a valid printer name\n", service));
93                         }
94                 }
95         }
96
97         /* Check for default vfs service?  Unsure whether to implement this */
98         if (iService == -1) {
99         }
100
101         /* just possibly it's a default service? */
102         if (iService == -1) {
103                 const char *pdefservice = lp_defaultservice();
104                 if (pdefservice && *pdefservice && 
105                     !strequal(pdefservice,service) &&
106                     !strstr(service,"..")) {
107                         /*
108                          * We need to do a local copy here as lp_defaultservice() 
109                          * returns one of the rotating lp_string buffers that
110                          * could get overwritten by the recursive find_service() call
111                          * below. Fix from Josef Hinteregger <joehtg@joehtg.co.at>.
112                          */
113                         pstring defservice;
114                         pstrcpy(defservice, pdefservice);
115                         iService = find_service(defservice);
116                         if (iService >= 0) {
117                                 /* REWRITE: all_string_sub(service, "_","/",0); */
118                                 iService = lp_add_service(service, iService);
119                         }
120                 }
121         }
122
123         if (iService >= 0 && !VALID_SNUM(iService)) {
124                 DEBUG(0,("Invalid snum %d for %s\n",iService, service));
125                 iService = -1;
126         }
127
128         if (iService == -1) {
129                 DEBUG(3,("find_service() failed to find service %s\n", service));
130         }
131
132         return iService;
133 }
134
135
136 /****************************************************************************
137   Make a connection, given the snum to connect to, and the vuser of the
138   connecting user if appropriate.
139 ****************************************************************************/
140 static NTSTATUS make_connection_snum(struct smbsrv_request *req,
141                                      int snum, enum ntvfs_type type,
142                                      DATA_BLOB password, 
143                                      const char *dev)
144 {
145         struct smbsrv_tcon *tcon;
146         NTSTATUS status;
147
148         if (!socket_check_access(req->smb_conn->connection->socket, 
149                                  lp_servicename(snum), 
150                                  lp_hostsallow(snum), 
151                                  lp_hostsdeny(snum))) {
152                 return NT_STATUS_ACCESS_DENIED;
153         }
154
155         tcon = conn_new(req->smb_conn);
156         if (!tcon) {
157                 DEBUG(0,("Couldn't find free connection.\n"));
158                 return NT_STATUS_INSUFFICIENT_RESOURCES;
159         }
160         req->tcon = tcon;
161
162         tcon->service = snum;
163
164         /* init ntvfs function pointers */
165         status = ntvfs_init_connection(req, type);
166         if (!NT_STATUS_IS_OK(status)) {
167                 DEBUG(0, ("ntvfs_init_connection failed for service %s\n", lp_servicename(SNUM(tcon))));
168                 conn_free(req->smb_conn, tcon);
169                 return status;
170         }
171         
172         /* Invoke NTVFS connection hook */
173         status = ntvfs_connect(req, lp_servicename(snum));
174         if (!NT_STATUS_IS_OK(status)) {
175                 DEBUG(0,("make_connection: NTVFS make connection failed!\n"));
176                 conn_free(req->smb_conn, tcon);
177                 return status;
178         }
179         
180         return NT_STATUS_OK;
181 }
182
183 /****************************************************************************
184  Make a connection to a service.
185  *
186  * @param service 
187 ****************************************************************************/
188 static NTSTATUS make_connection(struct smbsrv_request *req,
189                                 const char *service, DATA_BLOB password, 
190                                 const char *dev, uint16_t vuid)
191 {
192         int snum;
193         enum ntvfs_type type;
194         const char *type_str;
195
196         /* the service might be of the form \\SERVER\SHARE. Should we put
197            the server name we get from this somewhere? */
198         if (strncmp(service, "\\\\", 2) == 0) {
199                 char *p = strchr(service+2, '\\');
200                 if (p) {
201                         service = p + 1;
202                 }
203         }
204
205         snum = find_service(service);
206
207         if (snum == -1) {
208                 DEBUG(0,("couldn't find service %s\n", service));
209                 return NT_STATUS_BAD_NETWORK_NAME;
210         }
211
212         /* work out what sort of connection this is */
213         if (strcmp(lp_fstype(snum), "IPC") == 0) {
214                 type = NTVFS_IPC;
215                 type_str = "IPC";
216         } else if (lp_print_ok(snum)) {
217                 type = NTVFS_PRINT;
218                 type_str = "LPT:";
219         } else {
220                 type = NTVFS_DISK;
221                 type_str = "A:";
222         }
223
224         if (strcmp(dev, "?????") != 0 && strcasecmp(type_str, dev) != 0) {
225                 /* the client gave us the wrong device type */
226                 return NT_STATUS_BAD_DEVICE_TYPE;
227         }
228
229         return make_connection_snum(req, snum, type, password, dev);
230 }
231
232 /****************************************************************************
233 close a cnum
234 ****************************************************************************/
235 void close_cnum(struct smbsrv_tcon *tcon)
236 {
237         DEBUG(3,("%s closed connection to service %s\n",
238                  socket_get_peer_addr(tcon->smb_conn->connection->socket, tcon),
239                  lp_servicename(SNUM(tcon))));
240
241         /* tell the ntvfs backend that we are disconnecting */
242         ntvfs_disconnect(tcon);
243
244         conn_free(tcon->smb_conn, tcon);
245 }
246
247
248
249 /*
250   backend for tree connect call
251 */
252 NTSTATUS tcon_backend(struct smbsrv_request *req, union smb_tcon *con)
253 {
254         NTSTATUS status;
255         uint16_t vuid = UID_FIELD_INVALID;
256
257         /* can only do bare tcon in share level security */
258         if (req->session == NULL && lp_security() != SEC_SHARE) {
259                 return NT_STATUS_ACCESS_DENIED;
260         }
261
262         if (req->session) {
263                 vuid = req->session->vuid;
264         }
265
266         if (con->generic.level == RAW_TCON_TCON) {
267                 DATA_BLOB password;
268                 password = data_blob(con->tcon.in.password, strlen(con->tcon.in.password) + 1);
269
270                 status = make_connection(req, con->tcon.in.service, password, con->tcon.in.dev, vuid);
271                 
272                 if (!NT_STATUS_IS_OK(status)) {
273                         return status;
274                 }
275
276                 con->tcon.out.max_xmit = req->smb_conn->negotiate.max_recv;
277                 con->tcon.out.cnum = req->tcon->cnum;
278                 
279                 return status;
280         } 
281
282         status = make_connection(req, con->tconx.in.path, con->tconx.in.password, 
283                                  con->tconx.in.device, vuid);
284         if (!NT_STATUS_IS_OK(status)) {
285                 return status;
286         }
287
288         con->tconx.out.cnum = req->tcon->cnum;
289         con->tconx.out.dev_type = talloc_strdup(req, req->tcon->dev_type);
290         con->tconx.out.fs_type = talloc_strdup(req, req->tcon->fs_type);
291         con->tconx.out.options = SMB_SUPPORT_SEARCH_BITS | (lp_csc_policy(req->tcon->service) << 2);
292         if (lp_msdfs_root(req->tcon->service) && lp_host_msdfs()) {
293                 con->tconx.out.options |= SMB_SHARE_IN_DFS;
294         }
295
296         return status;
297 }