CreateProcessAsUser in user session (other than 0) from service (IIS 7) - iis-7

My setting is:
Windows Server 2008, IIS 7
What I would like to accomplish:
IIS 7 receives a request from my website to do some 3D operations (in WPF) and in the end IIS 7 should render an image and store it somewhere on the disk to get loaded into the website
What I know and what I already have tested yet:
IIS 7 runs as service in session 0 on Windows Server 2008
no service in session 0 has access to the video driver, that is no service in session 0 can perform any rendering tasks (explained here: Session 0 Isolation)
Microsoft proposes in another paper to create a process in a user session (by CreateProcessAsUser) to perform rendering sucessfully.
What I achieved up until now:
LogonUser works well
CreateProcessAsUser works well
The only (but important) part that doesn't work:
When I logon with username and password and create a process as user, the process is still in session 0 and therefore rendering fails. The user logged on successfully (I checked it). According to Microsoft it must be possible to create a process in a user session (not session 0).
How can I create a process as user in other session than 0?
Do I have to create a new session myself or something like this?
Thank you!
My code:
PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION();
STARTUPINFO startInfo = new STARTUPINFO();
Boolean bResult = false;
IntPtr hToken = IntPtr.Zero;
UInt32 uiResultWait = WAIT_FAILED;
int processCreationFlags;
try
{
// Logon user
bResult = Win32.LogonUser(
strName,
strDomain,
strPassword,
Win32.LogonType.LOGON32_LOGON_BATCH,
Win32.LogonProvider.LOGON32_PROVIDER_DEFAULT,
out hToken
);
if (!bResult) { throw new Exception("Logon error #" + Marshal.GetLastWin32Error()); }
CheckPrivileges(hToken);
// Create process
startInfo.cb = Marshal.SizeOf(startInfo);
startInfo.lpDesktop = null;
startInfo.dwFlags = Convert.ToInt32(STARTF.STARTF_USESHOWWINDOW);
startInfo.wShowWindow = Convert.ToInt16(SHOWWINDOW.SW_HIDE);
processCreationFlags = Convert.ToInt32(CreateProcessFlags.CREATE_BREAKAWAY_FROM_JOB);
retStr += "command line: " + strCommand + Environment.NewLine;
bResult = Win32.CreateProcessAsUser(
hToken,
null, //application name
strCommand, //command line
IntPtr.Zero, //process attributes
IntPtr.Zero, //thread attributes
false, //inherit handles
processCreationFlags, //process creation flags
IntPtr.Zero, //environment
curDir, //current directory
ref startInfo,
out processInfo
);
if (!bResult) { throw new Exception("CreateProcessAsUser error #" + Marshal.GetLastWin32Error()); }
// Wait for process to end
uiResultWait = WaitForSingleObject(processInfo.hProcess, INFINITE);
if (uiResultWait == WAIT_FAILED) { throw new Exception("WaitForSingleObject error #" + Marshal.GetLastWin32Error()); }
}
finally
{
// Close all handles
CloseHandle(hToken);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}

I'm not an expert in that field, just did some searching around and it seems that you cannot actually change session level, rather you should have a user with auto-logon enabled and communicate with a process running under that user.
This seemed the most useful: Windows 2008 RenderFarm Service: CreateProcessAsUser "Session 0 Isolation" and OpenGL

Related

CAS SSO for Asp.Net website, attributes not receiving on serviceValidate request

