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