Authenticating against a remote server and domain (C#/3.5) - c#

I have a .Net 3.5 service that runs on a stand-alone computer. The service queries the event logs of servers in multiple Active Directory domains. I would like to authenticate to these using managed code.
Currently, I authenticate either with p/invoke to WNetAddConnection2A or with processing out with a net use. For example, this works:
string user = "domain\\administrator";
string pass = "password";
string server = "server.domain.com";
string argument = String.Format("use \\\\{0}\\ipc$ /user:{1} {2}", server, user, pass);
System.Diagnostics.Process.Start("net.exe", argument);
System.Diagnostics.EventLog log = new EventLog("System", server);
Is there a way in the .Net 3.5 framework to authenticate and read the event log without having to proc out or call unmanaged code?

Have you seen the "System.Security.Principal.WindowsImpersonationContext"
and the demo written in MSDN.
Way back, in the .NET 1.1 days, I had to write a windows service for validation of users in 2 windows domains, used LDAP - and WindowsImpersonation as fail-back.
As I remember I got insperation form CodeProject and Google :-S
/Erling Damsgaard
DNS-IT ApS

Related

How to check if a windows user has access (Windows Integrated Security authentication) to SQL Server Analysis Services (SSAS) Server via impersonation

For a SQL Server instance, to check if a windows user is present and has any access or not one can try various ways as detailed here.
I'm looking for something similar for SQL Server Analysis Services (SSAS) server.
I went into properties of SSAS Server from right-click context menu and on Security tab I can see that there are several windows users already configured:
Is there any way to check from a client application (written in C#) by making some sort of test connection or does SSAS also maintains some metadata database of its own like master database in SQL Server instance (DB engine) which can be queried. I checked the Databases node in SSAS server but I don't see any default databases there:
In the client application I'm working upon, I've windows user name and password as input. In my client application there is a simple winform with two text boxes to take AD user name and password which need to be connected to a SSAS Server. My gut feel is that password is of no relevance here as SSAS supports only Windows integrated authentication mode. My client application would be running under an account which already has access to SSAS server I'm trying to connect.
Update: After getting help from #Vaishali, I'm able to figure out that it is possible to make a test connection to an SSAS server using ADOMD.Net.
Now, the problem here is that the connection string implicitly uses the AD account of the user with which I'm running the client application to connect to the SSAS server. I don't think it would be possible mention an windows AD account user name and password explicitly in the ADOMD.Net connection strings while using Windows Integrated authentication. Even connection strings of SQL Server don't allow mentioning the windows username and password explicitly in the connection string as mentioned here.
Update 2: I have got a lead from one of my friends that it is possible to fire some MDX query on SSAS to get user access details.
Update 3: SSAS server supports only Windows Integrated Security mode of authentication unlike SQL Server DB engine which also supports userid-password based SQL authentication. So, some form of impersonation would be required to fire MDX queries on behalf of other user for which I'm trying to check access on SSAS server through Windows Integrated Security only.
Hmphh...It was quite a journey to really be able to nail it through ADOMD.Net.
Core methodology: The core philosophy is the fact that connection to SSAS server supports only Windows Integrated Security based authentication. The SQL authentication like we do for sa user in SQL Server isn't supported in SSAS.
So, the basic idea was to try to connect to the SSAS server using Windows Integrated Security based authentication and fire an MDX query in the context of the user we are trying to check. If the query gets executed successfully then the user has access. If the query execution returns an error/exception then the user doesn't have access.
Please note that just to be able to open a connection to the SSAS server is not an indicator of user-access due to reasons described here. You must fire a query to check access.
For ADOMD.Net until v12.x:
Now, we know that Windows Integrated Security based authentication always takes the user details from the user-context under which the application/process is running. You can not pass the user credentials in the connection string of ADOMD.Net connection. Here is the code I wrote to accomplish it. You need to refer to Microsoft.AnalysisServices.AdomdClient.dll in your C# project.
using Microsoft.AnalysisServices.AdomdClient;
public static int IsSsasAccessibleToUser(string ssasServerName)
{
var hasAccess = 0;
try
{
using (var adomdConnection = new AdomdConnection($"provider=olap;datasource={ssasServerName};Catalog=myDatabaseName"))
using (var adomdCommand = new AdomdCommand())
{
adomdCommand.CommandText = "SELECT [CATALOG_NAME] AS [DATABASE],CUBE_CAPTION AS [CUBE/PERSPECTIVE],BASE_CUBE_NAME FROM $system.MDSchema_Cubes WHERE CUBE_SOURCE = 1";
adomdCommand.Connection = adomdConnection;
adomdConnection.Open();
adomdCommand.ExecuteNonQuery();
Log("ExecuteNonQuery call succeeded so the user has access");
hasAccess = 1;
}
}
catch (Exception ex)
{
Log("There was an error firing query on the database in SSAS server. so user doesn't have access");
}
return hasAccess;
}
Now, to leverage Windows Integrated Security based authentication we can run this code in two ways:
Out-Proc Impersonation : Put this code inside a console application. Use the "Run as different user" option in the context menu when we right click the exe. Put the credentials of the user Y (let's say) so that application starts in the context of user Y for which we need to validate the access on SSAS server. ADOMD.Net will use user Y's identity while connecting using Windows Integrated Security for SSAS server. If code succeeds the user has access.
In-Proc Impersonation: The other case could be that you are running the application as user X but you want to test the access of user Y. Here effectively you require in-place impersonation while running the above code. For achieving it I used a famous NuGet package "Simple Impersonation" which uses the default .Net library classes WindowsImpersonationContext and WindowsIdentity . Creator of this NuGet package had first posted a great answer here.
Observation in SQL Server Profiler: After you've impersonated user Y, you will clearly see the MDX query getting fired in the context of user Y if you capture the session as shown below:
Caveats and concerns:
One issue that I faced while using this in-proc impersonation is that it doesn't work if the SSAS server is located on the same machine where the application code is running. This is due to the inherent behavior of native LogonUser API (using LOGON32_LOGON_NEW_CREDENTIALS LogonType) which is called during impersonation calls by the NuGete package. You can try other logon types as detailed here which suites you need.
You require password of the user as well along with the domain name and user name to do impersonation.
For ADOMD.Net v13.x onwards
Then, I came across this ChangeEffectiveUser API documentation on MSDN here. But, intellisense wasn't showing this API. Then I found out this API got added in ADOMD.Net with SQL Server 2016 release. There are various ways to get the latest release:
C:\Program Files\Microsoft.NET\ADOMD.NET\130\Microsoft.AnalysisServices.AdomdClient.dll
I'm not sure who dumps this file at this location. Is it part of Microsoft.Net extensions or SQL Server installation.
In Installation folder of Microsoft SQL Server. I got it at path - C:\Program Files\Microsoft SQL Server\130\Setup Bootstrap\Update Cache\KB3182545\ServicePack\x64\Microsoft.AnalysisServices.AdomdClient.dll
NuGet package here. For some weird reason best known to MS the NuGet package of v13.x of ADOMD.Net has been named Unofficial.Microsoft.AnalysisServices.AdomdClient. Not sure why they introduced a separate NuGet package with Unofficial prefix when this should have been simply the next version of the already existing NuGet package Microsoft.AnalysisServices.AdomdClient present here.
So the new API ChangeEffectiveUser present in latest version on AdomdConnection clas can be used easily to impersonate any user as below:
adomdConnection.Open();
//impersonate the user after opening the connection
adomdConnection.ChangeEffectiveUser("domainName\UserNameBeingImpersonated");
//now the query gets fired in the context of the impersonated user
adomdCommand.ExecuteNonQuery();
Observing Impersonation in SQL Server Profiler: Although one peculiar observation I had in the SQL Server Profiler is that the logs of query being fired still shows the name of the original user with which your application process is running.
So to check whether impersonation is happening or not I removed the access rights of the user domainName\UserNameBeingImpersonated from SSAS server. After that, when I ran the above code again then it resulted in exception whose message clearly states that - the user domainName\UserNameBeingImpersonated doesn't have permission on the SSAS server or the database doesn't exist. This error message clearly suggests that impersonation is working.
Advantages and Backward compatibility of this approach:
Although the API is very recent as it came up with SQL Server 2016 but I was able to use it successfully with SSAS server 2014 as well. So it looks fairly backward compatible.
This API works irrespective of whether your SSAS server is local or remote.
You just require the domain name and user name for doing impersonation. No password require.
What to do if we simply want to check the access on the SSAS server without involving any database present on the SSAS server?
Change the connection string to not involve any database. Remove the Catalog key as following connection string - "provider=olap;datasource={ssasServerName};"
Fire the following query instead to check access - SELECT * FROM $System.discover_locks in the code snippet shown initially in the post.
If you wish to check if user has accessibility to SSAS server, one option you can try with C# is: try connecting SSAS with given user credential, if you succeed, you have access.
If you are looking for roles and security mapped to individual cube database, following link will be usefull.
http://www.lucasnotes.com/2012/09/list-ssas-user-roles-using-powershell.html#comment-form
C# code lines:
import library Microsoft.AnalysisServices.AdomdClient;
and code lines would be:
DataSet ds = new DataSet();
AdomdConnection myconnect = new AdomdConnection(#"provider=olap;datasource=.\SQL20f12");
AdomdDataAdapter mycommand = new AdomdDataAdapter();
mycommand.SelectCommand = new AdomdCommand();
mycommand.SelectCommand.Connection = myconnect;
try
{
myconnect.Open();
}
catch
{
MessageBox.Show("error in connection");
}
Hope this works for you.

AzMan gives different results to different servers

We have 2 web-servers which are theoretically identical, but are producing different results when performing an AzMan authorisation check.
We have the same web-site running on both machines (literally the same web-site - it's been XCOPYed from one to the other, and it runs under the same service account). All this web-site does is perform an authorisation check against an AzMan database (sitting on a separate SQL server).
However, on the working web-site (WebA) this check returns 0 (i.e. "user is authorised"), while on the broken web-site (WebB) this check returns 5 (i.e. "user is NOT authorised"). We are expecting 0 on both web-sites. The same user is accessing both web-sites, from the same PC.
Does anyone have any ideas for things we can check?
Environment details
Windows Server 2008 R2
Same AD domain
IIS 7.5
.NET 3.5
AzMan Database runs on SQL Server 2005/Windows Server 2008 R2.
Code
AzAuthorizationStoreClass authStore = new AzAuthorizationStoreClass();
// initialise the store
authStore.Initialize(0, "mssql://Driver={SQL Server};Server={OURDBSERVER};Trusted_Connection={Yes};/OURDATABASE/OURAPPLICATION", null);
// open the store
IAzApplication2 authApp = authStore.OpenApplication2("OURAPPLICATION", null);
// get the identity of the user NOT the service account
WindowsIdentity identity = Thread.CurrentPrincipal.Identity as WindowsIdentity;
// and from that derive the token
ulong userToken = (ulong)identity.Token.ToInt64();
// get the context based on the token
IAzClientContext3 clientContext =
(IAzClientContext3)authApp.InitializeClientContextFromToken(userToken, null);
// get the operation object based on the id
IAzOperation2 azManOperation = (IAzOperation2)authApp.OpenOperation(operationId, null);
// generate an audit identifier
string auditIdentifer =
string.Format("{0}{1} : O:{2}", "{the_correct_id}", identity.Name, operationId);
uint accessResult = clientContext.AccessCheck2(auditIdentifer, string.Empty, azManOperation.OperationID);
return accessResult.ToString();
Many thanks,
RB.
Thanks to David Hall for pointing me in the right direction.
Investigation showed that both web-sites were enabled for both Windows authentication and anonymous access. However, on one web-site the user was being logged in correctly, while on the broken web-site it was falling back to anonymous mode.
Disabling anonymous access fixed this problem by ensuring the user logs in to both web-sites.
However, this leaves another question of why the browser logs in anonymously on one web-site but not the other - one for ServerFault I think.
In our case, we were using ASP.NET impersonation with Windows Authentication and not Anonymous. Tt was working on the Windows 7 Enterprise x64 Development machine and not on the Windows Server 2008 R2 x64 test server. Both Application Pools were set up exactly the same with the same domain account credentials.
It turns out that ASP.NET impersonation was the root cause of the issue. After disabling ASP.NET impersonation, the App Pool account was now being used as the credentials to connect to the AzMan store successfully. The same issue was occurring when connecting to an AzMan store in Active Directory or SQL Server.
For clarity, the error I was getting was: Value does not fall within the expected range. from AzAuthorizationStoreClass.Initialize()
My final connection string was:
<add name="AzPolicyStore" connectionString="mssql://Driver={SQL
Server};Server=sqlserver\instance;/DatabaseName/AzStore" />

What library do I need to reference for remote connection to RDC server from console app?

We have a terminal server: "server.host.com". Normally, we RDP into that server for our business applications. However, I am performing some datamining and want to bring the .xml files to a local system for post processing.
My utility app would basically log in (impersonating my account), navigate to the directory where the .xml files are stored, then begin copying to a local directory. This utility would be set to run every morning at 3am unattended.
I am trying to determine whether WMI is the correct library, or Remoting, or some other library? Is this even going to be possible?
So if you are looking at using Microsoft's remote desktop, you can add a reference to Microsoft Terminal Services Control Type Library in the COM section. If you are working with a gui, you can just drag the control and do something like:
rdp.Server = txtServ.Text;
rdp.UserName = txtUser.Text;
IMsTscNonScriptable secured = (IMsTscNonScriptable)rdp.GetOcx();
secured.ClearTextPassword = txtPassword.Text;
rdp.Connect();
If you are using the console, should be just as simple just without the buttons and all.

.Net's Directory Services throws a strange exception

I have a small C# solution used to check users credentials. It works fine for two of my teammates, but on my PC I get an exception.
The relevant code:
PrincipalContext context = new PrincipalContext(ContextType.Domain);
if (context.ValidateCredentials(System.Environment.UserDomainName + "\\" + usr, pwd))
return true;
else
return false;
And the exception is:
DirectoryOperationException, "The server cannot handle directory requests.".
I tried creating context with the explicit server name and the 636 port number, but this didn't help as well.
Any ideas?
I had this problem too using IIS Express and VS 2010. What fixed it for me was a comment on another thread.
Validate a username and password against Active Directory?
but i'll save you the click and search... :) Just add ContextOpations.Negotiate to you Validate Credentials call like below.
bool valid = context.ValidateCredentials(user, pass, ***ContextOptions.Negotiate***);
I had this issue: things were working on my dev machine but didn't work on the server. Turned out that IIS on the server was set up to run as LocalMachine. I changed it to NetworkService (the default) and things started working.
So basically check the user of the app pool if this is running on IIS.
I had to just create a new app pool and assign it .NET 2.0, then assign the new app pool to our web app, and it started working. We had .NET 3.5 SP2, so the hotfix wasn't ideal for us. Since the WWW service is usually Local System, I questioned that too. But since it was .NET and security related, I gave a shot at the app pool first and it worked.
Perhaps you need the hotfix?
FIX: DirectoryOperationException exception
And you are an Admin or the id that your service is running under is an Admin on your PC right?
I take it you already looked into this:
System.DirectoryServices.Protocols
"You may receive a less than helpful DirectoryOperationException(“The server cannot handle directory requests.”) what isn’t quite so amusing about this is that it didn’t even try to communicate with the server. The solution was to add the port number to the server. So instead of passing “Server” to open the LdapConnection, I passed “server:636”. By the way, LDAPS is port 636 – rather than the 389 port used by LDAP."
Good point, I wouldn't expect that Win7/.NET 3.5 would need that patch. How about the info provided in this question:
Setting user's password via System.DirectoryServices.Protocols in AD 2008 R2

"Access Denied" when trying to connect to remote IIS server - C#

I receive an "Access Deined" COMException when I try to connect to a remote IIS 6 server from my C# application that is running under IIS 5.1.
Any ideas? I am experiencing all the same issues with the original questions.
Update - 4/1/09
I found this solution (http://www.codeproject.com/KB/cs/Start_Stop_IIS_Website.aspx) that consists of a window application connecting to an IIS server to start and stop web sites. I am able to run it on my workstation and connect to the IIS server.
Ugh....why can I run this stand alone application but not my ASP.NET application?
Original
I receive an "Access Denied" COMException when I try to connect to IIS from a remote machine using the DirectoryEntry.Exist method to check to see if the IIS server is valid.
string path = string.Format("IIS://{0}/W3SVC", server);
if(DirectoryEntry.Exist(path))
{
//do something is valid....
}
I am a member of an active directory group that has been added to the Administrators groups to the IIS server I am trying to connect to.
Has anyone experience this issue and know how to resolve it?
UPDATE:
#Kev - It is an ASP.NET application. Also, I can connect without an username and password to the remote server through IIS6 Manager.
#Chris - I am trying to connect to the remote server to display the number of virtual directorys and determine the .NET framework version of each directory. See this SO question.
#dautzenb - My ASP.NET application is running under IIS 5.1 trying to connect to an IIS 6 server. I can see fault audits in the security log for my local ASPNET account on the remote server. When I try to debug the application, I am running under my domain account and still get the Access is denied.
UPDATE 2:
#Kev - I was able to establish to create a DirectoryEntry object using the following overload:
public DirectoryEntry
(
string path,
string username,
string password
)
But, all of the properties contain a " threw an exception of type 'System.Runtime.InteropServices.COMException'" while I debug the app.
Also, the AuthenticationType property is set to Secure.
UPDATE 3:
The following two failure audit entries were in the remote IIS server's security event log every time I tried to establish a connection:
First event:
Event Category: Account Logon
Event ID: 680
Log attempt by: MICROSOFT_AUTHENTICATION_PACKAGE_V1_0
Logon account: ASPNET
Source Workstation:
Error Code: 0xC0000234
Second event:
Event Category: Logon/Logoff
Event ID: 529
Logon Failure:
Reason: Unknown user name or bad password
User Name: ASPNET
Domain: (MyDomain)
Logon Type: 3
Logon Process: NtLmSsp
Authentication Package: NTLM
Workstation Name: (MyWorkstationId)
Caller User Name: -
Caller Domain: -
Caller Logon ID: -
Caller Process ID: -
Transited Services: -
Source Network Address: 10.12.13.35
Source Port: 1708
Impersonation is set to true and the username and password are blank. It is using the ASPNET account on the remote IIS server.
If it is an identity problem, you could try setting your IIS 5.1 application to use Integrated Windows Authentication, and then add the following to you web.config on your IIS5.1 web site under system.web to enable impersonation.
<identity impersonate="true"/>
<authentication mode="Windows" />
Since this is an ASP.NET application, it runs in an Application Pool of IIS. This Application Pool runs using a specific user("Local System", "Network Service" or another user).
Does this user have enough rights to connect to a remote server ?
See MSDN for more info.
This looks like it may be a double-hop issue. If you are impersonating the current user of a website using NTLM, that impersonation is only valid on that server (your IIS 5.1 server in this case). If you try to connect to another server using the web site, you are actually going to have issues as it cannot pass the token to another server that was used during impersonation. The same is true if you are debugging your site through your machine, going to another box. Your local machine is authenticating you, but it cannot impersonate you to another server.
All of the solutions I have used in the past require you to hard code the app pool to use an account that has permissions, set the annony. account to a domain account with permissions on the other machine, or use a windows service running on the IIS 5.1 machine, under a domain account, to connect to the other server.
If you are using Kerberos, this wouldn't apply, but AD uses NTLM by default.
Where exactly are you trying to read too? Is it in under the same path as your application?
When I had this problem, I found that simply authenticating my self on a Windows file share solved the problem. From experience, I think that WMI/ADSI/COM doesn't have great support for not-already-authenticated users. I believe this issue occurs when you're not associated with a Windows domain.
If it is indeed a NTLM doublehop issue you could use the SETSPN utility to create service principal named instances for your target IIS servers.
Then you could go into Active Directory, and then allow the computer object (basically the NETWORK SERVICE or LOCAL SERVICE principals) to delegate its credentials to a correctly registered SPN.
Then you could hop-hop-hop all over the place! But, be warned! People can hurt themselves on sharp pointy things when you enable double-hop!
Good KB articles to read:
http://support.microsoft.com/kb/929650
I believe that DirectoryEntry.Exists silently ignores any credentials supplied and uses the creds of the authenticated user. This seems to match the behaviour you've described. For AD work, we never use it for this reason.
I'm sort of stumped at the moment as to why you can't get this working. There is a temporary work around you could try. When instantiating the DirectoryEntry object you could use one of the following constructor overloads:
public DirectoryEntry(
string path,
string username,
string password
)
Documented at: MSDN: DirectoryEntry Constructor (String, String, String)
...or...
public DirectoryEntry(
string path,
string username,
string password,
AuthenticationTypes authenticationType
)
Documented at: MSDN: DirectoryEntry Constructor (String, String, String, AuthenticationTypes)
As it happens I'm building a test AD environment on my virtual server box for a new project to do similar stuff. When I get it up and running I'll have a play around to see if I can reproduce the problem you're encountering. In the meantime let us know what happens if you try these constructor overloads referenced above.
Update (In answer to Michaels comment):
For reasons that evade me just now, we couldn't use DirectoryEntry.Exists() in a particular scenario, there is this snippet of code that gets called now and again in one of our apps:
public static bool MetabasePathExists(string metabasePath)
{
try
{
using(DirectoryEntry site = new DirectoryEntry(metabasePath))
{
if(site.Name != String.Empty)
{
return true;
}
return false;
}
}
catch(COMException ex)
{
if(ex.Message.StartsWith("The system cannot find the path specified"))
{
return false;
}
LogError(ex, String.Format("metabasePath={0}", metabasePath));
throw;
}
catch(Exception ex)
{
LogError(ex, String.Format("metabasePath={0}", metabasePath));
throw;
}
}
You could replace the constructor with one of the ones from above. Admittedly it's a stab in the dark :).

Categories