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