webForumDet fria alternativet

Åtkomst till fil på annan maskin

.NET

8 svar · 376 visningar · startad av Vide

Medlem sedan dec. 19998 577 inlägg
Frågan#1

Hej,

Jag bygger ett program som skall ha rättigheter att ändra i en specifik mapp på en annan server (jag skall skriva till en fil där). Problemet är att programmet (ASP.NET) körs under användaren ASPNET, och har således inte rättigheter att ändra i en mapp på en annan server.

Mitt sätt att angripa problemet är att ändra användaren under körningen av programmet (just när jag skall skriva till filen), den kod jag använder för detta finns här:
http://support.microsoft.com/default.aspx?scid=kb;en-us;306158#4

Dock fungerar inte detta, inloggningen misslyckas och jag ser i loggen, på den maskinen jag försöker att ändra på, att det fortfarande är ASPNET som försöker ändra.

Felmeddelandet jag får är detta:

server skrev:

Logon failure: unknown user name or bad password.

Koden som genererar felet (felraden i felstil):

      bool bLogin = impersonateValidUser( "xxx", "xxx", "xxx" );
      
      string strPath = @"\\xxx\xxx\xxx.txt";
      
      [b]using (StreamWriter sw = File.CreateText( strPath )) [/b]
      {
        sw.WriteLine("Testing!");
      }

	    undoImpersonation();

      return bLogin;

bLogin har värdet false när jag kollar den.

Någon som har några idéer om hur jag skall gå vidare med detta? :)

Mvh, Vide

Medlem sedan maj 20012 812 inlägg
#2

Jag råkade ut för någont liknande men ännu störrigare "features".

När man har loggat in som en riktigt användare via ASP.NET och startar en ny tråd, så körs inte den nya tråden som den inloggade användaren utan som ASP_NET kontot. Riktigt jävla störrigt.

