views:

225

answers:

3

I'm having a problem executing a command from a command line through an ASP script using the wscript.shell object.

Here is my code:

inPath = server.mappath("/connect/dev_f_fusion3/video/6EA63679C27E48538D79F7C7295201CF/6EA63679C27E48538D79F7C7295201CF.mov")
outPath = server.mappath("/connect/dev_f_fusion3/video/6EA63679C27E48538D79F7C7295201CF\6EA63679C27E48538D79F7C7295201CF.flv"
outPath = "\\webdev2\SVC\streams\dev_f_fusion3\6EA63679C27E48538D79F7C7295201CF.flv"

dim cmd
dim wshell
dim wffm

cmd = """C:\Program Files\ffmpeg\ffmpeg.exe"" -i """ & inPath & """ -ar ""22050"" -ab ""32"" -f ""flv"" -s ""320x240"" """ & outPath & """"
set wshell  = server.createobject("wscript.shell")
set wffm = wshell.exec(cmd)

set wffm = nothing
set wshell = nothing

As you can see I've defined the variable outPath twice as an example for you to help me. With the first outPath assignment the wscript.shell object executes just fine, however, with the second outPath assignment it fails, leading me to believe it has something to do with the UNC path I've specified as the outPath.

Is the wscript.shell object not allowed to access UNC paths? If so where I can change it so that it will allow them?

Thanks for the help!

+1  A: 

Assuming you use IIS 6, have a look at these links

Gaby
@Gaby: Thanks for the answer. I have no problem creating a virtual directory in IIS and pointing to another server via UNC paths the problem comes when I try to use the wscript.shell object and pass in a unc path as one of the command line parameters. I'm fairly certain it's a permissions issue with either the wscript.shell object or the ffmpeg executable not having access to UNC paths.
Ryan
@Ryan, it has to be that the account your asp is running under (*usually the Network Service*) is extremely limited (*for good reasons*). Most answers i have found about it point to using impersonation. have a look at http://www.west-wind.com/weblog/posts/2153.aspx as well :) (*sorry for overloading you with links ..*)
Gaby
+1  A: 

It would be helpful if you posted the specific error that you are getting. What type of authentication is being used for this request? If you are using anonymous then you need to use a domain anon user, or use a local account that has the same username and password on the target server.

Mike
@Mike: the problem is I don't get an error, the wscript.shell command just fails to execute. I've tried writing out wffm.stdout.readall() but nothing ever prints out to the page so I don't know what the error is. If you have another suggestion for how to get the error text from the wscript.shell please share. The asp script is set to run as a domain user under anonymous access. The AppPool the web site is set to run under in IIS is assigned the Network Service account.
Ryan
Process Monitor... You can try to run process monitor (http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx). I have sometimes had trouble with process monitor showing all of the network file access, but in some cases it will show you the error. Try running process monitor and look for the failed entry for your file. This should tell you the user that is accessing the file, and the access rights that are being requested.Another thing to try is to login as the anonymous user and see if you can access the file.
Mike
@Mike: Thanks for the handy tool you linked to. Using that I see that the ffmpeg.exe process is trying to be run as NT AUTHORITY\NETWORK SERVICE which must not have access to write files to the remote server. How do I remedy this? I thought setting the page up in IIS to run anonymous access as a domain account would cause everything on the page to run as that domain account? The domain account I have the page set up with does have read/write access to the remote server. The error I get in Proc Mon is Access Denied on a create file call to the remote server path.
Ryan
There are a couple of options here:1) Update the web service process so it is running as a domain user that has access to the desired share, instead of network service.2) You can use the "net use" api's, like WNetAddConnection() (http://msdn.microsoft.com/en-us/library/aa385413%28VS.85%29.aspx), to add the connection to the remote share as a different user. This is a more complex way to solve this, but does provide the most options on using local and/or domain user accounts for access. Although it takes some coding, I have used this method a few times before with complete success.
Mike
+1  A: 

Sounds like there are two potential sources of the problem here: 1. issues related to launching a new process and having that process access a UNC share 2. issues related to permissions whether or not a new process is used to acces the UNC share

I'd start by testing #2, by checking to see if your site can read a file from the remote share without launching a new process to do so. For example:

<%
dim filesys, filetxt
Const ForReading = 1, ForWriting = 2, ForAppending = 8 
Set filesys = CreateObject("Scripting.FileSystemObject")
Set filetxt = filesys.OpenTextFile("\\webdev2\SVC\streams\dev_f_fusion3\testfile.txt", ForReading, True) 
Response.Write (filetxt.ReadAll())
filetxt.Close 
%> 

If this fails, then your problem is probably relatively straightforward to solve: your website doesn't have access to the remote share. To fix, the easiest way would be to use anonymous authentication and pick an anonymous user with access to the share. Here's a tutorial explaing how to do this. Make sure to disable integrated windows auth, as the tutorial recommends!

Also make sure you are very careful with how you are processing and escaping user input that gets transformed into your EXE path, since you really don't want to enable a malicious visitor to be able to run arbitrary code on your network! Ideally, you'd never create any part of an executed path or command-line args directly based on user input, but only indirectly (e.g. use user input to look up a known-safe filename in a DB, and then use that DB result in your path).

There are other ways (other than anonymous auth under a domain user) to enable remote access, including delegation, using basic auth over HTTPS, etc. Let me know with more details if you can't use anonymous auth and I can help you figure out the right alternative.

However, if the file-access test above succeeds, your problem is more complex-- it means that code running inside the IIS process has the correct access, but a spawned process doesn't inherit the ability to access remote resources.

UPDATE: per your comment below, you've diagnosed that the problem is that the spawned process is launched under the wrong user account. This is fixable by calling some Win32 APIs directly, and there are .NET solutions too, but unless you're comfortable outside VBScript that may be more than you'll want to take on right now.

Can you copy the file from the remote machine into a temporary file on the local machine, then run FFMPEG.EXE, then (if you need to) save back changes to the remote server, and finally delete the temporary file?

If you can do this, it will avoid the problem described in this thread, will make it easier to catch other problems (e.g. network failure) inside your ASP code rather than hidden inside FFMPEG.EXE, and it may even boost performance depending on how efficiently FFMPEG.EXE handles remote file access.

Here's some code for copying a remote file into a temporary local location. Warning, there may be some typos below as I glued together a few samples to create it and haven't had time to test it myself.

<%
dim filesys, filetxt
Const ForReading = 1, ForWriting = 2, ForAppending = 8 
Set filesys = CreateObject("Scripting.FileSystemObject")

Const TemporaryFolder = 2
Dim tfolder, tname, tfile
Set tfolder = fso.GetSpecialFolder(TemporaryFolder)
tname = tFolder & "/" & fso.GetTempName

Dim remoteFilename
remoteFilename = "\\webdev2\SVC\streams\dev_f_fusion3\testfile.txt"

Const OverwriteExisting = True
filesys.CopyFile  remoteFilename, tName, OverwriteExisting    
%> 

I believe that this local-copying approach will give you the best reliability and probably best performance since copying entire files over a network connection is usually faster than reading one chunk at a time. Also your error handling and debugging will be easier if the operations most likley to fail (the networked file access) happen inside your code rather than someone else's.

But if you are convinced that calling your EXE with a UNC access is required, your best bet is building a COM component using C++, C#, or VB which replaces Wsh.Shell as your process launcher. I'd recomend building the component using a .NET language instead of native C++ because the calls will be much easier. If you do it in .NET, you'll want to use the System.Diagnostics.Process class to launch your process, using the ProcessStartInfo class to specify the username/password to use.

You can also try creating a connection using WNetAddConnection2 and then launch your process using CreateProcess or a similar Win32 API (or its .NET equivalent), but you may need to to play around to find the right parameters to use.

In other words, it's a non-trivially hard problem to build this wrapper. Your best bet is probably your current workaround.

Justin Grant
@Justin: Thanks for the reply. I already know the permissions are all in order as far as the domain user set up for the asp page to run as. I'm able to access the share via the file system object on the same page. The paths I'm trying to access are in no way influenced by user input other than the file they choose to upload via a form so I think my bases are covered there. I think it has to do with your #1 guess. The execution of the process ffmpeg.exe is launched with some other user account who doesn't have access to the share. Still looking into the problem though.
Ryan
@Justin: Using the Process Monitor tool recommended by @Mike, I was able to find out exactly the problem. The ffmpeg.exe process started in my code is being executed as NT AUTHORITY\NETWORK SERVICE which doesn't have access to the remote server. The error I get in Process Monitor is Access Denied on a create file call to the remote server path. I'm not sure how to fix this. It seems the process is automatically being started as the user who Identity is set up in the AppPool the site is running under. I don't want to change the ID in the AppPool though.
Ryan
@Ryan - unfortunately, looks like this problem is a tough one to solve with VBScript alone. See my updated answer above. My suggestion: consider copying the file from the remote server into a local temporary file and doing your operation locally. Your other options are harder, and perhaps slower and less reliable too.
Justin Grant
@Justin: That's what I did to fix the problem in the mean time. Not really a fix but a work around. I have full server access so I can perform fixes that require more than just VBScript as long as you can tell me how to go about fixing the problem.
Ryan
@Ryan - Afer some reflection, I believe your current workaround is actually the best solution. Networked file access from a separate EXE (especially one you can't easily debug from source) will likley cause trouble even if you resolve the security challenges above. For example, error messages from dropped connections or new security issues may not be clear. Also, apps not explicitly designed for networked file access may not perform ideally when run over a network-- for example, if they process file data in small chunks, the chattiness will slow down your app.
Justin Grant
Also, the various steps you'll need to do for connecting to the remote server may come with a per-call performance cost. Without trying them it's hard to know, though.
Justin Grant
But if you really want to do it the hard way, I revised my answer to give you some pointers.
Justin Grant