I have this code
// EXTERNAL MACHINE SCANNING START
ConnectionOptions connectionOptions = new();
connectionOptions.SecurePassword = dataContext.Cred.SecurePassword;
connectionOptions.Username = dataContext.Cred.UserName;
ManagementObjectCollection allUsers = null;
ManagementScope scope = new ManagementScope(#"\\" + dataContext.Comp.Ipaddress + #"\root\cimv2", connectionOptions);
// create connection and run query.
try
{
scope.Connect();
ObjectQuery objectQuery = new ObjectQuery(WQL);
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, objectQuery);
allUsers = searcher.Get();
}
But i get this error code.
System.Runtime.InteropServices.COMException: 'The stub received bad data. (0x800706F7)'
Ive also tried to use CimSession with this code
string nameSpc = #"root\cimv2";
string query = "SELECT * FROM Win32_Group WHERE LocalAccount = true AND (SID='S-1-5-32-544' OR SID='S-1-5-32-555')";
CimCredential credentials = new CimCredential(PasswordAuthenticationMechanism.Basic, dataContext.Cred.Domain, dataContext.Cred.UserName, dataContext.Cred.SecurePassword);
WSManSessionOptions sessionOptions = new WSManSessionOptions()
{
DestinationPort = (uint)dataContext.Comp.Port
};
sessionOptions.AddDestinationCredentials(credentials);
sessionOptions.NoEncryption = true;
CimSession session = CimSession.Create(dataContext.Comp.Ipaddress, sessionOptions);
IEnumerable<CimInstance> queryInstance = session.QueryInstances(nameSpc, "WQL", query);
But this code creates this error:
"The WinRM client cannot process the request. If the authentication scheme is different from Kerberos, or if the client computer is not joined to a domain, then HTTPS transport must be used or the destination machine must be added to the TrustedHosts configuration setting. Use winrm.cmd to configure TrustedHosts. Note that computers in the TrustedHosts list might not be authenticated. You can get more information about that by running the following command: winrm help config."
The machine this code is running on is NOT a member of the domain, and never will be. This is a way to collect the useraccounts needed from thousands of computers, for later processing by the company.
This functionality was handled by powershell before, but the company want it rewritten into c#. In powershell it is easy, just a couple of lines of code and thats it.
Ive setup a testplatform in a vm with a domaincontroller and a machine which logs on to the domain controller.
The machine where the code is running has had its group policy for WinRM set to allow basic authentication and unencrypted traffic.
Ive never delved into this area of coding before, so in using those frameworks and the general lingo of that area im quite a newb.
Does anyone have any idea what i might be doing wrong.
Related
I have a problem: I need to connect from a remote server to Active Directory, but the code has to be using the LdapConnection class. I need this because that way I can only test change notifiers when some event happen (such as user is deactivated or he changed group, data etc). OS on the remote server is Windows Server 2012.
I managed to do this from local using DirectoryServices with the following code:
String ldapPath = "LDAP://XRMSERVER02.a24xrmdomain.info";
directoryEntry = new DirectoryEntry(ldapPath, #"A24XRMDOMAIN\username", "pass");
//// Search AD to see if the user already exists.
DirectorySearcher search = new DirectorySearcher(directoryEntry);
search.Filter = "(&(objectClass=user))";
SearchResult result = search.FindOne();
This is okay and connection works but now I need to connect using the LdapConnection class.
I tried something like this on many ways but none of that helped me:
LdapConnection connection = new LdapConnection(XRMSERVER02.a24xrmdomain.info);
var credentials = new NetworkCredential(#"A24XRMDOMAIN\username", "pass");
connection.Credential = credentials;
connection.Bind();
It says that credentials are invalid but that is not true.
Explanations:
XRMSERVER02 - Domain controller
a24xrmdomain.info - Domain
A24XRMDOMAIN - Domain used for logging
Thanks for your help.
Even though I solved my problem I want to share with other developers what I achieved so far. Problem that I encountered was that I had remote server with OS Windows server 2012 and Active directory on it. I needed to connect on him via my local machine(Windows 10).
As I stated in my question it is possible to do that via DirectoryServices with the following code:
String ldapPath = "LDAP://(DomainController).a24xrmdomain.info";
directoryEntry = new DirectoryEntry(ldapPath, #"DOMAIN\username","pass");
//// Test search on AD to see if connection works.
DirectorySearcher search = new DirectorySearcher(directoryEntry);
search.Filter = "(&(objectClass=user))";
SearchResult result = search.FindOne();
This is one of the solutions, but since my task was to get notification and to identify when ever some object has changed in Active Directory, I needed connection to Active Directory on Remote server via LDAP class. Code for getting notifiers is taken from:
- Registering change notification with Active Directory using C#
I succeeded to connect with LDAP class via next code:
String ldapPath2 = "(DomainController).a24xrmdomain.info";
LdapConnection connection = new LdapConnection(ldapPath2);
var credentials = new NetworkCredential(#"username", "pass");
connection.Credential = credentials;
connection.Bind();
Want to mention that no IP address of remote server is needed, just Domain Controller that is used on him, and that Domain used for logging is unnecessary.
Happy coding
Try using NetworkCredential constructor with 3 parameters: username, password and domain. Specify domain separately from user name
How to know that AD exists?
I have only ip address. I tried to use those methods:
if(DirectoryEntry.Exists("LDAP://192.168.1.1"))
also
DirectoryEntry directoryEntry = new DirectoryEntry("LDAP://192.168.1.1")
but it didn't help.
I use LdapConnection right now, but I have a problem
LdapConnection connection = new LdapConnection(new LdapDirectoryIdentifier("192.168.1.1"));
connection.AuthType = AuthType.Basic;
NetworkCredential credential =
new NetworkCredential("a", '1");
connection.Credential = credential;
connection.Timeout = new TimeSpan(1000);
connection.Bind();
I'm getting 81 code and The LDAP unavailable.
Does somebody know is possible just to know is ip is correct and AD exists?
P.S. I use .NET 2
You can try this (works with .NET 2.0 and does not need credentials):
...
using System.DirectoryServices.Protocols;
...
string server = "192.168.1.1";
using (LdapConnection ldapConnection = new LdapConnection(server))
{
ldapConnection.AuthType = AuthType.Anonymous;
SearchRequest request = new SearchRequest(null, "(objectclass=*)",
SearchScope.Base, "defaultNamingContext");
SearchResponse result = (SearchResponse)ldapConnection.SendRequest(request);
if (result.Entries.Count == 1)
{
Console.WriteLine(result.Entries[0].Attributes["defaultNamingContext"][0]);
}
}
It binds anonymously to the AD domain controller and retrieves the rootDSE entry. It displays the DN of the AD domain.
You can also query another attributes, see https://msdn.microsoft.com/en-us/library/ms684291(v=vs.85).aspx
AD can only be set to run on port 389 and/or 636. So if the port is open, it is a pretty good chance that LDAP is present.
Know if it is AD or not, would, typically, require you to have a valid LDAP account to BIND to the LDAP service.
You can perform a LDAP query against the LDAP service and probably learn the VendorName.
I've noticed an interesting behavior when trying to connect to edirectory using DirectoryServices.
This is the code that I used to pull information from our dev edirectory, but I've noticed that it's retrieving information from our production Active Directory (from what I've read, DirectorySearcher can be used on edir as well):
string devIP = "xxx.xxx.xxx.xxx:636";
DirectorySearcher directorySearcher = new DirectorySearcher(devIP);
directorySearcher.Filter = "(&(objectClass=user)(uid=" + "jsmith" + "))";
SearchResultCollection searchResults = directorySearcher.FindAll();
(I know it's hitting production because jsmith doesn't exist in dev. Another thing I noticed was that the attributes that were returned are AD attributes like memberOf etc.)
What finally got it to work was using System.DirectoryServices.Protocols:
LdapConnection con = new LdapConnection(new LdapDirectoryIdentifier("d1.domain.com:636"));
con.Credential = new NetworkCredential("cn=USERNAME,ou=XXX,o=XXX", "password");
con.SessionOptions.SecureSocketLayer = true;
con.SessionOptions.VerifyServerCertificate = new VerifyServerCertificateCallback(ServerCallback);
con.AuthType = AuthType.Basic;
using (con)
{
con.Bind();
}
I've done some research, but I couldn't figure out why it would have been routing the DirectorySearcher to prod even though I explicitly specified the IP address and the username?
The dev server is on a different domain from my local machine (and I'm running the code on my local machine). Could it be possible that since my machine is on the same domain as prod, it's defaulting to the prod Active Directory and just ignoring the devIP that I'm passing it?
The same root cause with the following:
Check if the DirectoryEntry is valid in DirectorySearcher
The constructor you used DirectorySearcher(string) is actually expecting the filter, but not the search root path.
I want to connect hadoop in c# using HDInsight. I have created a cluster in AZURE and it was created successfully. and also I enabled remote desktop connection in it.
When I entered the credentials in C# and execute the job then I get the connection error. I am confused in providing the parameters. Kindly assist me.
var hadoop = Hadoop.Connect(new Uri("https://clustername.azurehdinsight.net"), "admin", "");
//I have set remote desktop password
var config = new HadoopJobConfiguration();
config.InputPath = "input/CodeFiles";
config.OutputFolder = "output/CodeFiles";
var result = hadoop.MapReduceJob.ExecuteJob<NamespaceCounterJob>();
Last line gives the exception.
Exception message is:
One or more errors occured
And this is the inner exception:
Unable to connect to the remote server
The Uri should be your Cluster Name, not Username, like:
var hadoop = Hadoop.Connect(new Uri("https://clustername.azurehdinsight.net"), "username", "password");
var config = new HadoopJobConfiguration();
config.InputPath = "input/CodeFiles";
config.OutputFolder = "output/CodeFiles";
var result = hadoop.MapReduceJob.ExecuteJob<NamespaceCounterJob>();
The cluster name is shown at the top of the detail page in the Azure portal for your cluster. Also, you don't need to enable RDP to use this method, it's actually the username set for "Hadoop Services" in the configuration tab of the cluster. Launching a job in this manner makes use of the WebHCat/Templeton endpoint, so RDP is not required.
Within c#, I need to be able to
Connect to a remote system, specifying username/password as appropriate
List the members of a localgroup on that system
Fetch the results back to the executing computer
So for example I would connect to \SOMESYSTEM with appropriate creds, and fetch back a list of local administrators including SOMESYSTEM\Administrator, SOMESYSTEM\Bob, DOMAIN\AlanH, "DOMAIN\Domain Administrators".
I've tried this with system.directoryservices.accountmanagement but am running into problems with authentication. Sometimes I get:
Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again. (Exception from HRESULT: 0x800704C3)
The above is trying because there will be situations where I simply cannot unmap existing drives or UNC connections.
Other times my program gets UNKNOWN ERROR and the security log on the remote system reports an error 675, code 0x19 which is KDC_ERR_PREAUTH_REQUIRED.
I need a simpler and less error prone way to do this!
davidg was on the right track, and I am crediting him with the answer.
But the WMI query necessary was a little less than straightfoward, since I needed not just a list of users for the whole machine, but the subset of users and groups, whether local or domain, that were members of the local Administrators group. For the record, that WMI query was:
SELECT PartComponent FROM Win32_GroupUser WHERE GroupComponent = "Win32_Group.Domain='thehostname',Name='thegroupname'"
Here's the full code snippet:
public string GroupMembers(string targethost, string groupname, string targetusername, string targetpassword)
{
StringBuilder result = new StringBuilder();
try
{
ConnectionOptions Conn = new ConnectionOptions();
if (targethost != Environment.MachineName) //WMI errors if creds given for localhost
{
Conn.Username = targetusername; //can be null
Conn.Password = targetpassword; //can be null
}
Conn.Timeout = TimeSpan.FromSeconds(2);
ManagementScope scope = new ManagementScope("\\\\" + targethost + "\\root\\cimv2", Conn);
scope.Connect();
StringBuilder qs = new StringBuilder();
qs.Append("SELECT PartComponent FROM Win32_GroupUser WHERE GroupComponent = \"Win32_Group.Domain='");
qs.Append(targethost);
qs.Append("',Name='");
qs.Append(groupname);
qs.AppendLine("'\"");
ObjectQuery query = new ObjectQuery(qs.ToString());
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection queryCollection = searcher.Get();
foreach (ManagementObject m in queryCollection)
{
ManagementPath path = new ManagementPath(m["PartComponent"].ToString());
{
String[] names = path.RelativePath.Split(',');
result.Append(names[0].Substring(names[0].IndexOf("=") + 1).Replace("\"", " ").Trim() + "\\");
result.AppendLine(names[1].Substring(names[1].IndexOf("=") + 1).Replace("\"", " ").Trim());
}
}
return result.ToString();
}
catch (Exception e)
{
Console.WriteLine("Error. Message: " + e.Message);
return "fail";
}
}
So, if I invoke Groupmembers("Server1", "Administrators", "myusername", "mypassword"); I get a single string returned with:
SERVER1\Administrator
MYDOMAIN\Domain Admins
The actual WMI return is more like this:
\\SERVER1\root\cimv2:Win32_UserAccount.Domain="SERVER1",Name="Administrator"
... so as you can see, I had to do a little string manipulation to pretty it up.
This should be easy to do using WMI. Here you have a pointer to some docs:
WMI Documentation for Win32_UserAccount
Even if you have no previous experience with WMI, it should be quite easy to turn that VB Script code at the bottom of the page into some .NET code.
Hope this helped!
I would recommend using the Win32 API function NetLocalGroupGetMembers. It is much more straight forward than trying to figure out the crazy LDAP syntax, which is necessary for some of the other solutions recommended here. As long as you impersonate the user you want to run the check as by calling "LoginUser", you should not run into any security issues.
You can find sample code for doing the impersonation here.
If you need help figuring out how to call "NetLocalGroupGetMembers" from C#, I reccomend that you checkout Jared Parson's PInvoke assistant, which you can download from codeplex.
If you are running the code in an ASP.NET app running in IIS, and want to impersonate the user accessing the website in order to make the call, then you may need to grant "Trusted for Delegation" permission to the production web server.
If you are running on the desktop, then using the active user's security credentials should not be a problem.
It is possible that you network admin could have revoked access to the "Securable Object" for the particular machine you are trying to access. Unfortunately that access is necessary for all of the network management api functions to work. If that is the case, then you will need to grant access to the "Securable Object" for whatever users you want to execute as. With the default windows security settings all authenticated users should have access, however.
I hope this helps.
-Scott
You should be able to do this with System.DirectoryServices.DirectoryEntry. If you are having trouble running it remotely, maybe you could install something on the remote machines to give you your data via some sort of RPC, like remoting or a web service. But I think what you're trying should be possible remotely without getting too fancy.
If Windows won't let you connect through it's login mechanism, I think your only option is to run something on the remote machine with an open port (either directly or through remoting or a web service, as mentioned).