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 /*
  23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  25  */
  26 
  27 /*
  28  * NETR SamLogon and SamLogoff RPC client functions.
  29  */
  30 
  31 #include <stdio.h>
  32 #include <strings.h>
  33 #include <stdlib.h>
  34 #include <time.h>
  35 #include <alloca.h>
  36 #include <unistd.h>
  37 #include <netdb.h>
  38 #include <thread.h>
  39 
  40 #include <smbsrv/libsmb.h>
  41 #include <smbsrv/libmlrpc.h>
  42 #include <smbsrv/libmlsvc.h>
  43 #include <smbsrv/ndl/netlogon.ndl>
  44 #include <smbsrv/netrauth.h>
  45 #include <smbsrv/smbinfo.h>
  46 #include <smbsrv/smb_token.h>
  47 #include <mlsvc.h>
  48 
  49 #define NETLOGON_ATTEMPTS       2
  50 
  51 static uint32_t netlogon_logon(smb_logon_t *, smb_token_t *);
  52 static uint32_t netr_server_samlogon(mlsvc_handle_t *, netr_info_t *, char *,
  53     smb_logon_t *, smb_token_t *);
  54 static void netr_invalidate_chain(void);
  55 static void netr_interactive_samlogon(netr_info_t *, smb_logon_t *,
  56     struct netr_logon_info1 *);
  57 static void netr_network_samlogon(ndr_heap_t *, netr_info_t *,
  58     smb_logon_t *, struct netr_logon_info2 *);
  59 static void netr_setup_identity(ndr_heap_t *, smb_logon_t *,
  60     netr_logon_id_t *);
  61 static boolean_t netr_isadmin(struct netr_validation_info3 *);
  62 static uint32_t netr_setup_domain_groups(struct netr_validation_info3 *,
  63     smb_ids_t *);
  64 static uint32_t netr_setup_token_info3(struct netr_validation_info3 *,
  65     smb_token_t *);
  66 static uint32_t netr_setup_token_wingrps(struct netr_validation_info3 *,
  67     smb_token_t *);
  68 
  69 /*
  70  * Shared with netr_auth.c
  71  */
  72 extern netr_info_t netr_global_info;
  73 
  74 static mutex_t netlogon_mutex;
  75 static cond_t netlogon_cv;
  76 static boolean_t netlogon_busy = B_FALSE;
  77 static boolean_t netlogon_abort = B_FALSE;
  78 
  79 /*
  80  * Helper for Kerberos authentication
  81  */
  82 uint32_t
  83 smb_decode_krb5_pac(smb_token_t *token, char *data, uint_t len)
  84 {
  85         struct krb5_validation_info info;
  86         ndr_buf_t *nbuf;
  87         uint32_t status = NT_STATUS_NO_MEMORY;
  88         int rc;
  89 
  90         bzero(&info, sizeof (info));
  91 
  92         /* Need to keep this until we're done with &info */
  93         nbuf = ndr_buf_init(&TYPEINFO(netr_interface));
  94         if (nbuf == NULL)
  95                 goto out;
  96 
  97         rc = ndr_buf_decode(nbuf, NDR_PTYPE_PAC,
  98             NETR_OPNUM_decode_krb5_pac, data, len, &info);
  99         if (rc != NDR_DRC_OK) {
 100                 status = RPC_NT_PROTOCOL_ERROR;
 101                 goto out;
 102         }
 103 
 104         status = netr_setup_token_info3(&info.info3, token);
 105 
 106         /* Deal with the "resource groups"? */
 107 
 108 
 109 out:
 110         if (nbuf != NULL)
 111                 ndr_buf_fini(nbuf);
 112 
 113         return (status);
 114 }
 115 
 116 /*
 117  * Code factored out of netr_setup_token()
 118  */
 119 static uint32_t
 120 netr_setup_token_info3(struct netr_validation_info3 *info3,
 121     smb_token_t *token)
 122 {
 123         smb_sid_t *domsid;
 124 
 125         domsid = (smb_sid_t *)info3->LogonDomainId;
 126 
 127         token->tkn_user.i_sid = smb_sid_splice(domsid,
 128             info3->UserId);
 129         if (token->tkn_user.i_sid == NULL)
 130                 goto errout;
 131 
 132         token->tkn_primary_grp.i_sid = smb_sid_splice(domsid,
 133             info3->PrimaryGroupId);
 134         if (token->tkn_primary_grp.i_sid == NULL)
 135                 goto errout;
 136 
 137         if (info3->EffectiveName.str) {
 138                 token->tkn_account_name =
 139                     strdup((char *)info3->EffectiveName.str);
 140                 if (token->tkn_account_name == NULL)
 141                         goto errout;
 142         }
 143 
 144         if (info3->LogonDomainName.str) {
 145                 token->tkn_domain_name =
 146                     strdup((char *)info3->LogonDomainName.str);
 147                 if (token->tkn_domain_name == NULL)
 148                         goto errout;
 149         }
 150 
 151         return (netr_setup_token_wingrps(info3, token));
 152 errout:
 153         return (NT_STATUS_INSUFF_SERVER_RESOURCES);
 154 }
 155 
 156 /*
 157  * Abort impending domain logon requests.
 158  */
 159 void
 160 smb_logon_abort(void)
 161 {
 162         (void) mutex_lock(&netlogon_mutex);
 163         if (netlogon_busy && !netlogon_abort)
 164                 syslog(LOG_DEBUG, "logon abort");
 165         netlogon_abort = B_TRUE;
 166         (void) cond_broadcast(&netlogon_cv);
 167         (void) mutex_unlock(&netlogon_mutex);
 168 }
 169 
 170 /*
 171  * This is the entry point for authenticating domain users.
 172  *
 173  * If we are not going to attempt to authenticate the user,
 174  * this function must return without updating the status.
 175  *
 176  * If the user is successfully authenticated, we build an
 177  * access token and the status will be NT_STATUS_SUCCESS.
 178  * Otherwise, the token contents are invalid.
 179  */
 180 void
 181 smb_logon_domain(smb_logon_t *user_info, smb_token_t *token)
 182 {
 183         uint32_t        status;
 184         int             i;
 185 
 186         if (user_info->lg_secmode != SMB_SECMODE_DOMAIN)
 187                 return;
 188 
 189         if (user_info->lg_domain_type == SMB_DOMAIN_LOCAL)
 190                 return;
 191 
 192         for (i = 0; i < NETLOGON_ATTEMPTS; ++i) {
 193                 (void) mutex_lock(&netlogon_mutex);
 194                 while (netlogon_busy && !netlogon_abort)
 195                         (void) cond_wait(&netlogon_cv, &netlogon_mutex);
 196 
 197                 if (netlogon_abort) {
 198                         (void) mutex_unlock(&netlogon_mutex);
 199                         user_info->lg_status = NT_STATUS_REQUEST_ABORTED;
 200                         return;
 201                 }
 202 
 203                 netlogon_busy = B_TRUE;
 204                 (void) mutex_unlock(&netlogon_mutex);
 205 
 206                 status = netlogon_logon(user_info, token);
 207 
 208                 (void) mutex_lock(&netlogon_mutex);
 209                 netlogon_busy = B_FALSE;
 210                 if (netlogon_abort)
 211                         status = NT_STATUS_REQUEST_ABORTED;
 212                 (void) cond_signal(&netlogon_cv);
 213                 (void) mutex_unlock(&netlogon_mutex);
 214 
 215                 if (status != NT_STATUS_CANT_ACCESS_DOMAIN_INFO)
 216                         break;
 217         }
 218 
 219         if (status != NT_STATUS_SUCCESS)
 220                 syslog(LOG_INFO, "logon[%s\\%s]: %s", user_info->lg_e_domain,
 221                     user_info->lg_e_username, xlate_nt_status(status));
 222 
 223         user_info->lg_status = status;
 224 }
 225 
 226 static uint32_t
 227 netlogon_logon(smb_logon_t *user_info, smb_token_t *token)
 228 {
 229         char resource_domain[SMB_PI_MAX_DOMAIN];
 230         char server[MAXHOSTNAMELEN];
 231         mlsvc_handle_t netr_handle;
 232         smb_domainex_t di;
 233         uint32_t status;
 234         int retries = 0;
 235 
 236         (void) smb_getdomainname(resource_domain, SMB_PI_MAX_DOMAIN);
 237 
 238         /* Avoid interfering with DC discovery. */
 239         if (smb_ddiscover_wait() != 0 ||
 240             !smb_domain_getinfo(&di)) {
 241                 netr_invalidate_chain();
 242                 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
 243         }
 244 
 245         do {
 246                 if (netr_open(di.d_dci.dc_name, di.d_primary.di_nbname,
 247                     &netr_handle) != 0)
 248                         return (NT_STATUS_OPEN_FAILED);
 249 
 250                 if (di.d_dci.dc_name[0] != '\0' &&
 251                     (*netr_global_info.server != '\0')) {
 252                         (void) snprintf(server, sizeof (server),
 253                             "\\\\%s", di.d_dci.dc_name);
 254                         if (strncasecmp(netr_global_info.server,
 255                             server, strlen(server)) != 0)
 256                                 netr_invalidate_chain();
 257                 }
 258 
 259                 if ((netr_global_info.flags & NETR_FLG_VALID) == 0 ||
 260                     !smb_match_netlogon_seqnum()) {
 261                         status = netlogon_auth(di.d_dci.dc_name, &netr_handle,
 262                             NETR_FLG_NULL);
 263 
 264                         if (status != 0) {
 265                                 (void) netr_close(&netr_handle);
 266                                 return (NT_STATUS_LOGON_FAILURE);
 267                         }
 268 
 269                         netr_global_info.flags |= NETR_FLG_VALID;
 270                 }
 271 
 272                 status = netr_server_samlogon(&netr_handle,
 273                     &netr_global_info, di.d_dci.dc_name, user_info, token);
 274 
 275                 (void) netr_close(&netr_handle);
 276         } while (status == NT_STATUS_INSUFFICIENT_LOGON_INFO && retries++ < 3);
 277 
 278         if (retries >= 3)
 279                 status = NT_STATUS_LOGON_FAILURE;
 280 
 281         return (status);
 282 }
 283 
 284 static uint32_t
 285 netr_setup_token(struct netr_validation_info3 *info3, smb_logon_t *user_info,
 286     netr_info_t *netr_info, smb_token_t *token)
 287 {
 288         char *username, *domain;
 289         unsigned char rc4key[SMBAUTH_SESSION_KEY_SZ];
 290         smb_sid_t *domsid;
 291         uint32_t status;
 292         char nbdomain[NETBIOS_NAME_SZ];
 293 
 294         domsid = (smb_sid_t *)info3->LogonDomainId;
 295 
 296         token->tkn_user.i_sid = smb_sid_splice(domsid, info3->UserId);
 297         if (token->tkn_user.i_sid == NULL)
 298                 return (NT_STATUS_NO_MEMORY);
 299 
 300         token->tkn_primary_grp.i_sid = smb_sid_splice(domsid,
 301             info3->PrimaryGroupId);
 302         if (token->tkn_primary_grp.i_sid == NULL)
 303                 return (NT_STATUS_NO_MEMORY);
 304 
 305         username = (info3->EffectiveName.str)
 306             ? (char *)info3->EffectiveName.str : user_info->lg_e_username;
 307 
 308         if (info3->LogonDomainName.str) {
 309                 domain = (char *)info3->LogonDomainName.str;
 310         } else if (*user_info->lg_e_domain != '\0') {
 311                 domain = user_info->lg_e_domain;
 312         } else {
 313                 (void) smb_getdomainname(nbdomain, sizeof (nbdomain));
 314                 domain = nbdomain;
 315         }
 316 
 317         if (username)
 318                 token->tkn_account_name = strdup(username);
 319         if (domain)
 320                 token->tkn_domain_name = strdup(domain);
 321 
 322         if (token->tkn_account_name == NULL || token->tkn_domain_name == NULL)
 323                 return (NT_STATUS_NO_MEMORY);
 324 
 325         status = netr_setup_token_wingrps(info3, token);
 326         if (status != NT_STATUS_SUCCESS)
 327                 return (status);
 328 
 329         /*
 330          * The UserSessionKey in NetrSamLogon RPC is obfuscated using the
 331          * session key obtained in the NETLOGON credential chain.
 332          * An 8 byte session key is zero extended to 16 bytes. This 16 byte
 333          * key is the key to the RC4 algorithm. The RC4 byte stream is
 334          * exclusively ored with the 16 byte UserSessionKey to recover
 335          * the the clear form.
 336          */
 337         if ((token->tkn_ssnkey.val = malloc(SMBAUTH_SESSION_KEY_SZ)) == NULL)
 338                 return (NT_STATUS_NO_MEMORY);
 339         token->tkn_ssnkey.len = SMBAUTH_SESSION_KEY_SZ;
 340         bzero(rc4key, SMBAUTH_SESSION_KEY_SZ);
 341         bcopy(netr_info->session_key.key, rc4key, netr_info->session_key.len);
 342         bcopy(info3->UserSessionKey.data, token->tkn_ssnkey.val,
 343             SMBAUTH_SESSION_KEY_SZ);
 344         rand_hash((unsigned char *)token->tkn_ssnkey.val,
 345             SMBAUTH_SESSION_KEY_SZ, rc4key, SMBAUTH_SESSION_KEY_SZ);
 346 
 347         return (NT_STATUS_SUCCESS);
 348 }
 349 
 350 /*
 351  * netr_server_samlogon
 352  *
 353  * NetrServerSamLogon RPC: interactive or network. It is assumed that
 354  * we have already authenticated with the PDC. If everything works,
 355  * we build a user info structure and return it, where the caller will
 356  * probably build an access token.
 357  *
 358  * Returns an NT status. There are numerous possibilities here.
 359  * For example:
 360  *      NT_STATUS_INVALID_INFO_CLASS
 361  *      NT_STATUS_INVALID_PARAMETER
 362  *      NT_STATUS_ACCESS_DENIED
 363  *      NT_STATUS_PASSWORD_MUST_CHANGE
 364  *      NT_STATUS_NO_SUCH_USER
 365  *      NT_STATUS_WRONG_PASSWORD
 366  *      NT_STATUS_LOGON_FAILURE
 367  *      NT_STATUS_ACCOUNT_RESTRICTION
 368  *      NT_STATUS_INVALID_LOGON_HOURS
 369  *      NT_STATUS_INVALID_WORKSTATION
 370  *      NT_STATUS_INTERNAL_ERROR
 371  *      NT_STATUS_PASSWORD_EXPIRED
 372  *      NT_STATUS_ACCOUNT_DISABLED
 373  */
 374 uint32_t
 375 netr_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info,
 376     char *server, smb_logon_t *user_info, smb_token_t *token)
 377 {
 378         struct netr_SamLogon arg;
 379         struct netr_authenticator auth;
 380         struct netr_authenticator ret_auth;
 381         struct netr_logon_info1 info1;
 382         struct netr_logon_info2 info2;
 383         struct netr_validation_info3 *info3;
 384         ndr_heap_t *heap;
 385         int opnum;
 386         int rc, len;
 387         uint32_t status;
 388 
 389         bzero(&arg, sizeof (struct netr_SamLogon));
 390         opnum = NETR_OPNUM_SamLogon;
 391 
 392         /*
 393          * Should we get the server and hostname from netr_info?
 394          */
 395 
 396         len = strlen(server) + 4;
 397         arg.servername = ndr_rpc_malloc(netr_handle, len);
 398         arg.hostname = ndr_rpc_malloc(netr_handle, NETBIOS_NAME_SZ);
 399         if (arg.servername == NULL || arg.hostname == NULL) {
 400                 ndr_rpc_release(netr_handle);
 401                 return (NT_STATUS_INTERNAL_ERROR);
 402         }
 403 
 404         (void) snprintf((char *)arg.servername, len, "\\\\%s", server);
 405         if (smb_getnetbiosname((char *)arg.hostname, NETBIOS_NAME_SZ) != 0) {
 406                 ndr_rpc_release(netr_handle);
 407                 return (NT_STATUS_INTERNAL_ERROR);
 408         }
 409 
 410         rc = netr_setup_authenticator(netr_info, &auth, &ret_auth);
 411         if (rc != SMBAUTH_SUCCESS) {
 412                 ndr_rpc_release(netr_handle);
 413                 return (NT_STATUS_INTERNAL_ERROR);
 414         }
 415 
 416         arg.auth = &auth;
 417         arg.ret_auth = &ret_auth;
 418         arg.validation_level = NETR_VALIDATION_LEVEL3;
 419         arg.logon_info.logon_level = user_info->lg_level;
 420         arg.logon_info.switch_value = user_info->lg_level;
 421 
 422         heap = ndr_rpc_get_heap(netr_handle);
 423 
 424         switch (user_info->lg_level) {
 425         case NETR_INTERACTIVE_LOGON:
 426                 netr_setup_identity(heap, user_info, &info1.identity);
 427                 netr_interactive_samlogon(netr_info, user_info, &info1);
 428                 arg.logon_info.ru.info1 = &info1;
 429                 break;
 430 
 431         case NETR_NETWORK_LOGON:
 432                 if (user_info->lg_challenge_key.len < 8 ||
 433                     user_info->lg_challenge_key.val == NULL) {
 434                         ndr_rpc_release(netr_handle);
 435                         return (NT_STATUS_INVALID_PARAMETER);
 436                 }
 437                 netr_setup_identity(heap, user_info, &info2.identity);
 438                 netr_network_samlogon(heap, netr_info, user_info, &info2);
 439                 arg.logon_info.ru.info2 = &info2;
 440                 break;
 441 
 442         default:
 443                 ndr_rpc_release(netr_handle);
 444                 return (NT_STATUS_INVALID_PARAMETER);
 445         }
 446 
 447         rc = ndr_rpc_call(netr_handle, opnum, &arg);
 448         if (rc != 0) {
 449                 bzero(netr_info, sizeof (netr_info_t));
 450                 status = NT_STATUS_INVALID_PARAMETER;
 451         } else if (arg.status != 0) {
 452                 status = NT_SC_VALUE(arg.status);
 453 
 454                 /*
 455                  * We need to validate the chain even though we have
 456                  * a non-zero status. If the status is ACCESS_DENIED
 457                  * this will trigger a new credential chain. However,
 458                  * a valid credential is returned with some status
 459                  * codes; for example, WRONG_PASSWORD.
 460                  */
 461                 (void) netr_validate_chain(netr_info, arg.ret_auth);
 462         } else {
 463                 status = netr_validate_chain(netr_info, arg.ret_auth);
 464                 if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) {
 465                         ndr_rpc_release(netr_handle);
 466                         return (status);
 467                 }
 468 
 469                 info3 = arg.ru.info3;
 470                 status = netr_setup_token(info3, user_info, netr_info, token);
 471         }
 472 
 473         ndr_rpc_release(netr_handle);
 474         return (status);
 475 }
 476 
 477 /*
 478  * netr_interactive_samlogon
 479  *
 480  * Set things up for an interactive SamLogon. Copy the NT and LM
 481  * passwords to the logon structure and hash them with the session
 482  * key.
 483  */
 484 static void
 485 netr_interactive_samlogon(netr_info_t *netr_info, smb_logon_t *user_info,
 486     struct netr_logon_info1 *info1)
 487 {
 488         BYTE key[NETR_OWF_PASSWORD_SZ];
 489 
 490         (void) memcpy(&info1->lm_owf_password,
 491             user_info->lg_lm_password.val, sizeof (netr_owf_password_t));
 492 
 493         (void) memcpy(&info1->nt_owf_password,
 494             user_info->lg_nt_password.val, sizeof (netr_owf_password_t));
 495 
 496         (void) memset(key, 0, NETR_OWF_PASSWORD_SZ);
 497         (void) memcpy(key, netr_info->session_key.key,
 498             netr_info->session_key.len);
 499 
 500         rand_hash((unsigned char *)&info1->lm_owf_password,
 501             NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ);
 502 
 503         rand_hash((unsigned char *)&info1->nt_owf_password,
 504             NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ);
 505 }
 506 
 507 /*
 508  * netr_network_samlogon
 509  *
 510  * Set things up for a network SamLogon.  We provide a copy of the random
 511  * challenge, that we sent to the client, to the domain controller.  This
 512  * is the key that the client will have used to encrypt the NT and LM
 513  * passwords.  Note that Windows 9x clients may not provide both passwords.
 514  */
 515 /*ARGSUSED*/
 516 static void
 517 netr_network_samlogon(ndr_heap_t *heap, netr_info_t *netr_info,
 518     smb_logon_t *user_info, struct netr_logon_info2 *info2)
 519 {
 520         uint32_t len;
 521 
 522         if (user_info->lg_challenge_key.len >= 8 &&
 523             user_info->lg_challenge_key.val != 0) {
 524                 bcopy(user_info->lg_challenge_key.val,
 525                     info2->lm_challenge.data, 8);
 526         } else {
 527                 bzero(info2->lm_challenge.data, 8);
 528         }
 529 
 530         if ((len = user_info->lg_nt_password.len) != 0) {
 531                 ndr_heap_mkvcb(heap, user_info->lg_nt_password.val, len,
 532                     (ndr_vcbuf_t *)&info2->nt_response);
 533         } else {
 534                 bzero(&info2->nt_response, sizeof (netr_vcbuf_t));
 535         }
 536 
 537         if ((len = user_info->lg_lm_password.len) != 0) {
 538                 ndr_heap_mkvcb(heap, user_info->lg_lm_password.val, len,
 539                     (ndr_vcbuf_t *)&info2->lm_response);
 540         } else {
 541                 bzero(&info2->lm_response, sizeof (netr_vcbuf_t));
 542         }
 543 }
 544 
 545 /*
 546  * netr_setup_authenticator
 547  *
 548  * Set up the request and return authenticators. A new credential is
 549  * generated from the session key, the current client credential and
 550  * the current time, i.e.
 551  *
 552  *              NewCredential = Cred(SessionKey, OldCredential, time);
 553  *
 554  * The timestamp, which is used as a random seed, is stored in both
 555  * the request and return authenticators.
 556  *
 557  * If any difficulties occur using the cryptographic framework, the
 558  * function returns SMBAUTH_FAILURE.  Otherwise SMBAUTH_SUCCESS is
 559  * returned.
 560  */
 561 int
 562 netr_setup_authenticator(netr_info_t *netr_info,
 563     struct netr_authenticator *auth, struct netr_authenticator *ret_auth)
 564 {
 565         bzero(auth, sizeof (struct netr_authenticator));
 566 
 567         netr_info->timestamp = time(0);
 568         auth->timestamp = netr_info->timestamp;
 569 
 570         if (netr_gen_credentials(netr_info->session_key.key,
 571             &netr_info->client_credential,
 572             netr_info->timestamp,
 573             (netr_cred_t *)&auth->credential) != SMBAUTH_SUCCESS)
 574                 return (SMBAUTH_FAILURE);
 575 
 576         if (ret_auth) {
 577                 bzero(ret_auth, sizeof (struct netr_authenticator));
 578                 ret_auth->timestamp = netr_info->timestamp;
 579         }
 580 
 581         return (SMBAUTH_SUCCESS);
 582 }
 583 
 584 /*
 585  * Validate the returned credentials and update the credential chain.
 586  * The server returns an updated client credential rather than a new
 587  * server credential.  The server uses (timestamp + 1) when generating
 588  * the credential.
 589  *
 590  * Generate the new seed for the credential chain. The new seed is
 591  * formed by adding (timestamp + 1) to the current client credential.
 592  * The only quirk is the uint32_t style addition.
 593  *
 594  * Returns NT_STATUS_INSUFFICIENT_LOGON_INFO if auth->credential is a
 595  * NULL pointer. The Authenticator field of the SamLogon response packet
 596  * sent by the Samba 3 PDC always return NULL pointer if the received
 597  * SamLogon request is not immediately followed by the ServerReqChallenge
 598  * and ServerAuthenticate2 requests.
 599  *
 600  * Returns NT_STATUS_SUCCESS if the server returned a valid credential.
 601  * Otherwise we retirm NT_STATUS_UNSUCCESSFUL.
 602  */
 603 uint32_t
 604 netr_validate_chain(netr_info_t *netr_info, struct netr_authenticator *auth)
 605 {
 606         netr_cred_t cred;
 607         uint32_t result = NT_STATUS_SUCCESS;
 608         uint32_t *dwp;
 609 
 610         ++netr_info->timestamp;
 611 
 612         if (netr_gen_credentials(netr_info->session_key.key,
 613             &netr_info->client_credential,
 614             netr_info->timestamp, &cred) != SMBAUTH_SUCCESS)
 615                 return (NT_STATUS_INTERNAL_ERROR);
 616 
 617         if (&auth->credential == 0) {
 618                 /*
 619                  * If the validation fails, destroy the credential chain.
 620                  * This should trigger a new authentication chain.
 621                  */
 622                 bzero(netr_info, sizeof (netr_info_t));
 623                 return (NT_STATUS_INSUFFICIENT_LOGON_INFO);
 624         }
 625 
 626         result = memcmp(&cred, &auth->credential, sizeof (netr_cred_t));
 627         if (result != 0) {
 628                 /*
 629                  * If the validation fails, destroy the credential chain.
 630                  * This should trigger a new authentication chain.
 631                  */
 632                 bzero(netr_info, sizeof (netr_info_t));
 633                 result = NT_STATUS_UNSUCCESSFUL;
 634         } else {
 635                 /*
 636                  * Otherwise generate the next step in the chain.
 637                  */
 638                 /*LINTED E_BAD_PTR_CAST_ALIGN*/
 639                 dwp = (uint32_t *)&netr_info->client_credential;
 640                 dwp[0] += netr_info->timestamp;
 641 
 642                 netr_info->flags |= NETR_FLG_VALID;
 643         }
 644 
 645         return (result);
 646 }
 647 
 648 /*
 649  * netr_invalidate_chain
 650  *
 651  * Mark the credential chain as invalid so that it will be recreated
 652  * on the next attempt.
 653  */
 654 static void
 655 netr_invalidate_chain(void)
 656 {
 657         netr_global_info.flags &= ~NETR_FLG_VALID;
 658 }
 659 
 660 /*
 661  * netr_setup_identity
 662  *
 663  * Set up the client identity information. All of this information is
 664  * specifically related to the client user and workstation attempting
 665  * to access this system. It may not be in our primary domain.
 666  *
 667  * I don't know what logon_id is, it seems to be a unique identifier.
 668  * Increment it before each use.
 669  */
 670 static void
 671 netr_setup_identity(ndr_heap_t *heap, smb_logon_t *user_info,
 672     netr_logon_id_t *identity)
 673 {
 674         static mutex_t logon_id_mutex;
 675         static uint32_t logon_id;
 676 
 677         (void) mutex_lock(&logon_id_mutex);
 678 
 679         if (logon_id == 0)
 680                 logon_id = 0xDCD0;
 681 
 682         ++logon_id;
 683         user_info->lg_logon_id = logon_id;
 684 
 685         (void) mutex_unlock(&logon_id_mutex);
 686 
 687         /*
 688          * [MS-APDS] 3.1.5.2 "NTLM Network Logon" says to set
 689          * ParameterControl to the 'E' + 'K' bits.  Those are:
 690          * (1 << 5) | (1 << 11), a.k.a
 691          */
 692         identity->parameter_control =
 693             MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT |
 694             MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
 695         identity->logon_id.LowPart = logon_id;
 696         identity->logon_id.HighPart = 0;
 697 
 698         ndr_heap_mkvcs(heap, user_info->lg_domain,
 699             (ndr_vcstr_t *)&identity->domain_name);
 700 
 701         ndr_heap_mkvcs(heap, user_info->lg_username,
 702             (ndr_vcstr_t *)&identity->username);
 703 
 704         /*
 705          * Some systems prefix the client workstation name with \\.
 706          * It doesn't seem to make any difference whether it's there
 707          * or not.
 708          */
 709         ndr_heap_mkvcs(heap, user_info->lg_workstation,
 710             (ndr_vcstr_t *)&identity->workstation);
 711 }
 712 
 713 /*
 714  * Sets up domain, local and well-known group membership for the given
 715  * token. Two assumptions have been made here:
 716  *
 717  *   a) token already contains a valid user SID so that group
 718  *      memberships can be established
 719  *
 720  *   b) token belongs to a domain user
 721  */
 722 static uint32_t
 723 netr_setup_token_wingrps(struct netr_validation_info3 *info3,
 724     smb_token_t *token)
 725 {
 726         smb_ids_t tkn_grps;
 727         uint32_t status;
 728 
 729         tkn_grps.i_cnt = 0;
 730         tkn_grps.i_ids = NULL;
 731 
 732         status = netr_setup_domain_groups(info3, &tkn_grps);
 733         if (status != NT_STATUS_SUCCESS) {
 734                 smb_ids_free(&tkn_grps);
 735                 return (status);
 736         }
 737 
 738         status = smb_sam_usr_groups(token->tkn_user.i_sid, &tkn_grps);
 739         if (status != NT_STATUS_SUCCESS) {
 740                 smb_ids_free(&tkn_grps);
 741                 return (status);
 742         }
 743 
 744         if (netr_isadmin(info3))
 745                 token->tkn_flags |= SMB_ATF_ADMIN;
 746 
 747         status = smb_wka_token_groups(token->tkn_flags, &tkn_grps);
 748         if (status == NT_STATUS_SUCCESS)
 749                 token->tkn_win_grps = tkn_grps;
 750         else
 751                 smb_ids_free(&tkn_grps);
 752 
 753         return (status);
 754 }
 755 
 756 /*
 757  * Converts groups information in the returned structure by domain controller
 758  * (info3) to an internal representation (gids)
 759  */
 760 static uint32_t
 761 netr_setup_domain_groups(struct netr_validation_info3 *info3, smb_ids_t *gids)
 762 {
 763         smb_sid_t *domain_sid;
 764         smb_id_t *ids;
 765         int i, total_cnt;
 766 
 767         if ((i = info3->GroupCount) == 0)
 768                 i++;
 769         i += info3->SidCount;
 770 
 771         total_cnt = gids->i_cnt + i;
 772 
 773         gids->i_ids = realloc(gids->i_ids, total_cnt * sizeof (smb_id_t));
 774         if (gids->i_ids == NULL)
 775                 return (NT_STATUS_NO_MEMORY);
 776 
 777         domain_sid = (smb_sid_t *)info3->LogonDomainId;
 778 
 779         ids = gids->i_ids + gids->i_cnt;
 780         for (i = 0; i < info3->GroupCount; i++, gids->i_cnt++, ids++) {
 781                 ids->i_sid = smb_sid_splice(domain_sid, info3->GroupIds[i].rid);
 782                 if (ids->i_sid == NULL)
 783                         return (NT_STATUS_NO_MEMORY);
 784 
 785                 ids->i_attrs = info3->GroupIds[i].attributes;
 786         }
 787 
 788         if (info3->GroupCount == 0) {
 789                 /*
 790                  * if there's no global group should add the primary group.
 791                  */
 792                 ids->i_sid = smb_sid_splice(domain_sid, info3->PrimaryGroupId);
 793                 if (ids->i_sid == NULL)
 794                         return (NT_STATUS_NO_MEMORY);
 795 
 796                 ids->i_attrs = 0x7;
 797                 gids->i_cnt++;
 798                 ids++;
 799         }
 800 
 801         /* Add the extra SIDs */
 802         for (i = 0; i < info3->SidCount; i++, gids->i_cnt++, ids++) {
 803                 ids->i_sid = smb_sid_dup((smb_sid_t *)info3->ExtraSids[i].sid);
 804                 if (ids->i_sid == NULL)
 805                         return (NT_STATUS_NO_MEMORY);
 806 
 807                 ids->i_attrs = info3->ExtraSids[i].attributes;
 808         }
 809 
 810         return (NT_STATUS_SUCCESS);
 811 }
 812 
 813 /*
 814  * Determines if the given user is the domain Administrator or a
 815  * member of Domain Admins
 816  */
 817 static boolean_t
 818 netr_isadmin(struct netr_validation_info3 *info3)
 819 {
 820         smb_domain_t di;
 821         int i;
 822 
 823         if (!smb_domain_lookup_sid((smb_sid_t *)info3->LogonDomainId, &di))
 824                 return (B_FALSE);
 825 
 826         if (di.di_type != SMB_DOMAIN_PRIMARY)
 827                 return (B_FALSE);
 828 
 829         if ((info3->UserId == DOMAIN_USER_RID_ADMIN) ||
 830             (info3->PrimaryGroupId == DOMAIN_GROUP_RID_ADMINS))
 831                 return (B_TRUE);
 832 
 833         for (i = 0; i < info3->GroupCount; i++)
 834                 if (info3->GroupIds[i].rid == DOMAIN_GROUP_RID_ADMINS)
 835                         return (B_TRUE);
 836 
 837         return (B_FALSE);
 838 }