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