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