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