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