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