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 * This module provides the netbios and SMB negotiation, connect and 30 * disconnect interface. 31 */ 32 33 #include <unistd.h> 34 #include <syslog.h> 35 #include <synch.h> 36 #include <string.h> 37 #include <strings.h> 38 #include <pthread.h> 39 #include <errno.h> 40 #include <sys/types.h> 41 #include <sys/socket.h> 42 #include <netinet/in.h> 43 #include <arpa/inet.h> 44 #include <inttypes.h> 45 #include <netdb.h> 46 47 #include <smbsrv/libsmbrdr.h> 48 #include <smbsrv/netbios.h> 49 #include <smbsrv/cifs.h> 50 51 #include <smbsrv/ntstatus.h> 52 #include <smbsrv/mlsvc.h> 53 #include <smbrdr.h> 54 #include <smbrdr_ipc_util.h> 55 56 57 static uint16_t smbrdr_ports[] = { 58 SMB_SRVC_TCP_PORT, 59 SSN_SRVC_TCP_PORT 60 }; 61 62 static int smbrdr_nports = sizeof (smbrdr_ports) / sizeof (smbrdr_ports[0]); 63 64 static struct sdb_session session_table[MLSVC_DOMAIN_MAX]; 65 static mutex_t smbrdr_screate_mtx; 66 static uint32_t session_id = 0; 67 68 static struct sdb_session *smbrdr_session_init(smb_ntdomain_t *); 69 static int smbrdr_trnsprt_connect(struct sdb_session *, uint16_t); 70 static int smbrdr_session_connect(smb_ntdomain_t *); 71 static int smbrdr_smb_negotiate(struct sdb_session *); 72 static int smbrdr_echo(struct sdb_session *); 73 static void smbrdr_session_disconnect(struct sdb_session *, int); 74 75 76 static void 77 smbrdr_session_clear(struct sdb_session *session) 78 { 79 bzero(session, sizeof (struct sdb_session) - sizeof (rwlock_t)); 80 } 81 82 /* 83 * Entry pointy for smbrdr initialization. 84 */ 85 void 86 smbrdr_init(void) 87 { 88 smbrdr_ipc_init(); 89 } 90 91 /* 92 * mlsvc_disconnect 93 * 94 * Disconnects the session with given server. 95 */ 96 void 97 mlsvc_disconnect(char *server) 98 { 99 struct sdb_session *session; 100 101 session = smbrdr_session_lock(server, 0, SDB_SLCK_WRITE); 102 if (session) { 103 smbrdr_session_disconnect(session, 0); 104 smbrdr_session_unlock(session); 105 } 106 } 107 108 /* 109 * smbrdr_negotiate 110 * 111 * Negotiate a session with a domain controller in the specified domain. 112 * The domain must be one of values from the smbinfo that indicates the 113 * resource domain or the account domain. 114 * 115 * If a session already exists, we can use that one. Otherwise we create 116 * a new one. This sets up the session key and session security info that 117 * we'll need later to authenticate the user. The session security info 118 * is returned to support the SMB client pass-through authentication 119 * interface. 120 * 121 * Returns 0 on success, otherwise -1. 122 */ 123 /*ARGSUSED*/ 124 int 125 smbrdr_negotiate(char *domain) 126 { 127 struct sdb_session *session = 0; 128 smb_ntdomain_t *di; 129 int retry = 1; 130 int res = 0; 131 132 if ((di = smb_getdomaininfo(0)) == NULL) { 133 syslog(LOG_DEBUG, "smbrdr_negotiate: cannot access domain"); 134 return (-1); 135 } 136 137 /* 138 * The mutex is to make session lookup and create atomic 139 * so we don't end up with two sessions with the same 140 * server. 141 */ 142 (void) mutex_lock(&smbrdr_screate_mtx); 143 while (retry > 0) { 144 session = smbrdr_session_lock(di->server, 0, SDB_SLCK_WRITE); 145 if (session != 0) { 146 if (nb_keep_alive(session->sock) == 0) { 147 /* session is good, use it */ 148 smbrdr_session_unlock(session); 149 break; 150 } else { 151 /* stale session */ 152 session->state = SDB_SSTATE_STALE; 153 smbrdr_session_unlock(session); 154 } 155 } 156 157 if (smbrdr_session_connect(di) != 0) { 158 if (retry > 0) { 159 di = smb_getdomaininfo(0); 160 if (di == NULL) { 161 res = -1; 162 break; 163 } 164 retry--; 165 } 166 } else { 167 /* session is created */ 168 retry = 0; 169 } 170 } 171 (void) mutex_unlock(&smbrdr_screate_mtx); 172 173 if (di == NULL) 174 syslog(LOG_DEBUG, "smbrdr_negotiate: cannot access domain"); 175 return (res); 176 } 177 178 /* 179 * smbrdr_session_connect 180 * 181 * This is the entry point for establishing an SMB connection to a 182 * domain controller. A session structure is allocated, a netbios 183 * session is set up and the SMB protocol is negotiated. If this is 184 * successful, the returned session structure can be used to logon 185 * to the the domain. A null pointer is returned if the connect fails. 186 */ 187 static int 188 smbrdr_session_connect(smb_ntdomain_t *di) 189 { 190 struct sdb_session *session; 191 uint16_t port; 192 int rc = 0; 193 194 /* 195 * smbrdr_session_init() will lock the session so that it wouldn't 196 * be accessible until it's established otherwise another thread 197 * might get access to a session which is not fully established. 198 */ 199 if ((session = smbrdr_session_init(di)) == NULL) { 200 syslog(LOG_DEBUG, "smbrdr_session_init failed"); 201 return (-1); 202 } 203 204 for (port = 0; port < smbrdr_nports; ++port) { 205 syslog(LOG_DEBUG, "smbrdr: trying port %d", 206 smbrdr_ports[port]); 207 208 rc = smbrdr_trnsprt_connect(session, smbrdr_ports[port]); 209 210 if (rc == 0) { 211 syslog(LOG_DEBUG, "smbrdr: connected port %d", 212 smbrdr_ports[port]); 213 break; 214 } 215 } 216 217 if (rc < 0) { 218 smbrdr_session_clear(session); 219 smbrdr_session_unlock(session); 220 syslog(LOG_DEBUG, "smbrdr: connect failed"); 221 return (-1); 222 } 223 224 if (smbrdr_smb_negotiate(session) < 0) { 225 (void) close(session->sock); 226 smbrdr_session_clear(session); 227 smbrdr_session_unlock(session); 228 syslog(LOG_DEBUG, "smbrdr: negotiate failed"); 229 return (-1); 230 } 231 232 smbrdr_session_unlock(session); 233 return (0); 234 } 235 236 237 /* 238 * smbrdr_trnsprt_connect 239 * 240 * Set up the TCP/IP and NETBIOS protocols for a session. This is just 241 * standard socket sutff. The paranoia check for socket descriptor 0 242 * is because we had a problem with this value and the console telnet 243 * interface will lock up if we use and/or close stdin (0). 244 * 245 * Return 0 on success. Otherwise return (-1) to indicate a problem. 246 */ 247 static int 248 smbrdr_trnsprt_connect(struct sdb_session *sess, uint16_t port) 249 { 250 char hostname[MAXHOSTNAMELEN]; 251 struct sockaddr_in sin; 252 int sock, rc; 253 mts_wchar_t unicode_server_name[SMB_PI_MAX_DOMAIN]; 254 char server_name[SMB_PI_MAX_DOMAIN]; 255 unsigned int cpid = oem_get_smb_cpid(); 256 257 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) <= 0) { 258 syslog(LOG_DEBUG, "smbrdr: socket failed: %s", strerror(errno)); 259 return (-1); 260 } 261 262 bzero(&sin, sizeof (struct sockaddr_in)); 263 sin.sin_family = AF_INET; 264 sin.sin_addr.s_addr = sess->di.ipaddr; 265 sin.sin_port = htons(port); 266 267 if ((rc = connect(sock, (struct sockaddr *)&sin, sizeof (sin))) < 0) { 268 syslog(LOG_DEBUG, "smbrdr: connect failed: %s", 269 strerror(errno)); 270 if (sock != 0) 271 (void) close(sock); 272 return (-1); 273 } 274 275 (void) mts_mbstowcs(unicode_server_name, sess->di.server, 276 SMB_PI_MAX_DOMAIN); 277 rc = unicodestooems(server_name, unicode_server_name, 278 SMB_PI_MAX_DOMAIN, cpid); 279 if (rc == 0) { 280 syslog(LOG_DEBUG, "smbrdr: unicode conversion failed"); 281 if (sock != 0) 282 (void) close(sock); 283 return (-1); 284 } 285 286 /* 287 * If we are using NetBIOS, we need to set up a NETBIOS session. 288 * This typically implies that we will be using port 139. 289 * Otherwise, we're doing NetBIOS-less SMB, i.e. SMB over TCP, 290 * which is typically on port 445. 291 */ 292 if (port == SSN_SRVC_TCP_PORT) { 293 if (smb_getnetbiosname(hostname, MAXHOSTNAMELEN) != 0) { 294 syslog(LOG_DEBUG, "smbrdr: no hostname"); 295 if (sock != 0) 296 (void) close(sock); 297 return (-1); 298 } 299 300 rc = nb_session_request(sock, 301 server_name, sess->scope, hostname, sess->scope); 302 303 if (rc != 0) { 304 syslog(LOG_DEBUG, 305 "smbrdr: NBT session request to %s failed %d", 306 server_name, rc); 307 if (sock != 0) 308 (void) close(sock); 309 return (-1); 310 } 311 } 312 313 sess->sock = sock; 314 sess->port = port; 315 syslog(LOG_DEBUG, "smbrdr: connected on port %d", port); 316 sess->state = SDB_SSTATE_CONNECTED; 317 return (0); 318 } 319 320 /* 321 * smbrdr_smb_negotiate 322 * 323 * Negotiate the protocol we are going to use as described in CIFS 324 * section 4.1.1. The only protocol we support is NT LM 0.12, so we 325 * really expect to see dialect 0 in the response. The only other 326 * data gathered is the session key. 327 * 328 * Negotiate using ASCII strings. 329 * 330 * Return 0 on success. Otherwise return a -ve error code. 331 */ 332 static int 333 smbrdr_smb_negotiate(struct sdb_session *sess) 334 { 335 unsigned short dialect; 336 smbrdr_handle_t srh; 337 smb_hdr_t smb_hdr; 338 smb_msgbuf_t *mb; 339 DWORD status; 340 int rc; 341 uint8_t tmp_secmode; 342 uint8_t tmp_clen; 343 344 status = smbrdr_request_init(&srh, SMB_COM_NEGOTIATE, sess, 0, 0); 345 346 if (status != NT_STATUS_SUCCESS) 347 return (-1); 348 349 mb = &srh.srh_mbuf; 350 rc = smb_msgbuf_encode(mb, "bwbs", 0, 12, 0x02, "NT LM 0.12"); 351 if (rc <= 0) { 352 smbrdr_handle_free(&srh); 353 return (-1); 354 } 355 356 status = smbrdr_exchange(&srh, &smb_hdr, 0); 357 if (status != NT_STATUS_SUCCESS) { 358 syslog(LOG_DEBUG, "smbrdr: negotiate: %s", 359 xlate_nt_status(status)); 360 smbrdr_handle_free(&srh); 361 return (-1); 362 } 363 364 sess->secmode = 0; 365 sess->sesskey = 0; 366 sess->challenge_len = 0; 367 368 rc = smb_msgbuf_decode(mb, 369 "1.(dialect)w(mode)b12.(key)l(cap)l10.(keylen)b2.", 370 &dialect, &tmp_secmode, &sess->sesskey, &sess->remote_caps, 371 &tmp_clen); 372 373 if (rc <= 0 || dialect != 0) { 374 smbrdr_handle_free(&srh); 375 return (-1); 376 } 377 sess->secmode = tmp_secmode; 378 sess->challenge_len = tmp_clen; 379 380 rc = smb_msgbuf_decode(mb, "#c", 381 sess->challenge_len, sess->challenge_key); 382 if (rc <= 0) { 383 smbrdr_handle_free(&srh); 384 return (-1); 385 } 386 387 smbrdr_handle_free(&srh); 388 389 if ((sess->secmode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED) && 390 (sess->secmode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED)) { 391 sess->sign_ctx.ssc_flags |= SMB_SCF_REQUIRED; 392 syslog(LOG_DEBUG, "smbrdr: %s: signing required", 393 sess->di.server); 394 } 395 396 sess->state = SDB_SSTATE_NEGOTIATED; 397 return (0); 398 } 399 400 /* 401 * smbrdr_session_init 402 * 403 * Allocate an available slot in session table for the specified domain 404 * information. 405 * 406 * IMPORTANT! the returned session will be locked caller has to unlock 407 * it by calling smbrdr_session_unlock() after it's done with 408 * the pointer. 409 */ 410 static struct sdb_session * 411 smbrdr_session_init(smb_ntdomain_t *di) 412 { 413 struct sdb_session *session = NULL; 414 int i; 415 416 if (di == NULL) 417 return (NULL); 418 419 for (i = 0; i < MLSVC_DOMAIN_MAX; ++i) { 420 session = &session_table[i]; 421 422 (void) rw_wrlock(&session->rwl); 423 if (session->state == SDB_SSTATE_START) { 424 smbrdr_session_clear(session); 425 bcopy(di, &session->di, sizeof (smb_ntdomain_t)); 426 (void) utf8_strupr(session->di.domain); 427 (void) utf8_strupr(session->di.server); 428 429 (void) smb_config_getstr(SMB_CI_NBSCOPE, session->scope, 430 sizeof (session->scope)); 431 432 (void) strlcpy(session->native_os, 433 "Solaris", SMB_PI_MAX_NATIVE_OS); 434 (void) strlcpy(session->native_lanman, 435 "Windows NT 4.0", SMB_PI_MAX_LANMAN); 436 session->sock = -1; 437 session->port = smbrdr_ports[0]; 438 session->smb_flags = SMB_FLAGS_CANONICALIZED_PATHS 439 | SMB_FLAGS_CASE_INSENSITIVE; 440 441 session->smb_flags2 = SMB_FLAGS2_KNOWS_LONG_NAMES 442 | SMB_FLAGS2_KNOWS_EAS; 443 444 /* 445 * Note that by sending vc=0 server will shutdown all 446 * the other connections with NAS if there is any. 447 */ 448 session->vc = 0; 449 session->sid = ++session_id; 450 if (session->sid == 0) 451 session->sid = 1; 452 session->state = SDB_SSTATE_INIT; 453 return (session); 454 } 455 (void) rw_unlock(&session->rwl); 456 } 457 458 syslog(LOG_DEBUG, "smbrdr: no session available"); 459 return (NULL); 460 } 461 462 /* 463 * smbrdr_session_disconnect 464 * 465 * This is the entry point for disconnecting an SMB connection. Ensure 466 * that all logons and shares associated with this session are 467 * terminated and then free the session. 468 * 469 * if 'cleanup' is 1 it means that only sessions that are not active 470 * should be cleaned up. if 'cleanup' is 0 disconnect the session in any 471 * states. 472 */ 473 static void 474 smbrdr_session_disconnect(struct sdb_session *session, int cleanup) 475 { 476 int state; 477 478 if (session == NULL) 479 return; 480 481 state = session->state; 482 if ((state != SDB_SSTATE_DISCONNECTING) && 483 (state != SDB_SSTATE_CLEANING) && 484 (state != SDB_SSTATE_START)) { 485 if ((cleanup == 0) || (state == SDB_SSTATE_STALE)) { 486 /* 487 * if session is in stale state it means the connection 488 * is lost so no logoff, tdcon, or close can actually 489 * be sent, thus only cleanup our side. 490 */ 491 session->state = (state == SDB_SSTATE_STALE) 492 ? SDB_SSTATE_CLEANING : SDB_SSTATE_DISCONNECTING; 493 (void) smbrdr_logoffx(&session->logon); 494 nb_close(session->sock); 495 smbrdr_session_clear(session); 496 } 497 } 498 } 499 500 /* 501 * smbrdr_session_unlock 502 * 503 * Unlock given session structure. 504 */ 505 void 506 smbrdr_session_unlock(struct sdb_session *session) 507 { 508 if (session) 509 (void) rw_unlock(&session->rwl); 510 } 511 512 /* 513 * smbrdr_session_lock 514 * 515 * Lookup the session associated with the specified domain controller. 516 * If a match is found, we return a pointer to the session, Otherwise 517 * we return null. Only sessions in "negotiated" state are checked. 518 * This mechanism is very simple and implies that we 519 * should only ever have one session open to any domain controller. 520 * 521 * IMPORTANT! the returned session will be locked caller has to unlock 522 * it by calling smbrdr_session_unlock() after it's done with 523 * the pointer. 524 */ 525 struct sdb_session * 526 smbrdr_session_lock(char *server, char *username, int lmode) 527 { 528 struct sdb_session *session; 529 int i; 530 531 if (server == NULL) 532 return (NULL); 533 534 for (i = 0; i < MLSVC_DOMAIN_MAX; ++i) { 535 session = &session_table[i]; 536 537 (lmode == SDB_SLCK_READ) ? (void) rw_rdlock(&session->rwl) : 538 (void) rw_wrlock(&session->rwl); 539 540 if ((session->state == SDB_SSTATE_NEGOTIATED) && 541 (strcasecmp(session->di.server, server) == 0)) { 542 if (username) { 543 if (strcasecmp(username, 544 session->logon.username) == 0) 545 return (session); 546 547 (void) rw_unlock(&session->rwl); 548 return (NULL); 549 } 550 return (session); 551 } 552 553 (void) rw_unlock(&session->rwl); 554 } 555 556 return (NULL); 557 } 558 559 /* 560 * mlsvc_session_native_values 561 * 562 * Given a file id (i.e. a named pipe fid), return the remote native 563 * OS and LM values for the associated session. 564 */ 565 int 566 mlsvc_session_native_values(int fid, int *remote_os, 567 int *remote_lm, int *pdc_type) 568 { 569 struct sdb_session *session; 570 struct sdb_netuse *netuse; 571 struct sdb_ofile *ofile; 572 573 if (remote_os == NULL || remote_lm == NULL) 574 return (-1); 575 576 if ((ofile = smbrdr_ofile_get(fid)) == 0) { 577 syslog(LOG_DEBUG, 578 "mlsvc_session_native_values: unknown file (%d)", fid); 579 return (-1); 580 } 581 582 netuse = ofile->netuse; 583 session = netuse->session; 584 585 *remote_os = session->remote_os; 586 *remote_lm = session->remote_lm; 587 if (pdc_type) 588 *pdc_type = session->pdc_type; 589 smbrdr_ofile_put(ofile); 590 return (0); 591 } 592 593 /* 594 * smbrdr_dump_sessions 595 * 596 * Debug function to dump the session table. 597 */ 598 void 599 smbrdr_dump_sessions(void) 600 { 601 struct sdb_session *session; 602 struct sdb_logon *logon; 603 char ipstr[16]; 604 int i; 605 606 for (i = 0; i < MLSVC_DOMAIN_MAX; ++i) { 607 session = &session_table[i]; 608 609 (void) rw_rdlock(&session->rwl); 610 if (session->state != SDB_SSTATE_START) { 611 (void) inet_ntop(AF_INET, 612 (const void *)(&session->di.ipaddr), 613 ipstr, sizeof (ipstr)); 614 615 syslog(LOG_DEBUG, "session[%d]: state=%d", 616 i, session->state); 617 syslog(LOG_DEBUG, "session[%d]: %s %s (%s)", i, 618 session->di.domain, session->di.server, ipstr); 619 syslog(LOG_DEBUG, "session[%d]: %s %s (sock=%d)", i, 620 session->native_os, session->native_lanman, 621 session->sock); 622 623 logon = &session->logon; 624 if (logon->type != SDB_LOGON_NONE) 625 syslog(LOG_DEBUG, "logon[%d]: %s (uid=%d)", 626 i, logon->username, logon->uid); 627 } 628 (void) rw_unlock(&session->rwl); 629 } 630 } 631 632 /* 633 * mlsvc_echo 634 */ 635 int 636 mlsvc_echo(char *server) 637 { 638 struct sdb_session *session; 639 int res = 0; 640 641 if ((session = smbrdr_session_lock(server, 0, SDB_SLCK_WRITE)) == 0) 642 return (1); 643 644 if (smbrdr_echo(session) != 0) { 645 session->state = SDB_SSTATE_STALE; 646 res = -1; 647 } 648 649 smbrdr_session_unlock(session); 650 return (res); 651 } 652 653 /* 654 * smbrdr_echo 655 * 656 * This request can be used to test the connection to the server. The 657 * server should echo the data sent. The server should ignore the tid 658 * in the header, so this request when there are no tree connections. 659 * See CIFS/1.0 section 4.1.7. 660 * 661 * Return 0 on success. Otherwise return a -ve error code. 662 */ 663 static int 664 smbrdr_echo(struct sdb_session *session) 665 { 666 static char *echo_str = "smbrdr"; 667 smbrdr_handle_t srh; 668 smb_hdr_t smb_hdr; 669 DWORD status; 670 int rc; 671 672 if ((session->state == SDB_SSTATE_DISCONNECTING) || 673 (session->state == SDB_SSTATE_CLEANING) || 674 (session->state == SDB_SSTATE_STALE)) { 675 return (-1); 676 } 677 678 status = smbrdr_request_init(&srh, SMB_COM_ECHO, session, 0, 0); 679 if (status != NT_STATUS_SUCCESS) 680 return (-1); 681 682 rc = smb_msgbuf_encode(&srh.srh_mbuf, "bwws", 1, 1, 683 strlen(echo_str), echo_str); 684 if (rc <= 0) { 685 smbrdr_handle_free(&srh); 686 return (-1); 687 } 688 689 status = smbrdr_exchange(&srh, &smb_hdr, 10); 690 smbrdr_handle_free(&srh); 691 692 if (status != NT_STATUS_SUCCESS) 693 return (-1); 694 695 return (0); 696 } 697