/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
* NETR SamLogon and SamLogoff RPC client functions.
*/
#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
#include <time.h>
#include <alloca.h>
#include <unistd.h>
#include <netdb.h>
#include <thread.h>
#include <smbsrv/libmlrpc.h>
#include <smbsrv/libmlsvc.h>
#include <smbsrv/netrauth.h>
#include <smbsrv/smb_token.h>
#include <mlsvc.h>
smb_logon_t *, smb_token_t *);
static void netr_invalidate_chain(void);
struct netr_logon_info1 *);
smb_logon_t *, struct netr_logon_info2 *);
netr_logon_id_t *);
smb_ids_t *);
smb_token_t *);
smb_token_t *);
/*
* Shared with netr_auth.c
*/
extern netr_info_t netr_global_info;
/*
* Helper for Kerberos authentication
*/
{
int rc;
/* Need to keep this until we're done with &info */
goto out;
if (rc != NDR_DRC_OK) {
goto out;
}
/* Deal with the "resource groups"? */
out:
return (status);
}
/*
* Code factored out of netr_setup_token()
*/
static uint32_t
{
goto errout;
goto errout;
goto errout;
}
goto errout;
}
return (NT_STATUS_INSUFF_SERVER_RESOURCES);
}
/*
* Abort impending domain logon requests.
*/
void
smb_logon_abort(void)
{
(void) mutex_lock(&netlogon_mutex);
if (netlogon_busy && !netlogon_abort)
(void) cond_broadcast(&netlogon_cv);
(void) mutex_unlock(&netlogon_mutex);
}
/*
* This is the entry point for authenticating domain users.
*
* If we are not going to attempt to authenticate the user,
* this function must return without updating the status.
*
* If the user is successfully authenticated, we build an
* access token and the status will be NT_STATUS_SUCCESS.
* Otherwise, the token contents are invalid.
*/
void
{
int i;
return;
return;
for (i = 0; i < NETLOGON_ATTEMPTS; ++i) {
(void) mutex_lock(&netlogon_mutex);
while (netlogon_busy && !netlogon_abort)
if (netlogon_abort) {
(void) mutex_unlock(&netlogon_mutex);
return;
}
(void) mutex_unlock(&netlogon_mutex);
(void) mutex_lock(&netlogon_mutex);
if (netlogon_abort)
(void) cond_signal(&netlogon_cv);
(void) mutex_unlock(&netlogon_mutex);
break;
}
if (status != NT_STATUS_SUCCESS)
}
static uint32_t
{
int retries = 0;
/* Avoid interfering with DC discovery. */
if (smb_ddiscover_wait() != 0 ||
!smb_domain_getinfo(&di)) {
return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
}
do {
&netr_handle) != 0)
return (NT_STATUS_OPEN_FAILED);
}
if (status != 0) {
(void) netr_close(&netr_handle);
return (NT_STATUS_LOGON_FAILURE);
}
}
(void) netr_close(&netr_handle);
if (retries >= 3)
return (status);
}
static uint32_t
{
return (NT_STATUS_NO_MEMORY);
return (NT_STATUS_NO_MEMORY);
} else {
}
if (username)
if (domain)
return (NT_STATUS_NO_MEMORY);
if (status != NT_STATUS_SUCCESS)
return (status);
/*
* The UserSessionKey in NetrSamLogon RPC is obfuscated using the
* session key obtained in the NETLOGON credential chain.
* An 8 byte session key is zero extended to 16 bytes. This 16 byte
* key is the key to the RC4 algorithm. The RC4 byte stream is
* exclusively ored with the 16 byte UserSessionKey to recover
* the the clear form.
*/
return (NT_STATUS_NO_MEMORY);
return (NT_STATUS_SUCCESS);
}
/*
* netr_server_samlogon
*
* NetrServerSamLogon RPC: interactive or network. It is assumed that
* we have already authenticated with the PDC. If everything works,
* we build a user info structure and return it, where the caller will
* probably build an access token.
*
* Returns an NT status. There are numerous possibilities here.
* For example:
* NT_STATUS_INVALID_INFO_CLASS
* NT_STATUS_INVALID_PARAMETER
* NT_STATUS_ACCESS_DENIED
* NT_STATUS_PASSWORD_MUST_CHANGE
* NT_STATUS_NO_SUCH_USER
* NT_STATUS_WRONG_PASSWORD
* NT_STATUS_LOGON_FAILURE
* NT_STATUS_ACCOUNT_RESTRICTION
* NT_STATUS_INVALID_LOGON_HOURS
* NT_STATUS_INVALID_WORKSTATION
* NT_STATUS_INTERNAL_ERROR
* NT_STATUS_PASSWORD_EXPIRED
* NT_STATUS_ACCOUNT_DISABLED
*/
{
int opnum;
/*
* Should we get the server and hostname from netr_info?
*/
return (NT_STATUS_INTERNAL_ERROR);
}
return (NT_STATUS_INTERNAL_ERROR);
}
if (rc != SMBAUTH_SUCCESS) {
return (NT_STATUS_INTERNAL_ERROR);
}
case NETR_INTERACTIVE_LOGON:
break;
case NETR_NETWORK_LOGON:
return (NT_STATUS_INVALID_PARAMETER);
}
break;
default:
return (NT_STATUS_INVALID_PARAMETER);
}
if (rc != 0) {
/*
* We need to validate the chain even though we have
* a non-zero status. If the status is ACCESS_DENIED
* this will trigger a new credential chain. However,
* a valid credential is returned with some status
* codes; for example, WRONG_PASSWORD.
*/
} else {
if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) {
return (status);
}
}
return (status);
}
/*
* netr_interactive_samlogon
*
* Set things up for an interactive SamLogon. Copy the NT and LM
* passwords to the logon structure and hash them with the session
* key.
*/
static void
struct netr_logon_info1 *info1)
{
}
/*
* netr_network_samlogon
*
* Set things up for a network SamLogon. We provide a copy of the random
* challenge, that we sent to the client, to the domain controller. This
* is the key that the client will have used to encrypt the NT and LM
* passwords. Note that Windows 9x clients may not provide both passwords.
*/
/*ARGSUSED*/
static void
{
} else {
}
} else {
}
} else {
}
}
/*
* netr_setup_authenticator
*
* Set up the request and return authenticators. A new credential is
* generated from the session key, the current client credential and
* the current time, i.e.
*
* NewCredential = Cred(SessionKey, OldCredential, time);
*
* The timestamp, which is used as a random seed, is stored in both
* the request and return authenticators.
*
* If any difficulties occur using the cryptographic framework, the
* function returns SMBAUTH_FAILURE. Otherwise SMBAUTH_SUCCESS is
* returned.
*/
int
{
return (SMBAUTH_FAILURE);
if (ret_auth) {
}
return (SMBAUTH_SUCCESS);
}
/*
* Validate the returned credentials and update the credential chain.
* The server returns an updated client credential rather than a new
* server credential. The server uses (timestamp + 1) when generating
* the credential.
*
* Generate the new seed for the credential chain. The new seed is
* formed by adding (timestamp + 1) to the current client credential.
* The only quirk is the uint32_t style addition.
*
* Returns NT_STATUS_INSUFFICIENT_LOGON_INFO if auth->credential is a
* NULL pointer. The Authenticator field of the SamLogon response packet
* sent by the Samba 3 PDC always return NULL pointer if the received
* SamLogon request is not immediately followed by the ServerReqChallenge
* and ServerAuthenticate2 requests.
*
* Returns NT_STATUS_SUCCESS if the server returned a valid credential.
* Otherwise we retirm NT_STATUS_UNSUCCESSFUL.
*/
{
return (NT_STATUS_INTERNAL_ERROR);
if (&auth->credential == 0) {
/*
* If the validation fails, destroy the credential chain.
* This should trigger a new authentication chain.
*/
return (NT_STATUS_INSUFFICIENT_LOGON_INFO);
}
if (result != 0) {
/*
* If the validation fails, destroy the credential chain.
* This should trigger a new authentication chain.
*/
} else {
/*
* Otherwise generate the next step in the chain.
*/
/*LINTED E_BAD_PTR_CAST_ALIGN*/
}
return (result);
}
/*
* netr_invalidate_chain
*
* Mark the credential chain as invalid so that it will be recreated
* on the next attempt.
*/
static void
netr_invalidate_chain(void)
{
}
/*
* netr_setup_identity
*
* Set up the client identity information. All of this information is
* specifically related to the client user and workstation attempting
* to access this system. It may not be in our primary domain.
*
* I don't know what logon_id is, it seems to be a unique identifier.
* Increment it before each use.
*/
static void
{
(void) mutex_lock(&logon_id_mutex);
if (logon_id == 0)
logon_id = 0xDCD0;
++logon_id;
(void) mutex_unlock(&logon_id_mutex);
/*
* [MS-APDS] 3.1.5.2 "NTLM Network Logon" says to set
* ParameterControl to the 'E' + 'K' bits. Those are:
* (1 << 5) | (1 << 11), a.k.a
*/
/*
* Some systems prefix the client workstation name with \\.
* It doesn't seem to make any difference whether it's there
* or not.
*/
}
/*
* Sets up domain, local and well-known group membership for the given
* token. Two assumptions have been made here:
*
* a) token already contains a valid user SID so that group
* memberships can be established
*
* b) token belongs to a domain user
*/
static uint32_t
{
if (status != NT_STATUS_SUCCESS) {
return (status);
}
if (status != NT_STATUS_SUCCESS) {
return (status);
}
if (netr_isadmin(info3))
if (status == NT_STATUS_SUCCESS)
else
return (status);
}
/*
* Converts groups information in the returned structure by domain controller
* (info3) to an internal representation (gids)
*/
static uint32_t
{
int i, total_cnt;
if ((i = info3->GroupCount) == 0)
i++;
return (NT_STATUS_NO_MEMORY);
return (NT_STATUS_NO_MEMORY);
}
if (info3->GroupCount == 0) {
/*
* if there's no global group should add the primary group.
*/
return (NT_STATUS_NO_MEMORY);
ids++;
}
/* Add the extra SIDs */
return (NT_STATUS_NO_MEMORY);
}
return (NT_STATUS_SUCCESS);
}
/*
* Determines if the given user is the domain Administrator or a
* member of Domain Admins
*/
static boolean_t
{
int i;
return (B_FALSE);
return (B_FALSE);
return (B_TRUE);
for (i = 0; i < info3->GroupCount; i++)
return (B_TRUE);
return (B_FALSE);
}