Activate native SMB audit runtime switches explicitly (#441)

* Add explicit native SMB runtime audit activation

* Select explicit PowerShell workflow shells and link PR changelog

* Clear expected refusal child exit codes after assertions

* Retain native SMB command provenance in capability diagnostics

* Bind SMB command guards to observed native CDXML module identities
This commit is contained in:
田中ザック Isaac Mathis authored and GitHub committed 2026-09-21 18:09:10 +09:00
1 parent b4fb77da02
commit 9d03a19082
13 files changed
+547 -2

No files matched your search

+2 -2
View File
@@ -41,7 +41,7 @@ jobs:
Copy-Item -Recurse -Path ./scripts -Destination release-binaries/
Copy-Item -Recurse -Path ./modules -Destination release-binaries/
New-Item -ItemType Directory -Path release-binaries/docs -Force | Out-Null
Copy-Item -Path ./docs/gpo-audit-packages.md, ./docs/gpo-package-deployment.md, ./docs/gpo-creation.md, ./docs/wmi-probe.md, ./docs/wec-state.md, ./docs/wec-update.md, ./docs/wec-runtime.md, ./docs/wef-deployment.md, ./docs/native-channel-access.md, ./docs/eventlog-settings.md, ./docs/channel-read.md, ./docs/native-rule-eligibility.md, ./docs/native-validation.md, ./docs/wef-arrival.md, ./docs/ipsec-prerequisites.md -Destination release-binaries/docs/
Copy-Item -Path ./docs/gpo-audit-packages.md, ./docs/gpo-package-deployment.md, ./docs/gpo-creation.md, ./docs/wmi-probe.md, ./docs/smb-runtime-activation.md, ./docs/smb-auditing.md, ./docs/wec-state.md, ./docs/wec-update.md, ./docs/wec-runtime.md, ./docs/wef-deployment.md, ./docs/native-channel-access.md, ./docs/eventlog-settings.md, ./docs/channel-read.md, ./docs/native-rule-eligibility.md, ./docs/native-validation.md, ./docs/wef-arrival.md, ./docs/ipsec-prerequisites.md -Destination release-binaries/docs/
- name: Set Artifact Name
if: contains(matrix.info.os, 'windows') == true
@@ -77,4 +77,4 @@ jobs:
with:
name: wela-documents
path: |
./*.pdf
./*.pdf
@@ -0,0 +1,58 @@
name: SMB runtime audit activation
on:
push:
branches: ['**']
paths:
- 'WELA.ps1'
- 'scripts/SmbRuntimeActivation.ps1'
- 'scripts/SmbAuditing.ps1'
- 'scripts/Configuration.ps1'
- 'scripts/WefArrival.ps1'
- 'tests/SmbRuntimeActivation*'
- '.github/workflows/smb-runtime-activation.yml'
pull_request:
workflow_dispatch:
permissions:
contents: read
jobs:
native-smb-activation:
strategy:
fail-fast: false
matrix:
os: [windows-2022, windows-2025]
shell: [powershell, pwsh]
runs-on: ${{ matrix.os }}
timeout-minutes: 15
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Focused regressions in Windows PowerShell 5.1
if: matrix.shell == 'powershell'
shell: powershell
run: |
./tests/SmbRuntimeActivation.Tests.ps1
./tests/SmbRuntimeActivation.Cli.Tests.ps1
- name: Native activation and restoration in Windows PowerShell 5.1
if: matrix.shell == 'powershell'
shell: powershell
env:
WELA_DISPOSABLE_SMB_ACTIVATION: 'true'
run: ./tests/SmbRuntimeActivation.Windows.Tests.ps1
- name: Focused regressions in PowerShell 7
if: matrix.shell == 'pwsh'
shell: pwsh
run: |
./tests/SmbRuntimeActivation.Tests.ps1
./tests/SmbRuntimeActivation.Cli.Tests.ps1
- name: Native activation and restoration in PowerShell 7
if: matrix.shell == 'pwsh'
shell: pwsh
env:
WELA_DISPOSABLE_SMB_ACTIVATION: 'true'
run: ./tests/SmbRuntimeActivation.Windows.Tests.ps1
- name: Retain native evidence
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: smb-runtime-${{ matrix.os }}-${{ matrix.shell }}
path: ${{ runner.temp }}/wela-smb-runtime-*/
if-no-files-found: warn
+2
View File
@@ -4,6 +4,8 @@
**改善:**
- `smb-runtime` を追加し、Windows 標準の SMB 監査スイッチ6個を明示的に有効化します。モジュール・ビルド・ADMX、型付きポリシーの競合、設定全体の変化を確認し、各変更の意図と確認結果を永続的に記録します。署名・暗号化・ゲスト接続・サービス設定を保持し、現在の有効化とイベント・永続性・Sigma の証明を区別します。使い捨て Server 2025 の有効化と復元、Server 2022 の拒否を PowerShell 5.1/7 で検証します。 (#441) (@Shirofune-Security)
- 既存のWindows標準ソース開始型購読1件のEnabledだけをレビュー後に変更する`wec-state`のPlan/Applyを追加しました。送信元認可と完全な定義、実行者・ログオン・トークン、永続的な変更前記録とネイティブ読戻しを確認して他の設定を保持し、既に一致する状態では保存・再有効化を行いません。稼働状況の観測と配送・ブックマークの継続性を区別し、使い捨てWindowsテストで実際の有効/無効切替と所有リソース・サービス状態の復元を確認します。 (関連 #368) (@Shirofune-Security)
- 強化プロファイルのオプション IPsec Main Mode 監査に、Windows ネイティブの前提条件確認を追加しました。有効なポリシーストアのルールと現在の関連付けを読み取り、適用可能・確認範囲内で未観測・不明を区別します。共有設定処理は書き込み直前に再確認し、明示的な選択とカスタムプロファイルの指定を保持します。Server 2022/2025・PowerShell 5.1/7 の一時ルールを使ったテストで監査ポリシーの復元を確認します。ネゴシエーション、イベント生成、Sigma の対応は保証しません。 (#370) (@Shirofune-Security)
+2
View File
@@ -4,6 +4,8 @@
**Improvements:**
- Added explicit `smb-runtime` activation of six native SMB audit switches, with reviewed module/build/ADMX capabilities, typed policy conflicts, complete configuration drift guards and durable per-switch receipts. Signing, encryption, guest access and service settings are preserved; runtime verification stays separate from events, persistence and Sigma credit. Disposable Server 2025 activation/restoration and Server 2022 refusal tests cover PowerShell 5.1/7. (#441) (@Shirofune-Security)
- Added reviewed `wec-state` Plan/Apply for the Enabled flag of one existing native source-initiated subscription. Exact authorization and complete definition checks, operator/logon/token guards, durable pending evidence and native readback preserve other settings; matching states never save or reactivate. Runtime remains separate, and interrupted delivery/bookmark continuity require multi-host validation. Disposable Windows lifecycle tests restore owned resources and service state. (Related #368) (@Shirofune-Security)
- Added native prerequisite evidence for the stronger profile's optional IPsec Main Mode auditing. Effective-store rule and current association observations distinguish scoped applicability, absence and unknown results; both shared configuration paths recheck before writing and preserve explicit selection/custom-profile intent. Native disposable-rule tests cover Server 2022/2025 and PowerShell 5.1/7 with exact audit-policy restoration, without negotiation/event or Sigma claims. (#370) (@Shirofune-Security)
+16
View File
@@ -24,6 +24,8 @@
[ValidateRange(16384, 32767)][int]$FirewallMinimumSizeKiB = 16384,
[string]$HtmlPath,
[ValidateSet('Audit', 'Plan', 'Configure')][string]$SmbAction = 'Audit',
[ValidateSet('Plan','Activate')][string]$SmbRuntimeAction = 'Plan',
[string]$SmbRuntimeOutputPath,
[ValidateSet('Audit', 'Plan', 'Configure', 'Rollback')][string]$AdSaclAction = 'Audit',
[string]$AdServer,
[ValidateSet('MdiDomain', 'MdiConfiguration', 'PkiObjects')][string[]]$AdSaclProfile,
@@ -163,6 +165,7 @@ $SaclTargetsPath = Join-Path $ScriptRoot "config/audit_sacl_targets.json"
. (Join-Path $ScriptRoot "scripts/AuditIntegrity.ps1")
. (Join-Path $ScriptRoot "scripts/FirewallLogging.ps1")
. (Join-Path $ScriptRoot "scripts/SmbAuditing.ps1")
. (Join-Path $ScriptRoot "scripts/SmbRuntimeActivation.ps1")
. (Join-Path $ScriptRoot "scripts/LdapDiagnostics.ps1")
. (Join-Path $ScriptRoot "scripts/ControlApplicability.ps1")
. (Join-Path $ScriptRoot "scripts/NativeValidation.ps1")
@@ -1934,6 +1937,8 @@ Usage:
# Firewall text logging is opt-in; it does not change firewall enforcement or rules.
./WELA.ps1 smb-auditing -SmbAction Audit -ResultsPath smb-audit.json
./WELA.ps1 smb-auditing -SmbAction Plan
./WELA.ps1 smb-runtime -SmbRuntimeAction Plan
./WELA.ps1 smb-runtime -SmbRuntimeAction Activate -SmbRuntimeOutputPath C:\Evidence\new-smb -Auto
./WELA.ps1 rule-eligibility -ResultsPath eligibility.json -HtmlPath eligibility.html
./WELA.ps1 event-measurement -MeasurementChannel Security
./WELA.ps1 event-measurement -MeasurementChannel Security -MeasurementAction Run -MeasurementOutputPath C:\Evidence\new-sample -MeasurementExportEvtx
@@ -1990,6 +1995,8 @@ Write-Host "WELA v$WELAVersion - $WELAReleaseName"
Write-Host ""
if ($Cmd -ne 'channel-read' -and @($PSBoundParameters.Keys | Where-Object { $_ -like 'ChannelRead*' }).Count) { throw 'ChannelRead options require channel-read. No command was run.' }
if ($Cmd -ne 'smb-runtime' -and @($PSBoundParameters.Keys | Where-Object { $_ -like 'SmbRuntime*' }).Count) {throw 'SmbRuntime options require smb-runtime. No command was run.'}
if ($Cmd -eq 'smb-runtime' -and @($PSBoundParameters.Keys | Where-Object { $_ -notin @('Cmd','SmbRuntimeAction','SmbRuntimeOutputPath','Auto','DryRun','Help') }).Count) {throw 'smb-runtime accepts only its dedicated options, Auto and DryRun. No command was run.'}
if ($Cmd -eq 'channel-read' -and @($PSBoundParameters.Keys | Where-Object { $_ -notin @('Cmd','ChannelReadName','ChannelReadOutputPath','Help') }).Count) { throw 'channel-read accepts only dedicated channel/output options. No command was run.' }
if ($Cmd -ne 'event-measurement' -and @($PSBoundParameters.Keys | Where-Object {$_ -like 'Measurement*'}).Count) {throw 'Measurement options require event-measurement. No command was run.'}
@@ -2133,6 +2140,7 @@ if ($DryRun -and -not ($Cmd -eq 'adcs-auditing' -and $AdcsAction -eq 'Configure'
-not ($Cmd -eq 'provider-packs' -and $ProviderAction -eq 'Configure') -and
-not ($Cmd -eq 'firewall-logging' -and $FirewallAction -eq 'Configure') -and
-not ($Cmd -eq 'smb-auditing' -and $SmbAction -eq 'Configure') -and
-not ($Cmd -eq 'smb-runtime' -and $SmbRuntimeAction -eq 'Activate') -and
-not ($Cmd -eq 'powershell-transcription' -and $TranscriptionAction -eq 'Configure') -and
-not ($Cmd -eq 'channel-settings' -and $ChannelAction -eq 'Configure') -and
-not ($Cmd -in @('wef-source','wec-collector') -and $WefAction -eq 'Configure') -and
@@ -2440,6 +2448,14 @@ switch ($Cmd.ToLower()) {
if ($report.ExitCode) { exit $report.ExitCode }
} catch { Write-Host "[Failed] Firewall logging: $_" -ForegroundColor Red; exit 1 }
}
'smb-runtime' {
if ($Help) {Write-Host 'Usage: ./WELA.ps1 smb-runtime [-SmbRuntimeAction Plan|Activate] [-SmbRuntimeOutputPath new-local-directory] [-Auto] [-DryRun]. Activates only six native SMB audit switches; policy and security settings are preserved. See docs/smb-runtime-activation.md.';return}
try {
$report=Invoke-WelaSmbRuntimeActivation -Action $SmbRuntimeAction -OutputPath $SmbRuntimeOutputPath -Auto:$Auto -DryRun:$DryRun
$report
if($report.ExitCode){exit $report.ExitCode}
}catch{Write-Host "[Failed] SMB runtime activation: $_" -ForegroundColor Red;exit 1}
}
'smb-auditing' {
if ($Help) {
Write-Host 'Usage: ./WELA.ps1 smb-auditing [-SmbAction Audit|Plan|Configure] [-Auto] [-DryRun] [-BackupPath new-directory] [-ResultsPath file.json]'
+2
View File
@@ -30,6 +30,8 @@ Microsoft's Policy CSP pages list **26100.3613** as the availability floor for t
## Policy registry versus effective runtime
The separate explicit [`smb-runtime` activation command](smb-runtime-activation.md) can activate the six native audit Booleans through reviewed SMB setters, with policy-conflict and complete configuration guards. This policy command does not invoke it automatically. Both operations keep event generation and policy persistence separate from current configuration observations.
Reports keep `Policy` (the actual policy-registry value/type) separate from `Runtime` (the corresponding property of `Get-SmbServerConfiguration` or `Get-SmbClientConfiguration`). WELA never substitutes the policy DWORD for a runtime observation:
- `Observed`: the getter exposes an actual Boolean. `RuntimeState=Active` means that Boolean was True, not that representative events were generated. False is `NotActive` before the desired policy exists, or `PendingVerification` when the policy registry contains DWORD 1. A correctly written/read-back policy therefore succeeds even when the runtime Boolean remains False. Pending verification does **not** assert propagation delay, a future activation deadline, or that a policy refresh/restart will fix the discrepancy. Its cause and activation timing are unknown; investigate and repeat Audit independently. WELA performs no refresh/restart and never weakens security to make a Boolean change.
+32
View File
@@ -0,0 +1,32 @@
# Explicit native SMB audit activation
Related to #377. `smb-runtime` explicitly activates the six reviewed native SMB audit switches when their actual runtime Booleans are False. It complements `smb-auditing`, which configures policy DWORDs and reports runtime state separately. Sysmon is excluded.
```powershell
.\WELA.ps1 smb-runtime
.\WELA.ps1 smb-runtime -SmbRuntimeAction Activate -DryRun
.\WELA.ps1 smb-runtime -SmbRuntimeAction Activate -SmbRuntimeOutputPath C:\Evidence\new-smb-activation -Auto
```
The default Plan and Activate dry-run only read. Activate requires a new evidence directory outside the source tree on a local fixed drive with an existing parent. It protects that directory for the actual user, Administrators and SYSTEM. Without `-Auto`, each required change asks for explicit consent. Existing True flags are checked without invoking their setters. Activation requires permissions to use the native SMB configuration cmdlets.
Only native 64-bit Windows 11 24H2/25H2 (builds 26100/26200) and Server 2025 (26100, including DC product type) are reviewed. Each switch also requires the exact local machine ADMX mapping, genuine Windows `SmbShare` module location, an actual Boolean setter parameter and a native CIM Boolean getter property. Missing definitions, properties, unsupported builds, unreadable values and unexpected configuration types stop the operation. Windows 11 and DC deployment acceptance remain separate from hosted member-server testing.
| Native command | Only permitted parameters |
| --- | --- |
| `Set-SmbServerConfiguration` | `AuditClientDoesNotSupportEncryption`, `AuditClientDoesNotSupportSigning`, `AuditInsecureGuestLogon` |
| `Set-SmbClientConfiguration` | `AuditServerDoesNotSupportEncryption`, `AuditServerDoesNotSupportSigning`, `AuditInsecureGuestLogon` |
Every selected value is set to Boolean True, one at a time. The command does not set signing/encryption requirements, enable guest access, modify shares, change services, restart Windows, refresh policy, change channels or generate traffic. It changes no registry-policy value. A current absent policy value is compatible and stays absent; a present policy must be DWORD 1. Any conflicting or malformed policy blocks the entire activation before writes. Absence does not establish local ownership or rule out future GPO/MDM changes. This is an explicit local runtime configuration operation, not a GPO edit or a promise of persistence.
The plan captures all six typed policy tuples, local ADMX hashes, host/build identity, native module/source fingerprints and every supported property exposed by both native configuration getters. Before each setter, WELA compares the complete current snapshot, writes and flushes a Pending receipt to disk, then checks the snapshot again after any prompt. The only permitted readback difference is that single audit Boolean becoming True. Every other native configuration property and policy tuple must remain unchanged before a Confirmed receipt is written. A final complete readback is required for `RuntimeAuditingActive`.
The evidence directory retains `plan.json`, numbered Pending/Confirmed receipts and `result.json`. Failure, drift, declined changes or incomplete readback produce a nonzero result. After a failed operation, remaining flags are skipped; earlier successful changes stay recorded. A setter may have changed its flag before throwing or before a receipt failure, so Pending alone is not proof of either success or no change. There is no automatic rollback. Reports and hashes establish observed consistency, not historic authenticity or protection against an administrator replacing the evidence. No atomic lock against concurrent Windows policy/configuration writers is claimed.
For manual recovery, select one original flag and compare its Pending/Confirmed receipts with fresh native configuration and policy. Restore only that flag's original Boolean through the matching native setter after reviewing concurrent changes and policy authority. Do not replay the entire configuration object or copy getter values into arbitrary setter parameters. Retain the recovery readback separately. Restoring a getter value does not prove the exact historical registry representation or future policy persistence.
**Runtime activation grants zero Sigma readiness credit.** The command neither generates nor verifies representative SMB events, forwarding, a backend query, guest behavior or persistence after policy refresh. Keep #377 open until its remaining secure-peer event and ingestion acceptance is completed; never weaken signing/encryption or enable guest access solely to manufacture test evidence.
Focused tests exercise typed configuration, policy conflicts, idempotence, durable-receipt failure, prompt/prewrite/final drift and partial native failures. The explicitly gated disposable GitHub VM fixture prepares only these audit flags as False on Server 2025, invokes the public CLI to activate all six, checks dry-run/idempotence and restores their original native values. It compares every other exposed native configuration property, all policy tuples and source context before/after. Server 2022 tests actual unsupported refusal. Both run under Windows PowerShell 5.1 and PowerShell 7. The fixture performs no SMB traffic or policy changes, and must never run on production.
Microsoft sources: [SMB client audit parameters](https://learn.microsoft.com/en-us/powershell/module/smbshare/set-smbclientconfiguration?view=windowsserver2025-ps), [SMB server audit parameters](https://learn.microsoft.com/en-us/powershell/module/smbshare/set-smbserverconfiguration?view=windowsserver2025-ps), [signing and encryption audit events](https://learn.microsoft.com/en-us/windows-server/storage/file-server/smb-signing-overview), [LanmanServer policy mappings](https://learn.microsoft.com/en-us/windows/client-management/mdm/policy-csp-lanmanserver), [LanmanWorkstation policy mappings](https://learn.microsoft.com/en-us/windows/client-management/mdm/policy-csp-lanmanworkstation).
+193
View File
@@ -0,0 +1,193 @@
# Explicit native audit-switch activation. No registry policy, security, share or service writes.
function Get-WelaSmbRuntimeKey { param($Value) ConvertTo-Json -InputObject $Value -Depth 24 -Compress }
function Get-WelaSmbRuntimeSources {
$result=[ordered]@{}
foreach($name in @('WELA.ps1','scripts/SmbRuntimeActivation.ps1','scripts/SmbAuditing.ps1','scripts/Configuration.ps1','scripts/WefArrival.ps1')) {
$result[$name]=(Get-FileHash -LiteralPath (Join-Path $script:ScriptRoot $name) -Algorithm SHA256 -ErrorAction Stop).Hash
}
[pscustomobject]$result
}
function Assert-WelaSmbRuntimeCommand {
param($Command,[ValidateSet('Server','Client')][string]$Side,[ValidateSet('Get','Set')][string]$Verb,[string]$ModuleBase)
# SmbShare exports functions from these native nested CDXML modules.
if($Command.Name -cne "$Verb-Smb${Side}Configuration" -or $Command.ModuleName -cne "Smb${Side}Configuration" -or
[string]$Command.CommandType -cne 'Function' -or [IO.Path]::GetFullPath($Command.Module.ModuleBase) -ine $ModuleBase){
$observed=[pscustomobject]@{Name=$Command.Name;ModuleName=$Command.ModuleName;ModuleBase=$Command.Module.ModuleBase;Type=[string]$Command.CommandType}
throw "SMB commands must resolve to the reviewed native SmbShare CDXML module. Expected $ModuleBase; observed $(Get-WelaSmbRuntimeKey $observed)"
}
if($Verb -eq 'Set') {
$component=if($Side -eq 'Server'){'LanmanServer'}else{'LanmanWorkstation'}
foreach($definition in @(Get-WelaSmbAuditDefinitions | Where-Object Component -eq $component)) {
if(-not $Command.Parameters.ContainsKey($definition.Name) -or $Command.Parameters[$definition.Name].ParameterType -ne [bool]) {
throw "Native setter lacks the exact Boolean parameter $($definition.Name)."
}
}
}
}
function Get-WelaSmbRuntimeCommands {
$base=[IO.Path]::GetFullPath((Join-Path ([Environment]::SystemDirectory) 'WindowsPowerShell/v1.0/Modules/SmbShare'))
$commands=[ordered]@{}
foreach($side in @('Server','Client')) {
foreach($verb in @('Get','Set')) {
$name="SmbShare\$verb-Smb${side}Configuration"
$found=@(Get-Command -Name $name -ErrorAction Stop)
if($found.Count -ne 1){throw 'Expected exactly one native module-qualified SMB command.'}
Assert-WelaSmbRuntimeCommand -Command $found[0] -Side $side -Verb $verb -ModuleBase $base
$commands[$name]=[pscustomobject]@{ModuleName=$found[0].ModuleName;ModuleBase=$base;ModuleVersion=$found[0].Module.Version.ToString();CommandType=$found[0].CommandType.ToString()}
}
}
$files=@(Get-ChildItem -LiteralPath $base -File -Recurse -ErrorAction Stop | Where-Object Extension -in @('.psd1','.psm1','.cdxml','.dll','.ps1xml') | Sort-Object FullName)
if($files.Count -lt 1 -or $files.Count -gt 100){throw 'Unexpected native SMB module inventory.'}
$hashes=[ordered]@{}
foreach($file in $files){
if($file.Length -gt 16MB -or ($file.Attributes -band [IO.FileAttributes]::ReparsePoint)){throw 'Unsupported SMB module source.'}
$hashes[$file.FullName]=(Get-FileHash -LiteralPath $file.FullName -Algorithm SHA256 -ErrorAction Stop).Hash
}
[pscustomobject]@{Commands=[pscustomobject]$commands;Files=[pscustomobject]$hashes}
}
function ConvertTo-WelaSmbRuntimeConfiguration {
param($Configuration,[ValidateSet('Server','Client')][string]$Side)
if($Configuration.CimClass.CimClassName -cne "MSFT_Smb${Side}Configuration"){throw 'Expected one actual native SMB configuration CIM instance.'}
$properties=@($Configuration.CimInstanceProperties | Sort-Object Name)
if($properties.Count -lt 3 -or $properties.Count -gt 160){throw 'Unexpected SMB configuration property count.'}
$result=[ordered]@{}
foreach($property in $properties) {
if($result.Contains($property.Name)){throw 'Duplicate SMB configuration property.'}
$value=$property.Value
foreach($item in @($value)) {
if($null -ne $item -and $item -isnot [bool] -and $item -isnot [string] -and
$item -isnot [byte] -and $item -isnot [uint16] -and $item -isnot [uint32] -and $item -isnot [uint64] -and
$item -isnot [int16] -and $item -isnot [int32] -and $item -isnot [int64]){throw "Unsupported native configuration value: $($property.Name)"}
if($item -is [string] -and $item.Length -gt 8192){throw 'Native configuration string exceeds bound.'}
}
if(@($value).Count -gt 128){throw 'Native configuration array exceeds bound.'}
$result[$property.Name]=[pscustomobject]@{CimType=$property.CimType.ToString();Value=$value}
}
$component=if($Side -eq 'Server'){'LanmanServer'}else{'LanmanWorkstation'}
foreach($definition in @(Get-WelaSmbAuditDefinitions | Where-Object Component -eq $component)) {
if(-not $result.Contains($definition.Name) -or $result[$definition.Name].Value -isnot [bool] -or $result[$definition.Name].CimType -cne 'Boolean') {
throw "Native getter lacks the exact Boolean property $($definition.Name)."
}
}
[pscustomobject]$result
}
function Get-WelaSmbRuntimeState {
$hostState=Get-WelaSmbAuditHost
if($hostState.Status -ne 'Candidate'){throw "SMB runtime activation is $($hostState.Status): $($hostState.Diagnostic)"}
$commands=Get-WelaSmbRuntimeCommands
$policies=[ordered]@{}
foreach($definition in Get-WelaSmbAuditDefinitions) {
$capability=Get-WelaSmbAuditCapability -Definition $definition -HostState $hostState
if($capability.Status -ne 'Supported'){throw "Unverified $($definition.Component)/$($definition.Name): $($capability.Diagnostic)"}
$policies["$($definition.Component)/$($definition.Name)"]=[pscustomobject]@{
Path=$definition.Path;Name=$definition.Name;AdmxSha256=$capability.AdmxSha256
Policy=Get-WelaRegistryState -Path $definition.Path -Name $definition.Name
}
}
$configurations=[ordered]@{}
foreach($side in @('Server','Client')) {
$command="SmbShare\Get-Smb${side}Configuration"
$native=@(& $command -ErrorAction Stop)
if($native.Count -ne 1){throw 'Expected exactly one native SMB configuration.'}
$configurations[$side]=ConvertTo-WelaSmbRuntimeConfiguration -Configuration $native[0] -Side $side
}
[pscustomobject][ordered]@{Computer=[Environment]::MachineName;Host=$hostState;Commands=$commands;Sources=Get-WelaSmbRuntimeSources;Policies=[pscustomobject]$policies;Configurations=[pscustomobject]$configurations}
}
function Get-WelaSmbRuntimePlan {
param($State)
foreach($definition in Get-WelaSmbAuditDefinitions) {
$id="$($definition.Component)/$($definition.Name)"
$policy=$State.Policies.$id.Policy
$side=if($definition.Component -eq 'LanmanServer'){'Server'}else{'Client'}
$value=$State.Configurations.$side.($definition.Name).Value
$compatible=($policy.ValueExists -is [bool] -and -not $policy.ValueExists) -or
($policy.ValueExists -eq $true -and $policy.Type -ceq 'DWord' -and
($policy.Value -is [int] -or $policy.Value -is [long] -or $policy.Value -is [uint32]) -and $policy.Value -eq 1)
[pscustomobject][ordered]@{Id=$id;Side=$side;Name=$definition.Name;Before=$value;Desired=$true;Policy=$policy
Status=$(if(-not $compatible){'BlockedPolicy'}elseif($value){'AlreadyActive'}else{'ActivationRequired'})
Diagnostic=$(if(-not $compatible){'Existing policy is not absent or DWORD 1; review its authority. It will not be overwritten.'}elseif($policy.ValueExists){'Policy DWORD 1 and runtime Boolean are separate observations.'}else{'Policy value is absent; explicit activation changes native local configuration only.'})}
}
}
function Set-WelaSmbRuntimeFlag {
param([string]$Id)
$matches=@(Get-WelaSmbAuditDefinitions | Where-Object {"$($_.Component)/$($_.Name)" -ceq $Id})
if($matches.Count -ne 1){throw 'Unknown SMB audit switch.'}
$definition=$matches[0]
$side=if($definition.Component -eq 'LanmanServer'){'Server'}else{'Client'}
$command="SmbShare\Set-Smb${side}Configuration"
$parameters=@{Confirm=$false;Force=$true;ErrorAction='Stop'}
$parameters[$definition.Name]=$true
$null=& $command @parameters
}
function Write-WelaSmbRuntimeReceipt {
param([string]$Root,[string]$Name,$Value)
if($Name -notmatch '^(plan|result|[1-6]-(pending|confirmed))\.json$'){throw 'Unexpected receipt filename.'}
$null=Resolve-WelaArrivalPath $Root
$path=Join-Path $Root $Name
$bytes=[Text.UTF8Encoding]::new($false).GetBytes((Get-WelaSmbRuntimeKey $Value))
if($bytes.Length -gt 4MB){throw 'SMB activation receipt exceeds bound.'}
$stream=[IO.File]::Open($path,[IO.FileMode]::CreateNew,[IO.FileAccess]::Write,[IO.FileShare]::None)
try {$stream.Write($bytes,0,$bytes.Length);$stream.Flush($true)}finally{$stream.Dispose()}
$expected=Get-WelaArrivalHash $bytes
if((Get-FileHash -LiteralPath $path -Algorithm SHA256 -ErrorAction Stop).Hash.ToLowerInvariant() -cne $expected){throw 'SMB receipt readback differs.'}
[pscustomobject]@{Name=$Name;Bytes=$bytes.Length;Sha256=$expected}
}
function Invoke-WelaSmbRuntimeActivation {
param([ValidateSet('Plan','Activate')][string]$Action='Plan',[string]$OutputPath,[switch]$Auto,[switch]$DryRun)
if($DryRun -and $Action -ne 'Activate'){throw 'DryRun requires SmbRuntimeAction Activate.'}
if($Action -eq 'Plan' -and ($Auto -or $OutputPath)){throw 'Plan reads only; Auto and OutputPath apply to Activate.'}
$report=[pscustomobject][ordered]@{SchemaVersion=1;Kind='WelaSmbRuntimeActivation';Action=$Action;DryRun=[bool]$DryRun;RecordedUtc=[DateTime]::UtcNow.ToString('o')
Status='Unverified';ExitCode=1;Before=$null;After=$null;Controls=@();Results=@();Artifacts=@();OutputPath=$null;Diagnostic=''
VerificationScope='Native local audit switches at the recorded observations; policy authority and persistence are unknown';ReadyRuleCredit=0;EventGeneration='Not tested';Forwarding='Not tested'}
try {
$state=Get-WelaSmbRuntimeState;$report.Before=$state
$report.Controls=@(Get-WelaSmbRuntimePlan $state)
if(@($report.Controls | Where-Object Status -eq BlockedPolicy).Count){throw 'One or more policy values conflict or are malformed. No audit flags were changed.'}
if($Action -eq 'Plan' -or $DryRun){$report.Status=if($DryRun){'DryRun'}else{'Planned'};$report.ExitCode=0;return $report}
if(-not $OutputPath){throw 'Activate requires a new SmbRuntimeOutputPath on a local fixed drive.'}
$output=New-WelaArrivalOutput -Path $OutputPath -SourcePath $script:ScriptRoot;$report.OutputPath=$output
$report.Artifacts+=Write-WelaSmbRuntimeReceipt $output 'plan.json' ([pscustomobject]@{State=$state;Controls=$report.Controls})
$expectedKey=Get-WelaSmbRuntimeKey $state
$index=0;$stopped=$false
foreach($control in $report.Controls) {
$index++
$row=[pscustomobject][ordered]@{Id=$control.Id;Before=$control.Before;After=$null;Status='Skipped';Diagnostic='';PendingReceipt=$null;ConfirmedReceipt=$null}
$report.Results+= $row
if($stopped){$row.Diagnostic='A prior activation failed; no further changes were attempted.';continue}
try {
$fresh=Get-WelaSmbRuntimeState
if((Get-WelaSmbRuntimeKey $fresh) -cne $expectedKey){throw 'Host, source, policy or native configuration drifted after the snapshot.'}
if($control.Before){$row.After=$true;$row.Status='AlreadyActive';continue}
if(-not $Auto -and (Read-Host "Activate only SMB audit flag $($control.Id)? (y/N)") -cnotin @('y','Y')){$row.Diagnostic='Declined by operator.';continue}
$row.PendingReceipt=Write-WelaSmbRuntimeReceipt $output "$index-pending.json" ([pscustomobject]@{Kind='Pending';Id=$control.Id;Before=$fresh;Desired=$true;RecordedUtc=[DateTime]::UtcNow.ToString('o')})
# Re-read after interaction and durable intent, immediately before the setter.
if((Get-WelaSmbRuntimeKey (Get-WelaSmbRuntimeState)) -cne $expectedKey){throw 'Context drifted before the native setter; activation refused.'}
Set-WelaSmbRuntimeFlag -Id $control.Id
$after=Get-WelaSmbRuntimeState;$row.After=$after.Configurations.($control.Side).($control.Name).Value
# The only permitted delta is this one Boolean. All policies and every
# other native configuration property (including security) must match.
$next=Get-WelaSmbRuntimeKey $fresh | ConvertFrom-Json
$next.Configurations.($control.Side).($control.Name).Value=$true
if((Get-WelaSmbRuntimeKey $after) -cne (Get-WelaSmbRuntimeKey $next)){throw 'Native readback did not show exactly the requested audit-only delta.'}
$row.ConfirmedReceipt=Write-WelaSmbRuntimeReceipt $output "$index-confirmed.json" ([pscustomobject]@{Kind='Confirmed';Id=$control.Id;Pending=$row.PendingReceipt;After=$after;RecordedUtc=[DateTime]::UtcNow.ToString('o')})
$state=$after;$expectedKey=Get-WelaSmbRuntimeKey $state
$row.Status='Activated';$row.Diagnostic='Native Boolean True observed; policy tuple and all other configuration properties preserved.'
}catch{$row.Status='Failed';$row.Diagnostic=$_.Exception.Message;$stopped=$true}
}
$report.After=Get-WelaSmbRuntimeState
if((Get-WelaSmbRuntimeKey $report.After) -cne $expectedKey){throw 'Final context differs from the last verified configuration. Review partial receipts; no automatic rollback is attempted.'}
if(@($report.Results | Where-Object Status -notin @('Activated','AlreadyActive')).Count){throw 'Some flags were not activated. Inspect per-control results and receipts.'}
$report.Status='RuntimeAuditingActive';$report.ExitCode=0
}catch{$report.Diagnostic=$_.Exception.Message}
if($report.OutputPath){$null=Write-WelaSmbRuntimeReceipt $report.OutputPath 'result.json' $report}
$report
}
+21
View File
@@ -0,0 +1,21 @@
$ErrorActionPreference='Stop'
$repo=Split-Path $PSScriptRoot -Parent
$engine=(Get-Process -Id $PID).Path
$script:checks=0
function Check-Cli {
param([string[]]$Arguments,[bool]$Success,[string]$Match)
$old=$ErrorActionPreference;$ErrorActionPreference='Continue'
try{$output=(& $engine -NoProfile -File (Join-Path $repo 'WELA.ps1') @Arguments 2>&1 | Out-String);$code=$LASTEXITCODE}finally{$ErrorActionPreference=$old}
if(($Success -and $code -ne 0) -or (-not $Success -and $code -eq 0) -or $output -notmatch $Match){throw "CLI guard failed: $($Arguments -join ' '), exit $code : $output"}
$script:checks++
}
Check-Cli @('smb-runtime','-Help') $true 'smb-runtime'
Check-Cli @('smb-runtime','-Profile','test','-Help') $false 'dedicated options'
Check-Cli @('help','-SmbRuntimeAction','Activate') $false 'SmbRuntime options require'
Check-Cli @('smb-runtime','-SmbAction','Configure','-Help') $false 'dedicated options'
Check-Cli @('smb-runtime','-DryRun') $false 'DryRun is supported only'
Check-Cli @('smb-runtime','-SmbRuntimeAction','Activate','-DryRun','-Help') $true 'smb-runtime'
Check-Cli @('smb-runtime','-BackupPath','unused','-Help') $false 'dedicated options'
Write-Host "PASS: $script:checks public SMB runtime CLI guards"
# Expected child failures are assertions, not the enclosing Actions step result.
$global:LASTEXITCODE=0
+130
View File
@@ -0,0 +1,130 @@
$ErrorActionPreference='Stop'
$script:ScriptRoot=Split-Path $PSScriptRoot -Parent
. (Join-Path $script:ScriptRoot 'scripts/Configuration.ps1')
. (Join-Path $script:ScriptRoot 'scripts/SmbAuditing.ps1')
. (Join-Path $script:ScriptRoot 'scripts/WefArrival.ps1')
. (Join-Path $script:ScriptRoot 'scripts/SmbRuntimeActivation.ps1')
$script:checks=0
function Assert($Condition,[string]$Message){if(-not $Condition){throw "FAIL: $Message"};$script:checks++}
function Reject([scriptblock]$Code,[string]$Message){$failed=$false;try{& $Code}catch{$failed=$true};Assert $failed $Message}
Reject {Set-WelaSmbRuntimeFlag 'LanmanWorkstation/EnableInsecureGuestLogons'} 'security parameter refused by actual setter adapter'
Reject {Set-WelaSmbRuntimeFlag 'LanmanServer/auditinsecureguestlogon'} 'mis-cased control refused'
$nativeModuleBase=[IO.Path]::GetFullPath([IO.Path]::GetTempPath())
$command=[pscustomobject]@{Name='Set-SmbServerConfiguration';ModuleName='SmbServerConfiguration';CommandType='Function';Module=[pscustomobject]@{ModuleBase=$nativeModuleBase};Parameters=@{}}
foreach($definition in @(Get-WelaSmbAuditDefinitions | Where-Object Component -eq LanmanServer)){$command.Parameters[$definition.Name]=[pscustomobject]@{ParameterType=[bool]}}
Assert-WelaSmbRuntimeCommand $command Server Set $nativeModuleBase
Assert $true 'actual nested native CDXML module metadata accepted'
$command.ModuleName='Other'
Reject {Assert-WelaSmbRuntimeCommand $command Server Set $nativeModuleBase} 'foreign module refused'
$command.ModuleName='SmbServerConfiguration'
Reject {Assert-WelaSmbRuntimeCommand $command Server Set ($nativeModuleBase+'other')} 'unexpected module directory refused'
$command.Parameters.AuditInsecureGuestLogon.ParameterType=[string]
Reject {Assert-WelaSmbRuntimeCommand $command Server Set $nativeModuleBase} 'mistyped native parameter refused'
$command.Parameters.Remove('AuditInsecureGuestLogon')
Reject {Assert-WelaSmbRuntimeCommand $command Server Set $nativeModuleBase} 'missing native parameter refused'
function FixtureConfiguration {
param([string]$Side='Server')
$component=if($Side -eq 'Server'){'LanmanServer'}else{'LanmanWorkstation'}
$properties=@(Get-WelaSmbAuditDefinitions | Where-Object Component -eq $component | ForEach-Object {[pscustomobject]@{Name=$_.Name;Value=$false;CimType='Boolean'}})
$properties+=[pscustomobject]@{Name='RequireSecuritySignature';Value=$true;CimType='Boolean'}
[pscustomobject]@{CimClass=[pscustomobject]@{CimClassName="MSFT_Smb${Side}Configuration"};CimInstanceProperties=$properties}
}
$native=FixtureConfiguration
$config=ConvertTo-WelaSmbRuntimeConfiguration $native Server
Assert ($config.RequireSecuritySignature.Value -eq $true -and $config.AuditInsecureGuestLogon.Value -eq $false) 'native typed security and audit properties retained'
$native.CimInstanceProperties[0].Value='False'
Reject {ConvertTo-WelaSmbRuntimeConfiguration $native Server} 'string audit Boolean rejected'
$native=FixtureConfiguration;$native.CimInstanceProperties[0].CimType='String'
Reject {ConvertTo-WelaSmbRuntimeConfiguration $native Server} 'wrong native CIM type rejected'
$native=FixtureConfiguration;$native.CimClass.CimClassName='MSFT_AnotherConfiguration'
Reject {ConvertTo-WelaSmbRuntimeConfiguration $native Server} 'wrong native class rejected'
$native=FixtureConfiguration;$native.CimInstanceProperties+=[pscustomobject]@{Name='Mystery';Value=[pscustomobject]@{a=1};CimType='Instance'}
Reject {ConvertTo-WelaSmbRuntimeConfiguration $native Server} 'unknown unrelated configuration remains unverified'
$root=Join-Path ([IO.Path]::GetTempPath()) ('wela-smb-activation-'+[guid]::NewGuid().ToString('N'))
$null=New-Item -ItemType Directory -Path $root
$script:receiptWriter=${function:Write-WelaSmbRuntimeReceipt}
function Reset-Fixture {
$policies=[ordered]@{}
foreach($definition in Get-WelaSmbAuditDefinitions){$policies["$($definition.Component)/$($definition.Name)"]=[pscustomobject]@{Policy=[pscustomobject]@{KeyExists=$false;ValueExists=$false;Type=$null;Value=$null}}}
$script:fixture=[pscustomobject][ordered]@{Computer='fixture';Host=[pscustomobject]@{Build=26100};Commands='native';Sources='hash';Policies=[pscustomobject]$policies;Configurations=[pscustomobject]@{Server=(ConvertTo-WelaSmbRuntimeConfiguration (FixtureConfiguration Server) Server);Client=(ConvertTo-WelaSmbRuntimeConfiguration (FixtureConfiguration Client) Client)}}
$script:writes=0;$script:reads=0;$script:driftRead=0;$script:failWrite=0;$script:securityDrift=$false;$script:receiptFail=$false;$script:promptDrift=$false
$script:out=Join-Path $root ([guid]::NewGuid().ToString('N'))
}
function Get-WelaSmbRuntimeState {
$script:reads++
if($script:reads -eq $script:driftRead){$script:fixture.Sources='changed'}
Get-WelaSmbRuntimeKey $script:fixture | ConvertFrom-Json
}
function Write-WelaSmbRuntimeReceipt {
param($Root,$Name,$Value)
if($script:receiptFail -and $Name -eq '1-pending.json'){throw 'Injected durable-write failure'}
& $script:receiptWriter $Root $Name $Value
}
function Set-WelaSmbRuntimeFlag {
param($Id)
$script:writes++
Assert (Test-Path (Join-Path $script:out "$($script:writes)-pending.json")) 'pending receipt exists before setter'
if($script:writes -eq $script:failWrite){throw 'Injected native setter failure'}
$parts=$Id.Split('/');$side=if($parts[0] -eq 'LanmanServer'){'Server'}else{'Client'}
$script:fixture.Configurations.$side.($parts[1]).Value=$true
if($script:securityDrift){$script:fixture.Configurations.Server.RequireSecuritySignature.Value=$false}
}
function Read-Host {param($Prompt) if($script:promptDrift){$script:fixture.Sources='changed at prompt'};'y'}
try {
Reset-Fixture
$plan=Invoke-WelaSmbRuntimeActivation
Assert ($plan.Status -eq 'Planned' -and $plan.Controls.Count -eq 6 -and $script:writes -eq 0) 'default Plan is six read-only audit controls'
Assert (-not (Test-Path $script:out)) 'Plan creates no evidence directory'
$dry=Invoke-WelaSmbRuntimeActivation -Action Activate -DryRun -OutputPath $script:out
Assert ($dry.Status -eq 'DryRun' -and $script:writes -eq 0 -and -not (Test-Path $script:out)) 'DryRun does not write'
Reject {Invoke-WelaSmbRuntimeActivation -Action Plan -Auto} 'irrelevant Plan consent rejected'
Reject {Invoke-WelaSmbRuntimeActivation -Action Plan -DryRun} 'invalid dry run action rejected'
$id='LanmanServer/AuditInsecureGuestLogon'
foreach($value in @(0,'1',2)) {
Reset-Fixture;$script:fixture.Policies.$id.Policy=[pscustomobject]@{KeyExists=$true;ValueExists=$true;Type='DWord';Value=$value}
$report=Invoke-WelaSmbRuntimeActivation -Action Activate -Auto -OutputPath $script:out
Assert ($report.ExitCode -eq 1 -and $script:writes -eq 0 -and -not (Test-Path $script:out)) 'conflicting or mistyped policy stops all mutations'
}
Reset-Fixture;$script:fixture.Policies.$id.Policy=[pscustomobject]@{KeyExists=$true;ValueExists=$true;Type='String';Value=1}
Assert ((Invoke-WelaSmbRuntimeActivation).ExitCode -eq 1) 'wrong registry kind blocks'
Reset-Fixture;$script:fixture.Policies.$id.Policy=[pscustomobject]@{KeyExists=$true;ValueExists=$true;Type='DWord';Value=1}
Assert ((Invoke-WelaSmbRuntimeActivation).ExitCode -eq 0) 'existing enabled policy is compatible'
Reset-Fixture
$report=Invoke-WelaSmbRuntimeActivation -Action Activate -Auto -OutputPath $script:out
Assert ($report.ExitCode -eq 0 -and $report.Status -eq 'RuntimeAuditingActive' -and $script:writes -eq 6) "six native activations succeed: $($report.Diagnostic)"
Assert (@($report.Results | Where-Object Status -eq Activated).Count -eq 6) 'all six report confirmed activation'
Assert ((Get-ChildItem -LiteralPath $script:out -File).Count -eq 14) 'plan, six pending, six confirmed, final result retained'
Assert ($report.After.Configurations.Server.RequireSecuritySignature.Value -eq $true) 'security property preserved'
Assert ($report.ReadyRuleCredit -eq 0 -and $report.EventGeneration -eq 'Not tested') 'activation grants no event or rule proof'
$prior=Get-Content -Raw -LiteralPath (Join-Path $script:out 'result.json')
$second=Invoke-WelaSmbRuntimeActivation -Action Activate -Auto -OutputPath $script:out
Assert ($second.ExitCode -eq 1 -and (Get-Content -Raw -LiteralPath (Join-Path $script:out 'result.json')) -ceq $prior) 'existing evidence is never overwritten'
$script:out=Join-Path $root ([guid]::NewGuid().ToString('N'));$script:writes=0
$repeat=Invoke-WelaSmbRuntimeActivation -Action Activate -Auto -OutputPath $script:out
Assert ($repeat.ExitCode -eq 0 -and $script:writes -eq 0 -and @($repeat.Results | Where-Object Status -eq AlreadyActive).Count -eq 6) 'idempotence requires no setters'
Reset-Fixture;$script:failWrite=2
$partial=Invoke-WelaSmbRuntimeActivation -Action Activate -Auto -OutputPath $script:out
Assert ($partial.ExitCode -eq 1 -and $script:writes -eq 2) 'partial native failure stops remaining writes'
Assert ($partial.Results[0].Status -eq 'Activated' -and $partial.Results[1].Status -eq 'Failed' -and $partial.Results[2].Status -eq 'Skipped') 'partial outcomes preserved'
Assert ((Test-Path (Join-Path $script:out '1-confirmed.json')) -and -not (Test-Path (Join-Path $script:out '2-confirmed.json'))) 'failed operation is never confirmed'
foreach($read in @(2,3,20)) {
Reset-Fixture;$script:driftRead=$read
$drift=Invoke-WelaSmbRuntimeActivation -Action Activate -Auto -OutputPath $script:out
Assert ($drift.ExitCode -eq 1) 'fresh/prewrite/final source drift fails closed'
if($read -lt 4){Assert ($script:writes -eq 0) 'prewrite drift performs no setter'}
}
Reset-Fixture;$script:promptDrift=$true
$drift=Invoke-WelaSmbRuntimeActivation -Action Activate -OutputPath $script:out
Assert ($drift.ExitCode -eq 1 -and $script:writes -eq 0) 'prompt-time drift refused'
Reset-Fixture;$script:securityDrift=$true
$drift=Invoke-WelaSmbRuntimeActivation -Action Activate -Auto -OutputPath $script:out
Assert ($drift.ExitCode -eq 1 -and $script:writes -eq 1 -and -not (Test-Path (Join-Path $script:out '1-confirmed.json'))) 'unrelated security delta prevents confirmation'
Reset-Fixture;$script:receiptFail=$true
$failed=Invoke-WelaSmbRuntimeActivation -Action Activate -Auto -OutputPath $script:out
Assert ($failed.ExitCode -eq 1 -and $script:writes -eq 0) 'failed durable intent blocks setter'
Assert (Test-Path (Join-Path $script:out 'result.json')) 'partial diagnostic survives pending-write failure'
Write-Host "PASS: $script:checks SMB runtime activation assertions"
}finally{Remove-Item -LiteralPath $root -Recurse -Force -ErrorAction SilentlyContinue}
@@ -0,0 +1,85 @@
# Mutates only six audit flags on disposable GitHub-hosted Windows VMs. Never run on production.
$ErrorActionPreference='Stop'
if($env:OS -ne 'Windows_NT' -or $env:GITHUB_ACTIONS -ne 'true' -or $env:WELA_DISPOSABLE_SMB_ACTIVATION -ne 'true') {throw 'Explicit disposable GitHub Windows test opt-in is required.'}
$script:ScriptRoot=Split-Path $PSScriptRoot -Parent
. (Join-Path $script:ScriptRoot 'scripts/Configuration.ps1')
. (Join-Path $script:ScriptRoot 'scripts/SmbAuditing.ps1')
. (Join-Path $script:ScriptRoot 'scripts/WefArrival.ps1')
. (Join-Path $script:ScriptRoot 'scripts/SmbRuntimeActivation.ps1')
$computer=Get-CimInstance Win32_ComputerSystem
$os=Get-CimInstance Win32_OperatingSystem
if($computer.PartOfDomain -or $computer.DomainRole -ne 2 -or $os.ProductType -ne 3 -or [int]$os.BuildNumber -notin @(20348,26100)){throw 'Fixture requires an isolated member-class Server 2022/2025 host.'}
$evidence=Join-Path $env:RUNNER_TEMP ('wela-smb-runtime-'+[guid]::NewGuid().ToString('N'))
$null=New-Item -ItemType Directory -Path $evidence
$reportPath=Join-Path $evidence 'activation'
$cleanup=[ordered]@{Build=[int]$os.BuildNumber;Engine=$PSVersionTable.PSVersion.ToString();OriginalCaptured=$false;AuditFlagsRestored=$false;FullContextRestored=$false;NativeActivation=$false;UnsupportedRefusal=$false}
$original=$null
try {
if([int]$os.BuildNumber -eq 20348) {
$report=Invoke-WelaSmbRuntimeActivation -Action Activate -Auto -OutputPath $reportPath
if($report.ExitCode -ne 1 -or $report.Diagnostic -notlike '*NotApplicable*' -or (Test-Path $reportPath)){throw 'Server 2022 activation was not refused before writes.'}
$report | ConvertTo-Json -Depth 24 | Set-Content -LiteralPath (Join-Path $evidence 'refusal.json') -Encoding UTF8
$global:LASTEXITCODE=0
$null=& (Join-Path $script:ScriptRoot 'WELA.ps1') smb-runtime -SmbRuntimeAction Activate -SmbRuntimeOutputPath $reportPath -Auto
if($LASTEXITCODE -ne 1 -or (Test-Path $reportPath)){throw 'Public CLI did not refuse unsupported Server 2022.'}
$cleanup.UnsupportedRefusal=$true
Write-Host 'PASS: actual Server 2022 native and public-CLI refusal, no output or setters.'
}else{
$original=Get-WelaSmbRuntimeState
if(@(Get-WelaSmbRuntimePlan $original | Where-Object Status -eq BlockedPolicy).Count){throw 'Fixture will not overwrite a conflicting policy.'}
$cleanup.OriginalCaptured=$true
$original | ConvertTo-Json -Depth 24 | Set-Content -LiteralPath (Join-Path $evidence 'original.json') -Encoding UTF8
foreach($definition in Get-WelaSmbAuditDefinitions) {
$side=if($definition.Component -eq 'LanmanServer'){'Server'}else{'Client'}
$command="SmbShare\Set-Smb${side}Configuration"
$parameters=@{Force=$true;Confirm=$false;ErrorAction='Stop'};$parameters[$definition.Name]=$false
$null=& $command @parameters
}
$prepared=Get-WelaSmbRuntimeState
$expected=Get-WelaSmbRuntimeKey $original | ConvertFrom-Json
foreach($definition in Get-WelaSmbAuditDefinitions) {
$side=if($definition.Component -eq 'LanmanServer'){'Server'}else{'Client'}
$expected.Configurations.$side.($definition.Name).Value=$false
}
if((Get-WelaSmbRuntimeKey $prepared) -cne (Get-WelaSmbRuntimeKey $expected)){throw 'Fixture preparation changed other settings or did not make audit flags False.'}
$dry=Invoke-WelaSmbRuntimeActivation -Action Activate -Auto -DryRun -OutputPath $reportPath
if($dry.ExitCode -ne 0 -or (Test-Path $reportPath) -or (Get-WelaSmbRuntimeKey (Get-WelaSmbRuntimeState)) -cne (Get-WelaSmbRuntimeKey $prepared)){throw 'Native dry-run changed context or wrote output.'}
$global:LASTEXITCODE=0
$cli=@(& (Join-Path $script:ScriptRoot 'WELA.ps1') smb-runtime -SmbRuntimeAction Activate -SmbRuntimeOutputPath $reportPath -Auto)
if($LASTEXITCODE -ne 0){throw "Public CLI exited $LASTEXITCODE"}
$report=Get-Content -Raw -LiteralPath (Join-Path $reportPath 'result.json') | ConvertFrom-Json
if($report.ExitCode -ne 0 -or $report.Status -ne 'RuntimeAuditingActive' -or @($report.Results | Where-Object Status -eq Activated).Count -ne 6){throw "Native six-flag activation failed: $($report.Diagnostic)"}
$active=Get-WelaSmbRuntimeState
foreach($definition in Get-WelaSmbAuditDefinitions) {
$side=if($definition.Component -eq 'LanmanServer'){'Server'}else{'Client'}
$expected.Configurations.$side.($definition.Name).Value=$true
}
if((Get-WelaSmbRuntimeKey $active) -cne (Get-WelaSmbRuntimeKey $expected)){throw 'Activation did not preserve every unrelated configuration field and policy tuple.'}
$repeat=Invoke-WelaSmbRuntimeActivation -Action Activate -Auto -OutputPath (Join-Path $evidence 'idempotent')
if($repeat.ExitCode -ne 0 -or @($repeat.Results | Where-Object Status -eq AlreadyActive).Count -ne 6){throw 'Native idempotence failed.'}
if(@(Get-ChildItem -LiteralPath $repeat.OutputPath -Filter '*-pending.json').Count){throw 'Idempotent run unexpectedly journaled a setter.'}
$cleanup.NativeActivation=$true
Write-Host 'PASS: actual Server 2025 public-CLI activation of all six native Boolean audit flags, dry-run, idempotence and preservation of all unrelated native configuration.'
}
}finally{
if($original) {
$failures=@()
foreach($definition in Get-WelaSmbAuditDefinitions) {
try {
$side=if($definition.Component -eq 'LanmanServer'){'Server'}else{'Client'}
$command="SmbShare\Set-Smb${side}Configuration"
$parameters=@{Force=$true;Confirm=$false;ErrorAction='Stop'};$parameters[$definition.Name]=[bool]$original.Configurations.$side.($definition.Name).Value
$null=& $command @parameters
}catch{$failures+=$_.Exception.Message}
}
$restored=Get-WelaSmbRuntimeState
$restored | ConvertTo-Json -Depth 24 | Set-Content -LiteralPath (Join-Path $evidence 'restored.json') -Encoding UTF8
$cleanup.AuditFlagsRestored=$failures.Count -eq 0
$cleanup.FullContextRestored=(Get-WelaSmbRuntimeKey $restored) -ceq (Get-WelaSmbRuntimeKey $original)
$cleanup | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $evidence 'acceptance.json') -Encoding UTF8
if(-not $cleanup.AuditFlagsRestored -or -not $cleanup.FullContextRestored){throw "Native SMB fixture cleanup mismatch: $($failures -join '; ')"}
Write-Host 'PASS: exact native audit flags and full configuration/policy/source context restored.'
}else{$cleanup | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $evidence 'acceptance.json') -Encoding UTF8}
Write-Host "Native SMB evidence: $evidence"
}
$global:LASTEXITCODE=0
+2
View File
@@ -7,6 +7,8 @@
**改善:**
- `smb-runtime` を追加し、Windows 標準の SMB 監査スイッチ6個を明示的に有効化します。モジュール・ビルド・ADMX、型付きポリシーの競合、設定全体の変化を確認し、各変更の意図と確認結果を永続的に記録します。署名・暗号化・ゲスト接続・サービス設定を保持し、現在の有効化とイベント・永続性・Sigma の証明を区別します。使い捨て Server 2025 の有効化と復元、Server 2022 の拒否を PowerShell 5.1/7 で検証します。 (#441) (@Shirofune-Security)
- 既存のWindows標準ソース開始型購読1件のEnabledだけをレビュー後に変更する`wec-state`のPlan/Applyを追加しました。送信元認可と完全な定義、実行者・ログオン・トークン、永続的な変更前記録とネイティブ読戻しを確認して他の設定を保持し、既に一致する状態では保存・再有効化を行いません。稼働状況の観測と配送・ブックマークの継続性を区別し、使い捨てWindowsテストで実際の有効/無効切替と所有リソース・サービス状態の復元を確認します。 (関連 #368) (@Shirofune-Security)
- 強化プロファイルのオプション IPsec Main Mode 監査に、Windows ネイティブの前提条件確認を追加しました。有効なポリシーストアのルールと現在の関連付けを読み取り、適用可能・確認範囲内で未観測・不明を区別します。共有設定処理は書き込み直前に再確認し、明示的な選択とカスタムプロファイルの指定を保持します。Server 2022/2025・PowerShell 5.1/7 の一時ルールを使ったテストで監査ポリシーの復元を確認します。ネゴシエーション、イベント生成、Sigma の対応は保証しません。 (#370) (@Shirofune-Security)
+2
View File
@@ -7,6 +7,8 @@
**Improvements:**
- Added explicit `smb-runtime` activation of six native SMB audit switches, with reviewed module/build/ADMX capabilities, typed policy conflicts, complete configuration drift guards and durable per-switch receipts. Signing, encryption, guest access and service settings are preserved; runtime verification stays separate from events, persistence and Sigma credit. Disposable Server 2025 activation/restoration and Server 2022 refusal tests cover PowerShell 5.1/7. (#441) (@Shirofune-Security)
- Added reviewed `wec-state` Plan/Apply for the Enabled flag of one existing native source-initiated subscription. Exact authorization and complete definition checks, operator/logon/token guards, durable pending evidence and native readback preserve other settings; matching states never save or reactivate. Runtime remains separate, and interrupted delivery/bookmark continuity require multi-host validation. Disposable Windows lifecycle tests restore owned resources and service state. (Related #368) (@Shirofune-Security)
- Added native prerequisite evidence for the stronger profile's optional IPsec Main Mode auditing. Effective-store rule and current association observations distinguish scoped applicability, absence and unknown results; both shared configuration paths recheck before writing and preserve explicit selection/custom-profile intent. Native disposable-rule tests cover Server 2022/2025 and PowerShell 5.1/7 with exact audit-policy restoration, without negotiation/event or Sigma claims. (#370) (@Shirofune-Security)