I need to implement authentication using Active Directory for my Winforms application.
I know that it is possible to get current Windows user credential like this:
AppDomain currentDomain = AppDomain.CurrentDomain;
// Set the principal policy to WindowsPrincipal.
currentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
But I need that system asks user to either:
select logged in credentials => this would be possible with PrincipalPolicy.WindowsPrincipal I guess
choose to enter different credentials in Windows login form (not in my application) => how can I get this working?
P.S. I know it is possible to send username/password like it is described here:
Validate a username and password against Active Directory?
but I don't want to have user credentials going through my application because off security risks
I found out also this project in CodeProject.com on how to authenticate against Active Directory using LDAP, but this also requires entering user credentials in my application...
I know that there is also Active Directory Federated Services, but as far as I know it is for Web based authentication...
Any solutions for desktop authentication against Active Directory?
Here is a method I wrote in VB.NET in the past, and converted to C# for you.
use System.DirectoryServices namespace (you need to make a reference) in order to check credentials of user against DC(LDAP) server in your network (windows network of course).
clarification: this method was requested by our company security department for circumstances that user need to log in to company software from a colleague computer. i recommend (for security reasons) to require from your IT department to add all users to a specific active directory group and check also if the user is a member of this AD group, also record every local ip address that connects to the software.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.DirectoryServices;
namespace WindowsFormsApplication15
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
if (IsAuthenticated("YOUR DOMAIN", "USERNAME", "PASSWORD") == false)
{
// not exist in active directory!
}
else
{
// user is exist in active directory
}
}
private string _path { get; set; }
private string _filterAttribute { get; set; }
public bool IsAuthenticated(string domain, string username, string pwd)
{
string domainAndUsername = domain + "\\" + username;
DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
try
{
object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + username + ")";
search.PropertiesToLoad.Add("cn");
SearchResult result = search.FindOne();
if ((result == null))
{
return false;
}
_path = result.Path;
_filterAttribute = result.Properties["cn"][0].ToString();
}
catch (Exception ex)
{
return false;
}
return true;
}
}
}
Related
I am developing a web application that authenticate the user against an Active Directory Server. Now if I run my code from the development PC under the domain of that AD server, my code is running smoothly. We need to run the code from a totally different network using VPN and here the development PC is not into that AD. I am getting following error while trying to access the AD server.
The specified domain either does not exist or could not be contacted.
My VPN is working fine. I could access remote desktops using this VPN. I know a little tweak is required to solve the problem but could not find it. I went through following links but could not find any solution.
Domain Authentication from .NET Client over VPN
How do I get the Current User identity for a VPN user in a Windows forms app?
Following is my settings in web.config
<appSettings>
<add key="LDAPPath" value="LDAP://DC=MYSERVER,DC=COM" />
<add key="ADGroupName" value="Analyst"/>
</appSettings>
and here is my code
public class LdapAuthentication
{
private string _path;
private string _filterAttribute;
public LdapAuthentication()
{
_path = System.Configuration.ConfigurationManager.AppSettings["LDAPPath"].ToString();
}
public bool IsAuthenticated(string username, string pwd)
{
try
{
DirectoryEntry entry = new DirectoryEntry(_path, username, pwd);
entry.Path = _path;
entry.Username = username;
entry.Password = pwd;
// Bind to the native AdsObject to force authentication.
object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + username + ")";
search.PropertiesToLoad.Add("cn");
SearchResult result = search.FindOne();
if (null == result)
{
return false;
}
// Update the new path to the user in the directory.
_path = result.Path;
_filterAttribute = (string)result.Properties["cn"][0];
}
catch (Exception ex)
{
throw new Exception("Error authenticating user. " + ex.Message);
}
return true;
}
}
Any help would be appreciated. Thank you.
I had a similar, though simpler problem. I had success in using the following code:
private bool DoLogin(string userName, string password)
{
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, "DomainName.com")) {
bool isValid = pc.ValidateCredentials(userName, password);
if (isValid) {
// authenticated
...
return true;
}
else {
// invalid credentials
...
return false;
}
}
}
Using the ".com" at the end of the domain name was important to get it working for me. Without it I got the same symptoms you describe.
I've just been grappling with this for a couple of hours. No problems when on the network, lots of problems when connecting via VPN. It seems that when you are connecting over a VPN, the 'connection string' for DirectoryEntry has to be a lot more precise. I finally got it to work with an LDAP address/connection string like this:
LDAP://ip_of_primary_domain_controller/fully qualified path of the container object where the binding user is located
So for example something like this worked for me:
DirectoryEntry directoryEntry = new DirectoryEntry(
"LDAP://192.168.0.20/OU=Service Accounts,OU=Admin Accounts,DC=myserver,DC=com",
"username#myserver.com", "password");
... where "username#myserver.com" is located in OU=Service Accounts,OU=Admin Accounts,DC=myserver,DC=com. If you use SysInternals ADExplorer (or similar) to search for your username, it will tell you the correct fully qualified path for the container.
See here for a long answer about exactly whats should be in the 'connection string': https://serverfault.com/a/130556
I want to retrieve the login name of a user from Active Directory.
For example the name is 'Jan Van der Linden'
After giving this name as parameter I must get his login name in return for example jvdlinden
Since you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
Basically, you can define a domain context and easily find users and/or groups in AD:
public string GetLoginName(string userName)
{
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// find user by name
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, userName);
if(user != null)
return user.SamAccountName;
else
return string.Empty;
}
The new S.DS.AM makes it really easy to play around with users and groups in AD:
this actually does almost the opposite but can be a starting point to check and modify as needed:
Finding a User in Active Directory with the Login Name
using .net library you can use the following code to get username or any info from active directory
using System.Management;
using System.Management.Instrumentation;
using System.Runtime.InteropServices;
using System.DirectoryServices;
ManagementObjectSearcher Usersearcher = new ManagementObjectSearcher("Select * From Win32_ComputerSystem Where (Name LIKE 'ws%' or Name LIKE 'it%')");
ManagementObjectCollection Usercollection = Usersearcher.Get();
string[] sep = { "\\" };
string[] UserNameDomain = Usercollection.Cast<ManagementBaseObject>().First()["UserName"].ToString().Split(sep, StringSplitOptions.None);
i add "Select * From Win32_ComputerSystem Where (Name LIKE 'ws%' or Name LIKE 'it%')"
this will get the user name by the full name
hope this could help you
Check this link has needed code snipple
Validate AD-LDAP USer
using (DirectoryEntry entry = new DirectoryEntry())
{
entry.Username = "DOMAIN\\LOGINNAME";
entry.Password = "PASSWORD";
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.Filter = "(objectclass=user)";
try
{
searcher.FindOne();
{
//Add Your Code if user Found..
}
}
catch (COMException ex)
{
if (ex.ErrorCode == -2147023570)
{
ex.Message.ToString();
// Login or password is incorrect
}
}
}
Without Identity:
private string GetLogonFromDisplayName(string displayName)
{
var search = new DirectorySearcher(string.Format("(&(displayname={0})(objectCategory=user))", displayName));
search.PropertiesToLoad.Add("sAMAccountName");
SearchResult result = search.FindOne();
if (result != null)
{
var logonNameResults = result.Properties["sAMAccountName"];
if (logonNameResults == null || logonNameResults.Count == 0)
{
return null;
}
return logonNameResults[0].ToString();
}
return null;
}
First off, I don't have any C# skills or experience. A friend of mine took a couple classes in college and was able to give me what I've got so far in this C# program.
I asked my friend to create a program that would look at WMI for the current logged on user's full name, then look at the RegisteredOwner value. If the full name is the same as the RegisteredOwner then the program quits (all silent), if the full name is different than the RegisteredOwner then the program would launch a form with some text and a yes/no option. If the user clicks yes, then the program sets the RegisteredOwner value to the logged on users full name, and if they click no, the program quits.
He delivered exactly what I asked for; however, it only runs if ran by a user with local admin rights and unfortunately, in my environment, no user is a local admin on their machine. When I presented the issue to him, he wasn't sure what he could do to resolve the problem, and after looking into this all day, I'm afraid there isn't much that can be done to resolve the issue and allow the program to be launched using the local users permissions.
So my question for you is do you know of a different way we could go with this program that would allow it to be run by a user without local admin rights? I would like to have the executable stored somewhere locally on the PC and then it in the startup items list of having something in the startup items list launch it. Maybe there's a way I can use a executable that works with non-local admin rights and then have it work with a windows service that's running under the System account?
When ran by a non local admin, nothing happens when you launch the script.
Below is the code.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Management;
using System.Security.Principal;
using Microsoft.Win32;
using System.Threading;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
bool compare;
public Form1()
{
InitializeComponent();
if (PreLoad())
compare = true;
else
{
this.Text = GetUser();
compare = false;
}
}
private bool PreLoad()
{
string temp = GetCaption(GetUser());
RegistryKey regKey1 = Registry.LocalMachine.CreateSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
string keyString = regKey1.GetValue("RegisteredOwner").ToString();
if (temp == keyString)
return true;
else
return false;
}
private void btnYes_Click(object sender, EventArgs e)
{
MessageBox.Show("Are you sure?", "Confirmation", MessageBoxButtons.OKCancel);
string temp = GetCaption(GetUser());
DoRegistryEdit(temp);
lblShowAll.Text = "-Successfully registered the machine to: " + temp + " -";
//Refreshes the screen so that the status message displays
this.Refresh();
Thread.Sleep(5000);
this.Close();
}
private void btnNo_Click(object sender, EventArgs e)
{
//MessageBox.Show("Better change computers then!");
this.Close();
}
private void Form1_Load(object sender, EventArgs e)
{
if (compare)
this.Close();
}
public string GetCaption(string userName)
{
String QueryStringTemp = "Select * from Win32_NetworkLoginProfile where Caption = '" + userName + "'";
System.Management.ObjectQuery oQuery = new ObjectQuery(QueryStringTemp);
ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(oQuery);
ManagementObjectCollection oReturnCollection = oSearcher.Get();
string capturedResults = "";
foreach (ManagementObject oReturn in oReturnCollection)
{
capturedResults += oReturn["FullName"].ToString();
}
return capturedResults;
}
public string GetUser()
{
System.Management.ObjectQuery oQuery = new ObjectQuery("Select * from Win32_ComputerSystem");
ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(oQuery);
ManagementObjectCollection oReturnCollection = oSearcher.Get();
string capturedResults = "";
foreach (ManagementObject oReturn in oReturnCollection)
{
capturedResults += oReturn["UserName"].ToString();
}
int hold = capturedResults.IndexOf("\\");
capturedResults = capturedResults.Substring(hold + 1);
return capturedResults;
}
public void DoRegistryEdit(string name)
{
RegistryKey masterKey = Registry.LocalMachine.CreateSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
if (masterKey == null)
MessageBox.Show("Null Master Key!");
else
{
try
{
masterKey.SetValue("RegisteredOwner", name);
}
catch (Exception ex)
{
MessageBox.Show("Uh OH!" + ex);
}
finally
{
masterKey.Close();
}
}
}
}
}
Any advice and suggestions would be appreciated!
WMI is the killer here. I suppose the whole "Management" part of WMI forces it to run in the admin space.
I found this resource on the Web:
http://skysigal.xact-solutions.com/Blog/tabid/427/EntryId/417/C-Compact-Framework-Getting-the-Registered-Owner.aspx
I tested it out to see that it worked respectably well on my Win7 X86 box. Judging from other sources on the web, this should be good for most recent versions of Windows, including several mobile editions.
Good luck!
How do I Retrieve a list of computers in a FOLDER in a domain.
lets say i have maydomain.dom as my domain
and I have a folder containing some computers.
Following code to list out computers in your domain and in active directory, this may help you
//ActiveDirectorySearch1
//Displays all computer names in an Active Directory
using System;
using System.DirectoryServices;
namespace ActiveDirectorySearch1
{
class Class1
{
static void Main (string[] args)
{
//Note : microsoft is the name of my domain for testing purposes.
DirectoryEntry entry = new DirectoryEntry(LDAP://microsoft);
DirectorySearcher mySearcher = new DirectorySearcher(entry);
mySearcher.Filter = ("(objectClass=computer)");
Console.WriteLine("Listing of computers in the Active Directory");
Console.WriteLine("============================================"); foreach(SearchResult resEnt in mySearcher.FindAll())
{
Console.WriteLine(resEnt.GetDirectoryEntry().Name.ToString()); }
Console.WriteLine("=========== End of Listing =============");
}
}
}
http://www.c-sharpcorner.com/UploadFile/jodonnell/ListAllComps07022005005654AM/ListAllComps.aspx
It's not exactly the same but maybe you can use the same principle as in this blog post: Find users in Active Directory folder using GUID
I am doing a C# application targeting WinXP, Vista, and 7 Operating Systems.
One feature is, I can Add, Remove, Modify the Group set to a user programmatically.
Can I ask for help how to make this happen?
Will it be possible to do this in WMI? My codes mainly using WMI to get the users..
Currently am using Windows7
I am trying to test this code
DirectoryEntry localMachine = new DirectoryEntry("WinNT://" + Environment.MachineName + ",Computer");
localMachine.Properties["member"].Add("Chevi");
localMachine.CommitChanges();
localMachine.Close();
and it's spitting this error
The directory property cannot be found in the cache.
I tried enumerating the Property collection and I got this
OperatingSystem
OperatingSystemVersion
Owner
Division
ProcessorCount
Processor
Name
If you're using local groups, you can do this by calling the system net command. For example, to add a user to a group, you'd call:
net localgroup MyGroup /add SomeUser
Type net help localgroup at a command prompt for more info.
You can also do this using WMI. This is VBScript but can be adapted to .NET or your preferred programming toolkit:
Dim oComputer
Computer = "computername"
Groupname = "Administrators"
ObjectToAdd = "Administrator"
' Bind to the computer.
Set oComputer = GetObject("WinNT://" & Computer & ",computer")
' get group object
Dim oGroup
Set oGroup = oComputer.GetObject("group", GroupName)
' Add the user object to the group.
oGroup.Add "WinNT://" & Computer & "/" & ObjectToAdd
Credit: Matt Hickman, http://www.tech-archive.net/Archive/WinXP/microsoft.public.windowsxp.wmi/2004-04/0007.html
I have also developed one windows application on Visual Studio 2010, using C#. This is a working version of the program, which will add an existing user to a particular group.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.DirectoryServices;
namespace Utility_Add_User_To_Group {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void btn_Add_Click(object sender, EventArgs e) {
string usr, grp;
usr = txt_UserName.Text;
grp = txt_GroupName.Text;
add(usr, grp);
groupBox2.Visible=true;
}
private void add(string usr, string grp) {
bool flagUsr, flagGrp;
try {
DirectoryEntry AD = new DirectoryEntry("WinNT://" +Environment.MachineName + ",computer");
DirectoryEntry group, user;
group = AD.Children.Find(grp, "group");
user = AD.Children.Find(usr, "user");
if (user != null) {
label3.Text += "User Name Exists!!!";
flagUsr = true;
} else {
label3.Text += "Sorry, No Such User Name Found!!!";
flagUsr = false;
}
if (group != null) {
label4.Text += "Group Exists!!!";
flagGrp = true;
} else {
label4.Text += "Sorry, Group Does Not Exists!!!";
flagGrp= false;
}
if(flagGrp == true && flagUsr == true) {
group.Invoke("Add", new object[] { user.Path.ToString() });
label5.Text += "Congratulations!!! User has been added to the group";
} else {
label5.Text += "Error Happened!!! User could not be added to the group!!!";
}
} catch (Exception e) {
label6.Text +=e.Message.ToString();
label6.Visible= true;
}
}
private void btn_Clear_Click(object sender, EventArgs e) {
normal();
}
private void normal() {
txt_GroupName.Text="";
txt_UserName.Text="";
txt_UserName.Focus();
groupBox2.Visible=false;
}
}
}
NetUserAdd to create a user
NetGroupAdd to create a group
NetGroupAddUser to add a user to a group
NetGroupDelUser to remove an user form a group
NetLocalGroupAdd to create a local group
NetLocalGroupAddMembers to add users to a local group
etc etc