I have successfully integrated CAS for our different clients. But this time 'samlValidate' response is not consistently supplying the required attribute. Login is failing randomly because of the missing attribute in the ticket validation response. Sometimes when I clear browser history, it's receiving the attribute in the response.
Expected response:
<cas:serviceResponse xmlns:cas='http://www.xxxxx.xxx/tp/cas'>
<cas:authenticationSuccess>
<cas:user>xxxxx</cas:user>
<cas:attributes>
<cas:userNumber>1234567</cas:userNumber>
</cas:attributes>
</cas:authenticationSuccess>
</cas:serviceResponse>
Response receiving randomly:
<cas:serviceResponse xmlns:cas='http://www.xxx.xxx/tp/cas'>
<cas:authenticationSuccess>
<cas:user>xxxxxx</cas:user>
</cas:authenticationSuccess>
</cas:serviceResponse>
Please note: We have created a custom code to integrate CAS with our Asp.Net webforms application.
string userId = string.Empty;
// Look for the "ticket=" after the "?" in the URL
string tkt = HttpContext.Current.Request.QueryString["ticket"];
// Service url is the url of the Researcher Portal
string service ="www.xyz.com";
string CASHOST="https://cas.xyz.ca:8443/cas"
// First time through there is no ticket=, so redirect to CAS login
if (tkt == null || tkt.Length == 0)
{
string redir = CASHOST + "login?" +
"service=" + service;
HttpContext.Current.Response.Redirect(redir);
}
// Second time (back from CAS) there is a ticket= to validate
string validateurl = CASHOST + "serviceValidate?" +
"ticket=" + tkt +
"&service=" + service;
StreamReader Reader = new StreamReader(new WebClient().OpenRead(validateurl));
string resp = Reader.ReadToEnd();
if (isDebuggingMode)
sbDebugString.Append("****Response **** \n " + resp);
// Some boilerplate to set up the parse.
NameTable nt = new NameTable();
XmlNamespaceManager nsmgr = new XmlNamespaceManager(nt);
XmlParserContext context = new XmlParserContext(null, nsmgr, null, XmlSpace.None);
XmlTextReader reader = new XmlTextReader(resp, XmlNodeType.Element, context);
string userNumber = null;
// A very dumb use of XML. Just scan for the "userNumber". If it isn't there, it will return an empty string.
while (reader.Read())
{
if (reader.IsStartElement())
{
string tag = reader.LocalName;
if (isDebuggingMode)
sbDebugString.Append("tag : " + tag + "\n");
if (tag == "userNumber")
{
userNumber = reader.ReadString();
if (isDebuggingMode)
sbDebugString.Append("userNumber : " + userNumber + "\n");
}
}
}
Where "userNumber" attribute is not receiving always so that login fails randomly.
Please share your thoughts to resolve this issue.
Thank you in advance.
If your client application is not receiving attributes, you will need to make sure:
The client is using a version of CAS protocol that is able to
release attributes.
The client, predicated on #1, is hitting the appropriate endpoint for service ticket validation (i.e. /p3/serviceValidate).
The CAS server itself is resolving and retrieving attributes correctly.
The CAS server is authorized to release attributes to that particular client application inside its service registry.
Starting with CAS Protocol 3:
Among all features, the most noticeable update between versions 2.0 and 3.0 is the ability to return the authentication/user attributes through the new /p3/serviceValidate endpoint.
You may also find this post useful:
https://apereo.github.io/2017/06/23/cas-protocol-compatibility/

IIS Worker process using 6gb RAM on web server with ASP.NET MVC web site

