I need to get the default credentials from the current logged in user and pass them to my Windows service to check the internet connectivity and upload files to Dropbox. I don't want the user to confirm his/her credentials every time. So how is it possible to get the currently active user's proxy settings + username + password?
This is my Code to retrieve the current User
private static string UserName()
{
string userName = null;
ManagementScope ms = new ManagementScope();
ObjectQuery qry = new ObjectQuery("Select * from Win32_ComputerSystem");
ManagementObjectSearcher search = new ManagementObjectSearcher(ms, qry);
ManagementObjectCollection result = search.Get();
foreach (ManagementObject rec in result)
userName = rec["UserName"] as string;
string[] buffer = userName.Split('\\');
return buffer[1];
}
and this Code is in use for getting the WindowsIdentity:
private static WindowsIdentity GetWindowsIdentity()
{
string userName = UserName();
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
using (UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, userName) ??
UserPrincipal.FindByIdentity(UserPrincipal.Current.Context, IdentityType.UserPrincipalName, userName))
{
return user == null
? null
: new WindowsIdentity(user.UserPrincipalName);
}
}
This is what I want to do via service:
System.Diagnostics.Debugger.Launch(); //launch debugger when service runs
WindowsImpersonationContext impersonationContext;
impersonationContext = GetWindowsIdentity().Impersonate();
//try to use the currently logged in user's credentials
WebRequest.DefaultWebProxy.Credentials = CredentialCache.DefaultCredentials;
try
{
WebClient wClient = new WebClient();
string xx = wClient.DownloadString(#"https://dropbox.com");
if (xx == "") //just download the sourcecode to chek if this page is available
return false;
}
catch (Exception ex)
{
using (StreamWriter sw = new StreamWriter(#"C:\Users\Testuser\Desktop\DownloadError.txt", true))
{
sw.WriteLine(ex.ToString());
}
return false;
}
impersonationContext.Undo();
The errorLog always shows, that the service was unable to connect. When i run this from an console or WinForms applications, it works without any problems.
Errorlog:
System.Net.WebException: Unable to connect to the remote server ---> System.Net.Sockets.SocketException: No connection could be made because the target machine actively refused it 172.217.17.238:80
at System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddress socketAddress)
at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Socket s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state, IAsyncResult asyncResult, Exception& exception)
--- End of inner exception stack trace ---
at System.Net.WebClient.DownloadDataInternal(Uri address, WebRequest& request)
at System.Net.WebClient.DownloadString(Uri address)
at System.Net.WebClient.DownloadString(String address)
at BeeShotService.Sender.TrySend(String s) in C:\Users\Testuser\Documents\Visual Studio 2017\Projects\Project_BackupUploder\UploaderService\Sender.cs:Zeile 70.
I've managed to solve the problem for simple webRequests with an additional Class:
class GetProxySettings
{
private const int INTERNET_OPTION_PROXY = 38;
[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool InternetQueryOption(IntPtr hInternet, uint dwOption, IntPtr lpBuffer, ref int lpdwBufferLength);
/// <summary>
/// Access types supported by InternetOpen function.
/// </summary>
private enum InternetOpenType
{
INTERNET_OPEN_TYPE_PRECONFIG = 0,
INTERNET_OPEN_TYPE_DIRECT = 1,
INTERNET_OPEN_TYPE_PROXY = 3,
}
/// <summary>
/// Contains information that is supplied with the INTERNET_OPTION_PROXY value
/// to get or set proxy information on a handle obtained from a call to
/// the InternetOpen function.
/// </summary>
private struct INTERNET_PROXY_INFO
{
public InternetOpenType DwAccessType
{
get; set;
}
public string LpszProxy
{
get; set;
}
public string LpszProxyBypass
{
get; set;
}
}
internal string[] GetV(WindowsIdentity windowsIdentity)
{
string[] settings = new string[] { "", "" };
using (windowsIdentity.Impersonate())
{
int bufferLength = 0;
IntPtr buffer = IntPtr.Zero;
InternetQueryOption(IntPtr.Zero, INTERNET_OPTION_PROXY, IntPtr.Zero,
ref bufferLength);
try
{
buffer = Marshal.AllocHGlobal(bufferLength);
if (InternetQueryOption(IntPtr.Zero, INTERNET_OPTION_PROXY, buffer,
ref bufferLength))
{
INTERNET_PROXY_INFO proxyInfo = (INTERNET_PROXY_INFO)
// Converting structure to IntPtr.
Marshal.PtrToStructure(buffer, typeof(INTERNET_PROXY_INFO));
// Getting the proxy details.
settings[0] = proxyInfo.LpszProxy.Split(':')[0];
settings[1] = proxyInfo.LpszProxy.Split(':')[1];
}
}
catch { }
}
return settings;
}
}
You need to add the WindowsIdentity of the currently logged-on user and will return a string[] with [0] = proxystring & [1] = proxy port.
The following works well as a service:
WebClient wClient = new WebClient();
wClient.Proxy = new WebProxy( GetProxySettings.GetV(GetWindowsIdentity())[0],GetProxySettings.GetV(GetWindowsIdentity())[1]);
string xx = wClient.DownloadString(#"https://www.dropbox.com");
Related
I want to copy a file from computer A(with account myAccount#mydomain) to computer B(userB#computerB) over the network using c#.
I tried the standard
File.Copy(source,destination)
and tried starting a cmd process (from computer A) and call the copy method
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.UseShellExecute = false;
startInfo.Domain = "computerB"; //ofcourse it wont work since its outside the local domain of A
startInfo.FileName = "cmd.exe";
startInfo.Arguments = #"/C COPY \\computerA\Path\File1.txt \\computerB\Path$ ";
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();
//It will exit the user name or password is incorrect
I tried also to use PSexec to impersonate computerB :
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new
System.Diagnostics.ProcessStartInfo();
startInfo.UseShellExecute = false;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = #"psexec \\computerB -u computerB\userB -p userBPassword cmd /c COPY \\computerA\Path\File1.txt \\computerB\Path$";
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();
//it will exit that the source file is unknown
To sum it up,computer A is able to see the source(itself) but not the destination(since computer B has only authorized local user).
computer B is able to see the destination(itself) but not the source(since computer A is outside its domain and its not shared over the network).
Is there a workaround for this issue?
It sounds like this is a fairly simple authentication problem of the type that pops up whenever the current user doesn't have rights on a share, both in a domain and out. The problem also arises when running under the system user and trying to access shares on other devices.
The solution is to open an authenticated connection to the target device using the WNetUseConnection API, perform your file operations then close the connection with a call to WNetCancelConnection2.
Here's some code I have used in the past for this:
class ConnectSMB : IDisposable
{
public string URI { get; private set; }
public bool Connected { get; private set; } = false;
public ConnectSMB(string uri, string username, string encPassword)
{
string pass = StringEncryption.Decode(encPassword);
string connResult = Native.ConnectToRemote(uri, username, pass);
if (connResult != null)
throw new Exception(connResult);
URI = uri;
Connected = true;
}
public void Dispose()
{
Close();
}
public void Close()
{
if (Connected)
{
Native.DisconnectRemote(URI);
URI = null;
Connected = false;
}
}
}
public class Native
{
#region Consts
const int RESOURCETYPE_DISK = 1;
const int CONNECT_UPDATE_PROFILE = 0x00000001;
#endregion
#region Errors
public enum ENetUseError
{
NoError = 0,
AccessDenied = 5,
AlreadyAssigned = 85,
BadDevice = 1200,
BadNetName = 67,
BadProvider = 1204,
Cancelled = 1223,
ExtendedError = 1208,
InvalidAddress = 487,
InvalidParameter = 87,
InvalidPassword = 1216,
MoreData = 234,
NoMoreItems = 259,
NoNetOrBadPath = 1203,
NoNetwork = 1222,
BadProfile = 1206,
CannotOpenProfile = 1205,
DeviceInUse = 2404,
NotConnected = 2250,
OpenFiles = 2401
}
#endregion
#region API methods
[DllImport("Mpr.dll")]
private static extern ENetUseError WNetUseConnection(
IntPtr hwndOwner,
NETRESOURCE lpNetResource,
string lpPassword,
string lpUserID,
int dwFlags,
string lpAccessName,
string lpBufferSize,
string lpResult
);
[DllImport("Mpr.dll")]
private static extern ENetUseError WNetCancelConnection2(
string lpName,
int dwFlags,
bool fForce
);
[StructLayout(LayoutKind.Sequential)]
private class NETRESOURCE
{
// Not used
public int dwScope = 0;
// Resource Type - disk or printer
public int dwType = RESOURCETYPE_DISK;
// Not used
public int dwDisplayType = 0;
// Not used
public int dwUsage = 0;
// Local Name - name of local device (optional, not used here)
public string lpLocalName = "";
// Remote Name - full path to remote share
public string lpRemoteName = "";
// Not used
public string lpComment = "";
// Not used
public string lpProvider = "";
}
#endregion
public static string ConnectToRemote(string remoteUNC, string username, string password)
{
NETRESOURCE nr = new NETRESOURCE
{
lpRemoteName = remoteUNC
};
ENetUseError ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);
if (ret == ENetUseError.NoError) return null;
return ret.ToString();
}
public static string DisconnectRemote(string remoteUNC)
{
ENetUseError ret = WNetCancelConnection2(remoteUNC, CONNECT_UPDATE_PROFILE, false);
if (ret == ENetUseError.NoError) return null;
return ret.ToString();
}
}
Create an instance of the ConnectSMB class before you do your remote file operations, then dispose (or close) it when you're done with the connection.
using (var smb = new ConnectSMB(#"\\computerB\Path", "userB", "userBPassword"))
{
File.Copy(source, destination);
}
I have a C# api hosted on domain A. This api receives files as form-data (I extract the file using HttpContext.Current.Request.Files). In this api, I need to implement a method to save the extracted file to a folder or shared folder in a remote machine that is in another domain say domain B (My company domain). I have the access credentials of the machine running in domain B.
I am using VS2015 for development. My target framework is .NET 4.5.2.
I have tried using WNetUseConnection(Mpr.dll) method to connect. Using this method, I am able to successfully connect and save the files when connecting from the same domain as the remote machine. But when i tried to connect from outside domain B(the remote machine's domain), I am unable to do so. I am getting error 53. I researched about this error and found that it is "network path was not found".
I feel like I'll need all the help I can get. Need to get this done in 2 days.
Given below is my function call to connect to the remote machine in domain B.
RemoteConnect.connectToRemote("\\xxx.xxx.xxx.xxx\C$", #"domain-B\username", "password");
I have given below the code I have in my connection class (RemoteConnect.cs) for using WNetUseConnection.
[DllImport("Mpr.dll")]
private static extern int WNetUseConnection(
IntPtr hwndOwner,
NETRESOURCE lpNetResource,
string lpPassword,
string lpUserID,
int dwFlags,
string lpAccessName,
string lpBufferSize,
string lpResult
);
[DllImport("Mpr.dll")]
private static extern int WNetCancelConnection2(
string lpName,
int dwFlags,
bool fForce
);
[StructLayout(LayoutKind.Sequential)]
private class NETRESOURCE
{
public int dwScope = 0;
public int dwType = 0;
public int dwDisplayType = 0;
public int dwUsage = 0;
public string lpLocalName = "";
public string lpRemoteName = "";
public string lpComment = "";
public string lpProvider = "";
}
public static string connectToRemote(string remoteUNC, string username, string password)
{
return connectToRemote(remoteUNC, username, password, false);
}
public static string connectToRemote(string remoteUNC, string username, string password, bool promptUser)
{
NETRESOURCE nr = new NETRESOURCE();
nr.dwType = RESOURCETYPE_DISK;
nr.lpRemoteName = remoteUNC;
// nr.lpLocalName = "F:";
int ret;
if (promptUser)
ret = WNetUseConnection(IntPtr.Zero, nr, "", "", CONNECT_INTERACTIVE | CONNECT_PROMPT, null, null, null);
else
ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);
if (ret == NO_ERROR) return null;
return getErrorForNumber(ret);
}
public static string disconnectRemote(string remoteUNC)
{
int ret = WNetCancelConnection2(remoteUNC, CONNECT_UPDATE_PROFILE, false);
if (ret == NO_ERROR) return null;
return getErrorForNumber(ret);
}
Have you tried NetworkCredential? Maybe I'm thinking too simple and am not understanding your problem..
This has always worked for my needs:
NetworkCredential networkCredential = new NetworkCredential("username", "password", "domainname");
CredentialCache credentialCache = new CredentialCache();
credentialCache.Add(new Uri(#"\\networkshare\"), "Basic", networkCredential);
//Proceed with whatever file-io you need using the normal .NET file io. Example:
string[] folders = System.IO.Directory.GetDirectories(#"\\networkshare\Users\Userimages");
I am trying to determinate if my program was launched by an Administrator with admin rights or by a normal user with Administrator rights using the following code:
private void Form1_Load(object sender, EventArgs e)
{
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);
bool isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);
if (isAdmin)
{
ini();
MessageBox.Show("The user is an admin with admin rights");
}
else
{
MessageBox.Show("Error The user is a normal user with admin rights", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
Application.Exit();
}
}
But when I test this code using a normal user account with admin rights, it fails, telling me The user is an admin with admin rights.
I am using .Net 3.5 and the code needs to be compatible with Windows Vista and up.
Can anybody give me some idea to solve this, please? Thank you!!!
To clarify a little bit more, another way to see this problem is this: How can I determinate if my program (running with admin rights by the admin) is running inside the standard user account context.
mmm you need to know is if the user connected to that computer has Admin Rights but an user that is part of a domain control ? (active Directory?) not the BuiltIn ones ? please Clarify., take a look to this post https://social.msdn.microsoft.com/Forums/vstudio/en-US/c2166023-fcfc-4c3c-ba8f-200977971c5d/how-to-check-if-given-user-have-admin-rights-in-c
There are more variables to account for, for instance Operating System. Which version of Microsoft Window's are you utilizing? Remember, the User Access Control (UAC) was introduced in a later version, which can impact the code. Another massive impactor will be a Domain Policy which is regulated through Active Directory.
To avoid those constraints, I would do the following:
private static bool IsElevated()
{
var user = WindowsIdentity.GetCurrent();
var role = WindowsPrincipal(user);
if(Environment.OSVersion.Platform == PlatformID.Win32NT || Environment.OSVersion.Version.Major > 6)
if(user != null)
if(role.IsInRole(WindowsBuiltInRole.Administrator))
return true;
return false;
}
This was a quick recreation, but you'll want to test the code to ensure it correctly queries the Operating System and User.
I did solved it!!!
Explanation
I realized that what I needed was the session ID (the enviroment) to get somehow the Logged in Username
(owner of the context).
Getting the session Id was really easy:
int session = System.Diagnostics.Process.GetCurrentProcess().SessionId;
By this time, my goal was getting the username linked to this session, then I found this
Get Windows user name from SessionID
Now, with the username I thought it was really easy to check if this username belongs
to an Administrator group. I was wrong. The easiest way that I found requeried to use wmi and pinvoke.
The main problem was, in order to make the queries, I must know the name of the administrators group in the current OS, that is language-dependent.
The solution was this:
http://www.pinvoke.net/default.aspx/advapi32.lookupaccountsid
I tweaked the code, put all pieces together and that was it.
Summarizing, I found the sessionid, found the username linked to session, found the OS Administrators group name, and did some Wmi queries to check if the user is an Administrator.
This solution works fine for me, but I must say that runs slow.
If anybody can improve this code, or if it is useful to anybody else, I left it here.
Just remember to add a reference to System.Management; I used Visual Studio C# Express 2010. The project was a Windows Forms Application.
Happy coding!!!
//Make sure to add a reference to System.Management;
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 System.Collections;
using System.Runtime.InteropServices;
namespace RealUSer
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//Getting the session id
int session = System.Diagnostics.Process.GetCurrentProcess().SessionId;
//Getting the username related to the session id
string user = GetUsernameBySessionId(session, false);
try
{
//Cheching if the user belongs to the local admin group using wmi
if (CheckAdminRights(user))
{
MessageBox.Show("The logged in User "+user+" is an Admin");
}
else
{
MessageBox.Show("The logged in User " + user + " is not an Admin");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
/// <summary>
/// This method checks if the context user, belongs to the local administrators group
/// </summary>
/// <param name="username"></param>
/// <returns></returns>
public bool CheckAdminRights(string username)
{
bool result = false;
List<string> admins = new List<string>();
//SelectQuery query = new SelectQuery("Select AccountType from Win32_UserAccount where Name=\"" + username + "\"");
SelectQuery query = new SelectQuery("SELECT * FROM win32_group WHERE Name=\"" + getAdministratorGroupName() + "\"");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
ManagementObjectCollection OC = searcher.Get();
IEnumerator enumerator = OC.GetEnumerator();
enumerator.MoveNext();
ManagementObject O = (ManagementObject)enumerator.Current;
ManagementObjectCollection adminusers = O.GetRelated("Win32_UserAccount");
foreach (ManagementObject envVar in adminusers)
{
admins.Add(envVar["Name"].ToString());
//Console.WriteLine("Username : {0}", envVar["Name"]);
//foreach (PropertyData pd in envVar.Properties)
// Console.WriteLine(string.Format(" {0,20}: {1}", pd.Name, pd.Value));
}
if (admins.Contains(username))
result = true;
return result;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///This code will find the administrators group name, independent of the OS language using the LookupAccountSid function with the BUILTIN\Administrators sid
#region
const int NO_ERROR = 0;
const int ERROR_INSUFFICIENT_BUFFER = 122;
enum SID_NAME_USE
{
SidTypeUser = 1,
SidTypeGroup,
SidTypeDomain,
SidTypeAlias,
SidTypeWellKnownGroup,
SidTypeDeletedAccount,
SidTypeInvalid,
SidTypeUnknown,
SidTypeComputer
}
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool LookupAccountSid(
string lpSystemName,
[MarshalAs(UnmanagedType.LPArray)] byte[] Sid,
StringBuilder lpName,
ref uint cchName,
StringBuilder ReferencedDomainName,
ref uint cchReferencedDomainName,
out SID_NAME_USE peUse);
public string getAdministratorGroupName()
{
String result = "";
StringBuilder name = new StringBuilder();
uint cchName = (uint)name.Capacity;
StringBuilder referencedDomainName = new StringBuilder();
uint cchReferencedDomainName = (uint)referencedDomainName.Capacity;
SID_NAME_USE sidUse;
// Sid for BUILTIN\Administrators
byte[] Sid = new byte[] { 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2 };
int err = NO_ERROR;
if (!LookupAccountSid(null, Sid, name, ref cchName, referencedDomainName, ref cchReferencedDomainName, out sidUse))
{
err = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
if (err == ERROR_INSUFFICIENT_BUFFER)
{
name.EnsureCapacity((int)cchName);
referencedDomainName.EnsureCapacity((int)cchReferencedDomainName);
err = NO_ERROR;
if (!LookupAccountSid(null, Sid, name, ref cchName, referencedDomainName, ref cchReferencedDomainName, out sidUse))
err = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
}
}
if (err == 0)
{
result = name.ToString();
}
return result;
}
#endregion
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private void Form1_Load(object sender, EventArgs e)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///This code will retrieve user name given the session id
#region
[DllImport("Wtsapi32.dll")]
private static extern bool WTSQuerySessionInformation(IntPtr hServer, int sessionId, WTS_INFO_CLASS wtsInfoClass, out System.IntPtr ppBuffer, out int pBytesReturned);
[DllImport("Wtsapi32.dll")]
private static extern void WTSFreeMemory(IntPtr pointer);
public static string GetUsernameBySessionId(int sessionId, bool prependDomain)
{
IntPtr buffer;
int strLen;
string username = "SYSTEM";
if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WTS_INFO_CLASS.WTSUserName, out buffer, out strLen) && strLen > 1)
{
username = Marshal.PtrToStringAnsi(buffer);
WTSFreeMemory(buffer);
if (prependDomain)
{
if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WTS_INFO_CLASS.WTSDomainName, out buffer, out strLen) && strLen > 1)
{
username = Marshal.PtrToStringAnsi(buffer) + "\\" + username;
WTSFreeMemory(buffer);
}
}
}
return username;
}
public enum WTS_INFO_CLASS
{
WTSInitialProgram,
WTSApplicationName,
WTSWorkingDirectory,
WTSOEMId,
WTSSessionId,
WTSUserName,
WTSWinStationName,
WTSDomainName,
WTSConnectState,
WTSClientBuildNumber,
WTSClientName,
WTSClientDirectory,
WTSClientProductId,
WTSClientHardwareId,
WTSClientAddress,
WTSClientDisplay,
WTSClientProtocolType
}
#endregion
}
}
Before I start, I've already visited Unknown Error (0x80005000) with LDAPS Connection and changed my code and while it did solve the problem it seems that it has mysteriously come back.
Here's the good stuff:
public static bool Authenticate(string username, string password, string domain)
{
bool authentic = false;
try
{
LdapConnection con = new LdapConnection(
new LdapDirectoryIdentifier(Host, Port));
if (IsSSL)
{
con.SessionOptions.SecureSocketLayer = true;
con.SessionOptions.VerifyServerCertificate = ServerCallback;
}
con.Credential = new NetworkCredential(username, password);
con.AuthType = AuthType.Basic;
con.Bind();
authentic = true;
}
catch (LdapException)
{
return false;
}
catch (DirectoryServicesCOMException)
{ }
return authentic;
}
public static bool IsSSL
{
get
{
return ConnectionString.ToLower().Contains("ldaps");
}
}
public static string ConnectionString
{
get
{
if (string.IsNullOrEmpty(_connectionString))
_connectionString = CompleteConfiguration.GetLDAPConnectionString();
return _connectionString;
}
set { _connectionString = value; }
}
public static int Port
{
get
{
var x = new Uri(ConnectionString);
int port = 0;
if (x.Port != -1)
{
port = x.Port;
}
else
{
port = x.OriginalString.ToLower().Contains("ldaps")
? 636
: 389;
}
return port;
}
}
public static string Host
{
get
{
var x = new Uri(ConnectionString);
return x.Host;
}
}
private static bool ServerCallback(LdapConnection connection, X509Certificate certificate)
{
return true;
}
Here's the bad stuff:
When I attempt to authenticate to the application I get the following error, to be precise this is triggered by the con.Bind() line:
[COMException (0x80005000): Unknown error (0x80005000)]
System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail) +378094
System.DirectoryServices.DirectoryEntry.Bind() +36
System.DirectoryServices.DirectoryEntry.get_NativeObject() +31
Complete.Authentication.GCAuthentication.Authenticate(String username, String password, String domain) in c:\Builds\6\Idealink.Open.Pancanal\Panama Canal\Sources\Idealink.Open\Complete.Authentication\GCAuthentication.cs:27
Complete.Authentication.AuthenticationFactory.ValidateUserLdap(String username, String password, String domain, Boolean isValid, String usernameWithDomain) in c:\Builds\6\Idealink.Open.Pancanal\Panama Canal\Sources\Idealink.Open\Complete.Authentication\AuthenticationFactory.cs:93
It is quite confusing as it seems that some user accounts work and others don't. However when I place the above code in an isolated test environment it does succeed each and every time regardless of which account I use. When I place it back on the Windows 2008 R2 Server with ASP.NET and IIS it fails as stated above. The failures are consistent though - accounts consistently fail or succeed, from that perspective there is no randomness.
The LDAP Server must be accessed using LDAPS and NOT LDAP which is why we cannot use the DirectoryEntry object - the LDAP server is controlled by a client and therefore cannot be reconfigured or altered in any way. We simply want to capture username/password on a web form and then use BIND on the LDAP server to check credentials.
We are using .NET 3.5 and cannot upgrade at this time so I respectfully ask that if your main suggestion and arguments are to upgrade than please hold off on your contribution.
Thanks, hope you can help
Would something like this work for you..?
const string Domain = "ServerAddress:389";
const string constrParts = #"OU=Users,DC=domain,DC=com";
const string Username = #"karell";
PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, Domain, constrParts);
UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(principalContext, username);
Here is a really good site for great references and examples
DirectoryServices DirectoryEntry
for Connection over SSL you could do something like the following
const int ldapInvalidCredentialsError = 0x31;
const string server = "your_domain.com:636";
const string domain = "your_domain.com";
try
{
using (var ldapSSLConn = new LdapConnection(server))
{
var networkCredential = new NetworkCredential(username, password, domain);
ldapSSLConn.SessionOptions.SecureSocketLayer = true;
ldapSSLConn.AuthType = AuthType.Negotiate;
ldapSSLConn.Bind(networkCredential);
}
// If the bind succeeds, the credentials are valid
return true;
}
catch (LdapException ldapEx)
{
// Invalid credentials a specific error code
if (ldapEx.ErrorCode.Equals(ldapInvalidCredentialsError))
{
return false;
}
throw;
}
MSDN list of Invalid LDAP Error Codes
I'm an expert programmer, so therefore, I don't have a clue as to WTH I'm doing :)
On a serious note; no, I'm not expert by any means. I do have a problem though, and don't know how to fix it. The good thing is, I (think I) know what the problem is, and I'm hoping someone here can help.
Here's the synopsis of the problem. I am creating a form in C# that will do some server and database administration task for me. I have a button that when clicked is supposed to return the service status of "x" service on "y" server. The status is printed on the screen to a textbox.
Here's my code:
private void button2_Click(object sender, EventArgs e)
{
string fs = "Service X Status = ";
string mr = "Service A Status = ";
string qp = "Service B Status = ";
string sp = "Spooler Service Status = ";
ServiceController fssc = new ServiceController("xService", "yServer");
ServiceController mrsc = new ServiceController("aService", "yServer");
ServiceController qpsc = new ServiceController("bService", "yServer");
ServiceController spsc = new ServiceController("Spooler", "yServer");
try
{
txtGtwySts.AppendText(sp + spsc.Status.ToString());
txtGtwySts.AppendText(Environment.NewLine);
txtGtwySts.AppendText(fs + fssc.Status.ToString());
txtGtwySts.AppendText(Environment.NewLine);
txtGtwySts.AppendText(mr + mrsc.Status.ToString());
txtGtwySts.AppendText(Environment.NewLine);
txtGtwySts.AppendText(qp + qpsc.Status.ToString());
}
catch (Exception crap)
{
string msg = "";
int i;
for (i = 0; i < crap.Message.Count(); i++)
{
msg += "Error # " + i + " Message: " + crap.Message + "\n";
}
MessageBox.Show(msg);
MessageBox.Show(i.ToString());
}
}
I get exceptions, basically saying: Cannot Open "Service" on "Server." Since this is a remote server, I'm assuming this is a credential/security problem. I do NOT, however, have any problems with the Spooler service.
My question is...How can I pass userID and password to this server so that it will authenticate or runas so I can check the status of these services, it that is the problem. If someone doesnt think its the problem, then please inform me where I've went wrong :)
Finally figured it out...
Created a new class, and is shown below:
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.Security.Permissions;
public class ImpersonateUser
{
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(
String lpszUsername,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
private static IntPtr tokenHandle = new IntPtr(0);
private static WindowsImpersonationContext impersonatedUser;
// If you incorporate this code into a DLL, be sure to demand that it
// runs with FullTrust.
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void Impersonate(string domainName, string userName, string password)
{
//try
{
// Use the unmanaged LogonUser function to get the user token for
// the specified user, domain, and password.
const int LOGON32_PROVIDER_DEFAULT = 0;
// Passing this parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 2;
tokenHandle = IntPtr.Zero;
// ---- Step - 1
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(
userName,
domainName,
password,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
ref tokenHandle); // tokenHandle - new security token
if (false == returnValue)
{
int ret = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(ret);
}
// ---- Step - 2
WindowsIdentity newId = new WindowsIdentity(tokenHandle);
// ---- Step - 3
{
impersonatedUser = newId.Impersonate();
}
}
}
// Stops impersonation
public void Undo()
{
impersonatedUser.Undo();
// Free the tokens.
if (tokenHandle != IntPtr.Zero)
{
CloseHandle(tokenHandle);
}
}
}
}
and the original code that I posted is wrapped by:
ImpersonateUser iu = new ImpersonateUser();
iu.Impersonate("[domain]","[username]","[password]");
// code you want to execute as impersonated user.....
iu.Undo();