views:

3571

answers:

2

Hi,

I have an unusual situation in which I need a sharepoint timer job to both have local administrator windows privileges and to have SHAREPOINT\System sharepoint privileges.

I can get the windows privileges by simply configuring the timer service to use an account which is a member of local administrators. I understand that this is not a good solution since it gives sharepoint timer service more rights then it is supposed to have. But it at least allows my sharepoint timer job to run stsadm.

Another problem with running the timer service under local administrator is that this user won't necessarily have SHAREPOINT\System sharepoint privilegies which I also need for this sharepoint job. It turns out that SPSecurity.RunWithElevatedPrivilegies won't work in this case. Reflector shows that RunWithElevatedPrivilegies checks if the current process is owstimer (the service process which runs sharepoint jobs) and performs no elevation this is the case (the rational here, I guess, is that the timer service is supposed to run under NT AUTHORITY\NetworkService windows account which which has SHAREPOINT\System sharepoint privileges, and thus there's no need to elevate privileges for a timer job)

The only possible solution here seems to be to run the timer service under it's usual NetworkSerice windows account and to run stsadm as a local administrator by storing the administrator credentials somewhere and passing them to System.Diagnostics.Process.Run() trough the StarInfo's Username, domain and password.

It seems everything should work now, but here is another problem I'm stuck with at the moment. Stsamd is failing with the following error popup (!) (Winternals filemon shows that stsadm is running under the administrator in this case):

The application failed to initialize properly (0x0c0000142). Click OK to terminate the application.

Event Viewer registers nothing except the popup.

The local administrator user is my account and when I just run stsadm interactively under this account everything is ok. It also works fine when I configure the timer service to run under this account.

Any suggestions are appreciated :)

+1  A: 

I'm not at work so this is off the top of my head, but: If you get a reference to the Site, can you try to create a new SPSite with the SYSTEM-UserToken?

SPUserToken sut = thisSite.RootWeb.AllUsers["SHAREPOINT\SYSTEM"].UserToken;
using (SPSite syssite = new SPSite(thisSite.Url,sut){
// Do what you have to do
}
Michael Stum
A: 

Other applications if run this way (i.e. from a timer job with explicit credentials) are failing the same way with "The application failed to initialize propely". I just worte a simple app which takes a path of another executable and its arguments as parameres and when run from that timer job it fails the same way.

internal class ExternalProcess
{
    public static void run(String executablePath, String workingDirectory, String programArguments, String domain, String userName,
                           String password, out Int32 exitCode, out String output)
    {
        Process process = new Process();

        process.StartInfo.UseShellExecute = false;
        process.StartInfo.RedirectStandardError = true;
        process.StartInfo.RedirectStandardOutput = true;

        StringBuilder outputString = new StringBuilder();
        Object synchObj = new object();

        DataReceivedEventHandler outputAppender =
            delegate(Object sender, DataReceivedEventArgs args)
                {
                    lock (synchObj)
                    {
                        outputString.AppendLine(args.Data);
                    }
                };

        process.OutputDataReceived += outputAppender;
        process.ErrorDataReceived += outputAppender;

        process.StartInfo.FileName = @"C:\AppRunner.exe";
        process.StartInfo.WorkingDirectory = workingDirectory;
        process.StartInfo.Arguments = @"""" + executablePath + @""" " + programArguments;


        process.StartInfo.UserName = userName;
        process.StartInfo.Domain = domain; 
        SecureString passwordString = new SecureString();
        foreach (Char c in password)
        {
            passwordString.AppendChar(c);
        }
        process.StartInfo.Password = passwordString;

        process.Start();

        process.BeginOutputReadLine();
        process.BeginErrorReadLine();

        process.WaitForExit();

        exitCode = process.ExitCode;
        output = outputString.ToString();
    }
}

AppRunner basically does the same as the above fragment, but without username and password

axk