I have a web site running in its own Application Pool (IIS 8). Settings for the pool are default i.e. recycle every 29 hours.
Our web server only has 8gb RAM and I have noticed that the worker process for this web site regularly climbs to 6gb RAM and slows the server to a crawl. This is the only site currently on the web server.
I also have SQL Express 2016 installed as well. The site is using EF version 6.1.3.
The MVC site is very straightforward. It has a GETPDF controller which finds a row in a table, gets PDF info stored in a field then serves it back to the browser as follows :-
using (eBillingEntities db = new eBillingEntities())
{
try
{
string id = model.id;
string emailaddress = Server.HtmlEncode(model.EmailAddress).ToLower().Trim();
eBillData ebill = db.eBillDatas.ToList<eBillData>().Where(e => e.PURL == id && e.EmailAddress.ToLower().Trim() == emailaddress).FirstOrDefault<eBillData>();
if (ebill != null)
{
// update the 'Lastdownloaded' field.
ebill.LastDownloaded = DateTime.Now;
db.eBillDatas.Attach(ebill);
var entry = db.Entry(ebill);
entry.Property(en => en.LastDownloaded).IsModified = true;
db.SaveChanges();
// Find out from the config record whether the bill is stored in the table or in the local pdf folder.
//
Config cfg = db.Configs.ToList<Config>().Where(c => c.Account == ebill.Account).FirstOrDefault<Config>();
bool storePDFDataInEBillTable = true;
if (cfg != null)
{
storePDFDataInEBillTable = cfg.StorePDFDataInEBillDataTable;
}
// End of Modification
byte[] file;
if (storePDFDataInEBillTable)
{
file = ebill.PDFData;
}
else
{
string pathToFile = "";
if (string.IsNullOrEmpty(cfg.LocalPDFDataFolder))
pathToFile = cfg.LocalBackupFolder;
else
pathToFile = cfg.LocalPDFDataFolder;
if (!pathToFile.EndsWith(#"\"))
pathToFile += #"\";
pathToFile += ebill.PDFFileName;
file = System.IO.File.ReadAllBytes(pathToFile);
}
MemoryStream output = new MemoryStream();
output.Write(file, 0, file.Length);
output.Position = 0;
HttpContext.Response.AddHeader("content-disposition", "attachment; filename=ebill.pdf");
return new FileStreamResult(output, "application/pdf");
}
else
return View("PDFNotFound");
}
catch
{
return View("PDFNotFound");
}
Are there any memory leaks here?
Will the file byte array and the memory stream get freed up?
Also, is there anything else I need to do concerning clearing up the entity framework references?
If the code looks OK, where would be a good place to start looking?
Regards
Are there any memory leaks here?
No.
Will the file byte array and the memory stream get freed up?
Eventually, yes. But that may be the cause of your excessive memory use.
Also, is there anything else I need to do concerning clearing up the entity framework references?
No.
If the code looks OK, where would be a good place to start looking?
If this code is the cause of your high memory use, it's because you are loading files into memory. And you're loading two copies of each file in memory, once in a byte[] and copying to a MemoryStream.
There's no need to do that.
To eliminate the second copy of the file use the MemoryStream(byte[]) constructor instead of copying the bytes from the byte[] to an empty MemoryStream.
To eliminate the first copy in memory, you can stream the data into a temporary file that will be the target of your FileStreamResult, or initialize the FileStreamResult using a ADO.NET stream.
See https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sqlclient-streaming-support
If you go to ADO.NET streaming your DbContext, will need to be scoped to your Controller, instead of a local variable, which is a good practice in any case.
In addition to David's advice. I noticed that I was doing the following
**db.eBillDatas.ToList<eBillData>()**
therefore I was getting all the data from the database then fetching it again with the where clause.
I didn't notice the problem until the database started to fill up.
I removed that part and now the IIS worker processing is about 100mb.

DotRas unauthorized access exception when adding phonebook an entry

I am devoloping an application with dotras dll. In the application my main goal is to connect to the internet with 3g USB modem. I am adding my code below :
RasPhoneBook book = new RasPhoneBook();
book.Open();
dialer = new RasDialer();
dialer.StateChanged += new EventHandler<StateChangedEventArgs> (dialer_StateChanged);
RasDevice device = RasDevice.GetDeviceByName("ZTE", RasDeviceType.Modem);
RasEntry entry = RasEntry.CreateDialUpEntry("MyEntry", "*99#", device);
entry.EncryptionType = RasEncryptionType.Optional;
entry.Options.ModemLights = true;
entry.NetworkProtocols.IP = true;
entry.NetworkProtocols.Ipx = false;
book.Entries.Add(entry);
dialer.PhoneBookPath = book.Path;
dialer.EntryName = "MyEntry";
dialer.Dial();
When I tried to add the phonebook my entry it's returning an exception :
UnauthorizedAccessException was Unhandled
The caller does not have required permission to perform the action requested.
I'm new about DotRas. So which permissions does it neeeded to add an entry to the phonebook?
Any ideas?
I have found the answer. The reason why I need to elevate privileges for the application is due to the application opening the All User's profile phone book. This is indicated by my call to book.Open()
If you encountered a problem like this remember you can always store the phone book next to your application, which will remove the need to elevate permissions.
The code should be :
RasPhoneBook book = new RasPhoneBook();
book.Open(".\\MyAppPhoneBook.pbk");

The underlying provider failed on Open

I made 3 Ajax processes to run the below code at the same time.
but one of the processes throw exception that message says "The underlying provider failed on Open."
try{
orderRepository orderRepo = new orderRepository(); // get context (Mysql)
var result = (from x in orderRepo.orders
where x.orderid == orderno
select new {x.tracking, x.status, x.charged }).SingleOrDefault();
charged = result.charged;
}catch(Exception e){
log.Error(e.Message); // The underlying provider failed on Open.
}
And, I run the 1 Ajax call that failed before, then It passes through.
It happen to 1 of 3 (Ajax) process, sometimes, 2 of 5 process.
I guess it because all process try to using Database same time. but I couldn't find the solution.
This is my connection string,
<add name="EFMysqlContext" connectionString="server=10.0.0.10;User Id=root;pwd=xxxx;Persist Security Info=True;database=shop_db" providerName="Mysql.Data.MySqlClient" />
Anybody know the solution or something I can try, please advise me.
Thanks
It sounds like a problem because of concurrent connection with SQL Server using same username. Have you tried destroying/disposing the repository(or connection) object after using it?
Give it a try:
try{
using( orderRepository orderRepo = new orderRepository()) // get context (Mysql)
{
var result = (from x in orderRepo.orders
where x.orderid == orderno
select new {x.tracking, x.status, x.charged }).SingleOrDefault();
charged = result.charged;
} // orderRepo object automatically gets disposed here
catch(Exception e){
log.Error(e.Message); // The underlying provider failed on Open.
} }
Not sure if it matters, but your provider name is Mysql.Data.MySqlClient and not MySql.Data.MySqlClient (if it is case-sensitive, this could be the cause).

Asp .net Session is timeout and all data wasn't saved , any idea?

I created an web application where a lot of words was needed in the application which take a lot of time and thinking when u need to write it.
Let's suppose the session timeout after 30 minutes,i started writing a lot of words and while thinking and writing the session timeout and redirect to the login page and all written data is lost.
Any idea for this problem except extending session timeout period ???
Currently your session is created and managed as In-Process mode and in this mode you cannot recover session state once it reaches timeout stage. You may set SQL Server Mode and configure your application for SQL Server Mode so your data will be persisted into Sql Server database.
Profile Properties is an alternate to save the state.
You can use some ajax function that regularly "calls home" (executes some dummy code on the server). This will keep the session alive as long as that user has this page open.
You might need to explicitly use the Session in that callback, such as
Session["LastAccess"] = DateTime.Now;
just to keep it alive.
If you execute this call every 15 minutes, the session will not time out and the load on the server is minimal.
Use asynchronous programming model which allowing some portion of code to be executed on a separate threads.
There are three style of Programming with APM
Wait Until Done Model
Polling Model
Callback Model
Based on your requirement and result you can choose the model which is more appropriate.
For instance, let us say you can to read the file and wait until done and sample code is
byte[] buffer = new byte[100];
string filename =
string.Concat(Environment.SystemDirectory, "\\mfc71.pdb");
FileStream strm = new FileStream(filename,
FileMode.Open, FileAccess.Read, FileShare.Read, 1024,
FileOptions.Asynchronous);
// Make the asynchronous call
strm.Read(buffer, 0, buffer.Length);
IAsyncResult result = strm.BeginRead(buffer, 0, buffer.Length, null, null);
// Do some work here while you wait
// Calling EndRead will block until the Async work is complete
int numBytes = strm.EndRead(result);
// Don't forget to close the stream
strm.Close();
Console.WriteLine("Read {0} Bytes", numBytes);
Console.WriteLine(BitConverter.ToString(buffer));
But creating your threads are not necessary or suggesting, .NET supports a built in thread pool that can be used in many situation where you thinking to create your own threads. Sample code
static void WorkWithParameter(object o)
{
string info = (string) o;
for (int x = 0; x < 10; ++x)
{
Console.WriteLine("{0}: {1}", info,
Thread.CurrentThread.ManagedThreadId);
// Slow down thread and let other threads work
Thread.Sleep(10);
}
}
Instead of creating a new thread and controlling it, we use the ThreadPool to this work by using its QueueWorkItem method
WaitCallback workItem = new WaitCallback(WorkWithParameter));
if (!ThreadPool.QueueUserWorkItem(workItem, "ThreadPooled"))
{
Console.WriteLine("Could not queue item");
}

Resources