Jorge's Quest For Knowledge!

All About Identity And Security On-Premises And In The Cloud – It's Just Like An Addiction, The More You Have, The More You Want To Have!

Archive for the ‘Active Directory Domain Services (ADDS)’ Category

(2019-05-27) (Un)Constrained Kerberos Delegation

Posted by Jorge on 2019-05-26


Within Windows Server Kerberos Delegation can be used to, for example, access data in a SQL database by a front-end service on behalf of the user accessing the front-end service.

The flow then looks like: USER (U)  —— access—-> FRONT-END SERVICE (FE) —— access on behalf of “U”—-> DATA (D).

While U accesses FE, FE then needs to access D on behalf of U. The “on behalf of” part is achieved through Kerberos Delegation.

When looking at Kerberos Delegation there are three flavors that can be used

  1. Unconstrained Kerberos Delegation
  2. Constrained Kerberos Delegation
  3. Resource-Based Kerberos Delegation

[ad.1] Unconstrained Kerberos Delegation

This is available in since Windows Server 2000 up to the latest and greatest version of Windows available today. It is VERY INSECURE, therefore avoid this if possible! Why is it insecure? Well, with UNconstrained Kerberos Delegation, the FE, when configured for Unconstrained Kerberos Delegation, is allowed to access ANY service without restrictions, and not just D. You could also call this “Account-Based Unconstrained Kerberos Delegation”

Imagine a bank vault (the “D”) and many other bank vaults and a bank clerk (the “FE”). The bank clerk is configured to access any access any bank vault on behalf of clients, but the bank clerk should really only access one specific bank vault (“D”) on behalf of clients. Yeah right! Better yet, the owner of the bank vaults do not have anything to say about this. Just shut up and allow access!

Read more about it:

[ad.2] Constrained Kerberos Delegation

This is available in since Windows Server 2003 (with FFL W2K3) up to the latest and greatest version of Windows available today. It is a better option than the previous option, so if you cannot implement the next option, then at least try this option! With Constrained Kerberos Delegation, the FE, when configured for Constrained Kerberos Delegation, is ONLY allowed to access specific services (e.g. only “D”) for which it has been configured to do so and not any service. You could also call this “Account-Based Constrained Kerberos Delegation”

Imagine a bank vault (the “D”) and many other bank vaults and a bank clerk (the “FE”). The bank clerk is configured to access only the specific bank vault (“D”) on behalf of clients. Other bank vaults cannot be accessed on behalf of clients. Sounds better, but the owner of that specific bank vaults still does not have anything to say about this. Just shut up and allow access!

Read more about it:

[ad.3] Resource-Based Kerberos Delegation

This is available in since Windows Server 2012 up to the latest and greatest version of Windows available today. This is the best option when compared to the previous 2 options. This does require the complete service chain (FE, D, AD domain for FE, AD domain for D and any domain in between) to run at least Windows Server 2012. With regards to AD domains, at least one Windows Server 2012 DC is needed and there are no dependencies on DFLs/FFLs. Guess what?! This is by default constrained, therefore no need to mention the word “Constrained”, otherwise it is the same as saying “wet water”! With “Resource-Based Kerberos Delegation”, the D, when configured for Resource-Based Kerberos Delegation, is ONLY allowing access by specific services (e.g. only “FE”).

Imagine a bank vault (the “D”) and many other bank vaults and a bank clerk (the “FE”). A specific bank vault (“D”) is configured by its owner to only allow access by the bank clerk (the “FE”) on behalf of clients. Sounds even better!

Read more about it:

With both “Unconstrained Kerberos Delegation” and “Constrained Kerberos Delegation”, delegation is configured at delegated account level (hence: “Account-Based (Un)Constrained Kerberos Delegation”), and not at resource level, which is weird! Looking at ACLs in all kinds of services (file, sql, AD, etc.), permissions are configured at resource level, as it should be! This changes in “Resource-Based Constrained Delegation”, which is my preferred way of delegation!

Additional Reading:

Enjoy and have fun!,

Jorge

————————————————————————————————————————————————————-
This posting is provided "AS IS" with no warranties and confers no rights!
Always evaluate/test everything yourself first before using/implementing this in production!
This is today’s opinion/technology, it might be different tomorrow and will definitely be different in 10 years!
DISCLAIMER:
https://jorgequestforknowledge.wordpress.com/disclaimer/
————————————————————————————————————————————————————-
########################### Jorge’s Quest For Knowledge ##########################
####################
http://JorgeQuestForKnowledge.wordpress.com/ ###################
————————————————————————————————————————————————————-

Advertisements

Posted in Active Directory Domain Services (ADDS), Delegation, Kerberos AuthN | Leave a Comment »

(2019-02-08) Troubleshooting HTTPS/LDAPS

Posted by Jorge on 2019-02-08


