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