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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * Tree connect and disconnect functions to support SMB shares. 30 * These functions are described in the CIFS draft 1.0 Protocol 31 * Specification (December 19, 1997). 32 */ 33 34 #include <sys/errno.h> 35 #include <string.h> 36 #include <strings.h> 37 #include <syslog.h> 38 #include <synch.h> 39 #include <pthread.h> 40 41 #include <smbsrv/libsmbrdr.h> 42 #include <smbrdr.h> 43 #include <smbsrv/ntstatus.h> 44 45 46 /* 47 * The table of shares set up with the domain controller. 48 */ 49 static struct sdb_netuse netuse_table[N_NETUSE_TABLE]; 50 51 static DWORD smbrdr_tree_connectx(struct sdb_session *session, 52 struct sdb_netuse *netuse, char *path, int path_len); 53 54 static struct sdb_netuse *smbrdr_netuse_alloc(struct sdb_session *session, 55 char *sharename); 56 static int smbrdr_tdcon(struct sdb_netuse *netuse); 57 58 static void 59 smbrdr_netuse_clear(struct sdb_netuse *netuse) 60 { 61 bzero(netuse, sizeof (struct sdb_netuse) - sizeof (mutex_t)); 62 } 63 64 static void 65 smbrdr_netuse_free(struct sdb_netuse *netuse) 66 { 67 smbrdr_netuse_clear(netuse); 68 (void) mutex_unlock(&netuse->mtx); 69 } 70 71 /* 72 * smbrdr_tree_connect 73 * 74 * Establish a share (tree connect). We need to retrieve the session 75 * for the specified host and allocate a netuse structure. We set up 76 * the path here (UNC encoded) to make handling the malloc/free easier 77 * and pass everything on to smbrdr_tree_connectx where. If everything 78 * goes well, a valid tid will be stored in the netuse structure. 79 * 80 * On success, a pointer to the netuse is returned. Otherwise the 81 * netuse is cleared and a null pointer is returned. 82 */ 83 DWORD 84 smbrdr_tree_connect(char *hostname, char *username, char *sharename, 85 unsigned short *tid) 86 { 87 struct sdb_session *session; 88 struct sdb_netuse *netuse; 89 char *path; 90 int path_len; 91 DWORD status; 92 93 *tid = 0; 94 95 /* 96 * Make sure there is a session & logon for given info 97 */ 98 session = smbrdr_session_lock(hostname, username, SDB_SLCK_READ); 99 if (session == NULL) { 100 syslog(LOG_DEBUG, "smbrdr_tree_connect: no session for %s@%s", 101 username, hostname); 102 return (NT_STATUS_INTERNAL_ERROR); 103 } 104 105 106 if ((netuse = smbrdr_netuse_alloc(session, sharename)) == 0) { 107 syslog(LOG_DEBUG, "smbrdr_tree_connect: init failed"); 108 smbrdr_session_unlock(session); 109 return (NT_STATUS_INTERNAL_ERROR); 110 } 111 112 /* 113 * Add some padding for the back-slash separators 114 * and the null-terminator. 115 */ 116 path_len = SMB_PI_MAX_HOST + MAX_SHARE_NAME + 5; 117 118 if ((path = (char *)malloc(path_len)) == 0) { 119 smbrdr_netuse_free(netuse); 120 smbrdr_session_unlock(session); 121 syslog(LOG_DEBUG, "smbrdr_tree_connect: %s", strerror(ENOMEM)); 122 return (NT_STATUS_NO_MEMORY); 123 } 124 125 bzero(path, path_len); 126 (void) snprintf(path, path_len, "\\\\%s\\%s", hostname, sharename); 127 if (session->remote_caps & CAP_UNICODE) 128 path_len = mts_wcequiv_strlen(path); 129 else 130 path_len = strlen(path); 131 132 if ((status = smbrdr_tree_connectx(session, netuse, path, path_len)) 133 != NT_STATUS_SUCCESS) { 134 smbrdr_netuse_free(netuse); 135 smbrdr_session_unlock(session); 136 free(path); 137 syslog(LOG_DEBUG, "smbrdr_tree_connect: %s failed", path); 138 return (status); 139 } 140 141 free(path); 142 (void) mutex_unlock(&netuse->mtx); 143 smbrdr_session_unlock(session); 144 *tid = netuse->tid; 145 return (NT_STATUS_SUCCESS); 146 } 147 148 149 /* 150 * smbrdr_tree_connectx 151 * 152 * This message requests a share (tree connect) request to the server 153 * associated with the session. The password is not relevant here if 154 * the session was establishment using setup_andx. The outgoing tid 155 * will be ignored - a valid one will be returned by the server. 156 * 157 * Returns 0 on success. Otherwise returns a -ve error code. 158 */ 159 static DWORD 160 smbrdr_tree_connectx(struct sdb_session *session, struct sdb_netuse *netuse, 161 char *path, int path_len) 162 { 163 smb_hdr_t smb_hdr; 164 smbrdr_handle_t srh; 165 smb_msgbuf_t *mb; 166 unsigned short flags; 167 char *password; 168 unsigned short password_len; 169 char *service; 170 unsigned service_len; 171 unsigned short data_bytes; 172 DWORD status; 173 int rc; 174 175 status = smbrdr_request_init(&srh, SMB_COM_TREE_CONNECT_ANDX, 176 session, &session->logon, 0); 177 178 if (status != NT_STATUS_SUCCESS) { 179 syslog(LOG_DEBUG, "smbrdr_tree_connectx: %s", 180 xlate_nt_status(status)); 181 return (status); 182 } 183 184 mb = &srh.srh_mbuf; 185 186 flags = 0; /* no flags */ 187 password = ""; 188 password_len = 1; /* including nul */ 189 service = "?????"; /* does this work? */ 190 service_len = strlen(service); 191 192 /* 193 * Calculate the BCC. The path is in UNICODE 194 * but the service is in ASCII. 195 */ 196 data_bytes = password_len; 197 data_bytes += path_len + 1; 198 data_bytes += service_len + 1; 199 200 rc = smb_msgbuf_encode(mb, "bb1.wwww#cus", 201 4, /* smb_wct */ 202 0xff, /* AndXCommand (none) */ 203 0xffff, /* AndXOffset */ 204 flags, /* Flags */ 205 password_len, /* PasswordLength */ 206 data_bytes+1, /* smb_bcc */ 207 password_len, password, /* Password */ 208 path, /* Path */ 209 service); /* Service */ 210 211 if (rc <= 0) { 212 syslog(LOG_DEBUG, "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 syslog(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 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 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 static int 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 syslog(LOG_DEBUG, "smbrdr_tdcon: %s", xlate_nt_status(status)); 310 /* should we clear here? */ 311 smbrdr_netuse_clear(netuse); 312 return (-1); 313 } 314 315 rc = smb_msgbuf_encode(&srh.srh_mbuf, "bw.", 0, 0); 316 if (rc < 0) { 317 syslog(LOG_DEBUG, "smbrdr_tdcon: encode failed"); 318 smbrdr_handle_free(&srh); 319 /* should we clear here? */ 320 smbrdr_netuse_clear(netuse); 321 return (rc); 322 } 323 324 status = smbrdr_exchange(&srh, &smb_hdr, 0); 325 if (status != NT_STATUS_SUCCESS) { 326 syslog(LOG_DEBUG, "smbrdr_tdcon: %s", xlate_nt_status(status)); 327 rc = -1; 328 } else { 329 rc = 0; 330 } 331 332 smbrdr_handle_free(&srh); 333 smbrdr_netuse_clear(netuse); 334 return (rc); 335 } 336 337 338 /* 339 * smbrdr_netuse_alloc 340 * 341 * Find a slot in the table for a share. Each share is associated with 342 * a session and assigned a local drive letter name and a sharename. 343 * If a slot is already allocated to the specified share, a pointer to 344 * it is returned. Otherwise we allocate and initialize a new slot in 345 * the table. If the table is full, a null pointer will be returned. 346 * 347 * IMPORTANT! the returned netuse will be locked caller has to unlock 348 * it after it's done with the pointer. 349 */ 350 static struct sdb_netuse * 351 smbrdr_netuse_alloc(struct sdb_session *session, char *sharename) 352 { 353 struct sdb_netuse *netuse; 354 int i; 355 356 if (session == NULL || sharename == NULL) 357 return (NULL); 358 359 for (i = 0; i < N_NETUSE_TABLE; ++i) { 360 netuse = &netuse_table[i]; 361 362 (void) mutex_lock(&netuse->mtx); 363 if (netuse->state == SDB_NSTATE_START) { 364 netuse->session = session; 365 netuse->letter = i + '0'; 366 netuse->sid = session->sid; 367 netuse->uid = session->logon.uid; 368 netuse->tid = 0; 369 (void) strcpy(netuse->share, sharename); 370 netuse->state = SDB_NSTATE_INIT; 371 return (netuse); 372 } 373 (void) mutex_unlock(&netuse->mtx); 374 } 375 376 syslog(LOG_DEBUG, "smbrdr_netuse_alloc: table full"); 377 return (0); 378 } 379 380 /* 381 * smbrdr_netuse_put 382 * 383 * Unlock given netuse structure. 384 */ 385 void 386 smbrdr_netuse_put(struct sdb_netuse *netuse) 387 { 388 (void) mutex_unlock(&netuse->mtx); 389 } 390 391 /* 392 * smbrdr_netuse_get 393 * 394 * Find the netuse structure associated with the specified tid and 395 * return a pointer to it. A null pointer is returned if no match 396 * can be found. 397 * 398 * IMPORTANT! the returned netuse will be locked caller has to unlock 399 * it after it's done with the pointer. 400 */ 401 struct sdb_netuse * 402 smbrdr_netuse_get(int tid) 403 { 404 struct sdb_session *session; 405 struct sdb_netuse *netuse; 406 int i; 407 408 for (i = 0; i < N_NETUSE_TABLE; ++i) { 409 netuse = &netuse_table[i]; 410 411 (void) mutex_lock(&netuse->mtx); 412 413 if (netuse->tid == tid) { 414 session = netuse->session; 415 416 /* 417 * status check: 418 * make sure all the structures are in the right state 419 */ 420 if (session && 421 (netuse->state == SDB_NSTATE_CONNECTED) && 422 (session->logon.state == SDB_LSTATE_SETUP) && 423 (session->state == SDB_SSTATE_NEGOTIATED)) { 424 /* sanity check */ 425 if ((netuse->sid == session->sid) && 426 (netuse->uid == session->logon.uid)) 427 return (netuse); 428 else 429 /* invalid structure */ 430 smbrdr_netuse_clear(netuse); 431 } 432 433 } 434 435 (void) mutex_unlock(&netuse->mtx); 436 } 437 438 syslog(LOG_DEBUG, "smbrdr_netuse_get: %d: no such TID", tid); 439 return (0); 440 } 441 442 /* 443 * smbrdr_dump_netuse 444 */ 445 void 446 smbrdr_dump_netuse() 447 { 448 struct sdb_netuse *netuse; 449 int i; 450 451 for (i = 0; i < N_NETUSE_TABLE; ++i) { 452 netuse = &netuse_table[i]; 453 (void) mutex_lock(&netuse->mtx); 454 if (netuse->session) { 455 syslog(LOG_DEBUG, "tree[%d]: %s (tid=%d)", i, 456 netuse->share, netuse->tid); 457 syslog(LOG_DEBUG, "tree[%d]: session(%d), user(%d)", 458 i, netuse->session->sock, netuse->uid); 459 } 460 (void) mutex_unlock(&netuse->mtx); 461 } 462 } 463