Create a user on a remote machine using c# - c#

i am trying to create an app, which automatically creates and configures merge replication between the 2 PCs in the same local network.
To achieve this, i need to programaticaly create one user on each of the two PCs, this user should have exactly the same user name and passwords as it is required by the replication.
There is no DOMAIN on the network.
I did everything using GUI on both machines and replication works, now i need to do this using a C#. The replication part is done using SQL server 2012 RMO and SMO SDKs.
While googling, i found this (and many many other results which are more or less identical to this). But i believe, that these all examples are meant for local PCs, as it seems people were not getting errors like i do.
My sample code is:
DirectoryEntry directoryEntry = new DirectoryEntry(#"WinNT://WORKGROUP/WIN7-PC,computer", #"admin", "123456");
DirectoryEntry user = directoryEntry.Children.Add("RepTest", "user");
user.Invoke("SetPassword", new object[] { "rep123" });
user.CommitChanges();
I get an error at user.CommitChanges() saying that Access is denied, so i figured i had made a mistake in the authentication details provided to new DirectoryEntry(.... I fixed that and tried again:
DirectoryEntry directoryEntry = new DirectoryEntry(#"WinNT://WORKGROUP/WIN7-PC,computer", #"win7-pc\admin", "123456");
DirectoryEntry user = directoryEntry.Children.Add("RepTest", "user");
user.Invoke("SetPassword", new object[] { "rep123" });
user.CommitChanges();
Now I get the error on the line of .Children.Add(... stating that Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed.
When the first time i did it, i checked the Path of user object, which was WinNT://WORKGROUP/WIN7-PC/RepTest, not sure if it has anything to do with anything, but maybe it is trying to use the yet to be created new user folder while being connected as admin user?
Is there a way to get this thing going?

Related

Get user groups call to LDAP server gets "The server is not operational" because the server called changes

I'm trying to get the groups for a user from an ADAM server using:
PrincipalContext yourDomain = new PrincipalContext(ContextType.ApplicationDirectory,
principalContextName,
principalContextContainer,
ContextOptions.ServerBind | ContextOptions.SimpleBind,
principalContextUserName,
principalContextPassword);
UserPrincipal user = UserPrincipal.FindByIdentity(yourDomain,
IdentityType.UserPrincipalName,
userName);
PrincipalSearchResult<Principal> groups = user.GetAuthorizationGroups();
And I keep getting the "server is not operational" error.
I finaly understood my problem but I dont know how to fix it.
The thing is that, the two ADAM servers are behind a firewall and an NLB, they both reply by the name of adam.company.local and the nslookup of that name, reply's 100.10.130.1
I can even ping the 100.10.130.1 and the name adam.company.local so the problem wasn't networking, but it is!
With WireShark I found out that at some point the server returns his own name and my calls start trying to call that name. Of course then the firewall blocks them and the exception occurs.
To confirm this, I made 2 lines on the hosts file, with both server names to 100.10.130.1 and the problem was gone, I could retrieve all the groups with ease.
Why is that? why does de connection change to a machine I cant get to?
The final question is how do I prevent it because on the publication machine I cant create these lines on the hosts file?

Get current windows user first name when the app is executed through network

I have an app (C#, .NET4.5) placed on a remote computer, accessed and executed via local network. Let's call the computer where the app is placed "AppPC" and the computer that is exeuting the app "UserPC".
I want to achieve the same I would get with this, which is the first name (not username) of the current Windows User:
System.DirectoryServices.AccountManagement.UserPrincipal.Current.DisplayName;
This code works just fine when the app is located on the same UserPC, but it throws an exception if the app is located on AppPC. More precisely is a System.DirectoryServices.Protocols.LdapException: "Can't establish connection. "
I can find the name of UserPC using this:
PrincipalContext ctx = new PrincipalContext(ContextType.Machine,null);
string pcName = ctx.ConnectedServer;
But from there, I do not know how to get the current user name.
I have tried getting all the UserPrincipal in ctx through a loop over a PrincipalSearcher.FindAll() but this only shows two users "Administrator" and "Guest" which is false, at least on UserPC. Maybe AppPC has that users, I'm not sure.
With System.Environment.UserName I can get the username but not the first name, which is what I'm interested in.
EDIT: Forgot to say that I have already checked this and doesn't work also (null):
How do I get the current username in .NET using C#?

Getting Error Querying Active Directory On The Server Only

I have the following block of code that queries Active Directory for users by Group Name using System.DirectoryServices.AccountManagement:
var domainContext = new PrincipalContext(ContextType.Domain, "company.container.internal");
var groupPrincipal = GroupPrincipal.FindByIdentity(domainContext, IdentityType.Name, "Lvl1Users");
if (groupPrincipal != null)
{
//Read the values
}
Now the site uses the following:
IIS7 on Win2k8
Windows Authentication
Impersonation = True
App Pool on .NET 4.0 using 'NETWORK SERVICE' as the account
On my local machine (you know how this goes) it all works great. My peers that try it locally also it works well. However once deployed to the server it shows the following:
An operations error occurred.
Everything I research says it's a permissions issue. 1 thing to note, on my local machine I'm on the MainNetwork domain which is the parent to company.container.internal domain which I am querying. The IIS machine is on company.container.internal and is querying the same domain. So honestly, I would think the more challenging situation is reading AD on my local machine which is on a different domain, but it works. On the server which is querying the same domain, it fails.
Here is what I've tried, and none of these has worked:
Change AppPool to 'LocalSystem'
Change AppPool to use a static super-duper Admin account
Used Impersonation in code to manipulate the context of the calls in a local block with an admin user on the MainNetwork domain.
Used Impersonation in code to manipulate the context of the calls in a local block with an admin user on the company.container.internal domain.
Adding in using (HostingEnvironment.Impersonate())
What gives here? I have tried impersonating every type of power admin on both domains, and used multiple AppPool settings, and I keep getting the same error. Is there anything that needs to change in the code with the declaration of the domains, or is there a permissions issue I'm missing?
I figured this out and it turned out that using HostingEnvironment.Impersonate() was still at the root to solve the problem. I had already tried this, but there was another issue with my code.
The issue is often that the context for which the Active Directory calls is made is under a user that does not have permissions (also can happen when identity impersonate="true" in ASP.NET, due to the fact that the users token is a "secondary token" that cannot be used when authenticating against another server from: http://bit.ly/1753RjA).
The following code will ensures that the block of code running, is run under the context of say the AppPool (i.e. NETWORKSERVICE) that the ASP.NET site is running under.
using (HostingEnvironment.Impersonate())
{
var domainContext = new PrincipalContext(ContextType.Domain, "myDomain.com");
var groupPrincipal = GroupPrincipal.FindByIdentity(domainContext, IdentityType.Name, "PowerUsers");
if (groupPrincipal != null)
{
//code to get the infomation
}
}
However, one super important detail is that all the code calling Active Directory must be in that block. I had used some code a team member of mine wrote that was returning a LINQ query results of type Users (custom class), but not evaluating the expression (bad practice). Therefore the expression tree was returned instead of the results.
What ended up happening is the calling code eventually evaluated the results and the An operations error occurred message still appeared. I though the code fix above didn't work. When in fact it did, but there was code evaluating the results outside the block.
In a nutshell, make sure all code to access Active Directory is inside that using block and the exception should be fixed one the service/app is deployed to the server.

problem connecting to Active Directory server in C# .NET

I'm currently writing some software in C# which needs to connect to an AD server and get some user details. When I connect using the code below it works against most AD servers that I connect to but there are a couple where it fails with an error of "Logon failure: unknown user name or bad password.". The server name / credentials I'm using are definately correct as I've tested them with an LDAP Browser and the AD server is using standard security (port 389 etc). Can anyone offer any advice?
Cheers
Tim
DirectoryEntry d = new DirectoryEntry("LDAP://" + domain, admin_username, admin_password);
try
{
object x = d.NativeObject;
}
catch
{
throw;
}
I've had similar issues programming .net / AD in the past. One thing I found useful is using an LDAP viewer to see if I can connect to certain servers, etc. In this way, I can at least determine if it is a .NET error (perhaps my code), a credential error, etc.
I use the free/lite version of Softerra's LDAP viewer (http://www.ldapbrowser.com/download.htm) although I'm sure there are many others to choose from out there. If you try the one listed here, make sure to download the 'LDAP browser' and not 'LDAP Administrator'. The browser is the free one.
Try connecting to the same LDAP path you're having trouble with in code, using a LDAP browser/viewer. This will at least as step one determine if it is a .NET/code issue or not. If you can't connect via the browser, it can be helpful to play around with the connection options, such as port, domain (FQDN), etc.
Hope this might help narrow things down.
Active Directory allows at least three different logon name styles:
LDAP - i.e. LDAP DN. For example: cn=JohnS, ou=Users, dc=example, dc=com
NTLM. For example: EXAMPLE\JohnS
Kerberos principal name: For example: johns#example.com
However, you cannot login with just JohnS like you do with Windows box. It's a very common mistake.

How should I validate a user's credentials against an ADAM instance over SSL?

Apologies in advance as I haven't had much experience with directories before.
I have an ASP.net application, and I have to validate its users against an Active Directory Application Mode instance running on Server 2k3. I was previously attempting a connection with DirectoryEntry and catching the COMException if the user's credentials (userPrincipalName & password) were wrong, but I had a number of problems when trying to bind as users who weren't a member of any ADAM groups (which is a requirement).
I recently found the System.DirectoryServices.AccountManagement library, which seems a lot more promising, but although it works on my local machine, I'm having some troubles when testing this in our testbed environment. Chances are I'm simply misunderstanding how to use these objects correctly, as I wasn't able to find any great documentation on the matter. Currently I am creating a PrincipalContext with a Windows username and password, then calling the AuthenticateCredentials with the user's userPrincipalName and password. Here's a very short exert of what I'm doing:
using (var serviceContext = new PrincipalContext(
ContextType.ApplicationDirectory,
serverAddress,
rootContainer,
ContextOptions.Negotiate | ContextOptions.SecureSocketLayer,
serviceAccountUsername,
serviceAccountPassword)) {
bool credentialsValid = serviceContext.ValidateCredentials(userID, password, ContextOptions.SecureSocketLayer | ContextOptions.SimpleBind)
}
If the user's credentials are valid, I then go on to perform other operations with that principal context. As I said, this works for both users with and without roles in my own environment, but not in our testbed environment. My old DirectoryEntry way of checking the user's credentials still works with the same configuration.
After a very long morning, I was able to figure out the problem!
The exception message I was receiving when calling ValidateCredentials was extremely vague. After installing Visual Studio 2008 in the test environment (which is on the other side of the country, mind you!), I was able to debug and retrieve the HRESULT of the error. After some very deep searching in to Google, I found some very vague comments about "SSL Warnings" being picked up as other exceptions, and that enabling "SCHANNEL logging" (which I'm very unfamiliar with!) might reveal some more insight. So, after switching that on in the registry and retrying the connection, I was presented with this:
The certificate received from the remote server does not contain the expected name. It is therefore not possible to determine whether we are connecting to the correct server. The server name we were expecting is ADAMServer. The SSL connection request has failed. The attached data contains the server certificate.
I found this rather strange, as the old method of connecting via SSL worked fine. In any case, my co-worker was able to spot the problem - the name on the SSL certificate that had been issued on the server was that of the DNS name ("adam2.net") and not the host name ("adamserver"). Although I'm told that's the norm, it just wasn't resolving the correct name when using PrincipalContext.
Long story short; re-issuing a certificate with the computer name and not the DNS name fixed the problem!

Categories