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