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 * SMB session logon and logoff functions. See CIFS section 4.1. 30 */ 31 32 #include <pthread.h> 33 #include <string.h> 34 #include <strings.h> 35 #include <syslog.h> 36 #include <synch.h> 37 #include <sys/errno.h> 38 #include <sys/types.h> 39 #include <sys/socket.h> 40 #include <netinet/in.h> 41 #include <arpa/inet.h> 42 #include <smbsrv/libsmbrdr.h> 43 #include <smbsrv/ntstatus.h> 44 #include <smbsrv/smb.h> 45 #include <smbrdr_ipc_util.h> 46 #include <smbrdr.h> 47 48 static int smbrdr_anonymous_logon(char *domain_controller, char *domain_name); 49 static int smbrdr_auth_logon(char *domain_controller, char *domain_name, 50 char *username); 51 static int smbrdr_session_setupx(struct sdb_logon *logon); 52 static boolean_t smbrdr_logon_validate(char *server, char *username); 53 static struct sdb_logon *smbrdr_logon_init(struct sdb_session *session, 54 char *username, unsigned char *pwd); 55 static int smbrdr_logon_user(char *server, char *username, unsigned char *pwd); 56 static int smbrdr_authenticate(char *primary_domain, char *account_name, 57 unsigned char *pwd); 58 59 /* 60 * mlsvc_logon 61 * 62 * If the username is MLSVC_ANON_USER, an anonymous session will be established. 63 * Otherwise, an authenticated session will be established based on the 64 * specified credentials. 65 */ 66 int 67 mlsvc_logon(char *domain_controller, char *domain, char *username) 68 { 69 int rc; 70 71 if (strcmp(username, MLSVC_ANON_USER) == 0) 72 rc = smbrdr_anonymous_logon(domain_controller, domain); 73 else 74 rc = smbrdr_auth_logon(domain_controller, domain, username); 75 76 return (rc); 77 } 78 79 /* 80 * smbrdr_anonymous_logon 81 * 82 * Set up an anonymous session. If the session to the resource domain 83 * controller appears to be okay we shouldn't need to do anything here. 84 * Otherwise we clean up the stale session and create a new one. 85 */ 86 static int 87 smbrdr_anonymous_logon(char *domain_controller, char *domain_name) 88 { 89 if (smbrdr_logon_validate(domain_controller, MLSVC_ANON_USER)) 90 return (0); 91 92 if (smbrdr_negotiate(domain_name) != 0) { 93 syslog(LOG_DEBUG, "smbrdr_anonymous_logon: negotiate failed"); 94 return (-1); 95 } 96 97 if (smbrdr_logon_user(domain_controller, MLSVC_ANON_USER, 0) < 0) { 98 syslog(LOG_DEBUG, "smbrdr_anonymous_logon: logon failed"); 99 return (-1); 100 } 101 102 return (0); 103 } 104 105 int 106 mlsvc_user_getauth(char *domain_controller, char *username, 107 smb_auth_info_t *auth) 108 { 109 struct sdb_session *session; 110 111 if (auth) { 112 bzero(auth, sizeof (smb_auth_info_t)); 113 session = smbrdr_session_lock(domain_controller, username, 114 SDB_SLCK_READ); 115 if (session) { 116 *auth = session->logon.auth; 117 smbrdr_session_unlock(session); 118 return (0); 119 } 120 } 121 122 return (-1); 123 } 124 125 /* 126 * smbrdr_auth_logon 127 * 128 * Set up a user session. If the session to the resource domain controller 129 * appears to be okay we shouldn't need to do anything here. Otherwise we 130 * clean up the stale session and create a new one. Once a session is 131 * established, we leave it intact. It should only need to be set up again 132 * due to an inactivity timeout or a domain controller reset. 133 */ 134 static int 135 smbrdr_auth_logon(char *domain_controller, char *domain_name, char *username) 136 { 137 int erc; 138 unsigned char *pwd_hash = NULL; 139 140 if (username == NULL || *username == 0) { 141 syslog(LOG_DEBUG, "smbrdr_auth_logon: no username"); 142 return (-1); 143 } 144 145 pwd_hash = smbrdr_ipc_get_passwd(); 146 if (!pwd_hash || *pwd_hash == 0) { 147 syslog(LOG_DEBUG, "smbrdr_auth_logon: no password"); 148 return (-1); 149 } 150 151 if (smbrdr_logon_validate(domain_controller, username)) 152 return (0); 153 154 if (smbrdr_negotiate(domain_name) != 0) { 155 syslog(LOG_DEBUG, "smbrdr_auth_logon: negotiate failed"); 156 return (-1); 157 } 158 159 erc = smbrdr_authenticate(domain_name, username, pwd_hash); 160 return ((erc == AUTH_USER_GRANT) ? 0 : -1); 161 } 162 163 /* 164 * smbrdr_authenticate 165 * 166 * Authenticate primary_domain\account_name. 167 * 168 * Returns: 169 * 0 User access granted 170 * 1 Guest access granted 171 * 2 IPC access granted 172 * (<0) Error 173 */ 174 static int 175 smbrdr_authenticate(char *primary_domain, char *account_name, 176 unsigned char *pwd) 177 { 178 smb_ntdomain_t *di; 179 180 if (pwd == NULL) 181 return (AUTH_USER_GRANT | AUTH_IPC_ONLY_GRANT); 182 183 184 if ((di = smb_getdomaininfo(0)) == 0) { 185 syslog(LOG_DEBUG, "smbrdr_authenticate[%s]: %s", account_name, 186 xlate_nt_status(NT_STATUS_CANT_ACCESS_DOMAIN_INFO)); 187 return (-1); 188 } 189 190 /* 191 * Ensure that the domain name is uppercase. 192 */ 193 (void) utf8_strupr(primary_domain); 194 195 /* 196 * We can only authenticate a user via a controller in the user's 197 * primary domain. If the user's domain name doesn't match the 198 * authenticating server's domain, reject the request before we 199 * create a logon entry for the user. Although the logon will be 200 * denied eventually, we don't want a logon structure for a user 201 * in the resource domain that is pointing to a session structure 202 * for the account domain. If this happened to be our resource 203 * domain user, we would not be able to use that account to connect 204 * to the resource domain. 205 */ 206 if (strcasecmp(di->domain, primary_domain)) { 207 syslog(LOG_DEBUG, "smbrdr_authenticate: %s\\%s: invalid domain", 208 primary_domain, account_name); 209 return (-2); 210 } 211 212 return (smbrdr_logon_user(di->server, account_name, pwd)); 213 } 214 215 /* 216 * smbrdr_logon_user 217 * 218 * This is the entry point for logging a user onto the domain. The 219 * session structure should have been obtained via a successful call 220 * to smbrdr_smb_connect. We allocate a logon structure to hold the 221 * user details and attempt to logon using smbrdr_session_setupx. 222 * Note that we expect the password fields to have been encrypted 223 * before this call. 224 * 225 * On success, the logon structure will be returned. Otherwise a null 226 * pointer will be returned. 227 */ 228 static int 229 smbrdr_logon_user(char *server, char *username, unsigned char *pwd) 230 { 231 struct sdb_session *session; 232 struct sdb_logon *logon; 233 struct sdb_logon old_logon; 234 235 if ((server == NULL) || (username == NULL) || 236 ((strcmp(username, MLSVC_ANON_USER) != 0) && (pwd == NULL))) { 237 return (-1); 238 } 239 240 session = smbrdr_session_lock(server, 0, SDB_SLCK_WRITE); 241 if (session == NULL) { 242 syslog(LOG_DEBUG, "smbrdr_logon_user: %s: no session with %s", 243 username, server); 244 return (-1); 245 } 246 247 bzero(&old_logon, sizeof (struct sdb_logon)); 248 249 logon = &session->logon; 250 if (logon->type != SDB_LOGON_NONE) { 251 if (strcasecmp(logon->username, username) == 0) { 252 /* The requested user has already been logged in */ 253 smbrdr_session_unlock(session); 254 return ((logon->type == SDB_LOGON_GUEST) 255 ? AUTH_GUEST_GRANT : AUTH_USER_GRANT); 256 } 257 258 old_logon = *logon; 259 } 260 261 logon = smbrdr_logon_init(session, username, pwd); 262 263 if (logon == NULL) { 264 syslog(LOG_DEBUG, "smbrdr_logon_user: %s: %s", 265 username, strerror(ENOMEM)); 266 smbrdr_session_unlock(session); 267 return (-1); 268 } 269 270 if (smbrdr_session_setupx(logon) < 0) { 271 free(logon); 272 smbrdr_session_unlock(session); 273 return (-1); 274 } 275 276 session->logon = *logon; 277 free(logon); 278 279 if (old_logon.type != SDB_LOGON_NONE) { 280 (void) smbrdr_logoffx(&old_logon); 281 } 282 283 smbrdr_session_unlock(session); 284 return ((logon->type == SDB_LOGON_GUEST) 285 ? AUTH_GUEST_GRANT : AUTH_USER_GRANT); 286 } 287 288 289 /* 290 * smbrdr_session_setupx 291 * 292 * Build and send an SMB session setup command. This is used to log a 293 * user onto the domain. See CIFS section 4.1.2. 294 * 295 * Returns 0 on success. Otherwise returns a -ve error code. 296 */ 297 static int 298 smbrdr_session_setupx(struct sdb_logon *logon) 299 { 300 struct sdb_session *session; 301 smb_hdr_t smb_hdr; 302 smbrdr_handle_t srh; 303 smb_msgbuf_t *mb; 304 char *native_os; 305 char *native_lanman; 306 unsigned short data_bytes; 307 unsigned short guest; 308 unsigned long capabilities; 309 unsigned short null_size; 310 size_t (*strlen_fn)(const char *s); 311 DWORD status; 312 int rc; 313 314 /* 315 * Paranoia check - we should never get this 316 * far without a valid session structure. 317 */ 318 if ((session = logon->session) == NULL) 319 return (-1); 320 321 if (session->remote_caps & CAP_UNICODE) { 322 strlen_fn = mts_wcequiv_strlen; 323 null_size = sizeof (mts_wchar_t); 324 session->smb_flags2 |= SMB_FLAGS2_UNICODE; 325 } else { 326 strlen_fn = strlen; 327 null_size = sizeof (char); 328 } 329 330 if (smbrdr_sign_init(session, logon) < 0) { 331 syslog(LOG_DEBUG, 332 "smbrdr_session_setupx: smbrdr_sign_init failed"); 333 return (-1); 334 } 335 336 status = smbrdr_request_init(&srh, SMB_COM_SESSION_SETUP_ANDX, 337 session, 0, 0); 338 339 if (status != NT_STATUS_SUCCESS) { 340 smbrdr_sign_fini(session); 341 syslog(LOG_DEBUG, "smbrdr_session_setupx: %s", 342 xlate_nt_status(status)); 343 return (-1); 344 } 345 mb = &srh.srh_mbuf; 346 347 /* 348 * Regardless of the server's capabilities or what's 349 * reported in smb_flags2, we should report our full 350 * capabilities. 351 */ 352 capabilities = CAP_UNICODE | CAP_NT_SMBS | CAP_STATUS32; 353 354 /* 355 * Compute the BCC for unicode or ASCII strings. 356 */ 357 data_bytes = logon->auth.ci_len + logon->auth.cs_len + null_size; 358 data_bytes += strlen_fn(session->native_os) + null_size; 359 data_bytes += strlen_fn(session->native_lanman) + null_size; 360 361 if (logon->type == SDB_LOGON_ANONYMOUS) { 362 /* 363 * Anonymous logon: no username or domain name. 364 * We still need to include two null characters. 365 */ 366 data_bytes += (2 * null_size); 367 368 rc = smb_msgbuf_encode(mb, "bb.wwwwlwwllwlu.u.", 369 13, /* smb_wct */ 370 0xff, /* AndXCommand (none) */ 371 32 + 26 + 3 + data_bytes, /* AndXOffset */ 372 SMBRDR_REQ_BUFSZ, /* MaxBufferSize */ 373 1, /* MaxMpxCount */ 374 0, /* VcNumber */ 375 0, /* SessionKey */ 376 1, /* CaseInsensitivePassLength */ 377 0, /* CaseSensitivePassLength */ 378 0, /* Reserved */ 379 capabilities, /* Capabilities */ 380 data_bytes, /* smb_bcc */ 381 0, /* No user or domain */ 382 session->native_os, /* NativeOS */ 383 session->native_lanman); /* NativeLanMan */ 384 } else { 385 data_bytes += strlen_fn(logon->username) + null_size; 386 data_bytes += strlen_fn(session->di.domain) + null_size; 387 388 rc = smb_msgbuf_encode(mb, "bb.wwwwlwwllw#c#cuuu.u.", 389 13, /* smb_wct */ 390 0xff, /* AndXCommand (none) */ 391 32 + 26 + 3 + data_bytes, /* AndXOffset */ 392 SMBRDR_REQ_BUFSZ, /* MaxBufferSize */ 393 1, /* MaxMpxCount */ 394 session->vc, /* VcNumber */ 395 session->sesskey, /* SessionKey */ 396 logon->auth.ci_len, /* CaseInsensitivePassLength */ 397 logon->auth.cs_len, /* CaseSensitivePassLength */ 398 0, /* Reserved */ 399 capabilities, /* Capabilities */ 400 data_bytes, /* smb_bcc */ 401 logon->auth.ci_len, /* ci length spec */ 402 logon->auth.ci, /* CaseInsensitivePassword */ 403 logon->auth.cs_len, /* cs length spec */ 404 logon->auth.cs, /* CaseSensitivePassword */ 405 logon->username, /* AccountName */ 406 session->di.domain, /* PrimaryDomain */ 407 session->native_os, /* NativeOS */ 408 session->native_lanman); /* NativeLanMan */ 409 } 410 411 if (rc <= 0) { 412 syslog(LOG_DEBUG, "smbrdr_session_setupx: encode failed"); 413 smbrdr_handle_free(&srh); 414 smbrdr_sign_fini(session); 415 return (-1); 416 } 417 418 status = smbrdr_exchange(&srh, &smb_hdr, 0); 419 if (status != NT_STATUS_SUCCESS) { 420 syslog(LOG_DEBUG, "smbrdr_session_setupx: %s", 421 xlate_nt_status(status)); 422 smbrdr_handle_free(&srh); 423 smbrdr_sign_fini(session); 424 return (-1); 425 } 426 427 rc = smb_msgbuf_decode(mb, "5.w2.u", &guest, &native_os); 428 429 /* 430 * There was a problem in decoding response from 431 * a Samba 2.x PDC. This server sends strings in ASCII 432 * format and there is one byte with value 0 between 433 * native_os and native_lm: 434 * 435 * FF 53 4D 42 73 00 .SMBs. 436 * 00 00 00 88 01 00 00 00 00 00 00 00 00 00 00 00 ................ 437 * 00 00 00 00 BB 00 64 00 00 00 03 FF 00 00 00 01 ......d......... 438 * 00 1C 00 55 6E 69 78 00 53 61 6D 62 61 20 32 2E ...Unix.Samba.2. 439 * 32 2E 38 61 00 53 41 4D 42 41 5F 44 4F 4D 00 2.8a.SAMBA_DOM. 440 * 441 * The byte doesn't seem to be padding because when change in 442 * native OS from Unix to Unix1 the 0 byte is still there: 443 * 444 * FF 53 4D 42 73 00 .SMBs. 445 * 00 00 00 88 01 00 00 00 00 00 00 00 00 00 00 00 ................ 446 * 00 00 00 00 BB 00 64 00 00 00 03 FF 00 00 00 00 ......d......... 447 * 00 1D 00 55 6E 69 78 31 00 53 61 6D 62 61 20 32 ...Unix1.Samba.2 448 * 2E 32 2E 38 61 00 53 41 4D 42 41 5F 44 4F 4D 00 .2.8a.SAMBA_DOM. 449 */ 450 if (rc > 0) { 451 if (session->remote_caps & CAP_UNICODE) 452 rc = smb_msgbuf_decode(mb, "u", &native_lanman); 453 else 454 rc = smb_msgbuf_decode(mb, ".u", &native_lanman); 455 } 456 457 if (rc <= 0) { 458 syslog(LOG_DEBUG, "smbrdr_session_setupx: decode failed"); 459 smbrdr_handle_free(&srh); 460 smbrdr_sign_fini(session); 461 return (-1); 462 } 463 464 session->remote_os = smbnative_os_value(native_os); 465 session->remote_lm = smbnative_lm_value(native_lanman); 466 session->pdc_type = smbnative_pdc_value(native_lanman); 467 468 logon->uid = smb_hdr.uid; 469 if (guest) 470 logon->type = SDB_LOGON_GUEST; 471 472 smbrdr_handle_free(&srh); 473 smbrdr_sign_unset_key(session); 474 475 logon->state = SDB_LSTATE_SETUP; 476 477 return (0); 478 } 479 480 /* 481 * smbrdr_logoffx 482 * 483 * Build and send an SMB session logoff (SMB_COM_LOGOFF_ANDX) command. 484 * This is the inverse of an SMB_COM_SESSION_SETUP_ANDX. See CIFS 485 * section 4.1.3. The logon structure should have been obtained from a 486 * successful call to smbrdr_logon_user. 487 * 488 * Returns 0 on success. Otherwise returns a -ve error code. 489 */ 490 int 491 smbrdr_logoffx(struct sdb_logon *logon) 492 { 493 struct sdb_session *session; 494 smbrdr_handle_t srh; 495 smb_hdr_t smb_hdr; 496 DWORD status; 497 int rc; 498 499 if (logon->state != SDB_LSTATE_SETUP) { 500 /* No user to logoff */ 501 bzero(logon, sizeof (struct sdb_logon)); 502 return (0); 503 } 504 505 if ((session = logon->session) == 0) { 506 bzero(logon, sizeof (struct sdb_logon)); 507 return (0); 508 } 509 510 logon->state = SDB_LSTATE_LOGGING_OFF; 511 smbrdr_netuse_logoff(logon->uid); 512 513 if ((session->state != SDB_SSTATE_NEGOTIATED) && 514 (session->state != SDB_SSTATE_DISCONNECTING)) { 515 bzero(logon, sizeof (struct sdb_logon)); 516 return (0); 517 } 518 519 status = smbrdr_request_init(&srh, SMB_COM_LOGOFF_ANDX, 520 session, logon, 0); 521 522 if (status != NT_STATUS_SUCCESS) { 523 logon->state = SDB_LSTATE_SETUP; 524 syslog(LOG_DEBUG, "smbrdr_logoffx: %s: %s", logon->username, 525 xlate_nt_status(status)); 526 return (-1); 527 } 528 529 rc = smb_msgbuf_encode(&srh.srh_mbuf, "bbbww", 2, 0xff, 0, 0, 0); 530 if (rc < 0) { 531 logon->state = SDB_LSTATE_SETUP; 532 smbrdr_handle_free(&srh); 533 syslog(LOG_DEBUG, "smbrdr_logoffx: %s: encode failed", 534 logon->username); 535 return (rc); 536 } 537 538 status = smbrdr_exchange(&srh, &smb_hdr, 0); 539 if (status != NT_STATUS_SUCCESS) { 540 syslog(LOG_DEBUG, "smbrdr_logoffx: %s: %s", logon->username, 541 xlate_nt_status(status)); 542 rc = -1; 543 } else { 544 rc = 0; 545 } 546 547 bzero(logon, sizeof (struct sdb_logon)); 548 smbrdr_handle_free(&srh); 549 return (rc); 550 } 551 552 553 /* 554 * smbrdr_logon_init 555 * 556 * Find a slot for account logon information. The account information 557 * is associated with a session so we need a valid session slot before 558 * calling this function. If we already have a record of the specified 559 * account, a pointer to that record is returned. Otherwise we attempt 560 * to allocate a new one. 561 */ 562 static struct sdb_logon * 563 smbrdr_logon_init(struct sdb_session *session, char *username, 564 unsigned char *pwd) 565 { 566 struct sdb_logon *logon; 567 int64_t smbrdr_lmcompl; 568 int rc; 569 570 logon = (struct sdb_logon *)malloc(sizeof (sdb_logon_t)); 571 if (logon == 0) 572 return (0); 573 574 bzero(logon, sizeof (struct sdb_logon)); 575 logon->session = session; 576 577 (void) smb_config_getnum(SMB_CI_LM_LEVEL, &smbrdr_lmcompl); 578 579 if (strcmp(username, "IPC$") == 0) { 580 logon->type = SDB_LOGON_ANONYMOUS; 581 logon->auth.ci_len = 1; 582 *(logon->auth.ci) = 0; 583 logon->auth.cs_len = 0; 584 } else { 585 logon->type = SDB_LOGON_USER; 586 rc = smb_auth_set_info(username, 0, pwd, 587 session->di.domain, session->challenge_key, 588 session->challenge_len, smbrdr_lmcompl, &logon->auth); 589 590 if (rc != 0) { 591 free(logon); 592 return (0); 593 } 594 } 595 596 (void) strlcpy(logon->username, username, MAX_ACCOUNT_NAME); 597 logon->state = SDB_LSTATE_INIT; 598 return (logon); 599 } 600 601 /* 602 * smbrdr_logon_validate 603 * 604 * if session is there and it's alive and also the required 605 * user is already logged in don't need to do anything 606 * otherwise clear the session structure. 607 */ 608 static boolean_t 609 smbrdr_logon_validate(char *server, char *username) 610 { 611 struct sdb_session *session; 612 boolean_t valid = B_FALSE; 613 614 session = smbrdr_session_lock(server, username, SDB_SLCK_WRITE); 615 if (session) { 616 if (nb_keep_alive(session->sock) == 0) { 617 valid = B_TRUE; 618 } else { 619 session->state = SDB_SSTATE_STALE; 620 syslog(LOG_DEBUG, 621 "smbrdr_logon_validate: stale session"); 622 } 623 624 smbrdr_session_unlock(session); 625 } 626 627 return (valid); 628 } 629