Skip to content

[Process] Permission Denied writing to sf_proc_00.out.lock when file is generated by a different AppPoolIdentity User #37294

@JasonStephensTAMU

Description

@JasonStephensTAMU

Symfony version(s) affected: 4.4.8|5.1.2

  • OS: Windows 10
  • Server: IIS version 10
  • PHP version 7.3
  • Laravel version 6.18

Note: Bug originally encountered on Laravel 6.18 using symfony/process 4.4.8. I have replicated the issue on a fresh symfony/skeleton 5.1.2 base with the process package required through composer.

Description

When two or more sites are running IIS / FastCGI as different AppPoolIdentity users with limited group permissions to the sys_temp_dir, the first to write sf_proc_00.* files used by the Process class takes ownership, however any additional sites that attempt to write to this file will receive the following error:

A temporary file could not be opened to write the process output: fopen(C:\WINDOWS\TEMP\sf_proc_00.out.lock): failed to open stream: Permission denied

Our permissions are configured to prevent one site from modifying temporary files that are created by another.

How to reproduce

  1. In IIS: Add two sites using the Symfony\Process component with different Application Pools
  2. In IIS Application Pools: For both application pools used, select "Advanced Settings" and confirm the "Identity" field is set to the built-in "ApplicationPoolIdentity" account,
  3. In IIS Feature Panel: For both sites, select "Authentication" > "Anonymous Authentication", ensure it is enabled and click "Edit..." then select "Application Pool Identity" and click OK.
  4. Set limited permissions for the IIS_IUSRS group on the sys_temp_dir (this is C:\Windows\Temp on my machine). Verify the Users group does not exceed these permissions.
    • Permissions used in testing: List folder / read data, Read attributes, Read extended attributes, Create files / write data, Read permissions
  5. For both sites, create some code that uses the Process class to run a command. (See Additional Context for the sample code I used to test)
  6. In Site A, run the code using the Process class and check the sys_temp_dir to confirm sf_proc_00 files were generated.
  7. In Site B, attempt to run the code using the Process class to throw the Permission Denied error.

Possible Solution

Perhaps a solution could be incrementing the filename when the lock files exist, but cannot be opened for writing, and removing the sf_proc_## files used in the WindowsPipes destructor.

I have limited experience in using the Process class, I'll post the code that has fixed it in my particular instance, however I do not know if it is ideal for all use cases.

symfony\process\Pipes\WindowsPipes.php, Line 58: (Try next iteration if lock file exists)

                    if (!$h = fopen($file.'.lock', 'w')) {
                        if (file_exists($file.'.lock'))
                            continue 2;

                        restore_error_handler();
                        throw new RuntimeException('A temporary file could not be opened to write the process output: '.$lastError);
                    }

symfony\process\Pipes\WindowsPipes.php, Line 88: (Remove files on destruct)

    public function __destruct()
    {
        $this->close();

        foreach ($this->files as $file) {
            unlink($file);
            unlink($file.'.lock');
        }
    }

Additional context

During replication, I made a simple controller that dumps output of the dir command for testing:

src\Controller\HomeController.php:

<?php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

class HomeController
{
    public function index()
    {
        $process = new Process(['dir']);
        $process->run();

        if(!$process->isSuccessful())
            throw new ProcessFailedException($process);

        return new Response($process->getOutput());
    }
}

And attached it to the base route:

config\routes.yaml:

index:
    path: /
    controller: App\Controller\HomeController::index

The same code was used on both Site A and B.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy