1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * Tree connect and disconnect functions to support SMB shares.
28 * These functions are described in the CIFS draft 1.0 Protocol
29 * Specification (December 19, 1997).
30 */
31
32 #include <sys/errno.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <synch.h>
36 #include <pthread.h>
37
38 #include <smbsrv/libsmbrdr.h>
39 #include <smbrdr.h>
40
41
42 /*
43 * The table of shares set up with the domain controller.
44 */
45 static struct sdb_netuse netuse_table[N_NETUSE_TABLE];
46
47 static DWORD smbrdr_tree_connectx(struct sdb_session *session,
48 struct sdb_netuse *netuse, char *path, int path_len);
49
50 static struct sdb_netuse *smbrdr_netuse_alloc(struct sdb_session *session,
51 char *sharename);
52
53 static void
smbrdr_netuse_clear(struct sdb_netuse * netuse)54 smbrdr_netuse_clear(struct sdb_netuse *netuse)
55 {
56 bzero(netuse, sizeof (struct sdb_netuse) - sizeof (mutex_t));
57 }
58
59 static void
smbrdr_netuse_free(struct sdb_netuse * netuse)60 smbrdr_netuse_free(struct sdb_netuse *netuse)
61 {
62 smbrdr_netuse_clear(netuse);
63 (void) mutex_unlock(&netuse->mtx);
64 }
65
66 /*
67 * smbrdr_tree_connect
68 *
69 * Establish a share (tree connect). We need to retrieve the session
70 * for the specified host and allocate a netuse structure. We set up
71 * the path here (UNC encoded) to make handling the malloc/free easier
72 * and pass everything on to smbrdr_tree_connectx where. If everything
73 * goes well, a valid tid will be stored in the netuse structure.
74 *
75 * On success, a pointer to the netuse is returned. Otherwise the
76 * netuse is cleared and a null pointer is returned.
77 */
78 DWORD
smbrdr_tree_connect(char * hostname,char * domain,char * username,char * sharename,unsigned short * tid)79 smbrdr_tree_connect(char *hostname, char *domain, char *username,
80 char *sharename, unsigned short *tid)
81 {
82 struct sdb_session *session;
83 struct sdb_netuse *netuse;
84 char *path;
85 int path_len;
86 DWORD status;
87
88 *tid = 0;
89
90 if (smbrdr_logon(hostname, domain, username) != 0)
91 return (NT_STATUS_LOGON_FAILURE);
92
93 session = smbrdr_session_lock(hostname, SDB_SLCK_READ);
94 if (session == NULL) {
95 smb_log(smbrdr_log_hdl, LOG_DEBUG,
96 "smbrdr_tree_connect: no session for %s@%s",
97 username, hostname);
98 return (NT_STATUS_INTERNAL_ERROR);
99 }
100
101
102 if ((netuse = smbrdr_netuse_alloc(session, sharename)) == 0) {
103 smb_log(smbrdr_log_hdl, LOG_DEBUG,
104 "smbrdr_tree_connect: init failed");
105 smbrdr_session_unlock(session);
106 return (NT_STATUS_INTERNAL_ERROR);
107 }
108
109 /*
110 * Add some padding for the back-slash separators
111 * and the null-terminator.
112 */
113 path_len = SMB_PI_MAX_HOST + MAX_SHARE_NAME + 5;
114
115 if ((path = (char *)malloc(path_len)) == 0) {
116 smbrdr_netuse_free(netuse);
117 smbrdr_session_unlock(session);
118 smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_tree_connect: %s",
119 strerror(ENOMEM));
120 return (NT_STATUS_NO_MEMORY);
121 }
122
123 bzero(path, path_len);
124 (void) snprintf(path, path_len, "\\\\%s\\%s", hostname, sharename);
125 if (session->remote_caps & CAP_UNICODE)
126 path_len = smb_wcequiv_strlen(path);
127 else
128 path_len = strlen(path);
129
130 if ((status = smbrdr_tree_connectx(session, netuse, path, path_len))
131 != NT_STATUS_SUCCESS) {
132 smbrdr_netuse_free(netuse);
133 smbrdr_session_unlock(session);
134 smb_log(smbrdr_log_hdl, LOG_DEBUG,
135 "smbrdr_tree_connect: %s failed", path);
136 free(path);
137 return (status);
138 }
139
140 free(path);
141 *tid = netuse->tid;
142 (void) mutex_unlock(&netuse->mtx);
143 smbrdr_session_unlock(session);
144 return (NT_STATUS_SUCCESS);
145 }
146
147
148 /*
149 * smbrdr_tree_connectx
150 *
151 * This message requests a share (tree connect) request to the server
152 * associated with the session. The password is not relevant here if
153 * the session was establishment using setup_andx. The outgoing tid
154 * will be ignored - a valid one will be returned by the server.
155 *
156 * Returns 0 on success. Otherwise returns a -ve error code.
157 */
158 static DWORD
smbrdr_tree_connectx(struct sdb_session * session,struct sdb_netuse * netuse,char * path,int path_len)159 smbrdr_tree_connectx(struct sdb_session *session, struct sdb_netuse *netuse,
160 char *path, int path_len)
161 {
162 smb_hdr_t smb_hdr;
163 smbrdr_handle_t srh;
164 smb_msgbuf_t *mb;
165 unsigned short flags;
166 char *password;
167 unsigned short password_len;
168 char *service;
169 unsigned service_len;
170 unsigned short data_bytes;
171 DWORD status;
172 int rc;
173
174 status = smbrdr_request_init(&srh, SMB_COM_TREE_CONNECT_ANDX,
175 session, &session->logon, 0);
176
177 if (status != NT_STATUS_SUCCESS) {
178 smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_tree_connectx: %s",
179 xlate_nt_status(status));
180 return (status);
181 }
182
183 mb = &srh.srh_mbuf;
184
185 flags = 0; /* no flags */
186 password = "";
187 password_len = 1; /* including nul */
188 service = "?????"; /* does this work? */
189 service_len = strlen(service);
190
191 /*
192 * Calculate the BCC. The path is in UNICODE
193 * but the service is in ASCII.
194 */
195 data_bytes = password_len;
196 data_bytes += path_len + 1;
197 data_bytes += service_len + 1;
198
199 rc = smb_msgbuf_encode(mb, "bb1.wwww#cus",
200 4, /* smb_wct */
201 0xff, /* AndXCommand (none) */
202 0xffff, /* AndXOffset */
203 flags, /* Flags */
204 password_len, /* PasswordLength */
205 data_bytes+1, /* smb_bcc */
206 password_len, password, /* Password */
207 path, /* Path */
208 service); /* Service */
209
210 if (rc <= 0) {
211 smb_log(smbrdr_log_hdl, LOG_DEBUG,
212 "smbrdr_tree_connectx: encode failed");
213 smbrdr_handle_free(&srh);
214 return (NT_STATUS_INTERNAL_ERROR);
215 }
216
217 status = smbrdr_exchange(&srh, &smb_hdr, 0);
218 if (status != NT_STATUS_SUCCESS) {
219 smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_tree_connectx: %s",
220 xlate_nt_status(status));
221 smbrdr_handle_free(&srh);
222 return (status);
223 }
224
225 netuse->tid = smb_hdr.tid;
226 netuse->state = SDB_NSTATE_CONNECTED;
227 smbrdr_handle_free(&srh);
228 return (NT_STATUS_SUCCESS);
229 }
230
231 /*
232 * smbrdr_netuse_logoff
233 *
234 * This function can be used when closing a session to ensure that all
235 * shares associated with the specified session are disconnected and
236 * the resources released. We also notify the pipe interface to ensure
237 * that any pipes associated with this share are also closed. This
238 * function silently ignores errors because we have no idea what state
239 * the session is in. We are more interested in releasing resources.
240 */
241 void
smbrdr_netuse_logoff(unsigned short uid)242 smbrdr_netuse_logoff(unsigned short uid)
243 {
244 struct sdb_netuse *netuse;
245 int i;
246
247 for (i = 0; i < N_NETUSE_TABLE; ++i) {
248 netuse = &netuse_table[i];
249 (void) mutex_lock(&netuse->mtx);
250 if (netuse->uid == uid)
251 (void) smbrdr_tdcon(netuse);
252 (void) mutex_unlock(&netuse->mtx);
253 }
254 }
255
256 int
smbrdr_tree_disconnect(unsigned short tid)257 smbrdr_tree_disconnect(unsigned short tid)
258 {
259 struct sdb_netuse *netuse;
260 int rc = -1;
261
262 netuse = smbrdr_netuse_get(tid);
263 if (netuse) {
264 (void) smbrdr_tdcon(netuse);
265 smbrdr_netuse_put(netuse);
266 rc = 0;
267 }
268
269 return (rc);
270 }
271
272 /*
273 * smbrdr_tdcon
274 *
275 * Disconnect a share. This message informs the server that we no longer
276 * wish to access the resource specified by tid, obtained via a prior
277 * smbrdr_tree_connect. The tid is passed in the SMB header so the setup
278 * for this call is very straightforward.
279 *
280 * Returns 0 on success. Otherwise returns a -ve error code.
281 */
282 int
smbrdr_tdcon(struct sdb_netuse * netuse)283 smbrdr_tdcon(struct sdb_netuse *netuse)
284 {
285 struct sdb_session *session;
286 smbrdr_handle_t srh;
287 smb_hdr_t smb_hdr;
288 DWORD status;
289 int rc;
290
291 netuse->state = SDB_NSTATE_DISCONNECTING;
292 smbrdr_ofile_end_of_share(netuse->tid);
293
294 if ((session = netuse->session) == NULL) {
295 smbrdr_netuse_clear(netuse);
296 return (0);
297 }
298
299 if ((session->state != SDB_SSTATE_NEGOTIATED) &&
300 (session->state != SDB_SSTATE_DISCONNECTING)) {
301 smbrdr_netuse_clear(netuse);
302 return (0);
303 }
304
305 status = smbrdr_request_init(&srh, SMB_COM_TREE_DISCONNECT,
306 session, &session->logon, netuse);
307
308 if (status != NT_STATUS_SUCCESS) {
309 smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_tdcon: %s",
310 xlate_nt_status(status));
311 /* should we clear here? */
312 smbrdr_netuse_clear(netuse);
313 return (-1);
314 }
315
316 rc = smb_msgbuf_encode(&srh.srh_mbuf, "bw.", 0, 0);
317 if (rc < 0) {
318 smb_log(smbrdr_log_hdl, LOG_DEBUG,
319 "smbrdr_tdcon: encode failed");
320 smbrdr_handle_free(&srh);
321 /* should we clear here? */
322 smbrdr_netuse_clear(netuse);
323 return (rc);
324 }
325
326 status = smbrdr_exchange(&srh, &smb_hdr, 0);
327 if (status != NT_STATUS_SUCCESS) {
328 smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_tdcon: %s",
329 xlate_nt_status(status));
330 rc = -1;
331 } else {
332 rc = 0;
333 }
334
335 smbrdr_handle_free(&srh);
336 smbrdr_netuse_clear(netuse);
337 return (rc);
338 }
339
340
341 /*
342 * smbrdr_netuse_alloc
343 *
344 * Find a slot in the table for a share. Each share is associated with
345 * a session and assigned a local drive letter name and a sharename.
346 * If a slot is already allocated to the specified share, a pointer to
347 * it is returned. Otherwise we allocate and initialize a new slot in
348 * the table. If the table is full, a null pointer will be returned.
349 *
350 * IMPORTANT! the returned netuse will be locked caller has to unlock
351 * it after it's done with the pointer.
352 */
353 static struct sdb_netuse *
smbrdr_netuse_alloc(struct sdb_session * session,char * sharename)354 smbrdr_netuse_alloc(struct sdb_session *session, char *sharename)
355 {
356 struct sdb_netuse *netuse;
357 int i;
358
359 if (session == NULL || sharename == NULL)
360 return (NULL);
361
362 for (i = 0; i < N_NETUSE_TABLE; ++i) {
363 netuse = &netuse_table[i];
364
365 (void) mutex_lock(&netuse->mtx);
366 if (netuse->state == SDB_NSTATE_START) {
367 netuse->session = session;
368 netuse->letter = i + '0';
369 netuse->sid = session->sid;
370 netuse->uid = session->logon.uid;
371 netuse->tid = 0;
372 (void) strcpy(netuse->share, sharename);
373 netuse->state = SDB_NSTATE_INIT;
374 return (netuse);
375 }
376 (void) mutex_unlock(&netuse->mtx);
377 }
378
379 smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_netuse_alloc: table full");
380 return (0);
381 }
382
383 /*
384 * smbrdr_netuse_put
385 *
386 * Unlock given netuse structure.
387 */
388 void
smbrdr_netuse_put(struct sdb_netuse * netuse)389 smbrdr_netuse_put(struct sdb_netuse *netuse)
390 {
391 (void) mutex_unlock(&netuse->mtx);
392 }
393
394 /*
395 * smbrdr_netuse_get
396 *
397 * Find the netuse structure associated with the specified tid and
398 * return a pointer to it. A null pointer is returned if no match
399 * can be found.
400 *
401 * IMPORTANT! the returned netuse will be locked caller has to unlock
402 * it after it's done with the pointer.
403 */
404 struct sdb_netuse *
smbrdr_netuse_get(int tid)405 smbrdr_netuse_get(int tid)
406 {
407 struct sdb_session *session;
408 struct sdb_netuse *netuse;
409 int i;
410
411 for (i = 0; i < N_NETUSE_TABLE; ++i) {
412 netuse = &netuse_table[i];
413
414 (void) mutex_lock(&netuse->mtx);
415
416 if (netuse->tid == tid) {
417 session = netuse->session;
418
419 /*
420 * status check:
421 * make sure all the structures are in the right state
422 */
423 if (session &&
424 (netuse->state == SDB_NSTATE_CONNECTED) &&
425 (session->logon.state == SDB_LSTATE_SETUP) &&
426 (session->state == SDB_SSTATE_NEGOTIATED)) {
427 /* sanity check */
428 if ((netuse->sid == session->sid) &&
429 (netuse->uid == session->logon.uid))
430 return (netuse);
431 else
432 /* invalid structure */
433 smbrdr_netuse_clear(netuse);
434 }
435
436 }
437
438 (void) mutex_unlock(&netuse->mtx);
439 }
440
441 smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_netuse_get: %d: no such TID",
442 tid);
443 return (0);
444 }
445
446 /*
447 * smbrdr_dump_netuse
448 */
449 void
smbrdr_dump_netuse()450 smbrdr_dump_netuse()
451 {
452 struct sdb_netuse *netuse;
453 int i;
454
455 for (i = 0; i < N_NETUSE_TABLE; ++i) {
456 netuse = &netuse_table[i];
457 (void) mutex_lock(&netuse->mtx);
458 if (netuse->session) {
459 smb_log(smbrdr_log_hdl, LOG_DEBUG,
460 "smbrdr_dump_netuse: tree[%d]: %s (tid=%d)", i,
461 netuse->share, netuse->tid);
462 smb_log(smbrdr_log_hdl, LOG_DEBUG,
463 "smbrdr_dump_netuse: tree[%d]: session(%d), "
464 "user(%d)", i, netuse->session->sock, netuse->uid);
465 }
466 (void) mutex_unlock(&netuse->mtx);
467 }
468 }
469