Why does C# DirectoryServices not work with my LDAP server? - c#

I have a AD/LDAP server running in production that is working fine. I need to test things, so I created my own locally using phpldapadmin.
When I attempt to do a wildcard search on objectClass, my code throws. This only happens on my local server.
System.DirectoryServices.DirectoryServicesCOMException: 'An invalid dn syntax has been specified.
I've tried multiple username syntaxes that DirectoryServices supports: they all work on the production server and fail on my local server. I can successfully get inside and navigate my local server using JXplorer.
class Program
{
static void Run(string ip, string username, string password)
{
var authType = System.DirectoryServices.AuthenticationTypes.None;
var directoryEntry = new DirectoryEntry(ip, username, password, authType);
var directorySearcher = new DirectorySearcher(directoryEntry, "(objectClass=*)");
directorySearcher.SearchScope = SearchScope.OneLevel;
// throws here
var searchResult = directorySearcher.FindOne();
}
static void Main(string[] args)
{
Run("LDAP://validlocalip", "admin#test", "test");
Console.ReadKey();
}
}

It doesn't like what you've passed as the path parameter to the DirectoryEntry constructor:
var directoryEntry = new DirectoryEntry(ip, username, password, authType);
That parameter is an LDAP path, not just an IP. If all you have is an IP, then you can try "LDAP://ip" but usually you'd use your domain name: "LDAP://domain.com"
More info here: https://learn.microsoft.com/en-ca/dotnet/api/system.directoryservices.directoryentry.path

Related

Program to downloading a file from S3 to remote EC2 Instance of Windows

