Archive for category Security

Active Directory: Get All Users using C# with Linq To Active Directory

I began working on a website which had the requirement of getting all users from Active Directory/LDAP.  I began working on a solution by searching the web. When I ran across a tool LINQ to Active Directory which allows one to use Linq against Active Directory. I found that code to be quite helpful in my task. This article discusses how to use the tool and additional code changes I created to make the tool even more specialized and less prone to memory leaks.

Steps: Get all users.

  1. Download the latest Linq to AD from codeplex LINQ to Active Directory. It expands a demo project and the main library project BdsSoft.DirectoryServices.Linq.
  2. Create a new blank solution and add the BdsSoft.DiretoryServices.Linq  project into it.
  3. Create a new project to consume the library.
  4. In the new project add these references
    1: to the BdsSoft.DirectoryServices
    2: System.DirectoryServices
    3: The com object Active DS Type library:

    Active DS Type Library in the COM tab

  5. Now we have to create our own entity. Basically it is a class where the properties are simply the name of the target LDAP categories. Here are the usings which will be needed:
    using ActiveDs;
    using BdsSoft.DirectoryServices.Linq;
    using System.Linq.Expressions;
    using System.DirectoryServices;
  6. Here is the entity class user which will hold each item:
    [DirectorySchema( "user", typeof( IADsUser ) )]
    class User
    {
        public string Name { get; set; }
    
        public string sAMAccountName { get; set; }
    
        public string objectCategory { get; set; }
    
        public string mail { get; set; }
    
        public string Description { get; set; }
    
        [DirectoryAttribute( "PasswordLastChanged", DirectoryAttributeType.ActiveDs )]
        public DateTime PasswordLastSet { get; set; }
    
        [DirectoryAttribute("distinguishedName")]
        public string Dn { get; set; }
    
        [DirectoryAttribute("memberOf")]
        public string[] Groups { get; set; }
    
    }
  7. The above class uses attributes to inform the Linq To AD library how to handle the entity and if need be change names of each of the properties of the class.
  8. Use this code to access AD from a console app, placing your AD server in the below code:
    static void Main( string[] args )
    {
    
        IEnumerable<User> users = GetADUsers();
    
        Console.WriteLine( "Users: " + users.Count().ToString() );
    
    }
    
    static DirectoryEntry ROOT = new DirectoryEntry( "LDAP://MyADDomainLocation.com" );
    
    private static IEnumerable<User> GetADUsers()
    {
        IEnumerable<User> users;
    
        var usersDS = new DirectorySource<User>( ROOT, SearchScope.Subtree );
    
                users = from usr in usersDS
                        where usr.Name == "A*" // FIlter A then any character(s)
                        select usr;
    
         users = users.OrderBy( user => user.Name ).ToList(); // Sort them alphabetically by name.
    
        return users;
    }

That will get you up and running but there are three problems with the library which you may run into.

  1. The code uses DirectorySearcher..::.FindAll Methodwhich states in the remarks that improper usage may result in a memory leak.
  2. The action even though user is stated, at least in my domain returned non users. A different LDAP category needs to be used.
  3. Doesn’t handle specific LDAP command to ignore inactive users.

I rectify those issues and will detail it in a future post.

Share

.Net Trust Tribal Knowledge

Caspol is used to setup trusts for .Net applications. But the command line version to view what has been done is not user friendly. A way to view trusts that have been setup can be found in the Microsoft .Net Framework Configuration 2.0 tool. That tool is installed with the .Net SDK. Note: this tool will not be listed in the SDK folder but a shortcut will be added to the Administrative Tools folder on the Program Menu.

Once it is running, check for your trusts that you have setup by looking at this folder

.Netframework 2.0 Configuration\My Computer\Runtime Security Policy\Machine
\ Code Groups

That should list all remote assembly(ies) that one has caspol setting up a trusts for… Note those don’t have to be assemblies but can be a full directory to trust.

By looking at All_Code\LocalIntranet_Zone one can see the pathing to your server as an URL. If you named the trust, it should be easy to find.

Debugging Trust Issues

If when debugging a trust issue, one needs to find the assembly in question as a listing such as \\Server\directory\MyAssembly.dll or within a listing such as \\Server\Directory\* . If that is not found, then trust needs to be setup using CasPol.

