Active Directory Permitted Logon Times with C# .Net 3.5 using System.DirectoryServices.AccountManagement

Recently I was working on a proof of concept for some active directory manipulation.  Using the System.DirectoryServices.AccountManagement was the easiest and best option for managing accounts (as the name implies).  However, the one thing that I really needed was a way to restrict when a particular user would be able to logon.

What I discovered was that the UserPrincipal class does in fact have a property called PermittedLogonTimes, but the catch is that there is no documentation on how to set the property for specific hours and to make this more confusing it is a byte[].

So after a little bit of playing around in Active Directory, setting the permitted logon times and reading it back out in C# it finally made sense.  The property is basically a byte[21] with a mask to cover the hours the user is allowed to logon.  Each day of the week is broken down into 3 byte values (refer to the table below for the index and the hours).

Sunday Monday Tuesday Wednesday Thursday Friday Saturday
Midnight-8am 1 4 7 10 13 16 19
8am-4pm 2 5 8 11 14 17 20
4pm-Midnight 3 6 9 12 15 18 0

Each hour in each individual block has a specific mask value.  To get a specific time span, all you need to do is add up the values for the hours you want.  Refer to the following table:

Group 1 Group 2 Group 3
1 12am 8am 4pm
2 1am 9am 5pm
4 2am 10am 6pm
8 3am 11am 7pm
16 4am 12pm 8pm
32 5am 1pm 9pm
64 6am 2pm 10pm
128 7am 3pm 11pm

For example, if you would like to allow logon on Monday from 1pm-5pm your byte array would be all zeros except value at index 5 would be 224 and index 6 would be 1.

I’ve written the following classes to aid in calculating the byte masks:

LogonTime – is used to pass the information for logon for a specific day.

PermittedLogonTime – used to calculate the actual values that need to be passed to Active Directory.

http://gist.github.com/568549

Update 5/6/2011 : Thanks to comments and testing by Simone Greci, we came to the conclusion that the masks provided here are for Pacific Standard Time (PST, GMT -8).  I would like to update this code when I have time but keep in mind of this limitation.

Update 10/18/2011: I’ve finally gotten around to rewriting the code.  I rewrote everything from the ground up so it also takes into account the timezone.  By default it should go to your machine’s time zone, but can be passed specific timezones.  I’ve included code that converts the AD byte mask back into a list of the LogonTime objects.  To make it easier to use, I’ve compiled it into a project so you can just import the dll and use it.  The project is up on github (here).

There is a demo console app that compares the output to my old code that worked for PST (-8 GMT).

Usage is fairly straight forward:

To create a new logon time using your machine’s time zone:

var time = new LogonTime(DayOfWeek.Monday, 
new DateTime(2011, 1, 1, 8, 0, 0), 
new DateTime(2011, 1, 1, 10, 0, 0));

To use a specific time zone:

var zone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
var time = new LogonTime(DayOfWeek.Monday, 
     new DateTime(2011, 1, 1, 8, 0, 0), 
     new DateTime(2011, 1, 1, 10, 0, 0), zone);

To get the byte mask to submit to AD:

var zone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
var time = new LogonTime(DayOfWeek.Monday, 
     new DateTime(2011, 1, 1, 8, 0, 0), 
     new DateTime(2011, 1, 1, 10, 0, 0), zone); 
var times = new List<LogonTime>();
times.add(time);
var mask = PermittedLogonTimes.GetByteMask(times)

To get the list of times from the AD mask:

byte[] mask;// populate from AD
var times = PermittedLogonTimes.GetLogonTimes(mask);

*The resulting times that are parsed have invalid dates, just the hour ranges.

I hope this helps those of you using those permitted logon times. Enjoy!

Advertisements

7 thoughts on “Active Directory Permitted Logon Times with C# .Net 3.5 using System.DirectoryServices.AccountManagement

  1. Thanks for the great post.

    The problem is that I need to get a list of intervals from a array of bytes of the Active Directory (Exactly the opposite!). I have created a class that fill this requirement.

    If you want to have it, just notify me.

    • That would be great. If you send it to me I can incorporate it into the sample. Do you account for the time zone when calculating the intervals? I haven’t had a chance to come back and factor in time zones into the code yet.

  2. Any chance of posting Samuel’s code, as i have the issue of wanting to read and write this field in AD – i’m trying to do it in vb.net though, so I am decoding your C#, but having it is a great start for me

    Thanks

    • Will do, when I receive it and with his permission. When I get some free time, I’m still planning on updating the code to account for timezones and can incorporate that code into a single class.

    • I just spent some time and wrote my own code that should reverse the byte mask back into my LogonTime objects. Unfortunately I don’t have access to an AD to test to make sure that it is working completely properly. The only thing I can validate is that it produces the same results as my old code. If you are brave enough to give it a try, let me know if this code works for you.

      thanks!

  3. Hi. Thanks a lot for sharing your code. It helped me a lot. I would just participate : it seems to me there is a case you didn’t handle : when the end time hour is midnight.
    To fix it, I changed LogonTime.ValidateTimes() to allow an ending hour set to 0. Then in PermittedLogonTimes.MarkHours() I changed the test “if (logonTime.BeginTime.Hour >= i && i < logonTime.EndTime.Hour)" and used a variable to replace logonTime.EndTime.Hour (this variable is set to 24 if EndTime.Hour ==0, EndTime.Hour otherwise).

    I'm sure a cleaner way of fixing it may be found.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s