ファイル共有のアクセス許可を取得する

おなかがすいた。

Windows7に入ってるレベルのPowerShellでファイル共有のアクセス許可を取得してくるスクリプト
ちょっときたないけどメモに残しておこうと思います。

これ、NTFSアクセス許可ではなく"ファイル共有アクセス許可"を取りに行く目的のスクリプトで、あまりネットで事例を見かけなくて苦労した。

ポイントは"Get-WmiObject win32_LogicalShareSecuritySetting"でファイル共有のセキュリティ設定をとってくるところ。
LogicalShareSecuritySettingで取れない管理共有とかは結果に出てこない。

$shares = Get-WmiObject win32_Share 
$result= @()
foreach ($Share in $Shares){
    
    $ShareSecuritySetting =  Get-WmiObject win32_LogicalShareSecuritySetting  `
        -Filter ("Name='"+$Share.Name+"'") 
        
    if(!($ShareSecuritySetting)){
        continue
    }
    $acls = $ShareSecuritySetting.GetSecurityDescriptor().Descriptor.DACL
    
    foreach($ACL in $ACLS){
        $Obj = @{}
        
        $obj["共有名"] = $Share.Name
        $obj["パス"]   = $Share.Path
    
        $User = $ACL.Trustee.Name
        if(!($user)){$user = $ACL.Trustee.SID}

        $Domain = $ACL.Trustee.Domain
        switch($ACL.AccessMask)
        {
            2032127 {$Perm = "Full Control"}
            1245631 {$Perm = "Change"}
            1179817 {$Perm = "Read"}
        }
        $Obj["アカウント"] = "$Domain\$user"
        $Obj["パーミッション"] = "$Perm"
        
        $result += New-Object PSObject -Property $Obj
    }
}
$result  | Format-List
 

実行するとこんな感じ。

(前略)
アカウント     : lasty\bounoki
パーミッション : Full Control
共有名         : test_share1
パス           : R:\test_share1

アカウント     : lasty\bounoki
パーミッション : Full Control
共有名         : test_share2
パス           : R:\test_share2

アカウント     : BUILTIN\Administrators
パーミッション : Full Control
共有名         : test_share2
パス           : R:\test_share2

アカウント     : lasty\motie
パーミッション : Change
共有名         : test_share2
パス           : R:\test_share2

参考にしたのはこちらのサイト。
http://www.waynezim.com/2014/03/powershell-file-sharing-permissions-report/

エラー処理は大事

PowerShellで「初期処理でループしながらファイルの削除とかやって、そのあとメイン処理に入ろう」なんてときに「コマンドレット実行結果NGでも空回って進んで欲しい!」って思うことありますよね。

Shellなので、コマンドのリターンコード見て条件分岐する、という発想でよいのですが、せっかくのPowerShellだし例外処理で実装してみよう、と思うわけです。ところが普通に書くとなかなかcatch節に入ってくれないです。

これは、デフォルトの$ErrorActionPreference(デバッグモード変数みたいなもん)の値が"Continue"であり、この時、「Write-Errorコマンドレットからエラー表示される処理はcatch節で補足されない」という仕様にヒットしているせいっぽいです。Remove-Itemがファイル削除に失敗してIOExceptionを投げる時、Write-Errorを使ってるのですね。


コマンドレットごとに、エラー時の動作を制御する引数として"-ErrorAction"というのが用意されていて、それを使って実行したコマンドレットについては$ErrorActionPreferenceの値をオーバーライドできます。
こいつを使って"Stop"にオーバーライドしてあげると、try/catchできるようになります。

試しに"hoge.txt"を作り、ロックを取得できるエディタ等で開いた状態で削除してみましょう。

New-Item hoge.txt -type file #ここでファイルを開いてロック

try{ Remove-Item hoge.txt }catch{ Write-Output "ナイスキャッチ" } # 削除を試みる
Remove-Item : 項目 E:\tmp\test\hoge.txt を削除できません: 別のプロセスで使用されているため、プロセスはファイル 'E:\tmp\test\hoge.txt' にアクセスできません。
発生場所 行:1 文字:17
+ try{ Remove-Item <<<<  "hoge.txt" }catch{ Write-Output "ナイスキャッチ" }
    + CategoryInfo          : WriteError: (E:\tmp\test\hoge.txt:FileInfo) [Remove-Item]、IOException
    + FullyQualifiedErrorId : RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand

ぬふう。
それでは、ErrorActionを指定してみる。

New-Item hoge.txt -type file #ここでファイルを開いてロック

try{ Remove-Item hoge.txt -ErrorAction:Stop }catch{ Write-Output "ナイスキャッチ" } # 削除を試みる
ナイスキャッチ

catch節に入った。

マニュアルは以下のコマンドから。

Get-Help about_CommonParameters

パケットを解析するときのメモ(おもにwiresharkを想定)

※以下、メモ書き途中につき参考程度とすること※

取得時

  • 取得パケットサイズを途中で切るようにする(150kくらい?)
  • データが大きくなる場合は以下を考慮
    • ファイルを分割する(大きくても100MBくらいにしておいたほうがあとで調査が楽)
    • キャプチャ時、画面を自動スクロールさせない

解析時

  • 時刻表示は"Time of Day"など、時刻表示にしておく(主に他システムlogとの突き合わせのため)
  • TCPの場合、パケットリストパネルに以下を表示する
    • Windowサイズ
    • Seq番号
    • Ack番号
    • Length(Ethernet)

留意点

  • シーケンス番号は相対値。実値を表示したい場合はPreferenceのProtocolのTCPから表示を変更する。
  • 通常表示されてるLengthはフレームサイズなので(=TCPセグメントサイズじゃない)、Seq番号計算するにはちゃんとセグメントサイズを見る必要がある。

おまけ

  • WireSharkでとったログをpacketyzerで取り込むには"Modified tcpdump - libpcap"で保存する。

.zshrcの設定について

ついでに.zshrcの設定もメモろう。
参考にしたのはこちらのサイト。ちゅーか導入したばかりでお試し中につき、丸パクリ状態・・・

# Lines configured by zsh-newuser-install
HISTFILE=~/.histfile
HISTSIZE=20000
SAVEHIST=20000
bindkey -e
# End of lines configured by zsh-newuser-install
# The following lines were added by compinstall
zstyle :compinstall filename '/home/bounoki/.zshrc'

autoload -Uz compinit
compinit
# End of lines added by compinstall
setopt  ignore_eof
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'

autoload -U colors  ; colors
PROMPT="%{$fg[green]%}%m@%n[%h]%#%{$reset_color%} "
RPROMPT="%{$fg[green]%}[%(5~,%-2~/.../%2~,%~)]${WINDOW:+"[$WINDOW]"}%{$reset_color%}"

preexec () {
    if [[ "$TERM" == "screen" ]]; then
        local CMD=${1[(wr)^(*=*|sudo|-*)]}
        echo -ne "\ek$CMD\e\\"
    fi
}

PowerShellで「デスクトップ」とか「マイドキュメント」のパスを調べる

Windowsでプログラムを書いてて、「デスクトップのパスが取得したい!」とか思うときありますよね。
Google先生に聞いてみると「%USERPROFILE%\デスクトップで取得可能だよ!」とか言われてしまい、駄目だこいつ・・・はやくなんとかしないと・・・状態になります。ご存知の通り、エクスプローラで"場所"を"移動"している場合Google先生案は役に立たないのです。

そういう時、新米Windowsエンジニアの僕にはすでに次の発想に至る用意があります。つまり、レジストリを探せ」です。
Windowsの設定、情報の永続化の多くは「レジストリ」にレジスターすることにより実現されています。例えば、ユーザごとの設定であればHKLU、端末ごとの設定であればHKCUといった具合です(例外もあるよ)。
今回の対象は、"HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"にあるみたいです。

以下のスクリプトで取得できます。

# デスクトップとかマイドキュメントのパスが格納されているレジストリ
$shell_folders_path="HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"

# 値はプロパティとして保持されているらしいので、Get-ItemPropertyで取り出す 
$items = Get-ItemProperty $shell_folders_path 

# 試しにいくつか取得してみる。
$props_to_show = @( "DeskTop" ; "Personal" ; "My Pictures" ; "History" ; "Cookies" )
$props_to_show | Foreach-Object {
    Write-Output ( $_ + "はこれ→" + $items.$_ )
}

# 全部出力する
$items

上記の通りです。実行してみると以下のように出力されます(自分はデータは大体Eドライブに入れてます)。

DeskTopはこれ→E:\<ユーザ名>\Desktop
Personalはこれ→E:\<ユーザ名>\Documents
My Picturesはこれ→E:\<ユーザ名>\Pictures
Historyはこれ→C:\Users\<ユーザ名>\AppData\Local\Microsoft\Windows\History
Cookiesはこれ→C:\Users\<ユーザ名>\AppData\Roaming\Microsoft\Windows\Cookies

…($items全出力は割愛)

これ書いてて気づいたんですが、マイドキュメントは「Personal」っていうプロパティみたいです。