My ADFS environment has an attribute store configured to an ADLDS and for that I used the “ldapattributestore” that still is available in the codeplex archives (https://archive.codeplex.com/?p=ldapattributestore). Because that ADLDS instance was running on a DC with ADDS, I configured the ADLDS instance to use port 5389 for LDAP and port 5636 for LDAPS. The “connection" string” for that LDAP Attribute Store was configured to use a secure connection (i.e. LDAPS) over port 5636. As I was checking functionality of my test environment I also tested this part by targeting the “Show My Claims” app (https://jorgequestforknowledge.wordpress.com/2015/07/04/displaying-the-issued-claims-in-a-security-token-on-screen/) that dives into that LDAP Attribute Store and displays specific claims on screen.

As I did not use my test environment for some time, several certs were expired and needed replacement. I opened the Local Machine certificate store and replaced every certificate that was expired or was going to expire soon with a new certificate using the exact same subject and SANs. After updating all certificates I started configuring and testing every single application using a certificate. So as you can imagine at some point in time I ended up with ADFS, the Show My Claims web site and with that the LDAPS attribute.

As an initial test I accessed the Show My Claims web site and due to the Issuance Transform Rules ADFS needed to dive into the ADLDS attribute store to source claims being displayed on screen. Unfortunately that failed. Looking at the ADFS Admin Event Log, ADFS was having issues accessing the Attribute Store. Those issues were related to the usage of LDAPS.

In that case I needed to start the basic testing of LDAPS through the good old LDP and see what was going on.

So after starting LDP and entered the FQDN of the ADLDS instance and its configured LDAPS port   

image

Figure 1: Starting LDP And Connecting To The ADLDS Instance Using The FQDN, The LDAPS Port

After clicking OK I was not surprised about the error “cannot open connection”. The more interesting question was “WHY?”

image

Figure 2: Error When Connecting To The ADLDS Instance

For this ADLDS instance I had a certificate with a subject and SAN that contained “IDSTORE.IAMTEC.NL”. I also permissioned the corresponding private key with the service account being used by the ADLDS instance. So far so good, but even with that it did not work. That’s when I decided to check all the requirements of a certificate to be used for LDAPS and used the following Microsoft article:

The certificate that I was using fulfilled all requirements and with that in mind it was time to enable debug logging for SCHANNEL when using HTTPS or LDAPS:

To make sure I was not missing any information, I configured the following for debug logging of SCHANNEL:

  • 0x0001 Log error messages
  • 0x0002 Log warnings
  • 0x0004 Log informational and success events

After enabling debug logging for SCHANNEL I tried it again with LDP and in the SYSTEM Event Log I saw the following error.   

image

Figure 3: Error When Accessing The Private Key

A fatal error occurred when attempting to access the TLS server credential private key. The error code returned from the cryptographic module is 0x8009030D. The internal error state is 10001.

I reconfirmed the certificate for IDSTORE.IAMTEC.NL had its private key configured for the service account in use by the ADLDS instance. It still did nit work

I also saw the following event in the SYSTEM Event Log

image

Figure 4: The Private Key Being Used By The ADLDS Instance

The TLS server credential’s private key has the following properties:

   CSP name: Microsoft RSA SChannel Cryptographic Provider
   CSP type: 12
   Key name: d40c17eadd0fb4c1e33541e54ebf55b1_c9c5687f-fc0b-4765-bc6d-2435ba59e1b2
   Key Type: key exchange
    Key Flags: 0x20

The attached data contains the certificate.

The event shown above mentioned the private key being used by the the ADLDS instance, but it still did not mention which certificate was being used. Switching to the details TAB, it told me more about the corresponding certificate that was being used. Going through the data I noticed it contained the SANs “*.IAMTEC.NET” and “*.IAMTEC.NL”. That was surprising to me as the certificate that envisioned for the ADLDS only contained “IDSTORE.IAMTEC.NL” as subject and as SAN.

image

Figure 5: The Certificate Data That Belong To The Private Key

After seeing this I remembered the following listed in the first MSFT article:

Multiple SSL certificates

Schannel, the Microsoft SSL provider, selects the first valid certificate that it finds in the local computer store. If there are multiple valid certificates available in the local computer store, Schannel may not select the correct certificate

What I never realized was that my wildcard certificate was going to be used first before even considering the usage of the certificate that contained the more specific subject and SAN.

As displayed below I permissioned the private key that belonged to the wildcard certificate ….

image

Figure 6: Permissioning The Private Key Of The Wildcard Certificate

Retried my test with LDP as shown below….

image

Figure 7: Retrying The LDAPS Test With DP

….and now it worked. You can see at the top it is connecting securely through LDAPS and that it is using a cipher strength of 256 bits

After this I check the LDAP Attribute Store configuration to make sure everything was configured correctly. And it was. Retrying accessing the Show My Claims site I was able to the claims that were sourced from the ADLDS instance

Check!

Cheers,
Jorge

————————————————————————————————————————————————————-
This posting is provided "AS IS" with no warranties and confers no rights!
Always evaluate/test everything yourself first before using/implementing this in production!
This is today’s opinion/technology, it might be different tomorrow and will definitely be different in 10 years!
DISCLAIMER:
https://jorgequestforknowledge.wordpress.com/disclaimer/
————————————————————————————————————————————————————-
########################### Jorge’s Quest For Knowledge ##########################
####################
http://JorgeQuestForKnowledge.wordpress.com/ ###################
————————————————————————————————————————————————————-

Posted in Active Directory Domain Services (ADDS), Active Directory Federation Services (ADFS), Active Directory Lightweight Directory Services (ADLDS), Attribute Store, Certificates, Claim Types, LDAPS, LDAPS, LDP | Leave a Comment »

(2017-09-11) Registering The Azure AD Connect Health Agent Through A Proxy

Posted by Jorge on 2017-09-11


If you need to register your Azure AD Connect Health Agent for ADFS, Sync or ADDS through a proxy, you need to first configure the proxy server and port for Azure AD Connect Health.

You have the following options:

  • Read the current proxy settings for AAD Connect Health –> Get-AzureAdConnectHealthProxySettings
  • Import from Internet Explorer (if confgured) –> Set-AzureAdConnectHealthProxySettings -ImportFromInternetSettings
  • Import from WinHTTP (if configured) –> Set-AzureAdConnectHealthProxySettings -ImportFromWinHttp
  • Specify Proxy addresses manually –> Set-AzureAdConnectHealthProxySettings -HttpsProxyAddress <fqdn>:<port>
  • Clear existing proxy configuration –> Set-AzureAdConnectHealthProxySettings -NoProxy

Now let’s assume you have used one of the options to configure the proxy settings. After that you can use the following PowerShell commands to register the Azure AD Connect Health Agent

$azureADUserName = "<replace this with a native or federated account that has the global admin role and is NOT MFA enabled/enforced>"

$azureADPassword = ‘<replace this with the password>’

$azureADSecurePassword = ConvertTo-SecureString $azureADPassword -AsPlainText –Force

$azureADCreds = New-Object System.Management.Automation.PSCredential $azureADUserName, $azureADSecurePassword

REMARK: specifying the credentials through PowerShell may be required if the server installing Azure AD Connect Health does not support cookies and/or javascript.

REMARK: Of course you can also use –> $azureADCreds = Get-Credential

For Azure AD Connect Health for ADFS –> Register-AzureADConnectHealthADFSAgent -Credential $azureADCreds

For Azure AD Connect Health for ADDS –> Register-AzureADConnectHealthADDSAgent-Credential $azureADCreds

For Azure AD Connect Health for Sync –> Register-AzureADConnectHealthSyncAgent-Credential $azureADCreds –AttributeFiltering [$false | $true] -StagingMode [$false | $true]

If the registration is succesful, you should see something similar to te following

image

Figure 1: Successful Registration Of Azure AD Connect Health (In This Case For ADFS)

However, you may see the following error:

SNAGHTML4a070dba

Figure 2: Unsuccessful Registration Of Azure AD Connect Health (In This Case For ADFS) With The Error: “user_realm_discovery_failed: User Realm Discovery Failed”

Always when receiving an error, open the specified log file to see/understand what went wrong.

In this case opening the file I saw the following:

2017-08-25 07:20:39.295 ProductName: Azure AD Connect Health AD FS Agent, FileVersion: 3.0.68.0, Current UTC Time: 2017-08-25 07:20:39Z

2017-08-25 07:20:39.295 AHealthServiceUri (ARM): https://management.azure.com/providers/Microsoft.ADHybridHealthService/

2017-08-25 07:20:39.31 AdHybridHealthServiceUri: https://s1.adhybridhealth.azure.com/

2017-08-25 07:20:39.326 AHealthServiceUri (ARM): https://management.azure.com/providers/Microsoft.ADHybridHealthService/

2017-08-25 07:20:39.326 AdHybridHealthServiceUri: https://s1.adhybridhealth.azure.com/

2017-08-25 07:20:39.373 User Context outbound connections to https://management.azure.com/providers/Microsoft.ADHybridHealthService/ will use proxy address https://management.azure.com/providers/Microsoft.ADHybridHealthService/ (if equal, no proxy is used)

2017-08-25 07:20:39.373 Service Context: Outbound connections to https://management.azure.com/providers/Microsoft.ADHybridHealthService/ will use proxy address http://proxy.iamtec.nl:3128/ (if equal, no proxy is used)

2017-08-25 07:20:39.373 UploadLogFileFromDisk: arguments LogFileLocation=C:\Users\XXX\AppData\Local\Temp\3\AdHealthAdfsAgentConfiguration.2017-08-25_09-20-39.log;ProductName=Azure AD Connect Health AD FS Agent;ProductVersion=3.0.68.0;MachineName=SERVER;ServiceUri=https://s1.adhybridhealth.azure.com/;Result=started;type=registration,activityId=432fd061-6219-498a-8a98-bf68d90a5431

2017-08-25 07:20:39.389 UploadLogFileFromDisk: TenantId: unknown

2017-08-25 07:20:39.389 UploadLogFileFromDisk: MachineId: unknown

2017-08-25 07:20:39.389 UploadLogFileFromDisk: reading file contents of C:\Users\XXX\AppData\Local\Temp\3\AdHealthAdfsAgentConfiguration.2017-08-25_09-20-39.log

2017-08-25 07:20:39.389 UploadLogFileFromDisk: log length: 1842

2017-08-25 07:20:39.389 UploadFile: start, url: https://s1.adhybridhealth.azure.com/providers/Microsoft.ADHybridHealthService/diagnostics/logs/installer/product/Azure-AD-Connect-Health-AD-FS-Agent/version/3.0.68.0/machine/SERVER?result=started&type=registration

powershell.exe Information: 0 : AdHealthWebproxy settings: HttpsProxyAddress: http://proxy.iamtec.nl:3128/, initialization status: Succeeded; Registry value did not exist; Value was not a string, No Initialization Exception

2017-08-25 07:20:40.451 UploadFile: upload successful

2017-08-25 07:20:40.451 UploadLogFileFromDisk: response: Http response code: OK

"log successfully uploaded"

2017-08-25 07:20:40.451 AHealthServiceApiVersion: 2014-01-01

2017-08-25 07:20:40.451 –> https://management.core.windows.net/

powershell.exe Information: 0 : 8/25/2017 7:20:40 AM:  – AdHealthAgentConfigurationUtility: ADAL .NET with assembly version ‘2.24.0.0’, file version ‘2.24.30411.1323’ and informational version ‘4482033c814db2fd31423c79b5027d0b5dfb7a6d’ is running…

powershell.exe Information: 0 : 8/25/2017 7:20:40 AM: 432eb23e-19c6-4c97-9bc2-ca4f7138fb3e – AcquireTokenNonInteractiveHandler: === Token Acquisition started:

                Authority: https://login.windows.net/common/

                Resource: https://management.core.windows.net/

                ClientId: cf6d7e68-f018-4e0a-a7b3-126e053fb88d

                CacheType: Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache (0 items)

                Authentication Target: User

powershell.exe Information: 0 : 8/25/2017 7:20:40 AM: 432eb23e-19c6-4c97-9bc2-ca4f7138fb3e – <RunAsync>d__0: No matching token was found in the cache

powershell.exe Information: 0 : 8/25/2017 7:20:40 AM: 432eb23e-19c6-4c97-9bc2-ca4f7138fb3e – AsyncMethodBuilderCore: Sending user realm discovery request to ‘https://login.windows.net/common/UserRealm/jorge@iamtec.nl?api-version=1.0&#8217;

powershell.exe Error: 0 : 8/25/2017 7:21:10 AM: 432eb23e-19c6-4c97-9bc2-ca4f7138fb3e – AsyncMethodBuilderCore: Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException: user_realm_discovery_failed: User realm discovery failed —> System.Net.WebException: The operation has timed out

   at System.Net.HttpWebRequest.GetResponse()

   at Microsoft.IdentityModel.Clients.ActiveDirectory.HttpWebRequestWrapper.<GetResponseSyncOrAsync>d__2.MoveNext()

— End of stack trace from previous location where exception was thrown —

   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)

   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

   at Microsoft.IdentityModel.Clients.ActiveDirectory.UserRealmDiscoveryResponse.<CreateByDiscoveryAsync>d__0.MoveNext()

   — End of inner exception stack trace —

   at Microsoft.IdentityModel.Clients.ActiveDirectory.UserRealmDiscoveryResponse.<CreateByDiscoveryAsync>d__0.MoveNext()

