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