Active Directory property "badPwdCount" - c#

Problem:
We've upgraded the AD server from 2003 to 2008 and due to some "bad code", where developer has coded in such a way that, he directly casts "badPwdCount" property value to INT and it blows up because of NULL value conversion - NULL reference exception - NULL cannot be converted to INT.
Bigger problem:
We cannot do a deployment at this point because there are over 100 individual apps that depended on this change and we're looking for a least involved way of dealing with it for now.
Background:
Now the way this "badPwdCount" property works is, that when user logs on to the domain, it will get set to zero, otherwise it's NULL. The problem is that none of these users are ever going to log on interactively because they're external and we authenticate them via API and they cannot log in using the API either..
Question:
Does anyone know if this value is in the registry or somewhere, where I can get to it and set it to zero? Was also thinking of initiating a log in per user via a script, but wanted to gather other ideas too...
MSDN page for badPwdCount:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms675244(v=vs.85).aspx

Normally this would be easy, all you would need to do is update all the users in active directory and set the value to 0 if it is null. There are various ways you could do this, for example a script or code, or a bulk update tool.
In this case, badPwdCount is a special property that is not replicated (i.e. it is different for each domain controller) and so far as I can tell, there is no way to update it manually or by script, however, I think I have a solution for you.
You should be able to easily trigger a single failed login for every user in active directory against each domain controller, causing the value to be incremented.
Since you tagged your post with C#, here is some C# code that will do the trick for you:
using System.DirectoryServices.AccountManagement;
using System.DirectoryServices.ActiveDirectory;
...
using (Domain domain = Domain.GetComputerDomain())
{
foreach (DomainController domainController in domain.DomainControllers)
{
using (PrincipalContext context = new PrincipalContext(ContextType.Domain, domainController.Name))
using (UserPrincipal userPrincipal = new UserPrincipal(context))
using (PrincipalSearcher searcher = new PrincipalSearcher(userPrincipal))
using (PrincipalSearchResult<Principal> results = searcher.FindAll())
{
foreach (UserPrincipal user in results.OfType<UserPrincipal>())
{
context.ValidateCredentials(user.SamAccountName, "THEREISNOWAYTHISISTHECORRECTPASSWORD");
}
}
}
}
PS. If this screws up your AD I take no responsibility for it!

Related

Realm sync permissions for flexibly-named partitions based on user id

I'm new to Realm Sync (and Realm). I'm trying to convert a REST / SQL Server system to Realm Sync (to avoid having to write my own local-device caching code).
I got a simple configuration working, with a single API-key user and the null partition, read and write permissions just set to true.
But for my more complex application, I want smaller sub-partitions to reduce the amount of data that needs to be cached on local devices, and I want the sub-partitions to be able to be created dynamically by the client. Ideally, I would like to allow an API-key user to connect to any partition whose name starts with their user id (or some other known string, e.g. the profile name). But I can't find a way to get a "starts with" condition into the permissions.
My best attempt was to try setting Read and Write sync permissions to:
{
"%%partition": {
"$regex": "^%%user.id"
}
}
but my client just fails to connect, saying Permission denied (BIND, REFRESH). (Yes, I tried using "$regex": /^%%user.id/ but the Realm UI rejected that syntax.) The Realm Sync log says "user does not have permission to sync on partition (ProtocolErrorCode=206)".
As you can see in the log image, the partition name was equal to the user id for this test.
Is what I'm trying to do possible? If so, how do I set up the Sync Permissions to make it work?
This can be done using a function. If, like me, you're new to Realm Sync and not fluent in Javascript, don't worry - it turns out to be not too hard to do, after all. (Thanks Jay for encouraging me to try it!)
I followed the instructions on the Define a Function page to create my userCanAccessPartition function like this:
exports = function(partition){
return partition.startsWith(context.user.id);
};
Then I set my sync permissions to:
{
"%%true": {
"%function": {
"name": "userCanAccessPartition",
"arguments": ["%%partition"]
}
}
}

Manipulate Windows "Users" group without access to domain?

