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