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 * NETR SamLogon and SamLogoff RPC client functions. 28 */ 29 30 #include <stdio.h> 31 #include <strings.h> 32 #include <stdlib.h> 33 #include <time.h> 34 #include <alloca.h> 35 #include <unistd.h> 36 #include <netdb.h> 37 #include <thread.h> 38 39 #include <smbsrv/libsmb.h> 40 #include <smbsrv/libsmbrdr.h> 41 #include <smbsrv/libmlrpc.h> 42 #include <smbsrv/libmlsvc.h> 43 #include <smbsrv/ndl/netlogon.ndl> 44 #include <smbsrv/netrauth.h> 45 #include <smbsrv/ntstatus.h> 46 #include <smbsrv/smbinfo.h> 47 #include <smbsrv/smb_token.h> 48 #include <mlsvc.h> 49 50 static DWORD netlogon_logon_private(netr_client_t *, smb_userinfo_t *); 51 static DWORD netr_server_samlogon(mlsvc_handle_t *, netr_info_t *, char *, 52 netr_client_t *, smb_userinfo_t *); 53 static void netr_invalidate_chain(void); 54 static void netr_interactive_samlogon(netr_info_t *, netr_client_t *, 55 struct netr_logon_info1 *); 56 static void netr_network_samlogon(ndr_heap_t *, netr_info_t *, 57 netr_client_t *, struct netr_logon_info2 *); 58 static void netr_setup_identity(ndr_heap_t *, netr_client_t *, 59 netr_logon_id_t *); 60 61 /* 62 * Shared with netr_auth.c 63 */ 64 extern netr_info_t netr_global_info; 65 66 static mutex_t netlogon_logon_mutex; 67 68 /* 69 * netlogon_logon 70 * 71 * This is the entry point for authenticating a remote logon. The 72 * parameters here all refer to the remote user and workstation, i.e. 73 * the domain is the user's account domain, not our primary domain. 74 * In order to make it easy to track which domain is being used at 75 * each stage, and to reduce the number of things being pushed on the 76 * stack, the client information is bundled up in the clnt structure. 77 * 78 * If the user is successfully authenticated, an access token will be 79 * built and NT_STATUS_SUCCESS will be returned. Otherwise a non-zero 80 * NT status will be returned, in which case the token contents will 81 * be invalid. 82 */ 83 DWORD 84 netlogon_logon(netr_client_t *clnt, smb_userinfo_t *user_info) 85 { 86 DWORD status; 87 88 (void) mutex_lock(&netlogon_logon_mutex); 89 90 status = netlogon_logon_private(clnt, user_info); 91 92 (void) mutex_unlock(&netlogon_logon_mutex); 93 return (status); 94 } 95 96 static DWORD 97 netlogon_logon_private(netr_client_t *clnt, smb_userinfo_t *user_info) 98 { 99 char resource_domain[SMB_PI_MAX_DOMAIN]; 100 char server[NETBIOS_NAME_SZ * 2]; 101 mlsvc_handle_t netr_handle; 102 smb_domain_t di; 103 DWORD status; 104 int retries = 0, server_changed = 0; 105 106 (void) smb_getdomainname(resource_domain, SMB_PI_MAX_DOMAIN); 107 108 if (!smb_domain_getinfo(&di)) 109 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); 110 111 if ((mlsvc_echo(di.d_dc)) < 0) { 112 /* 113 * We had a session to the DC but it's not responding. 114 * So drop the credential chain. 115 */ 116 netr_invalidate_chain(); 117 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); 118 } 119 120 do { 121 status = netr_open(di.d_dc, di.d_nbdomain, &netr_handle); 122 if (status != 0) 123 return (status); 124 125 if (di.d_dc && (*netr_global_info.server != '\0')) { 126 (void) snprintf(server, sizeof (server), 127 "\\\\%s", di.d_dc); 128 server_changed = strncasecmp(netr_global_info.server, 129 server, strlen(server)); 130 } 131 132 if (server_changed || 133 (netr_global_info.flags & NETR_FLG_VALID) == 0 || 134 !smb_match_netlogon_seqnum()) { 135 status = netlogon_auth(di.d_dc, &netr_handle, 136 NETR_FLG_NULL); 137 138 if (status != 0) { 139 (void) netr_close(&netr_handle); 140 return (NT_STATUS_LOGON_FAILURE); 141 } 142 143 netr_global_info.flags |= NETR_FLG_VALID; 144 } 145 146 status = netr_server_samlogon(&netr_handle, 147 &netr_global_info, di.d_dc, clnt, user_info); 148 149 (void) netr_close(&netr_handle); 150 } while (status == NT_STATUS_INSUFFICIENT_LOGON_INFO && retries++ < 3); 151 152 if (retries >= 3) 153 status = NT_STATUS_LOGON_FAILURE; 154 155 return (status); 156 } 157 158 static DWORD 159 netr_setup_userinfo(struct netr_validation_info3 *info3, 160 smb_userinfo_t *user_info, netr_client_t *clnt, netr_info_t *netr_info) 161 { 162 smb_sid_attrs_t *other_grps; 163 char *username, *domain; 164 int i, nbytes; 165 unsigned char rc4key[SMBAUTH_SESSION_KEY_SZ]; 166 167 user_info->sid_name_use = SidTypeUser; 168 user_info->rid = info3->UserId; 169 user_info->primary_group_rid = info3->PrimaryGroupId; 170 user_info->domain_sid = smb_sid_dup((smb_sid_t *)info3->LogonDomainId); 171 172 if (user_info->domain_sid == NULL) 173 return (NT_STATUS_NO_MEMORY); 174 175 user_info->user_sid = smb_sid_splice(user_info->domain_sid, 176 user_info->rid); 177 if (user_info->user_sid == NULL) 178 return (NT_STATUS_NO_MEMORY); 179 180 user_info->pgrp_sid = smb_sid_splice(user_info->domain_sid, 181 user_info->primary_group_rid); 182 if (user_info->pgrp_sid == NULL) 183 return (NT_STATUS_NO_MEMORY); 184 185 username = (info3->EffectiveName.str) 186 ? (char *)info3->EffectiveName.str : clnt->username; 187 domain = (info3->LogonDomainName.str) 188 ? (char *)info3->LogonDomainName.str : clnt->domain; 189 190 if (username) 191 user_info->name = strdup(username); 192 if (domain) 193 user_info->domain_name = strdup(domain); 194 195 if (user_info->name == NULL || user_info->domain_name == NULL) 196 return (NT_STATUS_NO_MEMORY); 197 198 nbytes = info3->GroupCount * sizeof (smb_rid_attrs_t); 199 if (nbytes) { 200 if ((user_info->groups = malloc(nbytes)) != NULL) { 201 user_info->n_groups = info3->GroupCount; 202 (void) memcpy(user_info->groups, 203 info3->GroupIds, nbytes); 204 } else { 205 return (NT_STATUS_NO_MEMORY); 206 } 207 } 208 nbytes = info3->SidCount * sizeof (smb_sid_attrs_t); 209 if (nbytes) { 210 if ((other_grps = malloc(nbytes)) != NULL) { 211 user_info->other_grps = other_grps; 212 for (i = 0; i < info3->SidCount; i++) { 213 other_grps[i].attrs = 214 info3->ExtraSids[i].attributes; 215 216 other_grps[i].sid = smb_sid_dup( 217 (smb_sid_t *)info3->ExtraSids[i].sid); 218 219 if (other_grps[i].sid == NULL) 220 break; 221 } 222 user_info->n_other_grps = i; 223 } else { 224 return (NT_STATUS_NO_MEMORY); 225 } 226 } 227 /* 228 * The UserSessionKey in NetrSamLogon RPC is obfuscated using the 229 * session key obtained in the NETLOGON credential chain. 230 * An 8 byte session key is zero extended to 16 bytes. This 16 byte 231 * key is the key to the RC4 algorithm. The RC4 byte stream is 232 * exclusively ored with the 16 byte UserSessionKey to recover 233 * the the clear form. 234 */ 235 if ((user_info->session_key = malloc(SMBAUTH_SESSION_KEY_SZ)) == NULL) 236 return (NT_STATUS_NO_MEMORY); 237 bzero(rc4key, SMBAUTH_SESSION_KEY_SZ); 238 bcopy(netr_info->session_key.key, rc4key, netr_info->session_key.len); 239 bcopy(info3->UserSessionKey.data, user_info->session_key, 240 SMBAUTH_SESSION_KEY_SZ); 241 rand_hash((unsigned char *)user_info->session_key, 242 SMBAUTH_SESSION_KEY_SZ, rc4key, SMBAUTH_SESSION_KEY_SZ); 243 mlsvc_setadmin_user_info(user_info); 244 return (NT_STATUS_SUCCESS); 245 } 246 247 /* 248 * netr_server_samlogon 249 * 250 * NetrServerSamLogon RPC: interactive or network. It is assumed that 251 * we have already authenticated with the PDC. If everything works, 252 * we build a user info structure and return it, where the caller will 253 * probably build an access token. 254 * 255 * Returns an NT status. There are numerous possibilities here. 256 * For example: 257 * NT_STATUS_INVALID_INFO_CLASS 258 * NT_STATUS_INVALID_PARAMETER 259 * NT_STATUS_ACCESS_DENIED 260 * NT_STATUS_PASSWORD_MUST_CHANGE 261 * NT_STATUS_NO_SUCH_USER 262 * NT_STATUS_WRONG_PASSWORD 263 * NT_STATUS_LOGON_FAILURE 264 * NT_STATUS_ACCOUNT_RESTRICTION 265 * NT_STATUS_INVALID_LOGON_HOURS 266 * NT_STATUS_INVALID_WORKSTATION 267 * NT_STATUS_INTERNAL_ERROR 268 * NT_STATUS_PASSWORD_EXPIRED 269 * NT_STATUS_ACCOUNT_DISABLED 270 */ 271 DWORD 272 netr_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info, 273 char *server, netr_client_t *clnt, smb_userinfo_t *user_info) 274 { 275 struct netr_SamLogon arg; 276 struct netr_authenticator auth; 277 struct netr_authenticator ret_auth; 278 struct netr_logon_info1 info1; 279 struct netr_logon_info2 info2; 280 struct netr_validation_info3 *info3; 281 ndr_heap_t *heap; 282 int opnum; 283 int rc, len; 284 DWORD status; 285 286 bzero(&arg, sizeof (struct netr_SamLogon)); 287 opnum = NETR_OPNUM_SamLogon; 288 289 /* 290 * Should we get the server and hostname from netr_info? 291 */ 292 293 len = strlen(server) + 4; 294 arg.servername = ndr_rpc_malloc(netr_handle, len); 295 arg.hostname = ndr_rpc_malloc(netr_handle, NETBIOS_NAME_SZ); 296 if (arg.servername == NULL || arg.hostname == NULL) { 297 ndr_rpc_release(netr_handle); 298 return (NT_STATUS_INTERNAL_ERROR); 299 } 300 301 (void) snprintf((char *)arg.servername, len, "\\\\%s", server); 302 if (smb_getnetbiosname((char *)arg.hostname, NETBIOS_NAME_SZ) != 0) { 303 ndr_rpc_release(netr_handle); 304 return (NT_STATUS_INTERNAL_ERROR); 305 } 306 307 rc = netr_setup_authenticator(netr_info, &auth, &ret_auth); 308 if (rc != SMBAUTH_SUCCESS) { 309 ndr_rpc_release(netr_handle); 310 return (NT_STATUS_INTERNAL_ERROR); 311 } 312 313 arg.auth = &auth; 314 arg.ret_auth = &ret_auth; 315 arg.validation_level = NETR_VALIDATION_LEVEL3; 316 arg.logon_info.logon_level = clnt->logon_level; 317 arg.logon_info.switch_value = clnt->logon_level; 318 319 heap = ndr_rpc_get_heap(netr_handle); 320 321 switch (clnt->logon_level) { 322 case NETR_INTERACTIVE_LOGON: 323 netr_setup_identity(heap, clnt, &info1.identity); 324 netr_interactive_samlogon(netr_info, clnt, &info1); 325 arg.logon_info.ru.info1 = &info1; 326 break; 327 328 case NETR_NETWORK_LOGON: 329 netr_setup_identity(heap, clnt, &info2.identity); 330 netr_network_samlogon(heap, netr_info, clnt, &info2); 331 arg.logon_info.ru.info2 = &info2; 332 break; 333 334 default: 335 ndr_rpc_release(netr_handle); 336 return (NT_STATUS_INVALID_PARAMETER); 337 } 338 339 rc = ndr_rpc_call(netr_handle, opnum, &arg); 340 if (rc != 0) { 341 bzero(netr_info, sizeof (netr_info_t)); 342 status = NT_STATUS_INVALID_PARAMETER; 343 } else if (arg.status != 0) { 344 status = NT_SC_VALUE(arg.status); 345 346 /* 347 * We need to validate the chain even though we have 348 * a non-zero status. If the status is ACCESS_DENIED 349 * this will trigger a new credential chain. However, 350 * a valid credential is returned with some status 351 * codes; for example, WRONG_PASSWORD. 352 */ 353 (void) netr_validate_chain(netr_info, arg.ret_auth); 354 } else { 355 status = netr_validate_chain(netr_info, arg.ret_auth); 356 if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) { 357 ndr_rpc_release(netr_handle); 358 return (status); 359 } 360 361 info3 = arg.ru.info3; 362 status = netr_setup_userinfo(info3, user_info, clnt, netr_info); 363 } 364 365 ndr_rpc_release(netr_handle); 366 return (status); 367 } 368 369 /* 370 * netr_interactive_samlogon 371 * 372 * Set things up for an interactive SamLogon. Copy the NT and LM 373 * passwords to the logon structure and hash them with the session 374 * key. 375 */ 376 static void 377 netr_interactive_samlogon(netr_info_t *netr_info, netr_client_t *clnt, 378 struct netr_logon_info1 *info1) 379 { 380 BYTE key[NETR_OWF_PASSWORD_SZ]; 381 382 (void) memcpy(&info1->lm_owf_password, 383 clnt->lm_password.lm_password_val, sizeof (netr_owf_password_t)); 384 385 (void) memcpy(&info1->nt_owf_password, 386 clnt->nt_password.nt_password_val, sizeof (netr_owf_password_t)); 387 388 (void) memset(key, 0, NETR_OWF_PASSWORD_SZ); 389 (void) memcpy(key, netr_info->session_key.key, 390 netr_info->session_key.len); 391 392 rand_hash((unsigned char *)&info1->lm_owf_password, 393 NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ); 394 395 rand_hash((unsigned char *)&info1->nt_owf_password, 396 NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ); 397 } 398 399 /* 400 * netr_network_samlogon 401 * 402 * Set things up for a network SamLogon. We provide a copy of the random 403 * challenge, that we sent to the client, to the domain controller. This 404 * is the key that the client will have used to encrypt the NT and LM 405 * passwords. Note that Windows 9x clients may not provide both passwords. 406 */ 407 /*ARGSUSED*/ 408 static void 409 netr_network_samlogon(ndr_heap_t *heap, netr_info_t *netr_info, 410 netr_client_t *clnt, struct netr_logon_info2 *info2) 411 { 412 uint32_t len; 413 414 bcopy(clnt->challenge_key.challenge_key_val, info2->lm_challenge.data, 415 8); 416 417 if ((len = clnt->nt_password.nt_password_len) != 0) { 418 ndr_heap_mkvcb(heap, clnt->nt_password.nt_password_val, len, 419 (ndr_vcbuf_t *)&info2->nt_response); 420 } else { 421 bzero(&info2->nt_response, sizeof (netr_vcbuf_t)); 422 } 423 424 if ((len = clnt->lm_password.lm_password_len) != 0) { 425 ndr_heap_mkvcb(heap, clnt->lm_password.lm_password_val, len, 426 (ndr_vcbuf_t *)&info2->lm_response); 427 } else { 428 bzero(&info2->lm_response, sizeof (netr_vcbuf_t)); 429 } 430 } 431 432 /* 433 * netr_setup_authenticator 434 * 435 * Set up the request and return authenticators. A new credential is 436 * generated from the session key, the current client credential and 437 * the current time, i.e. 438 * 439 * NewCredential = Cred(SessionKey, OldCredential, time); 440 * 441 * The timestamp, which is used as a random seed, is stored in both 442 * the request and return authenticators. 443 * 444 * If any difficulties occur using the cryptographic framework, the 445 * function returns SMBAUTH_FAILURE. Otherwise SMBAUTH_SUCCESS is 446 * returned. 447 */ 448 int 449 netr_setup_authenticator(netr_info_t *netr_info, 450 struct netr_authenticator *auth, struct netr_authenticator *ret_auth) 451 { 452 bzero(auth, sizeof (struct netr_authenticator)); 453 454 netr_info->timestamp = time(0); 455 auth->timestamp = netr_info->timestamp; 456 457 if (netr_gen_credentials(netr_info->session_key.key, 458 &netr_info->client_credential, 459 netr_info->timestamp, 460 (netr_cred_t *)&auth->credential) != SMBAUTH_SUCCESS) 461 return (SMBAUTH_FAILURE); 462 463 if (ret_auth) { 464 bzero(ret_auth, sizeof (struct netr_authenticator)); 465 ret_auth->timestamp = netr_info->timestamp; 466 } 467 468 return (SMBAUTH_SUCCESS); 469 } 470 471 /* 472 * Validate the returned credentials and update the credential chain. 473 * The server returns an updated client credential rather than a new 474 * server credential. The server uses (timestamp + 1) when generating 475 * the credential. 476 * 477 * Generate the new seed for the credential chain. The new seed is 478 * formed by adding (timestamp + 1) to the current client credential. 479 * The only quirk is the DWORD style addition. 480 * 481 * Returns NT_STATUS_INSUFFICIENT_LOGON_INFO if auth->credential is a 482 * NULL pointer. The Authenticator field of the SamLogon response packet 483 * sent by the Samba 3 PDC always return NULL pointer if the received 484 * SamLogon request is not immediately followed by the ServerReqChallenge 485 * and ServerAuthenticate2 requests. 486 * 487 * Returns NT_STATUS_SUCCESS if the server returned a valid credential. 488 * Otherwise we retirm NT_STATUS_UNSUCCESSFUL. 489 */ 490 DWORD 491 netr_validate_chain(netr_info_t *netr_info, struct netr_authenticator *auth) 492 { 493 netr_cred_t cred; 494 DWORD result = NT_STATUS_SUCCESS; 495 DWORD *dwp; 496 497 ++netr_info->timestamp; 498 499 if (netr_gen_credentials(netr_info->session_key.key, 500 &netr_info->client_credential, 501 netr_info->timestamp, &cred) != SMBAUTH_SUCCESS) 502 return (NT_STATUS_INTERNAL_ERROR); 503 504 if (&auth->credential == 0) { 505 /* 506 * If the validation fails, destroy the credential chain. 507 * This should trigger a new authentication chain. 508 */ 509 bzero(netr_info, sizeof (netr_info_t)); 510 return (NT_STATUS_INSUFFICIENT_LOGON_INFO); 511 } 512 513 result = memcmp(&cred, &auth->credential, sizeof (netr_cred_t)); 514 if (result != 0) { 515 /* 516 * If the validation fails, destroy the credential chain. 517 * This should trigger a new authentication chain. 518 */ 519 bzero(netr_info, sizeof (netr_info_t)); 520 result = NT_STATUS_UNSUCCESSFUL; 521 } else { 522 /* 523 * Otherwise generate the next step in the chain. 524 */ 525 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 526 dwp = (DWORD *)&netr_info->client_credential; 527 dwp[0] += netr_info->timestamp; 528 529 netr_info->flags |= NETR_FLG_VALID; 530 } 531 532 return (result); 533 } 534 535 /* 536 * netr_invalidate_chain 537 * 538 * Mark the credential chain as invalid so that it will be recreated 539 * on the next attempt. 540 */ 541 static void 542 netr_invalidate_chain(void) 543 { 544 netr_global_info.flags &= ~NETR_FLG_VALID; 545 } 546 547 /* 548 * netr_setup_identity 549 * 550 * Set up the client identity information. All of this information is 551 * specifically related to the client user and workstation attempting 552 * to access this system. It may not be in our primary domain. 553 * 554 * I don't know what logon_id is, it seems to be a unique identifier. 555 * Increment it before each use. 556 */ 557 static void 558 netr_setup_identity(ndr_heap_t *heap, netr_client_t *clnt, 559 netr_logon_id_t *identity) 560 { 561 static mutex_t logon_id_mutex; 562 static uint32_t logon_id; 563 564 (void) mutex_lock(&logon_id_mutex); 565 566 if (logon_id == 0) 567 logon_id = 0xDCD0; 568 569 ++logon_id; 570 clnt->logon_id = logon_id; 571 572 (void) mutex_unlock(&logon_id_mutex); 573 574 identity->parameter_control = 0; 575 identity->logon_id.LowPart = logon_id; 576 identity->logon_id.HighPart = 0; 577 578 ndr_heap_mkvcs(heap, clnt->domain, 579 (ndr_vcstr_t *)&identity->domain_name); 580 581 ndr_heap_mkvcs(heap, clnt->username, 582 (ndr_vcstr_t *)&identity->username); 583 584 /* 585 * Some systems prefix the client workstation name with \\. 586 * It doesn't seem to make any difference whether it's there 587 * or not. 588 */ 589 ndr_heap_mkvcs(heap, clnt->workstation, 590 (ndr_vcstr_t *)&identity->workstation); 591 } 592