# Race Condition in File Creation via os.path.exists() TOCTOU

Language: Python
Severity: High
CWE: CWE-367

## Source
7

## Flow
7-8-9-10

## Sink
8

## Vulnerable Code
```python
import os
import json

def provision_iot_device_certificate(device_id, cert_data):
    cert_dir = '/var/iot/device_certs'
    cert_path = os.path.join(cert_dir, f'{device_id}.pem')
    if not os.path.exists(cert_path):
        with open(cert_path, 'w') as cert_file:
            cert_file.write(cert_data)
        os.chmod(cert_path, 0o600)
        return {'status': 'provisioned', 'device': device_id, 'cert_path': cert_path}
    else:
        return {'status': 'already_exists', 'device': device_id}
```

## Explanation

The code suffers from a Time-of-Check Time-of-Use (TOCTOU) race condition. Between checking if the file exists (line 7) and creating it (line 8), an attacker could create a symlink at that path pointing to a sensitive file, causing the certificate data to overwrite the target file with attacker-controlled device_id in the path.

## Remediation

The fix eliminates the TOCTOU race condition by using os.open() with O_CREAT | O_EXCL | O_NOFOLLOW flags, which atomically creates the file only if it doesn't already exist and refuses to follow symlinks. Additionally, input validation on device_id prevents path traversal attacks, and the file permissions are set atomically at creation time via the mode parameter to os.open(), removing the need for a separate chmod call.

## Secure Code
```python
import os
import json
import re

def provision_iot_device_certificate(device_id, cert_data):
    cert_dir = '/var/iot/device_certs'
    
    # Validate device_id to prevent path traversal
    if not re.match(r'^[a-zA-Z0-9_\-]+$', device_id):
        return {'status': 'error', 'device': device_id, 'message': 'Invalid device_id'}
    
    cert_path = os.path.join(cert_dir, f'{device_id}.pem')
    
    # Verify the resolved path is within the expected directory
    real_cert_dir = os.path.realpath(cert_dir)
    # Use os.path.normpath to check intended path stays within cert_dir
    if not os.path.normpath(cert_path).startswith(real_cert_dir + os.sep):
        return {'status': 'error', 'device': device_id, 'message': 'Path traversal detected'}
    
    # Use O_CREAT | O_EXCL | O_WRONLY to atomically create the file only if it doesn't exist
    # O_NOFOLLOW prevents following symlinks
    try:
        fd = os.open(cert_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY | os.O_NOFOLLOW, 0o600)
    except FileExistsError:
        return {'status': 'already_exists', 'device': device_id}
    except OSError as e:
        return {'status': 'error', 'device': device_id, 'message': str(e)}
    
    try:
        with os.fdopen(fd, 'w') as cert_file:
            cert_file.write(cert_data)
    except Exception:
        # Clean up on write failure
        os.unlink(cert_path)
        raise
    
    return {'status': 'provisioned', 'device': device_id, 'cert_path': cert_path}
```