I have a C#/WPF application that is used to manipulate local users and groups on a system. We only care about local users and groups, regardless of whether the machine is joined to a domain or not. When we create a user in our application, I want to add the user to the "Users" group. Normally this works fine, but if the machine is domain-joined and NOT connected to the network (e.g. a laptop out of the office), I get "the network path is not found" errors when trying to add a local user to the "Users" group.
I think the reason is because the "Users" group contains domain users, as shown in this screenshot.
And this is essentially my code:
public static void AddUserToGroup(UserPrincipal oUserPrincipal, string groupName)
{
using (PrincipalContext pc = new PrincipalContext(ContextType.Machine))
{
GroupPrincipal group = GroupPrincipal.FindByIdentity(pc, groupName);
if (group == null)
{
group = CreateLocalWindowsGroup(groupName);
}
if (!group.Members.Contains(oUserPrincipal)) // this line throws "network path not found" exception if the machine is domain joined, but can't contact the domain controller
group.Members.Add(oUserPrincipal);
group.Save();
}
}
I can't figure out how to approach this with the API, but it seems like it should be possible because I can add the exact same user to the same group manually with the "Local Users and Groups" tool with no issues, regardless of network connectivity. How can I get around this issue?
This is one reason I don't like using the AccountManagement namespace.
The GroupPrincipal.Members property returns a PrincipalCollection, which is just a collection of Principal objects. The actual type will be UserPrincipal or GroupPrincipal depending on what the actual member is.
But, those Principal classes, when they're created, load all the details for that object. So just the act of creating a UserPrincipal for a domain user triggers it to go out to the domain and get all the details for the user.
You're better off using DirectoryEntry directly, which is what the AccountManagement namespace uses in the background anyway. It gives you more control over what's actually happening.
var usersGroup = new DirectoryEntry($"WinNT://{Environment.MachineName}/{groupName}");
usersGroup.Invoke("Add", new object[] { $"WinNT://{Environment.MachineName}/{userName}" });
This assumes a userName variable with the name of the local user. If the user is already in the group, it will throw an exception, so you may want to catch that.
Besides actually working in this case, this will run faster since you're not wasting time collecting details for all the existing members when you have no intention of using any of that data.
Update: To read all the members of a local group, use .Invoke("Members"). Then you have to create a new DirectoryEntry with each member in the collection. For example:
foreach (var member in (IEnumerable) usersGroup.Invoke("Members")) {
using (var memberDe = new DirectoryEntry(member)) {
Console.WriteLine(memberDe.Name);
}
}
The DirectoryEntry class is really a wrapper around the Windows native ADSI Interfaces. For a group, the underlying object will really be IADsGroup. When you call .Invoke on a DirectoryEntry object, that lets you call the IADsGroup methods (you'll see the Members method listed in the documentation there). All of the object-specific classes like IADsGroup and IADsUser all inherit from IADs, so the methods from that are usable too.
This only applies to local groups. With Active Directory groups, you don't have to resort to using the IADs methods.

How to set PermittedWorkstations to an user using UserPrincipal in C#

I have a system that creates, in realtime, an user in AD and prints a tag for credentials. That user is used by a person to navigate in the internet.
Everything was ok until now. That user cannot log on company's computer, only on the proxy servers.
In class UserPrincipal, we have the "PermittedWorkstations" property, but it is readonly.
Is there a way to set the PermittedWorkstations (or set the computers restrictions os the users, adding the computers that he is able to logon - like this image http://i.stack.imgur.com/tu2Kp.png)?
As DeFirmo already mentioned: it is unlikely that the problem will be solved by setting the "Permitted Workstation", but nevertheless:
Use direct ADSI commands, the following works for me:
using System.DirectoryServices;
....
using (DirectoryEntry de = new DirectoryEntry("LDAP://CN=NewlyCreatedUser,CN=Users,DC=ad,DC=local"))
{
de.Properties["userWorkstations"].Add("WS1,WS2");
de.CommitChanges();
}
You must use the Add method of PermittedWorkstations Of User.Principal
User.PermittedWorkstations.Add("SampleWorkstation"));

C# MVC4 windows username and cookie issues

First, I'm sad to say I'm not sure whether this code should be in the _Layout.cshtml or somewhere in the controller. It needs to run on all pages, so I've put it in the _Layout.cshtml page.
This is for an intranet web app. What I'm attempting to do is this: if a cookie (holding the user's userid) is not found, get the windows username, run it through a class that will go into the database and get the corresponding user's username, and - if we get a user id back - make a cookie containing it. Doesn't sound too hard, but one line in particular, and various incarnations of it, is refusing to be supportive. Here's the code as a whole.
if(!Context.Response.Cookies.AllKeys.Contains("userid")){
var winuser = System.Web.HttpContext.Current.User.Identity.Name;
var winuserid = myprojectname.Models.MyIntranetDataContext.getUserId(winuser).UserID();
if (winuserid == null) {
Response.Redirect("/someotherpage");
} else {
HttpCookie cookieuser = new HttpCookie("userid");
DateTime now = DateTime.Now;
cookieuser.Value = winuserid;
cookieuser.Expires = now.AddMonths(1);
Response.Cookies.Add(cookieuser);
}
}
Line 2 - var winuser... - appears to be the problem. In this current incarnation, I'm getting a build error: An object reference is required for the non-static field, method, or property 'myprojectname.Models.MyIntranetDataContext.getUserId(string)'
It doesn't like it when I add a .ToString to it either.
I've tried making winuser this as well:
Page.User.Identity.Name;
That gave no build errors. When I attempt to Start Debugging, she blows up with this beauty of an error: 'Cannot perform runtime binding on a null reference'
Once I get the windows username, all will be well.
Really, this isn't about cookies, or even mvc to much of an extent (except maybe guidance on where to put this code - the _Layout.cshtml?). Really it's about getting the windows username, which I seem unable to do. Thanks in advance for any assistance you are able to provide.
Note, the above names aren't actual - just for example only.
If they are on the domain, couldn't you use something like the following to retrieve that information?
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
WindowsPrincipal principal = (WindowsPrincipal)Thread.CurrentPrincipal;
WindowsIdentity identity = (WindowsIdentity)principal.Identity;
String userName= principal.Identity.Name;

How do I detect if my program runs in an Active Directory environment?

How do I detect if my program runs in an Active Directory environment?
I'm using C# and .Net 2.0
Try getting Environment.UserDomainName and comparing it to Environment.MachineName. If the two are the same then it's likely that the user does not have a domain. If they are not the same then the user is logged into a domain which must have a directory server.
This code will check if the Computer itself is a member of a domain
using System.DirectoryServices.ActiveDirectory;
bool isDomain = false;
try
{
Domain.GetComputerDomain();
isDomain = true;
}
catch (ActiveDirectoryObjectNotFoundException)
{
}
However the computer can be in a domain, but the currently logged in user may be a local user account. If you want to check for this use the Domain.GetCurrentDomain() function
One way might be to query the LOGONSERVER environmental variable. That'll give the server name of your AD controller... Which, as far as I know, will be blank (or match current workstation? Not sure) if it isn't currently logged into a domain.
Example Usage:
string ADServer = Environment.GetEnvironmentVariable("LOGONSERVER");
I found something that works:
using System.Net.NetworkInformation;
IPGlobalProperties.GetIPGlobalProperties().DomainName;
Works with a local user and a domain user.
From http://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentry.path.aspx
To bind to the current domain using LDAP, use the path "LDAP://RootDSE", then get the default naming context and rebind the entry.
So without a domain the binding to "LDAP://RootDSE" should either fail or return nothing. I didn't try it for myself.
use System.DirectoryServices; // add reference to system.directoryservices.dll
...
DirectoryEntry ent = new DirectoryEntry("LDAP://RootDSE");
String str = ent.Properties["defaultNamingContext"][0];
DirectoryEntry domain = new DirectoryEntry("LDAP://" + str);
This is definitely a cleaner way of checking for an Active Directory than relying on an environment variable (which the user could delete or add to spoof the program).

Categories