— End of stack trace from previous location where exception was thrown —

   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)

   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()

   at Microsoft.IdentityModel.Clients.ActiveDirectory.AcquireTokenNonInteractiveHandler.<PreTokenRequest>d__4.MoveNext()

— End of stack trace from previous location where exception was thrown —

   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)

   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

   at Microsoft.IdentityModel.Clients.ActiveDirectory.AcquireTokenHandlerBase.<RunAsync>d__0.MoveNext()

                ErrorCode: user_realm_discovery_failed

                StatusCode: 0

2017-08-25 07:21:10.645 Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException: user_realm_discovery_failed: User realm discovery failed —> System.Net.WebException: The operation has timed out

   at System.Net.HttpWebRequest.GetResponse()

   at Microsoft.IdentityModel.Clients.ActiveDirectory.HttpWebRequestWrapper.<GetResponseSyncOrAsync>d__2.MoveNext()

— End of stack trace from previous location where exception was thrown —

   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)

   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

   at Microsoft.IdentityModel.Clients.ActiveDirectory.UserRealmDiscoveryResponse.<CreateByDiscoveryAsync>d__0.MoveNext()

   — End of inner exception stack trace —

   at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.RunAsyncTask[T](Task`1 task)

   at Microsoft.Identity.Health.Common.Clients.PowerShell.ConfigurationModule.AdHealthAgentConfigurationUtility.ObtainAadAccessTokenWithExistingCredential(PSCredential psCredential)

   at Microsoft.Identity.Health.Common.Clients.PowerShell.ConfigurationModule.RegisterADHealthAgent.ProcessRecord()

                ErrorCode: user_realm_discovery_failed

                StatusCode: 0

2017-08-25 07:21:10.66 UploadLogFileFromDisk: arguments LogFileLocation=C:\Users\XXX\AppData\Local\Temp\3\AdHealthAdfsAgentConfiguration.2017-08-25_09-20-39.log;ProductName=Azure AD Connect Health AD FS Agent;ProductVersion=3.0.68.0;MachineName=SERVER;ServiceUri=https://s1.adhybridhealth.azure.com/;Result=failed;type=registration,activityId=432fd061-6219-498a-9b98-bf68d90a5431

2017-08-25 07:21:10.66 UploadLogFileFromDisk: TenantId: unknown

2017-08-25 07:21:10.66 UploadLogFileFromDisk: MachineId: unknown

2017-08-25 07:21:10.66 UploadLogFileFromDisk: reading file contents of C:\Users\XXX\AppData\Local\Temp\3\AdHealthAdfsAgentConfiguration.2017-08-25_09-20-39.log

2017-08-25 07:21:10.66 UploadLogFileFromDisk: log length: 7608

2017-08-25 07:21:10.66 UploadFile: start, url: https://s1.adhybridhealth.azure.com/providers/Microsoft.ADHybridHealthService/diagnostics/logs/installer/product/Azure-AD-Connect-Health-AD-FS-Agent/version/3.0.68.0/machine/SERVER?result=failed&type=registration

2017-08-25 07:21:11.082 UploadFile: upload successful

2017-08-25 07:21:11.082 UploadLogFileFromDisk: response: Http response code: OK

"log successfully uploaded"

The green lines above list what/where things go wrong. In this case user realm discovery is failing. However, the yellow lines are giving you a hint why things are going wrong.

Looking at the second yellow line you see the service context is using the proxy server configured for Azure AD Connect Health. However, the first yellow line tells you the user context context is not using any proxy server at all. Because of that it is failing the realm discovery

The solution for this is to configure the user context with a proxy server and port. That is done by configuring the proxy settings in Internet Explorer with the same values

image

Figure 3: Configuring The User Context With Proxy Settings

After doing this, retry registration.

If it still fails check the log file again. You might see the following:

powershell.exe Information: 0 : 9/11/2017 11:54:12 AM: 68215d80-864f-43af-9b6a-8f3f41f276af – <RunAsync>d__0: No matching token was found in the cache

powershell.exe Information: 0 : 9/11/2017 11:54:12 AM: 68215d80-864f-43af-9b6a-8f3f41f276af – AsyncMethodBuilderCore: Sending user realm discovery request to ‘https://login.windows.net/common/UserRealm/jorge@iamtec.nl?api-version=1.0&#8217;

powershell.exe Error: 0 : 9/11/2017 11:54:12 AM: 68215d80-864f-43af-9b6a-8f3f41f276af – AsyncMethodBuilderCore: Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException: user_realm_discovery_failed: User realm discovery failed —> System.Net.WebException: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel. —> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.

   at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, Exception exception)

   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)

   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)

   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)

   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)

   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)

at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)

   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)

   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)

   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)

   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)

   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)

   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)

In this case, your proxy server is terminating SSL traffic for inspection. If you have not imported the root certificate of the certificate being used by the proxy server into the “Trusted Root Certificate Authority” store, you will see an error like this. Import the root certificate of the certificate being used by the proxy server into the “Trusted Root Certificate Authority” store to fix this.

Hope this helps you!

Cheers,
Jorge

————————————————————————————————————————————————————-
This posting is provided "AS IS" with no warranties and confers no rights!
Always evaluate/test everything yourself first before using/implementing this in production!
This is today’s opinion/technology, it might be different tomorrow and will definitely be different in 10 years!
DISCLAIMER:
https://jorgequestforknowledge.wordpress.com/disclaimer/
————————————————————————————————————————————————————-
########################### Jorge’s Quest For Knowledge ##########################
####################
http://JorgeQuestForKnowledge.wordpress.com/ ###################
————————————————————————————————————————————————————-

Posted in Active Directory Domain Services (ADDS), Active Directory Federation Services (ADFS), Azure AD Connect Health, Azure AD Connect Health, Azure AD Connect Health, Windows Azure Active Directory | Leave a Comment »

(2017-05-23) Bug In GPP Registry Wizard Prevents Registry Settings From Applying

Posted by Jorge on 2017-05-23


In the blog post (2017-03-01) Hardening – Disabling Weak Ciphers, Hashes And Protocols On ADFS, WAP, AAD Connect, Azure AD MFA Server And Azure AD Application Proxy I explain how to harden several hybrid identity related servers. I provide the individual settings and I also provide at the end of the blog post how you can use a GPO to configure all the settings from AD. For the registry settings I configured one server using the REG ADD commands and then I used the Registry Wizard in the GPP to consume all the settings I configured. The GPO I used also contained other regular policy settings.

After having all the settings in the GPO as described above, I found out the registry settings specifically never were applied to the servers, although the GPO was being processed. I confirmed processing of the GPO by using GPRESULT remotely and locally. I also looked at the registry settings multiple times to see if I could find any anomalies, but unfortunately I did not see anything strange. Well it took me some time, but if you look very carefully there is something strange to it

image

Figure 1: Setting Registry Values Through A GPO

I’m going to spare you the time that it took me to find what was wrong and guide you through the steps so that you understand where it goes wrong and how you can fix it.

If you look at figure 2 below what are you noticing? Hint: Look at the values in every column!

Correct! The “Hive” column does not have any value specified. THAT is the reason the registry setting is not applied at all to targeted servers

image

Figure 2: A Sample Registry Setting That Was Read Through The Registry Wizard – Empty Hive Value

However, if you open a registry setting for which the “Hive” value is not listed as shown in figure 2, you can see in figure 3, the “Hive” value IS listed. Confusing right?!

image

Figure 3: A Sample Registry Setting That Was Read Through The Registry Wizard – Populated Hive Value

The solution here is to reconfigure the “Hive value and committing the change into the GPO. Bu if you look at the [Apply] button figure 3 you see it is grayed out.

As shown in figure 4 just reselect the already listed “Hive” value.

image

Figure 4: A Sample Registry Setting That Was Read Through The Registry Wizard – Reselecting The Hive Value

After doing that the [Apply] button becomes available to be clicked/pressed.

image

Figure 5: A Sample Registry Setting That Was Read Through The Registry Wizard – Recommitting The Hive Value

After you have clicked/pressed the [Apply] button, you can see the “Hive” value is indeed populated as shown in the figure 6.

image

Figure 6: Sample Registry Setting That Was Read Through The Registry Wizard – Populated Hive Value For One Setting

Now do this for every registry setting read by the wizard

image

Figure 7: Sample Registry Setting That Was Read Through The Registry Wizard – Populated Hive Value For Another Setting

Because the “Hive” value was not specified for the registry settings in the GPO, those same registry settings were not applied, although the GPO that contained them was processed! Respecifying the “Hive” value solved the problem. And yes you will have to do this for every registry setting.

This issue only occurs when you use the Registry Wizard within the GPP and specify a remote server as the target server. If you specify the local server as the target then “Hive” value is populated correctly.

This occurred on both W2K12R2 and W2K16 servers

Cheers,
Jorge
———————————————————————————————
* This posting is provided "AS IS" with no warranties and confers no rights!
* Always evaluate/test yourself before using/implementing this!
* DISCLAIMER:
https://jorgequestforknowledge.wordpress.com/disclaimer/
———————————————————————————————
############### Jorge’s Quest For Knowledge #############
#########
http://JorgeQuestForKnowledge.wordpress.com/ ########
———————————————————————————————

Posted in Active Directory Domain Services (ADDS), Group Policy Objects, Group Policy Preferences | Leave a Comment »

(2017-03-16) Domain Join Through An RODC Instead Of An RWDC (Update 2)

Posted by Jorge on 2017-03-16


In the blog post (2009-01-01) Domain Join through an RODC instead of an RWDC I explained the so called read-only domain join against an RODC. In that blog post you will find a VBS script that helps you achieve that goal. Prior to the VBS script you see multiple ways of pre-creating the computer and having the password of the computer account replicate to the RODC.

In this blog post I provide an updated PowerShell script (don’t forget the execution policy on the server!) that performs the read-only domain join. You can get the PowerShell script through this link, or you can copy it from below.

### Abstract: This PoSH Script Joins A Stand Alone Server To An AD Domain Through A Targeted RODC ### Written by: Jorge de Almeida Pinto [MVP-EMS] ### BLOG: https://jorgequestforknowledge.wordpress.com/ ### ### 2013-04-12: Initial version of the script in PowerShell (v0.1) ### 2017-03-16: Added description for code 1219, added logging (same folder as script), check and clean any site related setting in registry, ### added check that script is executed with admin credentials, added check for connectivity to RODC (v0.2) ### ### WARNING: This script checks connectivity to the targeted RODC for a specific set of ports. The script is configured with default ports ### that are required, but it is also configured with a "Custom RPC Static Port For NetLogon" (40961) as that is what I have configured in ### my test/demo environment. If you are using a different port number, then make sure to change that first before running the script. ### If you use the dynamic range of RPC ports OR you do not have a firewall between your servers and RODCs, then remove that custom port number! ### <# .SYNOPSIS Joins a stand alone server to an AD domain through a targeted RODC. .DESCRIPTION Joins a stand alone server to an AD domain through a targeted RODC. .PARAMETER adDomain The FQDN of the AD domain, the server needs to be joined to. .PARAMETER rodcFQDN The FQDN of the RODC that will be targeted to join the server to the AD domain through a read-only join. .PARAMETER ipAddressLocalHost The IP address of the local server. .PARAMETER compAccountPWD The password of the computer account that was set during the pre-creation of that computer account. .EXAMPLE - Join the server SERVER1 to the AD domain COMPANY.COM through the RODC RODC1.COMPANY.COM .\Read-Only-Domain-Join.ps1 -adDomain COMPANY.COM -rodcFQDN RODC1.COMPANY.COM -ipAddressLocalHost 192.168.6.3 -compAccountPWD 'MyPa$$w0rd' .NOTES This script requires local administrator permissions. #> Param( [Parameter(Mandatory=$TRUE, ValueFromPipeline=$TRUE, ValueFromPipelineByPropertyName=$TRUE, HelpMessage='Please specify the FQDN of the AD domain to join to.')] [ValidateNotNullOrEmpty()] [string]$adDomain, [Parameter(Mandatory=$TRUE, ValueFromPipeline=$TRUE, ValueFromPipelineByPropertyName=$TRUE, HelpMessage='Please specify the FQDN of the RODC to target for the read-only domain join.')] [ValidateNotNullOrEmpty()] [string]$rodcFQDN, [Parameter(Mandatory=$TRUE, ValueFromPipeline=$TRUE, ValueFromPipelineByPropertyName=$TRUE, HelpMessage='Please specify the IP address of the local stand alone server.')] [ValidateNotNullOrEmpty()] [string]$ipAddressLocalHost, [Parameter(Mandatory=$TRUE, ValueFromPipeline=$TRUE, ValueFromPipelineByPropertyName=$TRUE, HelpMessage='Please specify the password that was set for the pre-created computer account.')] [ValidateNotNullOrEmpty()] [string]$compAccountPWD ) ### FUNCTION: Logging Data To The Log File Function Logging($dataToLog, $lineType) { $datetimeLogLine = "[" + $(Get-Date -format "yyyy-MM-dd HH:mm:ss") + "] : " Out-File -filepath "$logFileFullPath" -append -inputObject "$datetimeLogLine$dataToLog" #Write-Output($datetimeLogLine + $dataToLog) If ($lineType -eq $NULL) { Write-Host "$datetimeLogLine$dataToLog" } If ($lineType -eq "SUCCESS") { Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Green } If ($lineType -eq "ERROR") { Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Red } If ($lineType -eq "WARNING") { Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Red } If ($lineType -eq "HEADER") { Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Magenta } If ($lineType -eq "REMARK") { Write-Host "$datetimeLogLine$dataToLog" -ForeGroundColor Cyan } } ### FUNCTION: Test Credentials For Admin Privileges Function Test-Admin { $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent() (New-Object Security.Principal.WindowsPrincipal $currentUser).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) } ### FUNCTION: Test The Port Connection # Source: # Based Upon http://gallery.technet.microsoft.com/scriptcenter/97119ed6-6fb2-446d-98d8-32d823867131 Function PortConnectionCheck($fqdnServer,$port,$timeOut) { $tcpPortSocket = $null $portConnect = $null $tcpPortWait = $null $tcpPortSocket = New-Object System.Net.Sockets.TcpClient $portConnect = $tcpPortSocket.BeginConnect($fqdnServer,$port,$null,$null) $tcpPortWait = $portConnect.AsyncWaitHandle.WaitOne($timeOut,$false) If(!$tcpPortWait) { $tcpPortSocket.Close() Return "ERROR" } Else { #$error.Clear() $ErrorActionPreference = "SilentlyContinue" $tcpPortSocket.EndConnect($portConnect) | Out-Null If (!$?) { Return "ERROR" } Else { Return "SUCCESS" } $tcpPortSocket.Close() $ErrorActionPreference = "Continue" } } ### FUNCTION: Determine The Network ID To Which The IP Address And Subnet Mask Belong # Written By Nathan Linley | http://myitpath.blogspot.com # Source Of Original Script: http://poshcode.org/2888 Function Get-NetworkID ([string]$ipAddress, [string]$subnetMask) { $ipOctets = $ipAddress.split(".") $subnetOctets = $subnetMask.split(".") $result = "" For ($i = 0; $i -lt 4; $i++) { $result += $ipOctets[$i] -band $subnetOctets[$i] $result += "." } $result = $result.substring(0,$result.length -1) return $result } ### FUNCTION: Determine The Subnet Mask Based Upon The Specified Mask Bits Function Get-SubnetMask-ByLength ([int]$length) { If ($length -eq $null -or $length -gt 32 -or $length -lt 0) { Write-Error "Function 'Get-SubnetMask-ByLength'...: Invalid Subnet Mask Length Provided. Please Provide A Number BETWEEN 0 And 32" Return $null } switch ($length) { "32" {return "255.255.255.255"} "31" {return "255.255.255.254"} "30" {return "255.255.255.252"} "29" {return "255.255.255.248"} "28" {return "255.255.255.240"} "27" {return "255.255.255.224"} "26" {return "255.255.255.192"} "25" {return "255.255.255.128"} "24" {return "255.255.255.0"} "23" {return "255.255.254.0"} "22" {return "255.255.252.0"} "21" {return "255.255.248.0"} "20" {return "255.255.240.0"} "19" {return "255.255.224.0"} "18" {return "255.255.192.0"} "17" {return "255.255.128.0"} "16" {return "255.255.0.0"} "15" {return "255.254.0.0"} "14" {return "255.252.0.0"} "13" {return "255.248.0.0"} "12" {return "255.240.0.0"} "11" {return "255.224.0.0"} "10" {return "255.192.0.0"} "9" {return "255.128.0.0"} "8" {return "255.0.0.0"} "7" {return "254.0.0.0"} "6" {return "252.0.0.0"} "5" {return "248.0.0.0"} "4" {return "240.0.0.0"} "3" {return "224.0.0.0"} "2" {return "192.0.0.0"} "1" {return "128.0.0.0"} "0" {return "0.0.0.0"} } } ### Clear The Screen Clear-Host ### Configure The Appropriate Screen And Buffer Size To Make Sure Everything Fits Nicely $uiConfig = (Get-Host).UI.RawUI $uiConfig.WindowTitle = "+++ READ-ONLY DOMAIN JOIN THROUGH AN RODC +++" $uiConfig.ForegroundColor = "Yellow" $uiConfigBufferSize = $uiConfig.BufferSize $uiConfigBufferSize.Width = 140 $uiConfigBufferSize.Height = 9999 $uiConfigScreenSizeMax = $uiConfig.MaxPhysicalWindowSize $uiConfigScreenSizeMaxWidth = $uiConfigScreenSizeMax.Width $uiConfigScreenSizeMaxHeight = $uiConfigScreenSizeMax.Height $uiConfigScreenSize = $uiConfig.WindowSize If ($uiConfigScreenSizeMaxWidth -lt 140) { $uiConfigScreenSize.Width = $uiConfigScreenSizeMaxWidth } Else { $uiConfigScreenSize.Width = 140 } If ($uiConfigScreenSizeMaxHeight -lt 75) { $uiConfigScreenSize.Height = $uiConfigScreenSizeMaxHeight - 5 } Else { $uiConfigScreenSize.Height = 75 } $uiConfig.BufferSize = $uiConfigBufferSize $uiConfig.WindowSize = $uiConfigScreenSize ### Definition Of Some Constants $execDateTime = Get-Date $execDateTimeYEAR = $execDateTime.Year $execDateTimeMONTH = $execDateTime.Month $execDateTimeDAY = $execDateTime.Day $execDateTimeHOUR = $execDateTime.Hour $execDateTimeMINUTE = $execDateTime.Minute $execDateTimeSECOND = $execDateTime.Second $execDateTimeCustom = [STRING]$execDateTimeYEAR + "-" + $("{0:D2}" -f $execDateTimeMONTH) + "-" + $("{0:D2}" -f $execDateTimeDAY) + "_" + $("{0:D2}" -f $execDateTimeHOUR) + "." + $("{0:D2}" -f $execDateTimeMINUTE) + "." + $("{0:D2}" -f $execDateTimeSECOND) $localComputer = Get-WmiObject -Class Win32_ComputerSystem $localComputerName = $localComputer.Name $scriptFileFullPath = $MyInvocation.MyCommand.Definition $currentScriptFolderPath = Split-Path $scriptFileFullPath $logFileFullPath = Join-Path $currentScriptFolderPath $($execDateTimeCustom + "_Read-Only-Domain-Join_" + $localComputerName + ".log") $rodcNBT = $rodcFQDN.Substring(0,$rodcFQDN.IndexOf(".")) $userName = $adDomain + "\" + $localComputerName + "`$" $userPassword = $compAccountPWD $ports = 53,88,135,389,445,464,636,3268,3269,40961 # DNS, Kerberos, RPC Endpoint Mapper, LDAP, SMB, Kerberos Change/Set Password, LDAP-SSL, GC, GC-SSL, Custom RPC Static Port For NetLogon ### Definition Of Some Variables Set-Variable JOIN_DOMAIN -option Constant -value 1 # Joins a computer to a domain. If this value is not specified, the join is a computer to a workgroup Set-Variable ACCT_CREATE -option Constant -value 2 # Creates an account on a domain Set-Variable ACCT_DELETE -option Constant -value 4 # Deletes an account when a domain exists Set-Variable WIN9X_UPGRADE -option Constant -value 16 # The join operation is part of an upgrade from Windows 98 or Windows 95 to Windows 2000 or Windows NT Set-Variable DOMAIN_JOIN_IF_JOINED -option Constant -value 32 # Allows a join to a new domain, even if the computer is already joined to a domain Set-Variable JOIN_UNSECURE -option Constant -value 64 # Performs an unsecured join Set-Variable MACHINE_PASSWORD_PASSED -option Constant -value 128 # The machine, not the user, password passed. This option is only valid for unsecure joins Set-Variable DEFERRED_SPN_SET -option Constant -value 256 # Writing SPN and DnsHostName attributes on the computer object should be deferred until the rename that follows the join Set-Variable NETSETUP_JOIN_READONLY -option Constant -value 2048 # Use an RODC to perform the domain join against Set-Variable INSTALL_INVOCATION -option Constant -value 262144 # The APIs were invoked during install ### Domain Join Options To Use $domainJoinOption = $JOIN_DOMAIN + $MACHINE_PASSWORD_PASSED + $NETSETUP_JOIN_READONLY Logging "" Logging "**********************************************************" "HEADER" Logging "* *" "HEADER" Logging "* --> Read-Only Domain Join Through An RODC <-- *" "HEADER" Logging "* *" "HEADER" Logging "* Written By: Jorge de Almeida Pinto [MVP-EMS] *" "HEADER" Logging "* *" "HEADER" Logging " BLOG: 'Jorge's Quest For Knowledge' *" "HEADER" Logging " (https://jorgequestforknowledge.wordpress.com/) *" "HEADER" Logging "* *" "HEADER" Logging "**********************************************************" "HEADER" Logging "" ### Pre-Requisites Check Logging "" Logging "------------------------------------------------------------------------------------------------------------------" "HEADER" Logging "+++ PRE-REQUISITES CHECK +++" "HEADER" Logging "" Logging "ATTENTION: To Execute This Script, The Following Pre-Requisites Must Be met:" "WARNING" Logging " * Local Server Is Configured Correctly With IP Address, Subnet Mask And DNS Servers..." "WARNING" Logging " * Admin Account Must Be(Direct) Member Of Local 'Administrators' Group!..." "WARNING" Logging " * If UAC Is Used, Admin Account Must Be Running Within An Elevated Administrator Command Prompt!..." "WARNING" Logging " * Required Ports Must Be Opened Between This Server And Targeted RODC!..." "WARNING" Logging "" Logging "ATTENTION: This Script Will Fail Without The Pre-Requisites Mentioned Above!" "WARNING" Logging "" Logging "Press Any Key To Continue...(TWICE)" Logging "" $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") ### Checking For Admin Credentials And If Those Admin Credentials have Been Elevated Due To UAC If (!(Test-Admin)) { Logging "" Logging "WARNING:" "ERROR" Logging " * Your Admin Account IS NOT A (Direct) Member Of The Local 'Administrators' Group!..." "ERROR" Logging " * Your Admin Account IS NOT Running Within An Elevated Administrator Command Prompt!..." "ERROR" Logging "" Logging "Aborting Script..." "ERROR" Logging "" EXIT } Else { Logging "" Logging "SUCCESS:" "SUCCESS" Logging " * Your Admin Account IS A (Direct) Member Of The Local 'Administrators' Group!..." "SUCCESS" Logging " * Your Admin Account IS Running Within An Elevated Administrator Command Prompt!..." "SUCCESS" Logging "" Logging "Continuing Script..." "SUCCESS" Logging "" } ### Checking Connectivity (TCP Only!) Between This Server And The Target RODC $checkOK = $true $ports | %{ $port = $_ $connectionResult = $null $connectionResult = PortConnectionCheck $rodcFQDN $port 500 If ($connectionResult -eq "SUCCESS") { Logging "The RODC '$rodcFQDN' IS Accessible And Listening On Port '$port'..." "SUCCESS" } If ($connectionResult -eq "ERROR") { Logging "The RODC '$rodcFQDN' IS NOT Accessible And Listening On Port '$port'..." "ERROR" $checkOK = $false } } If (!$checkOK) { Logging "" Logging "WARNING:" "ERROR" Logging " * One Or More Of The Required Ports IS/ARE NOT Available..." "ERROR" Logging "" Logging "Aborting Script..." "ERROR" Logging "" EXIT } Else { Logging "" Logging "SUCCESS:" "SUCCESS" Logging " * All The Required Ports ARE Available..." "SUCCESS" Logging "" Logging "Continuing Script..." "SUCCESS" Logging "" } ### Checking Local Registry Settings For Site Definition $regNameExistSiteName = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" -Name SiteName -ErrorAction SilentlyContinue If ($regNameExistSiteName) { Remove-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" -Name SiteName -Force Logging "" Logging "Registry Value 'SiteName' In 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' Exists..." Logging "" Logging "Registry Value 'SiteName' Has Been Deleted..." Logging "" } $regNameExistDynamicSiteName = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" -Name DynamicSiteName -ErrorAction SilentlyContinue If ($regNameExistDynamicSiteName) { Remove-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" -Name DynamicSiteName -Force Logging "" Logging "Registry Value 'DynamicSiteName' In 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' Exists..." Logging "" Logging "Registry Value 'DynamicSiteName' Has Been Deleted..." Logging "" } ### Change Preferred Error Action $ErrorActionPreference = "SilentlyContinue" ### Connecting To AD On The RODC And Getting Some NCs $rootDSEldapPath = "LDAP://$rodcFQDN/rootDSE" $directoryEntryrootDSE = New-Object System.DirectoryServices.DirectoryEntry($rootDSEldapPath, $userName, $userPassword) $defaultNamingContext = $directoryEntryrootDSE.defaultNamingContext $configurationNamingContext = $directoryEntryrootDSE.configurationNamingContext ### Checking Pre-Created Computer Account Exists And The Correct Password Of The Computer Account Is Being Used $defaultNCldapPath = "LDAP://$rodcFQDN/$defaultNamingContext" $defaultNCdirectoryEntry = New-Object System.DirectoryServices.DirectoryEntry($defaultNCldapPath, $userName, $userPassword) $SearcherSRVCompAccount = $null $SearcherSRVCompAccount = New-Object DirectoryServices.DirectorySearcher($defaultNCdirectoryEntry) $SearcherSRVCompAccount.SearchScope = "Subtree" $SearcherSRVCompAccount.Filter = "(&(objectClass=computer)(sAMAccountName=$localComputerName`$))" $SearcherSRVCompAccountResult = $null $SearcherSRVCompAccountResult = $SearcherSRVCompAccount.FindOne() $dnSRVCompAccount = $null $dnSRVCompAccount = $SearcherSRVCompAccountResult.Properties.distinguishedname If ($dnSRVCompAccount) { Logging "" Logging "SUCCESS:" "SUCCESS" Logging " * A Computer Account For This Server DOES Exist...And" "SUCCESS" Logging " * A Correct Password Is Being Used..." "SUCCESS" Logging "" Logging "Continuing Script..." "SUCCESS" Logging "" } Else { Logging "" Logging "WARNING:" "ERROR" Logging " * A Computer Account For This Server DOES NOT Exist...Or" "ERROR" Logging " * An Incorrect Password Is Being Used..." "ERROR" Logging "" Logging "Aborting Script..." "ERROR" Logging "" EXIT } ### Change Preferred Error Action To Default $ErrorActionPreference = "Continue" $regNameExistSiteName = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" -Name SiteName -ErrorAction SilentlyContinue If ($regNameExistSiteName) { Remove-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" -Name SiteName -Force Logging "" Logging "Registry Value 'SiteName' In 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' Exists..." Logging "" Logging "Registry Value 'SiteName' Has Been Deleted..." Logging "" } $regNameExistDynamicSiteName = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" -Name DynamicSiteName -ErrorAction SilentlyContinue If ($regNameExistDynamicSiteName) { Remove-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters" -Name DynamicSiteName -Force Logging "" Logging "Registry Value 'DynamicSiteName' In 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' Exists..." Logging "" Logging "Registry Value 'DynamicSiteName' Has Been Deleted..." Logging "" } ### Initiating Read-Only Domain Join Logging "" Logging "------------------------------------------------------------------------------------------------------------------" "HEADER" Logging "+++ INITIATING READ-ONLY DOMAIN JOIN +++" "HEADER" ### Determining The AD Site Of The Specified/Targeted RODC $rodcCompAccountldapPath = "LDAP://$rodcFQDN/CN=$rodcNBT,OU=Domain Controllers,$defaultNamingContext" $rodcCompAccountdirectoryEntry = $null $rodcCompAccountdirectoryEntry = New-Object System.DirectoryServices.DirectoryEntry($rodcCompAccountldapPath, $userName, $userPassword) $SearcherRodcCompAccount = $null $SearcherRodcCompAccount = New-Object DirectoryServices.DirectorySearcher($rodcCompAccountdirectoryEntry) $SearcherRodcCompAccount.SearchScope = "Base" $SearcherRodcCompAccount.Filter = "(&(objectClass=computer)(dNSHostName=$rodcFQDN))" $SearcherRodcCompAccount.PropertiesToLoad.Add("msDS-SiteName") | Out-Null $SearcherRodcCompAccountResult = $null $SearcherRodcCompAccountResult = $SearcherRodcCompAccount.FindOne() $rodcADSite = $null [string]$rodcADSite = $SearcherRodcCompAccountResult.Properties."msds-sitename" ### Matching The IP Address Of The Local Server Against An AD Site In The AD Forest $subnetsContainerldapPath = "LDAP://$rodcFQDN/CN=Subnets,CN=Sites,$configurationNamingContext" $subnetsContainerdirectoryEntry = $null $subnetsContainerdirectoryEntry = New-Object System.DirectoryServices.DirectoryEntry($subnetsContainerldapPath, $userName, $userPassword) $searcherSubnets = $null $searcherSubnets = New-Object DirectoryServices.DirectorySearcher($subnetsContainerdirectoryEntry) $searcherSubnets.SearchScope = "Subtree" $searcherSubnets.PropertiesToLoad.Add("name") | Out-Null $searcherSubnets.PropertiesToLoad.Add("siteObject") | Out-Null # We Can Take Network Masks In Both Length And Full Octet Format # We Need To Use Both. LDAP Searches # Use Length, And Network ID Generation Is By Full Octet Format. $startMaskLength = 32 For ($i = $startMaskLength; $i -ge 0; $i--) { # Loop Through Netmasks From /32 To /0 Looking For A Subnet Match In AD # Go Through All Masks From Longest To Shortest $subnetMask = &Get-SubnetMask-ByLength $i $networkID = &Get-NetworkID $ipAddressLocalHost $subnetMask # LDAP Search For The Network $searcherSubnets.filter = "(&(objectClass=subnet)(objectCategory=subnet)(cn=" + $networkID + "/" + $i + "))" $subnetObjectResult = $null $subnetObjectResult = $searcherSubnets.FindOne() #$subnetObjectsList = $searcherSubnets.FindAll() #$subnetsTable = @() #$subnetObjectsList.Properties | %{ # $subnetsTableObj = "" | Select "AD Subnet","AD Site" # $subnetsTableObj."AD Subnet" = ($_.name)[0] # $subnetsTableObj."AD Site" = $(($_.siteobject)[0]).Substring(3,$(($_.siteobject)[0]).IndexOf(",")-3) # $subnetsTable += $subnetsTableObj #} #$subnetsTable | FT -Autosize If ($subnetObjectResult -ne $null) { # If A Match Is Found, Return It Since It Is The Longest Length (Closest Match) $localComputerADSubnet = $null [string]$localComputerADSubnet = $($subnetObjectResult.Properties.name) $localComputerADSite = $null [string]$localComputerADSite = $($subnetObjectResult.Properties.siteobject).Substring(3,$($subnetObjectResult.Properties.siteobject).IndexOf(",")-3) #return $localComputerADSite Break } $subnetObjectResult = $null [string]$localComputerADSubnet = $null [string]$localComputerADSite = $null } If ($localComputerADSubnet -eq $null -Or $localComputerADSite -eq $null) { [string]$localComputerADSubnet = "NO_MATCH_FOUND" [string]$localComputerADSite = "NO_MATCH_FOUND" } ### Present The Information Logging "" Logging "Trying To Join The Local Computer '$localComputerName' To The AD Domain '$adDomain' Using The RODC '$rodcFQDN'..." Logging "" Logging "FQDN AD Domain............: $adDomain" Logging "FQDN RODC.................: $rodcFQDN" Logging "AD Site RODC..............: $rodcADSite" Logging "AD Site Local Computer....: $localComputerADSite" Logging "Matching AD Subnet........: $localComputerADSubnet" Logging "Local Computer Name.......: $localComputerName ($localComputerName`$)" Logging "Distinguished Name........: $dnSRVCompAccount" Logging "Computer Account Password.: $compAccountPWD" Logging "" ### AD Sites Must Match, Otherwise Something Is Wrong If ($rodcADSite.ToUpper() -ne $localComputerADSite.ToUpper() -Or $localComputerADSite -eq "NO_MATCH_FOUND") { Logging "" Logging "WARNING:" "ERROR" Logging " * The AD Site Of The Local Computer DOES NOT Match The AD Site Of The Specified RODC..." "ERROR" Logging " * Make Sure The IP Address Of The Local Server Is Configured Correctly So That It Will Match Against The Same AD Site As The Targeteed RODC..." "ERROR" Logging " * The Cause Of The Mismatch Can Be:" "ERROR" Logging " * The Specified IP Address IS NOT Correct..." "ERROR" Logging " * The Specified RODC IS NOT Correct..." "ERROR" Logging " * The AD Subnet For The Local Computer Is Linked To The Incorrect AD Site..." "ERROR" Logging "" Logging "Aborting Script..." "ERROR" Logging "" EXIT } ### Joining The Local Computer To The AD Domain Using The Specified Domain Join Options $returnErrorCode = $localComputer.JoinDomainOrWorkGroup($adDomain + "\" + $rodcFQDN, $compAccountPWD, $null, $null, $domainJoinOption) # List of 'system error codes' (http://msdn.microsoft.com/en-us/library/ms681381.aspx) and # List of 'network management error codes' (http://msdn.microsoft.com/en-us/library/aa370674(VS.85).aspx) $returnErrorDescription = switch ($($returnErrorCode.ReturnValue)) { 0 {"SUCCESS: The Operation Completed Successfully."} 5 {"FAILURE: Access Is Denied."} 53 {"FAILURE: The Network Path Was Not Found."} 64 {"FAILURE: The Specified Network Name Is No Longer Available."} 87 {"FAILURE: The Parameter Is Incorrect."} 1219 {"FAILURE: Logon Failure: Multiple Credentials In Use For Target Server."} 1326 {"FAILURE: Logon Failure: Unknown Username Or Bad Password."} 1355 {"FAILURE: The Specified Domain Either Does Not Exist Or Could Not Be Contacted."} 2691 {"FAILURE: The Machine Is Already Joined To The Domain."} default {"FAILURE: Unknown Error!"} } If ($($returnErrorCode.ReturnValue) -eq "0") { Logging "Domain Join Result Code...: $($returnErrorCode.ReturnValue)" "SUCCESS" Logging "Domain Join Result Text...: $returnErrorDescription" "SUCCESS" } Else { Logging "Domain Join Result Code...: $($returnErrorCode.ReturnValue)" "ERROR" Logging "Domain Join Result Text...: $returnErrorDescription" "ERROR" } If ($($returnErrorCode.ReturnValue) -eq "0") { Logging "" Logging "REMARK:" "REMARK" Logging " * The Computer Account Password Will Be Changed Shortly After The Domain Join!" "REMARK" Logging "" Logging "!!! THE COMPUTER WILL REBOOT AUTOMATICALLY IN 2 MINUTES !!!" "REMARK" Logging "" Logging "!!! TO STOP THE REBOOT USE THE COMMAND: SHUTDOWN /A !!!" "REMARK" SHUTDOWN /R /T 120 } Logging "" Logging "+++ FINISHED +++" "HEADER" Logging "------------------------------------------------------------------------------------------------------------------" "HEADER"

Have fun!

Cheers,

Jorge

———————————————————————————————

* This posting is provided "AS IS" with no warranties and confers no rights!

* Always evaluate/test yourself before using/implementing this!

* DISCLAIMER: https://jorgequestforknowledge.wordpress.com/disclaimer/

———————————————————————————————

############### Jorge’s Quest For Knowledge #############

######### http://JorgeQuestForKnowledge.wordpress.com/ ########

———————————————————————————————

Posted in Active Directory Domain Services (ADDS), Domain Join, PowerShell, Read-Only Domain Controller, Tooling/Scripting | 6 Comments »

(2016-12-20) The LDAP Filter Prettyfier

Posted by Jorge on 2016-12-20


Have you ever received an LDAP filter that was so hideous or so complex you had to reformat it to understand it? Well, you no longer need reformat it yourself!

Willem Kasdorp, a Microsoft PFE, has written a very effective PowerShell script that reformat any LDAP filter to a more understandable form. You can read more about this, and also get the PowerShell code from here.

I once received an LDAP filter similar to what you see below:

"(|(&(|(mail=*@company.nl)(mail=*@company.com))(!mail=*testuser*)(!mail=*internal*)(!mail=*1111*)(!mail=*zz-pa1111*)(!mail=*2222*)(!mail=*somestring1*)(!mail=*somestring2*)(!mail=vendor*)(!mail=*somestring3*)(!mail=*somestring4*)(!displayName=aaa*)(!name=bbb*)(!name=ccc*)(!name=ddd*)(!sAMAccountName=eee*)(!sAMAccountName=admin*)(!sAMAccountName=fff*)(!title=functional*)(!displayName=ggg*)(!displayName=hhh*)(!displayName=iii*)(!msExchHideFromAddressLists=TRUE))(&(|(mail=*@company.nl)(mail=*@company.com))(!mail=*somestring5*)(!mail=*somestring6*)(!mail=*3333*)(!mail=*zz-pa2222*)(!mail=*4444*)(!mail=*somestring7*)(!mail=*somestring1*)(!mail=vendor*)(!mail=*somestring8*)(!mail=*somestring9*)(!name=jjj)(!name=kkk*)(!name=lll*)(!sAMAccountName=mmm*)(!sAMAccountName=admin*)(!sAMAccountName=nnn*)(!title=functional*)(!displayName=ooo*)(!displayName=ppp*)(!displayName=qqq*)(msExchHideFromAddressLists=TRUE)(userAccountControl:1.2.840.113556.1.4.803:=2)))"

There is NO WAY you will understand this LDAP filter without reformatting it first to some more understandable form!

So, let’s use the PowerShell script and see what the LDAP filter is actually doing

image

Figure 1: Prettyfying The LDAP Filter To An Understandable Format

(|
  (&
    (|
      (mail=*@company.nl)
      (mail=*@company.com)
    )
    (!mail=*testuser*)
    (!mail=*internal*)
    (!mail=*1111*)
    (!mail=*zz-pa1111*)
    (!mail=*2222*)
    (!mail=*somestring1*)
    (!mail=*somestring2*)
    (!mail=vendor*)
    (!mail=*somestring3*)
    (!mail=*somestring4*)
    (!displayName=aaa*)
    (!name=bbb*)
    (!name=ccc*)
    (!name=ddd*)
    (!sAMAccountName=eee*)
    (!sAMAccountName=admin*)
    (!sAMAccountName=fff*)
    (!title=functional*)
    (!displayName=ggg*)
    (!displayName=hhh*)
    (!displayName=iii*)
    (!msExchHideFromAddressLists=TRUE)
  )
  (&
    (|
      (mail=*@company.nl)
      (mail=*@company.com)
    )
    (!mail=*somestring5*)
    (!mail=*somestring6*)
    (!mail=*3333*)
    (!mail=*zz-pa2222*)
    (!mail=*4444*)
    (!mail=*somestring7*)
    (!mail=*somestring1*)
    (!mail=vendor*)
    (!mail=*somestring8*)
    (!mail=*somestring9*)
    (!name=jjj)
    (!name=kkk*)
    (!name=lll*)
    (!sAMAccountName=mmm*)
    (!sAMAccountName=admin*)
    (!sAMAccountName=nnn*)
    (!title=functional*)
    (!displayName=ooo*)
    (!displayName=ppp*)
    (!displayName=qqq*)
    (msExchHideFromAddressLists=TRUE)
    (userAccountControl:1.2.840.113556.1.4.803:=2)
  )
)

Cheers,
Jorge
———————————————————————————————
* This posting is provided "AS IS" with no warranties and confers no rights!
* Always evaluate/test yourself before using/implementing this!
* DISCLAIMER:
https://jorgequestforknowledge.wordpress.com/disclaimer/
———————————————————————————————
############### Jorge’s Quest For Knowledge #############
#########
http://JorgeQuestForKnowledge.wordpress.com/ ########
———————————————————————————————

Posted in Active Directory Domain Services (ADDS), AD Queries | 2 Comments »

(2016-10-13) Namespace Already Defined As The Target Namespace For Another File In The Policy Store

Posted by Jorge on 2016-10-13


My AD is running W2K12R2 DCs with the corresponding ADMX/ADML files. While preparing it for an updated Windows 10 client (Version 1607, OS build 14393.22) (this was installed ages ago and updated to the latest and greatest using Windows Update), I also decided to update the policy definitions on the central policy store. So I copied the files in the folder “C:\Windows\PolicyDefinitions” on the updated Windows 10 computer to the folder representing the central policy store on a DC (<Drive>:\<folder>\domain\Policies\PolicyDefinitions). SYSVOL replication should take care of the rest.

Now after starting the GPMC and targeting any of the GPOs you have for either viewing or editing

…and clicking on the Settings tab followed by clicking the “Show All”, you may see something similar to the following

image

Figure 1: Error Message About An Already Defined Namespace In Some Other ADMX File

…or opening a GPO, you may see something similar to the following

image

Figure 2: Error Message About An Already Defined Namespace In Some Other ADMX File

image

Figure 3: Error Message About An Already Defined Namespace In Some Other ADMX File

Looking at my Windows 10 client, its PolicyDefinitions folder contained the following files:

  • LocationProviderAdm.admx (en-US folder contained LocationProviderAdm.adml)
  • Microsoft-Windows-Geolocation-WLPAdm.admx (en-US folder contained Microsoft-Windows-Geolocation-WLPAdm.adml)
  • WindowsStore.admx (en-US folder contained WindowsStore.adml)

After this I also checked my Windows Server 2016 TP5 machine and its PolicyDefinitions folder contained the following files

Looking at my Windows 10 client, its PolicyDefinitions folder contained the following files:

  • LocationProviderAdm.admx (en-US folder contained LocationProviderAdm.adml)
  • WindowsStore.admx (en-US folder contained WindowsStore.adml)

My W2K12R2 DC had initially the following files in the central policy store:

  • LocationProviderAdm.admx (en-US folder contained LocationProviderAdm.adml)
  • WinStoreUI.admx (en-US folder contained WinStoreUI.adml)

After copying the files from the Windows 10 client to the central policy store on the W2K12R2 DC it ended up with the following files:

  • LocationProviderAdm.admx (en-US folder contained LocationProviderAdm.adml)
  • Microsoft-Windows-Geolocation-WLPAdm.admx (en-US folder contained Microsoft-Windows-Geolocation-WLPAdm.adml)
  • WindowsStore.admx (en-US folder contained WindowsStore.adml)
  • WinStoreUI.admx (en-US folder contained WinStoreUI.adml)

Looking at the end result of the files, it explained the error messages in Figure 1, 2 and 3.

The solution to this one in my case:

  • Using Notepad++, I compared “LocationProviderAdm.admx” and “Microsoft-Windows-Geolocation-WLPAdm.admx” and found no differences
    • I deleted:
      • Microsoft-Windows-Geolocation-WLPAdm.admx
      • en-US\Microsoft-Windows-Geolocation-WLPAdm.adml
  • Using Notepad++, I compared “WindowsStore.admx” and “WinStoreUI.admx” and found differences
    • I deleted:
      • WinStoreUI.admx (this file was older and contained less data than the other file)
      • en-US\WinStoreUI.adml

You can also read more about this in the following KB article MS-KBQ3077013 – "’Microsoft.Policies.Sensors.WindowsLocationProvider’ is already defined" error when you edit a policy in Windows

Cheers,
Jorge
———————————————————————————————
* This posting is provided "AS IS" with no warranties and confers no rights!
* Always evaluate/test yourself before using/implementing this!
* DISCLAIMER:
https://jorgequestforknowledge.wordpress.com/disclaimer/
———————————————————————————————
############### Jorge’s Quest For Knowledge #############
#########
http://JorgeQuestForKnowledge.wordpress.com/ ########
———————————————————————————————

Posted in Active Directory Domain Services (ADDS), Group Policy Objects | Leave a Comment »

 
%d bloggers like this: