C#: Cannot set the Contact-Attribute "targetAddress" with my DirectoryEntry - Connection - c#

I want to create a Contact-Object in a specific OU in our AD with some Attributes:
sn, givenName, mail, description, displayname, proxyAddresses and targetaddress.
I found lots of examples how to set the Attributes for a Contact-Object in Active Directory with C# and I`m able to create the Object with all Attributes except "targetaddress".
Could someone please Point me in the right Direction for this. Thank you!
public void CreateContact2(string Vorname, string Nachname, string EmailAdresse, string Beschreibung, string myDomainPath)
{
string CN = Vorname + " " + Nachname;
string mailNickName = EmailAdresse.Remove(EmailAdresse.IndexOf("#"));
string EmailAdresse2 = "SMTP:" + EmailAdresse;
DirectoryEntry directoryEntry;
try
{
directoryEntry = new DirectoryEntry("LDAP://myDomainPath")
directoryEntry.RefreshCache();
DirectoryEntry contact = directoryEntry.Children.Add("CN=" + CN, "Contact");
contact.Properties["sn"].Value = Nachname;
contact.Properties["givenName"].Value = Vorname;
contact.Properties["mail"].Value = EmailAdresse;
contact.Properties["description"].Value = Beschreibung;
contact.Properties["displayName"].Value = Nachname + ", " + Vorname;
contact.Properties["proxyAddresses"].Add(EmailAdresse2);
contact.Properties["targetaddress"].Value = EmailAdresse2;
contact.CommitChanges();
}
catch (Exception e)
{
// Do some error processing
var msg = e.Message.ToString();
Console.WriteLine("Fehler in Funktion CreateContact():" + msg);
}
}
The Contact-Object creates fine if I create it without the targetaddress-Attribute
But with it I´m
getting System.Runtime.InteropServices.COMException: “The specified
directory service attribute or value does not exist”
failure. Any Ideas?

I had he same issue , what i did is to restart and run Visual Studio as Administrator . It may be due to not having enough privileges to run some COM Methods

Thank you so much Guys!
I had found the answer by myself and . It was my mistake. The attribute wasn´t right delegated.

Related

How to get a valid DirectoryEntry?

I am trying to read an LDAP directory entry in a C# console application and I somehow don't succeed. Setting up the connection works fine:
private static LdapConnection ldapConnection;
public static void Main()
{
NetworkCredential credentials = new NetworkCredential("uid=admin,ou=system", "secret");
ldapConnection = new LdapConnection("127.0.0.1:10389");
ldapConnection.SessionOptions.ProtocolVersion = 3;
ldapConnection.AuthType = AuthType.Basic;
try
{
ldapConnection.Bind(credentials);
Console.WriteLine("Ldap connection is created.");
}
catch (Exception e)
{
Console.WriteLine("\r\nUnexpected exception occurred:\r\n\t" + e.GetType() + ":" + e.Message);
return;
}
//searchUsers();
//getSchemaClassName();
}
I can even find me some users:
private static void searchUsers()
{
string[] attrs = new string[] { "cn", "sn" };
string[] followedBy = new string[] { " ", "\n" };
var request = new SearchRequest("ou=users,ou=system", "(objectClass=*)", System.DirectoryServices.Protocols.SearchScope.Subtree, attrs);
var response = (SearchResponse)ldapConnection.SendRequest(request);
foreach (SearchResultEntry entry in response.Entries)
{
for (int i = 0; i < attrs.Length; i++)
{
if (entry.Attributes.Contains(attrs[i]))
{
Console.Write(entry.Attributes[attrs[i]].GetValues(Type.GetType("System.String"))[0] + followedBy[i]);
}
}
}
}
But when I for example try to read a directory entry's schema classname, I find that the directory entry contains a bunch of errors. This is the code:
private static void getSchemaClassName()
{
string path = "ldap://localhost:10389/cn=David,ou=users,ou=system"; //path is ok, points to an existing user
DirectoryEntry de = new DirectoryEntry(path, "uid=admin,ou=system", "secret");
Console.WriteLine("Schema classname:" + de.SchemaClassName); //breakpoint here
}
When I place a breakpoint on the "breakpoint here"-line and subsequently examine de properties, I find that Guid, Name, Options, Parent, SchemaClassName and some others display the "The directory service is unavailable" error. Which even makes sense to me, since ldapConnection isn't in any way involved in this code, whereas it is in searchUsers(). But I don't know how to connect this code to ldapConnection; every DirectoryEntry example I come across on the internet apparently works without the connection. How can I get this fixed?
Thx, Cooz
Edit:
Changing getSchemaClassName() to the following helped somewhat...
private static void getSchemaClassName()
{
string path = "LDAP://localhost:10389/cn=David,ou=users,ou=system";
DirectoryEntry de = new DirectoryEntry(path, "uid=admin,ou=system", "secret", AuthenticationTypes.None);
Console.WriteLine("Schema classname:" + de.SchemaClassName); //breakpoint here
}
...but not enough. Now at first sight only the Guid and NativeGuid properties of de give a System.Runtime.InteropServices.COMException and the rest seems fine. de.SchemaClassName is "organizationalPerson". Perfect.
However, upon setting the breakpoint once again and expanding for example de.SchemaEntry.SchemaEntry, almost everything that becomes visible there throws said exception: Guid, Name, ObjectSecurity, Options, SchemaClassName, SchemaEntry... you name it. The code still is not much use to me. What might be the possible cause of all those errors and how can I get it fixed?

Adding group to local administrators

I am trying to add an existing group in Local Administrators. The group "ABC\Some Active Group" exists. I can add that through Windows GUI but I need to add it through code. Here is what I have tried so far:
public static bool AddGroup(string machineName, string groupName)
{
bool ifSuccessful = false;
try
{
DirectoryEntry localMachine = new DirectoryEntry("WinNT://" + machineName);
DirectoryEntry admGroup = localMachine.Children.Find("administrators", "group");
//admGroup.Children.Add(groupName, "Group");
admGroup.Invoke("Add", groupName);
admGroup.CommitChanges();
ifSuccessful = true;
}
catch (Exception ex)
{
ifSuccessful = false;
//logging
Console.WriteLine(machineName + " ----------" + ex.Message);
}
return ifSuccessful;
}
and I am calling it like:
AddGroup(Environment.MachineName, #"ABC\Some Active Group");
I get the exception, (its an inner exception)
An invalid directory pathname was passed
I also tried adding it like:
admGroup.Children.Add(groupName, "Group");
But then I got the exception:
The Active Directory object located at the path
WinNT://ABC/MachineName/Administrators is not a container
I have been able to successfully get all the users and groups with admGroup, I can't just add one. Can someone please tell me what am I doing wrong ?
You need to call AddGroup like this
AddGroup(Environment.MachineName, "WinNT://" + Environment.MachineName + "/Some Active Group");

Updating AD User information

I am having a problem updating user information in an Active Directory DB...
When I run the following code I get this error:
The specified directory service attribute or value does not exist
The problem is the path it is using to save the information is this:
CN=AD Test,OU=Container Name,DC=us,DC=flg,DC=int
Ad Test is the username in AD that I am trying to update.
and I believe it should be:
CN=Ad Test,OU=Container Name, OU=Server Name,DC=us,DC=flg,DC=int
I am new to Directory services so I would greatly appreciate any help in finding out why I cannot update... Thank you in advance
public bool UpdateActiveDirectory(string LdapServerName, string CustId, Employee SQLresult)
{
try
{
DirectoryEntry rootEntry = new DirectoryEntry("LDAP://" + LdapServerName, "usrename", "password", AuthenticationTypes.Secure);
DirectorySearcher searcher = new DirectorySearcher(rootEntry);
searcher.Filter = "(sAMAccountName=" + SQLresult.LogonNT + ")";
searcher.PropertiesToLoad.Add("title");
searcher.PropertiesToLoad.Add("street");
searcher.PropertiesToLoad.Add("1");
searcher.PropertiesToLoad.Add("st");
searcher.PropertiesToLoad.Add("postalCode");
searcher.PropertiesToLoad.Add("department");
searcher.PropertiesToLoad.Add("mail");
searcher.PropertiesToLoad.Add("manager");
searcher.PropertiesToLoad.Add("telephoneNumber");
SearchResult result = searcher.FindOne();
if (result != null)
{
// create new object from search result
DirectoryEntry entryToUpdate = result.GetDirectoryEntry();
entryToUpdate.Properties["title"].Value = SQLresult.Title;
entryToUpdate.Properties["street"].Value = SQLresult.Address;
entryToUpdate.Properties["1"].Value = SQLresult.City;
entryToUpdate.Properties["st"].Value = SQLresult.State;
entryToUpdate.Properties["postalCode"].Value = SQLresult.ZipCode;
entryToUpdate.Properties["department"].Value = SQLresult.Department;
entryToUpdate.Properties["mail"].Value = SQLresult.EMailID;
entryToUpdate.Properties["manager"].Value = SQLresult.ManagerName;
entryToUpdate.Properties["telephoneNumber"].Value = SQLresult.Phone;
entryToUpdate.CommitChanges();
Console.WriteLine("User Updated");
}
else
{
Console.WriteLine("User not found!");
}
}
catch (Exception e)
{
Console.WriteLine("Exception caught:\n\n" + e.ToString());
}
return true;
}
Maybe just a typo?
The third property you're trying to update:
entryToUpdate.Properties["1"].Value = SQLresult.City;
is that a one (1) in there? It should be a small L (l) instead.
Also: the manager's name must be the Distinguished Name of the manager - the whole
CN=Manager,CN=Ad Test,OU=Container Name, OU=Server Name,DC=us,DC=flg,DC=int
thing - not just the name itself.
If that doesn't help anything - just go back to old-school debugging technique:
update just a single property; if it fails --> that's your problem case - figure out why it's a problem.
If it works: uncomment a second property and run again
-> repeat over and over again, until you find your culprit

Check if password never expired from code is not working on specific network

I got some code of a tool which does many security checks on client machines and I'm trying to understand some bug and solve it - need your help.
There is a test which checks if the user password is set to "never expire". I tried this test offline and in 3 different networks and it seems to be working. There is some WIFI guest network in my company which return exception while executing the code.
Here is the exception I'm getting:
The network path was not found.
at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
at System.DirectoryServices.DirectoryEntry.Bind()
at System.DirectoryServices.DirectoryEntry.get_NativeObject()
at System.DirectoryServices.DirectoryEntry.InvokeGet(String propertyName)
at Intel.HostCheck.Engine.Checks.PasswordAge.CheckPasswordNeverExpired()
See the the following code for understanding the test implementation:
private CheckResult CheckPasswordNeverExpired()
{
try
{
DirectoryEntry de;
Logger.Debug("Password Age: isCurrentUserLocalUser - " + HelpFunction.isCurrentUserLocalUser());
if (HelpFunction.isCurrentUserLocalUser())
de = new DirectoryEntry("WinNT://" + Environment.MachineName + "/" + Environment.UserName);
else de = new DirectoryEntry("WinNT://" + Environment.UserDomainName + "/" + Environment.UserName);
object currentUser = de.InvokeGet("UserFlags");
if (currentUser != null)
{
Logger.Debug("PasswordAge: " + currentUser);
Logger.Debug("PasswordAge: " + Convert.ToBoolean((int)currentUser & UF_DONT_EXPIRE_PASSWD).ToString());
}
if (Convert.ToBoolean((int)currentUser & UF_DONT_EXPIRE_PASSWD))
{
Logger.Debug("Password Age: check result: fail ");
return CheckResult.Fail;
}
Logger.Debug("Password Age: check result: pass ");
return CheckResult.Pass;
}
catch (Exception ex)
{
Logger.LogException(ex, "Error in PasswordAge->Check()");
return CheckResult.Exception;
}
}
}
public static bool isCurrentUserLocalUser()
{
return Environment.MachineName == Environment.UserDomainName;
}
Just for clarification, it's not my code so I'm not completely sure what the code owner did.
I'm seeing that there are no responses. I will ask different question.
Is there any option to obtain password age or password last set values without be depend on network connection? Is it possible to check it locally?
I know this value also exists under "Local security policy" but I didn't find how to get it by code.

How can I get DOMAIN\USER from an AD DirectoryEntry?

How can I get the Windows user and domain from an Active Directory DirectoryEntry (SchemaClassName="user") object?
The user name is in the sAMAccountName property but where can I look up the domain name?
(I can't assume a fixed domain name because the users are from various subdomains.)
This assumes that results is a SearchResultCollection obtained from a DirectorySearcher, but you should be able to get the objectsid from a DirectoryEntry directly.
SearchResult result = results[0];
var propertyValues = result.Properties["objectsid"];
var objectsid = (byte[])propertyValues[0];
var sid = new SecurityIdentifier(objectsid, 0);
var account = sid.Translate(typeof(NTAccount));
account.ToString(); // This give the DOMAIN\User format for the account
You won't find what you're looking for in the DirectoryEntry, unfortunately.
You have the sAMAccountName which typically is something like myuser (without the domain). You have the distinguishedName which is something like LDAP://cn=joe myuser,cn=Users,dc=yourCompany,dc=com. You also have a userPrincipalName but that's usually a name in the format of joeUser#mycompany.com.
But you won't find any attribute that has the domain\MyUser in it, unfortunately. You'll have to put that together from your information about the domain name, and the sAMAccountName of the DirectoryEntry.
For more information and some excellent Excel sheets on all the LDAP and WinNT properties in System.DirectoryServices, check out the Hilltop Lab website by ADSI MVP Richard Mueller.
Marc
To get the DirectoryEntry domain name you can use recursion on
directoryEntry.Parent.
And then if directoryEntry.SchemaClassName == "domainDNS"
you can get the domain name like this:
directoryEntry.Properties["Name"].Value
I found a partitions container in CN=Partitions,CN=Configuration that contains all domains.
When you match the user to the partion you can read the real domain name from the nETBIOSName+"\"+sAMAccountName property.
public static string GetDomainNameUserNameFromUPN(string strUPN)
{
try
{
WindowsIdentity wi = new WindowsIdentity(strUPN);
WindowsPrincipal wp = new WindowsPrincipal(wi);
return wp.Identity.Name;
}
catch (Exception ex)
{
}
return "";
}
If you are using the System.DirectoryServices libraries, you should have a SearchResultsCollection from a DirectorySearcher.
Within each SearchResult's Properties collection, there is a "distinguishedname" property. That will contain all the DC parts that make up the domain your directory entry belongs to.
I'm extending a previous answer by #laktak to provide the details of what he meant.
There is a partitions container in CN=Partitions,CN=Configuration that contains all domains which gives you the cn which is the Netbios domain name and the nCName property that contains the distinguishedName prefix a user will have if they are in this domain.
So start by searching ldap for (objectClass=*) in CN=Partitions,CN=Configuration and store the (cn, nCName) pairs of each result to a map.
Next you query ldap using (sAMAccountName=USERIDHERE) and get the distinguishedName from the user. Now go through the (cn, nCName) pairs and find the nCName that prefixes the distinguishedName from the user, and the corresponding cn is your desired Domain name.
I wrote this pieces of code for my own usage (in VB.net, easy translation) :
<System.Runtime.CompilerServices.Extension()>
Public Function GetDomainFQDN(ByVal Entry As DirectoryServices.DirectoryEntry) As String
Try
While Entry.SchemaClassName <> "domainDNS"
Entry = Entry.Parent
End While
Dim DN As String = Entry.Properties("DistinguishedName").Value
Return DN.Replace("DC=", "").Replace(",", ".")
Catch ex As Exception
Debug.WriteLine(ex.ToString)
Return String.Empty
End Try
End Function
<System.Runtime.CompilerServices.Extension()>
Public Function GetDomainNetbiosName(ByVal Entry As DirectoryServices.DirectoryEntry) As String
Try
While Entry.SchemaClassName <> "domainDNS"
Entry = Entry.Parent
End While
Return Entry.Properties("Name").Value
Catch ex As Exception
Debug.WriteLine(ex.ToString)
Return String.Empty
End Try
End Function
I feel obligated to add my answer which was inspired from Nicholas DiPiazza's answer here. Hope this PowerShell code helps someone!
$hash = #{} //this contains the map of CN and nCNAME
$Filter = '(nETBIOSName=*)'
$RootOU = "CN=Partitions,CN=Configuration,DC=DOMAIN,DC=LOCAL" //Change this to your org's domain
$Searcher = New-Object DirectoryServices.DirectorySearcher
$Searcher.SearchScope = "subtree"
$Searcher.Filter = $Filter
$Searcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$($RootOU)")
$Searcher.FindAll()|sort | foreach { $hash[($_.Properties.ncname).Trim()] = ($_.Properties.cn).Trim() }
$hash.GetEnumerator() | sort -Property Value
If the user details are available in $userDetails, then the you can get the correct domain with this:
$hash[[regex]::Match($userDetails.DistinguishedName, 'DC=.*').Value]
and the final username would look like this:
$hash[[regex]::Match($userDetails.DistinguishedName, 'DC=.*').Value] + "\" + $userDetails.SamAccountName
1) You can get the userPrincipalName from the DirectoryEntry.
2) Then, split the UPN up between the Username and Domain Name.
3) Then call GetNetBIOSName() on it.
public static DirectoryEntry GetDirectoryObject(string strPath)
{
if (strPath == "")
{
strPath = ConfigurationManager.AppSettings["LDAPPath"]; //YOUR DEFAULT LDAP PATH ie. LDAP://YourDomainServer
}
string username = ConfigurationManager.AppSettings["LDAPAccount"];
string password = ConfigurationManager.AppSettings["LDAPPassword"];
//You can encrypt and decrypt your password settings in web.config, but for the sake of simplicity, I've excluded the encryption code from this listing.
}
catch (Exception ex)
{
HttpContext.Current.Response.Write("user: " + username + ", LDAPAccount: "+ ConfigurationManager.AppSettings["LDAPAccount"] + ".<br /> "+ ex.Message +"<br />");
if (HttpContext.Current.User.Identity != null)
{
HttpContext.Current.Response.Write("HttpContext.Current.User.Identity: " + HttpContext.Current.User.Identity.Name + ", " + HttpContext.Current.User.Identity.IsAuthenticated.ToString() + "<br />");
HttpContext.Current.Response.Write("Windows Identity: " + WindowsIdentity.GetCurrent().Name + ", " + HttpContext.Current.User.Identity.IsAuthenticated.ToString());
}
else
{
HttpContext.Current.Response.Write("User.Identity is null.");
}
HttpContext.Current.Response.End();
}
DirectoryEntry oDE = new DirectoryEntry(strPath, username, password, AuthenticationTypes.Secure);
return oDE;
}
public static string GetNetBIOSName(string DomainName)
{
string netBIOSName = "";
DirectoryEntry rootDSE =GetDirectoryObject(
"LDAP://"+DomainName+"/rootDSE");
string domain = (string)rootDSE.Properties[
"defaultNamingContext"][0];
// netBIOSName += "Naming Context: " + domain + "<br />";
if (!String.IsNullOrEmpty(domain))
{
//This code assumes you have a directory entry at the /CN=Partitions, CN=Configuration
//It will not work if you do not have this entry.
DirectoryEntry parts = GetDirectoryObject(
"LDAP://"+DomainName+"/CN=Partitions, CN=Configuration," + domain);
foreach (DirectoryEntry part in parts.Children)
{
if ((string)part.Properties[
"nCName"][0] == domain)
{
netBIOSName += (string)part.Properties[
"NetBIOSName"][0];
break;
}
}
}
return netBIOSName;
}
public static string GetDomainUsernameFromUPN(string strUPN)
{
string DomainName;
string UserName;
if (strUPN.Contains("#"))
{
string[] ud = strUPN.Split('#');
strUPN= ud[0];
DomainName = ud[1];
DomainName=LDAPToolKit.GetNetBIOSName(DomainName);
UserName= DomainName + "\\" + strUPN;
}
else
{
UserName= strUPN;
}
return UserName;
}

Categories