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 */ 45static struct sdb_netuse netuse_table[N_NETUSE_TABLE]; 46 47static DWORD smbrdr_tree_connectx(struct sdb_session *session, 48 struct sdb_netuse *netuse, char *path, int path_len); 49 50static struct sdb_netuse *smbrdr_netuse_alloc(struct sdb_session *session, 51 char *sharename); 52 53static void 54smbrdr_netuse_clear(struct sdb_netuse *netuse) 55{ 56 bzero(netuse, sizeof (struct sdb_netuse) - sizeof (mutex_t)); 57} 58 59static void 60smbrdr_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 */ 78DWORD 79smbrdr_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 */ 158static DWORD 159smbrdr_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 */ 241void 242smbrdr_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 256int 257smbrdr_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 */ 282int 283smbrdr_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 */ 353static struct sdb_netuse * 354smbrdr_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 */ 388void 389smbrdr_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 */ 404struct sdb_netuse * 405smbrdr_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 */ 449void 450smbrdr_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