Jag kom dock runt problemet med följande kod:

		#region Win32 API
		[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
		public extern static bool CloseHandle(IntPtr handle);

		[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
		public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, 
			int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
		#endregion

		#region Declaration
		private IMessage myMessage;
		private WindowsIdentity myWindowsIdentity = WindowsIdentity.GetCurrent();
		private WindowsImpersonationContext myWindowsImpersonationContext;
		#endregion

		#region Properties
		internal IMessage Message
		{
			set{myMessage = value;}
		}
		#endregion

		#region Method
		internal void LogMessage()
		{
			//-- Create a new WindowsIdentity if needed
			ImpersonatNewWindowsIdentity();

			//-- Get a instance of the logManager. LogManager is a singelton class.
			LogManager myLogManager = LogManager.GetInstance();

			//-- LogMessage to all receiver
			myLogManager.LogMessage(myMessage);

			//-- Undo impersonate if needed
			UndoImpersonate();
		}

		private bool CheckIfWindowsIdentityIsSame()
		{
			return WindowsIdentity.GetCurrent().Name == myWindowsIdentity.Name;
		}
		private void ImpersonatNewWindowsIdentity()
		{
			if(CheckIfWindowsIdentityIsSame())
				return;

			IntPtr newToken = IntPtr.Zero;

			DuplicateToken(myWindowsIdentity.Token, 2, ref newToken);
			myWindowsImpersonationContext = WindowsIdentity.Impersonate(newToken);			

			//CloseHandle(newToken);
		}
		private void UndoImpersonate()
		{
			if(myWindowsImpersonationContext != null)
				myWindowsImpersonationContext.Undo();
		}
		#endregion

		#region Constructors
		public LogManagerThreadWrapper()
		{
			//
			// TODO: Add constructor logic here
			//
		}
		#endregion

Nu har jag dock ett försprång på dig, det är nämligen när jag skapar denna klass så kör jag som den riktiga användare, alltså kommer min myWindowsIdentity innehålla token för den riktiga användaren och inte ASP_NET.

Så om du lyckas fixa till så att WindowsIdentity myWindowsIdentity innehåller den WindowsIdentity du vill så kan du använda ovanstående kod.

Vet inte om det hjälper dig, men jag har förmig att det inte var det svåra utan det svåra var att få Contexten att köra som rätt användare.

Hoppas det hjälper dig lite på vägen iallafall.

- magnus

Medlem sedan maj 20012 812 inlägg
#3

Hur gick det Vide!

Jag tittade vidare på det och följande kod fungerar för mig att öppna en fil som endast den nya användare har rättigheter till.

Nu är det en Console application men bör ju fungerar även i ASP.NET miljö.

using System;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.IO;

namespace ConsoleApplication13
{
	/// <summary>
	/// Summary description for Class1.
	/// </summary>
	class Class1
	{
		#region -- Declare Const --------------------------------------------
		public const int LOGON32_LOGON_INTERACTIVE = 2;
		public const int LOGON32_PROVIDER_DEFAULT = 0;
		#endregion

		#region -- Win32 API Implemetion ------------------------------------
		[DllImport("advapi32.dll")]
		public static extern int LogonUserA(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);
		#endregion

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main(string[] args)
		{
			//-- Used to Impersonat the new windows Identity
			WindowsImpersonationContext myWindowsImpersonationContext;

			//-- tokens to the users 
			IntPtr newWindowsIdentitytoken = IntPtr.Zero;

			//-- Call Win32 API to get the token for the specified user
			LogonUserA(USERNAME, DOMAINNAME, PASSWORD , LOGON32_LOGON_INTERACTIVE, 
				LOGON32_PROVIDER_DEFAULT, ref newWindowsIdentitytoken);

			//-- Open file that only the new user can do, this will throw an Access denied Exception
			try
			{
				FileStream fs2 = File.Open(@"C:\onlyUSERNAMEcanopenthis.txt",FileMode.Open);
				fs2.Close();
			}catch(Exception e){}

			myWindowsImpersonationContext = WindowsIdentity.Impersonate(newWindowsIdentitytoken);	

			//-- Open file that only the new user can do
			try{
				FileStream fs2 = File.Open(@"C:\onlyUSERNAMEcanopenthis.txt",FileMode.Open);
				fs2.Close();
			}catch(Exception e){}
			//-- Remove the impersonation
			myWindowsImpersonationContext.Undo();

			//-- Check to see that we can't open it anymore, this will throw an 'Access Denied Exception'
			try
			{
				FileStream fs2 = File.Open(@"C:\onlyUSERNAMEcanopenthis.txt",FileMode.Open);
				fs2.Close();
			}catch(Exception e){}

CloseHandle(newWindowsIdentitytoken);
		}
	}
}
Medlem sedan dec. 19998 577 inlägg
#4

Tack för dina svar Gladh!

Jag håller fortfarande på och dribblar med accesser, det är lite komplicerat med tanke på domänaccesser och liknande.

Fungerar din kod om du skall ha en sökväg liknande @"\\server\mapp\fil.txt"?

Medlem sedan dec. 19998 577 inlägg
#5

Denna information för jag via ett fel:

ASP.NET is not authorized to access the requested resource. Consider granting access rights to the resource to the ASP.NET request identity. ASP.NET has a base process identity (typically {MACHINE}\ASPNET on IIS 5 or Network Service on IIS 6) that is used if the application is not impersonating. If the application is impersonating via <identity impersonate="true"/>, the identity will be the anonymous user (typically IUSR_MACHINENAME) or the authenticated request user.

Jag har satt detta i web.config:
<identity impersonate="true"/>

... och "authenticated request user" borde vara den jag försöker att logga in som.

Vidare så får jag upp en prompt när jag inte logga in med någon användare, där den försöker att logga in på den lokala webben, så det verkar som att den tappar access till att köra scriptet på webben.

Medlem sedan maj 20012 812 inlägg
#6

Hej Vide. Jag fick följande att fungerar utan problem. Detta är en Webapplication som försöker nå en fil över en filshare där ASPNET inte har några rättigheter.

Först så skapade jag en Security assembly med en class user som används för att logga in.

using System;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.IO;

namespace TS.Classes.Security
{
	/// <summary>
	/// Summary description for User.
	/// </summary>
	public class User
	{
		#region -- WIN 32 API Impelentation -------------------------------------------------
		[DllImport("advapi32.dll")]
		private static extern int LogonUserA(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType,  int dwLogonProvider, ref IntPtr phToken);

		[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
		private extern static bool CloseHandle(IntPtr handle);

		[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
		private extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
		#endregion

		#region -- Declaration --------------------------------------------------------------
		//-- Constat ints for WIN 32 API
		private const int LOGON32_LOGON_INTERACTIVE = 2;
		private const int LOGON32_PROVIDER_DEFAULT = 0;

		/// <summary>
		/// Used to Impersonat the new windows Identity
		/// </summary>
		WindowsImpersonationContext _WindowsImpersonationContext;

		/// <summary>
		/// tokens to the new Widnows Identity
		/// </summary>
 		IntPtr _WindowsIdentitytoken = IntPtr.Zero;
		#endregion

		#region -- Methods ------------------------------------------------------------------
		/// <summary>
		/// Logs on with a new user
		/// </summary>
		/// <remarks>
		/// <para>
		/// Uses the WIN 32 API LogonUserA to log on a user and get a token back.
		/// </para>
		/// <para>
		/// The token is used later to impersonate the new user as WindowsIdentity, so the application
		/// is runned under the new User
		/// </para>
		/// <para>
		/// This metohd is treadsafe, that means if one thread logs in with a new user, no other threads are
		/// affected by this logon.
		/// </para>
		/// </remarks>
		/// <param name="userName">username of the user</param>
		/// <param name="domainName">domain that the user belongs to</param>
		/// <param name="password">password to the user</param>
		/// <returns>return true if everything went ok, else false</returns>
		public bool LogOn(string userName, string domainName, string password)
		{
			try
			{
				//-- Logon the new user
				LogonUserA(userName, domainName, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref _WindowsIdentitytoken);

				//-- Create an Impersonation off the new user
				_WindowsImpersonationContext = WindowsIdentity.Impersonate(_WindowsIdentitytoken);	

				//-- Returns true
				return true;
			}
			catch(Exception exception)
			{
				return false;
			}
		}
		/// <summary>
		/// 
		/// </summary>
		/// <returns>returns true if the logoff went ok, else false</returns>
		public bool LogOff()
		{
			try
			{
				//-- Undo impersonation
				_WindowsImpersonationContext.Undo();
				//-- logoff the new user
				CloseHandle(_WindowsIdentitytoken);
				//-- returns true;
				return true;
			}
			catch(Exception exception)
			{
				return false;
			}
		}
		#endregion

		#region -- Constructor --------------------------------------------------------------
		public User(){}
		#endregion
	}
}

Sedan så skapade jag en dummy webapplication som försöker läsa filen som jag har på min fileshare.

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.IO;

using TS.Classes.Security;

namespace WebApplication5
{
	/// <summary>
	/// Summary description for WebForm1.
	/// </summary>
	public class WebForm1 : System.Web.UI.Page
	{
		private void Page_Load(object sender, System.EventArgs ex)
		{
			//-- Open file that only the new user can do, this will throw an Access denied Exception
			try
			{
				FileStream fs2 = File.Open(@"\\homlyn01\mgl\email.cs",FileMode.Open);
				fs2.Close();
			}
			catch(Exception e)
			{
				string s = e.Message;
			}

			User user = new User();
			user.LogOn("username", "domain","password");

			//-- Open file that only the new user can do
			try
			{
				FileStream fs2 = File.Open(@"\\homlyn01\mgl\email.cs",FileMode.Open);
				fs2.Close();
			}
			catch(Exception e){}

			user.LogOff();

			//-- Open file that only the new user can do
			try
			{
				FileStream fs2 = File.Open(@"\\homlyn01\mgl\email.cs",FileMode.Open);
				fs2.Close();
			}
			catch(Exception e){}
		}

		#region Web Form Designer generated code
		override protected void OnInit(EventArgs e)
		{
			//
			// CODEGEN: This call is required by the ASP.NET Web Form Designer.
			//
			InitializeComponent();
			base.OnInit(e);
		}
		
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{    
			this.Load += new System.EventHandler(this.Page_Load);
		}
		#endregion
	}
}

Detta fungerar utmärkt utan några som helst problem. Har dock för mig att det kan vara så att din webserver inte "flyttar" med kredentials över nätverket, men det brukar vara rätt ovanligt så det är det nog inte, någon inställning man kan sätta...

Som sagt, ovanstående återspeglar ditt problem och det fungerar perfekt för mig i vår miljö.

- Magnus

Medlem sedan dec. 19998 577 inlägg
#7

Det löste sig genom att ange imperonate i web.config:
<identity impersonate="true" userName="domain\user" password="password"/>

Dock känns det lite som fel väg att gå, men det fungerar. ;)

Medlem sedan maj 20012 812 inlägg
#8

Gör du så så kommer ju allt på websidan att köras som den person. Alltså inte bara skrivning och läsning till fil, utan allt på sidan körs som en "riktigt" user.

Trodde inte att det var något alternativ för dig!

Bra att det löste sig iallafall.

- Magnus

Medlem sedan dec. 19998 577 inlägg
#9

Egentligen tycker inte jag att det är ett bra alternativ, men det har godkänts av chefen så, det får fixas till senare när man har mer tid.

Tack för hjälpen iaf! :)

261 ms totalt · 4 externa anrop · v20260731065814-full.6fe65c25
128 ms — deklarationer (db)
0 ms — hämta statistik (cache)
130 ms — hämta tråd, inlägg och bilagor (db)
123 ms — ändringar (db)