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