Suggestions

  1. One can temporarily turn off caspol security by opening up a DOS shell and typing caspol -s off. While the window is open, caspol will be turned off for debugging purposes. Once the window is closed it will be turned on.
  2. If one has remote assemblies on an intranet directory, one can set the whole directory to be trusted by specify a * when using caspol instead of an assembly or executable. That way if there are multiple assemblies within that directory, you don’t have to specify trust for each one.
Share

Tags: ,

Security Principles and Local Admin Rights in C# .Net

Having started work at a company where my local box did not have me as a local admin , got me to thinking about admin rights. The following code displays the current user stats and attempts to access a protected method that only local admins should access. The code should discriminate against all those that don’t have local admin rights. Within the code I demonstrate declarative security (using the Attribute over the method) Imperative security calls to specific security methods within the code. First off one needs these usings:

using System.Security.Principal;
using System.Security.Permissions;

Here is the code,

   1: public static void GetRole()
   2: {
   3:     System.AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
   4:  
   5:     WindowsIdentity curIdentity = WindowsIdentity.GetCurrent();
   6:     WindowsPrincipal myPrincipal = new WindowsPrincipal(curIdentity);
   7:  
   8:     List<string> groups = new List<string>();
   9:  
  10:     foreach (IdentityReference irc in curIdentity.Groups)
  11:     {
  12:         groups.Add(((NTAccount)irc.Translate(typeof(NTAccount))).Value);
  13:     }
  14:  
  15:     Console.WriteLine(
  16: @"Name:           {0},
  17: System:
   {1}
  18: Authenticated:  {2}
  19: BuiltinAdmin:   {3}
  20: Identity:       {4}
  21: Groups:         {5}"
  22:         curIdentity.Name,
  23:         curIdentity.IsSystem,
  24:         curIdentity.IsAuthenticated,
  25:         myPrincipal.IsInRole(  WindowsBuiltInRole.Administrator ) ? "True" : "False",
  26:         myPrincipal.Identity,
  27:         string.Join(string.Format(",{0}\t\t", Environment.NewLine), groups.ToArray()));
  28:  
  29:  
  30:     try
  31:     {
  32:         Console.WriteLine(Environment.NewLine);
  33:         ManagersOnly();
  34:     }
  35:     catch (System.Security.SecurityException scx)
  36:     {
  37:         Console.WriteLine(scx.Message + " " + scx.FirstPermissionThatFailed.ToString());
  38:     }
  39:     Console.WriteLine(Environment.NewLine);
  40:  
  41:  
  42: }
  43:  
  44:  
  45: [PrincipalPermissionAttribute(SecurityAction.Demand, Role = @"BUILTIN\Administrators")]
  46: private static void ManagersOnly()
  47: {
  48:     Console.WriteLine("Key to the Executive Wash Room");
  49: }

Here are some highlights

  • Line 3 is the killer. Without it we become rudderless when a check is made. (Causes an exception to be thrown even though we are in the admins group). This line is setting the processing up to work with the OS system token which will be geared toward the current user and windows security on the current thread. This doesn’t actually come into play until we call ManagersOnly method on line 33. Otherwise if this was commented out we would get an exception “Request for principal permission failed” would occur on line 33. (The rub is line 25 would actually work, so be careful when using declarative or imperative security, they can behave differently).
  • Line 5 of note the current identity will never have an actual user name (unless its the login name), password or other identifying aspects of the actual user.
  • Line 12 is where we convert the numeric ids to a relatable group names that we are all familiar with.
  • Line 15 informs us of what we are currently logged in as and will display different aspects of the account.
  • Line 25 is in a way using imperative security, where we could take that check and programmatically divine where to allow access. What is nice is that the call is type safe unlike on line 45 where we can make a spelling mistake and it all fails.
  • Line 33 calls the Declarative Security attributed method, any call to a role based security should be within a try catch block. Depending on where the code is, some problems exists as objects which are being disposed and the rights have changed on the GC thread which is trying to finalize objects.
  • Line 45 is the declarative security as done in the attribute above the method. We could actually drill down even more to specify an exact user name if we wanted. We will stick with groups (roles) and look for the local admin. Note spelling mistakes will cost you so BUILTIN\Administrator is not BUILTIN\Administrators.

The final output looks like this:

Name:          ORION\OmegaMan
System:        False
Authenticated: True
BuiltinAdmin:  True
Identity: System.Security.Principal.WindowsIdentity Groups: ORION\None,
Everyone,
BUILTIN\Administrators,
BUILTIN\Users,
NT AUTHORITY\INTERACTIVE,
NT AUTHORITY\Authenticated Users,
LOCAL

Key to the Executive Wash Room

Share