I am writing a program in C# (dotnet 6/Mac) which will issue a PowerShell command to a remote EC2 instance running Windows (2012/PowerShell version 5.x) to download a file from S3.
I am on a Mac and I am able to connect to the EC2 Instance with PowerShell for Mac.
Here is the C# program:
public void DownloadS3FileToRemoteMachine(string host,
string user,
string password,
string bucket,
string s3path,
string localPath)
{
string s3DownloadCommand =
$"aws s3 cp s3://{bucket}{s3path} {localPath}";
var securePass = new SecureString();
foreach (char p in password)
{
securePass.AppendChar(p);
}
var credential = new PSCredential(user, securePass);
var connectionInfo = new WSManConnectionInfo
{
ComputerName = host,
Credential = credential,
NoEncryption = true,
Scheme = WSManConnectionInfo.HttpScheme
};
using Runspace rs = RunspaceFactory.CreateRunspace(connectionInfo);
using PowerShell? ps = PowerShell.Create(rs).AddScript(s3DownloadCommand);
Collection<PSObject>? results;
try
{
rs.Open();
results = ps.Invoke();
if (ps.HadErrors)
{
string errors = string.Join(Environment.NewLine, ps.Streams
.Error
.ReadAll()
.Select(err => err.ErrorDetails.Message));
_logger.LogError("Error while downloading the file from S3 to local path {LocalPath}, " +
"error {ErrorMsg}", localPath, errors);
}
}
catch (Exception e)
{
_logger.LogError(e, "Error copying the file from S3 to remote machine");
throw;
}
string enumerable = string.Join("\n", results.Select(r => r.ToString()));
_logger.LogInformation(enumerable);
}
With this, I get the error:
Connecting to remote server 10.62.166.198 failed with the following error message : Authorization failed For more information, see the about_Remote_Troubleshooting Help topic.
However, I know that the code works because I have access to another Windows machine running Window 10 on my local network and I am able to successfully download the file on that machine.
If I remove the line NoEncryption = true from the configuration then I get a different message:
Connecting to remote server 10.62.166.198 failed with the following error message : MI_RESULT_FAILED For more information, see the about_Remote_Troubleshooting Help topic.
Any help will be tremendously appreciated.
Thanks to the amazing people on the PowerShell Discord channel (access via https://www.reddit.com/r/PowerShell) I learnt that the current functionality of PowerShell to remote from Linux to Windows is very limited.
I had to choose OpenSSH option i.e. install OpenSSH on Windows machine, thereafter, I was able to access the service via C# like so:
var info = new ConnectionInfo(host, user, new PasswordAuthenticationMethod(user, password));
using var client = new SshClient(info);
client.HostKeyReceived += (_, e) => e.CanTrust = true;
client.Connect();
string s3DownloadCommand = $"aws s3 cp s3://{bucket}/{source} {destination}";
using SshCommand? cmd = client.CreateCommand(s3DownloadCommand);
string? output = cmd.Execute();

Facing Unknown error (0x80005000) while adding the user t LDAP in C#

I am facing Unknown error (0x80005000) while adding user to LDAP server(Apache), the following is my code. Could anyone please let me know where I am doing mistake.
namespace TestMethods
{
public class Program
{
static void Main(string[] args)
{
var ldi = new LdapDirectoryIdentifier("localhost", 10389);
AddUser("username", "o=Company");
}
public static void AddUser(string username, string group)
{
try
{
DirectoryEntry dirEntry = new
DirectoryEntry("LDAP://localhost:10389,o=Company" + group);
Console.WriteLine("Added to the path");// Working
dirEntry.Invoke("Add", new object[] { username });//Received Exception here
dirEntry.CommitChanges();
Console.WriteLine("Added to the path");
dirEntry.Close();
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
I believe you should use a / to separate the server name from the DN in your path:
LDAP://localhost:10389/o=Company
The constructor of DirectoryEntry doesn't make any network requests, so your path isn't validated until you actually use it.
However, if you are not using Active Directory, then I don't think Invoke will work for you. The description of DirectoryEntry.Invoke says:
Calls a method on the native Active Directory Domain Services object.
Even then, I'm not sure which Add method you're trying to use.
The way to create a new object using DirectoryEntry is like this (assuming dirEntry is pointing to a path where it can be created):
var newUser = dirEntry.Children.Add($"uid={username}", "inetOrgPerson");
// Set other attributes like this:
// newUser.Properties["someAttribute"].Value = "something";
//Save - this is where the object is actually created
newUser.CommitChanges();
I've never used Apache's LDAP server (I know AD better), so you may have to edit the schema ("inetOrgPerson") if you need to.

Cannot get LDAP connection working - always "Unknown error"

I'm researching what I need to build an Active Directory search tool which ultimately will be used in a C# ASP.NET web app.
I know very little about AD (and don't particularly want to know any more than necessary) so I've asked our tech ops to set up a dummy instance on a server. This they've done giving it the domain dummy.local
I now need to work out my LDAP connection string. Here I'm completely stuck. I'm using a user account which is a member of Domain Admins. After loads of hunting round the web I've tried all sorts of things to work out the various components of the LDAP connection string. For example, if I run the following in cmd.exe for that domain admin user on the server...
dsquery user -samid "username" | dsget user -memberof -expand
...this gives me the following information:
"CN=Domain Admins,CN=Users,DC=dummy,DC=local"
"CN=Remote Desktop Users,CN=Builtin,DC=dummy,DC=local"
"CN=Users,CN=Builtin,DC=dummy,DC=local"
"CN=Administrators,CN=Builtin,DC=dummy,DC=local"
"CN=Domain Users,CN=Users,DC=dummy,DC=local"
"CN=Denied RODC Password Replication Group,CN=Users,DC=dummy,DC=local"
I've also run the following in a C# console app...
using (var context = new PrincipalContext(ContextType.Domain))
using (var comp = ComputerPrincipal.FindByIdentity(context, Environment.MachineName))
{
Console.WriteLine(String.Join(",", comp.DistinguishedName.Split(',').SkipWhile(s => !s.StartsWith("OU=")).ToArray()));
}
...and this gives me the following:
OU=Domain Controllers,DC=dummy,DC=local
I thought I therefore had all the properties I needed to build my LDAP connection string. I ended up with this:
LDAP://COMSECWEBDEV.dummy.local/ou=Domain Controllers,/cn=Domain Admins,/cn=Users,/dc=dummy,/dc=local
I've tried using this connection string, with the username and password of the domain admin which I know is correct, but everything I try always gives me the same error:
System.Runtime.InteropServices.COMException (0x80005000): Unknown error (0x80005000)
at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
at System.DirectoryServices.DirectoryEntry.Bind()
at System.DirectoryServices.DirectoryEntry.get_AdsObject()
at System.DirectoryServices.DirectorySearcher.FindAll(Boolean findMoreThanOne)
at System.DirectoryServices.DirectorySearcher.FindOne()
Since this error gives me no detail I have no idea what I'm doing wrong. I'm sure I'm just not getting the connection string right, but I've no idea how to work out the correct string.
For completeness, here is the console code which I'm testing with:
static void Main(string[] args)
{
var connString = ConfigurationSettings.AppSettings["lc"];
var username = ConfigurationSettings.AppSettings["lu"];
var password = ConfigurationSettings.AppSettings["lpw"];
using (DirectoryEntry de = new DirectoryEntry(connString, username, password))
{
DirectorySearcher search = new DirectorySearcher(de);
search.PageSize = 1001;// To Pull up more than 100 records.
DirectorySearcher directorySearcher = new DirectorySearcher(de);
directorySearcher.Filter = string.Format("(&(objectClass=user)(objectCategory=user) (sAMAccountName={0}))", username);
directorySearcher.PropertiesToLoad.Add("msRTCSIP-PrimaryUserAddress");
try
{
var result = directorySearcher.FindOne();
var found = false;
if (result != null)
{
if (result.Properties["msRTCSIP-PrimaryUserAddress"] != null)
{
found = true;
Console.WriteLine("Found: " + result.Properties["msRTCSIP-PrimaryUserAddress"][0]);
}
}
if (!found)
{
Console.WriteLine("Sad face");
}
}
catch (Exception x)
{
Console.WriteLine(x.Message);
}
Console.WriteLine("------------");
}
}
I was trying to figure out how to properly format a LDAP connection string last week, and found this entry over on serverfault:
How can I figure out my LDAP connection string?
I noticed you had a "/" between each OU or DC entry - I didn't include those in mine, and I don't see them included in the above example either.
I'm far from an expert (obviously) but just figured I would throw it out there.

C# script to connect to Oracle Directory Server Enterprise Edition

I have a C# script I am trying to use to connect to an Oracle Directory Server Enterprise Edition
So far i have had very little success.
I am receiving a Unknown error (0x80005000) error message
Can somebody tell me what I am doing wrong. I have been researching the web and most online boards say that this error message is because the LDAP in the path needs to be in uppercase letters.
As you can see I have done that but still no luck.
Below is my code
private static readonly string PATH = "LDAP://LDAPDEV.example.com:389/o=example.com";
private static readonly string USERNAME = uid=SERVICE_USR,ou=ApplicationIDs,o=example.com";
private static readonly string PASSWORD = "test1234";
string DN = "";
// connect to determine proper distinguishedname
DirectoryEntry Entry = new DirectoryEntry(Path, USERNAME, PASSWORD, AuthenticationTypes.None);
try
{
// Bind to the native AdsObject to force authentication.
Object obj = Entry.NativeObject;
DirectorySearcher Search = new DirectorySearcher(Entry);
Search.ReferralChasing = ReferralChasingOption.All
}
catch (Exception ex)
{
throw new Exception("Error looking up distinguishedname. ", ex);
}
finally
{
anonymousEntry.Close();
}
return DN;
}
string sDomain="LDAPDEV.example.com:389";
string sDefaultOU = #"o=example.com";
string sServiceUser = #"uid=user,ou=ApplicationIDs,o=example.com";
string sServicePassword = "password";
try
{
PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain, sDomain, sDefaultOU, ontextOptions.SimpleBind, sServiceUser,sServicePassword);
}
catch (Exception ex)
{
ex.Message;
}
Here is something that you can use to get some information by using PrincipalContext look at this working example and replace the values with your domain name values.
// if you are doing this via web application if not you can replace
// the Request/ServerVariables["LOGON_USER"]
// with Environment.UserName; this will return your user name like msmith for example so var userName = Environvironment.UserName;
var userName = Request.ServerVariables["LOGON_USER"].Split(new string[] {"\\"}, StringSplitOptions.RemoveEmptyEntries);
var pc = new PrincipalContext(ContextType.Domain,"yourdomain.com",null, null);
var userFind = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, userName);
this is basically all you need to see the information like email address SamAccountName ect.. use the debugger to inspect all the property values available in the pc variable and userFind variable

"The specified domain either does not exist or could not be contacted."

I'm trying to use the DomainServices class to retrieve a list of OU's from my Active Directory.
Here's my code:
public List<OrganizationalUnit> FindOrganizationalUnits(string domainName, string domainExtension, string parentOrganizationUnit)
{
string tmpDirectory = String.Format("LDAP://ou={0},dc={1},dc={2}",
parentOrganizationUnit,
domainName,
domainExtension
);
DirectoryEntry directory = new DirectoryEntry(tmpDirectory);
DirectorySearcher searcher = new DirectorySearcher(directory);
searcher.Filter = "(objectClass=organizationalUnit)";
searcher.SearchScope = SearchScope.Subtree;
searcher.PropertiesToLoad.Add("displayName");
var organizationalUnits = new List<OrganizationalUnit>();
foreach (SearchResult result in searcher.FindAll())
{
//I just create and return a new OrganizationalUnit object based on the SearchResult result.
organizationalUnits.Add(new OrganizationalUnit(result));
}
return organizationalUnits;
}
Is there some configuration I have to set on my server end to let me use DirectoryServices to query it's AD objects?
Thanks for the help.
What type of app are you running this code from? AD queries have to be made from an authenticated resource. You can either use the current credentials of the user, or pass in a new name/password.
Services usually don't have any issue, running under LocalSystem, but if this is a web app running under IIS standard permissions, it might cause an issue.
Try adding some credentials where you're instantiating your DirectoryEntry class.

Categories