challenge ACLの制御

通常、作成者以外は、読み書きができないファイルを新規作成してください。
# UNIX的にいうと「0600」のファイルです。

すでに存在していた場合、新しい内容で上書きしてください。

symlinkアタック等を考慮する必要はありません。

余裕のある人はファイルがすでに存在していた場合、
パーミッションを変更してみてください。

Posted feedbacks - Nested

Flatten Hidden
バッチというよりは、もはや外部コマンドの使い方になってしまう
のですが、 Windowsでは cacls.exe (Change ACLS)を利用します。

が、

読み取り権限と書き込み権限だけ付与したはずなのに、後者を付与
した段階で実行権限も付与されてしまいました。実行権限だけ無効
にするといった細かい設定はxcacls.exeを利用しないとできないの
かもしれません。

  e.g.
    :: ファイルを作成
    C\:>(echo @date /t) > #244.bat
    
    :: 読み取り権限を付与
    C\:>echo y|cacls #244.bat /p USERDOMAIN\username:r
    
    :: 書き込み権限を付与
    C\:>echo y|cacls #244.bat /e /g USERDOMAIN\username:w
    
    :: アクセス制御リストを確認
    C\:>cacls #244.bat
    C:\#244.bat USERDOMAIN\username:(特殊なアクセス:)
                                    READ_CONTROL
                                    SYNCHRONIZE
                                    FILE_GENERIC_WRITE
                                    FILE_WRITE_DATA
                                    FILE_APPEND_DATA
                                    FILE_WRITE_EA
                                    FILE_EXECUTE        ← ?
                                    FILE_WRITE_ATTRIBUTES
    
    (別のユーザーでログオン)
    
    :: ファイルを読み込む
    C\:>type #244.bat
    アクセスが拒否されました。
    
    :: ファイルに書き込む
    C\:>(echo @time /t) > #244.bat
    アクセスが拒否されました。
    
    :: ファイルを実行
    C\:>#244.bat
    アクセスが拒否されました。

# Windows XPで動作を確認しました。
ども、raysntardです。
バッチファイルですしそういうものじゃないでしょうか。
windows系なら「CREATOR OWNER」の方でくるかなと思っていたのですが
会社のPCで確認したら存在しませんでしたorz

XPなら共通だと思っていたんだけどメーカーPCだとなかったりするみたいです^^;
あ、これはどうもご丁寧に。

 CREATOR OWNERは知りませんでした。ちょっと調べてみたのですが、
ファイルを作成すると暗黙的に CREATOR OWNER(作成者・所有者)に
所属するようですね。

お題の「作成者以外読み書きできないようにする」は、言い換えれ
ば「作成者だけが読み書きできるようにする」ということなので、
 CREATOR OWNERの ACLを設定するのが筋だったかもしれません。

ただ、[ユーザーまたはグループの選択]ダイアログで検索したり、
[コンピューターの管理] - [ローカルユーザーとグループ] を見て
も CREATOR OWNERというエントリが見当たらないんですよね。

#  ACLの操作云々よりも、先に Windowsの ACLの仕組みを学ぶべき
# だったか orz
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from __future__ import with_statement
import sys
import os
import ctypes
from ctypes.util import find_library

path = sys.argv[1]
fchmod = ctypes.cdll[find_library('c')].fchmod

with os.fdopen(os.open(path,
                       os.O_CREAT|os.O_WRONLY|os.O_TRUNC,
                       0600), 'w') as fp:
    fchmod(fp.fileno(), 0600)
    print >>fp, "message in a bottle"
素朴な実装
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import System.Directory
import System.Environment
import System.IO
import System.Posix.Files

main :: IO ()
main = getArgs >>= dispatch (f,g) doesFileExist . head >>= flip setFileMode mode
  where f fp = return fp
        g fp = openFile fp WriteMode >>= hClose >> return fp

mode = foldr unionFileModes nullFileMode [ownerReadMode,ownerWriteMode]

dispatch :: (a -> IO b,a -> IO b) -> (a -> IO Bool) -> a -> IO b
dispatch (f,g) p x = p x >>= \ q -> if q then f x else g x

お題のタイトルに則って、POSIX ACLを設定します。

Linux 2.6 (POSIX ACL対応) + ext3 FS (mountオプションにaclを指定) で動作確認しました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/sh

filename=$1

if [ -e "$filename" ]; then
    if [ -f "$filename" ]; then
        chmod 600 "$filename"
        setfacl -b "$filename"
        setfacl -n -m u:$USER:rw "$filename"
    else
        echo "$filename is not file" 1>&2
        exit 1
    fi
else
    tmpdir=$(umask 077; mktemp -d acl.XXXXXXXX)
    (umask 077; touch "$tmpdir/$filename")
    setfacl -n -m u:$USER:rw "$tmpdir/$filename"
    mv "$tmpdir/$filename" .
    rmdir $tmpdir
fi
なんと、Linux系でもWindowsみたいなACL使えたのですね。
しらなかったです。
Unix系はふつうにchmodでパーミッションの変更しかできないと思っていました。

知っていれば、パーミッションの問題で悩むこともへったのに~
残念。

とりあえず、下記は普通のパーミッション設定を行うだけのものです。
ACL版はただいま調査中

chmod を使っていないのはわざとです(笑

// gcc -Wall  doukaku244.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
  static const char *basename = "a.txt";
  char filename[255] = "";
  int fd;

  /* とりあえずファイル作成 */
  fd = open(basename, O_WRONLY | O_CREAT | O_EXCL | O_SYNC, 0600);
  if( fd == -1 )
  {
    /* すでに存在していた場合、別名でとりあえず作成 */
    snprintf(filename, sizeof(filename), "%s.%05d", basename, getpid());
    printf("filename:%s\n", filename);
    fd = open(filename, O_WRONLY | O_CREAT | O_SYNC, 0600);
    if( fd == -1 )
    {
      printf("create file failed.\n");
      return 1;
    }
  }

  write(fd, filename, strlen(filename));
  write(fd, "\n", 1);

  /* クローズ、別名で作成した場合、正規の名前に変更する */
  close(fd);
  if( strlen(filename) != 0 )
  {
    unlink(basename);
    rename(filename, basename);
  }

  return 0;
}
/* EOF */
FreeBSD 7.1で動作を確認しました。
// gcc (GCC) 4.2.1 20070719  [FreeBSD]

最初、user以外を省略していたら
EINVAL( invalid argument)になってしまってはまりました。

まだ、開発中とのことなので業務ではまだ利用できないかもしれませんが
使えるようになればパーミッションの問題で悩むことは減らせそうです。

gcc -Wall -std=c99 doukaku244_acl.c && ./a.out
$ getfacl b.txt
# file: b.txt
# owner: raynstard
# group: raynstard
user::rw-
user:kazuki:r--
group::---
mask::r--
other::---
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/acl.h>
#include <sys/param.h>

#include <string.h>
#include <fcntl.h>

#include <errno.h>

int main(int argc, char *argv[])
{
  static const char *basename = "b.txt";
  char filename[255] = "";
  int fd;

  acl_t  file_acl;

  /* とりあえずファイル作成 */
  fd = open(basename, O_WRONLY |O_CREAT | O_EXCL | O_SYNC, 0666);
  if( fd == -1 )
  {
    /* すでに存在していた場合、別名でとりあえず作成 */
    snprintf(filename, sizeof(filename), "%s.%05d", basename, getpid());
    printf("filename:%s\n", filename); // 確認用
    fd = open(filename, O_WRONLY |O_CREAT | O_SYNC, 0666);
    if( fd == -1 )
    {
      printf("create file failed.\n");
      return 1;
    }
  }

  /* ACLの確認用にkazukiだけは読み取りだけ可能とする */
  file_acl = acl_from_text("mask::r--,u::rw-,u:kazuki:r,g::,o::");
  if( file_acl == NULL )
  {
    printf("acl 生成失敗\n");
    return 1;
  }

  /*************************************************************/
  /* ちょっと確認                */
  /*************************************************************/
  if( acl_valid(file_acl) != 0 )
  {
    fprintf(stderr, "error:[%d] ", errno);
    perror("acl_valid() ");
  }

  char *acl_str = acl_to_text(file_acl, NULL);
  if( acl_str != NULL )
  {
    printf("acl:[%s]\n",  acl_str);
    if( acl_free( (void*)acl_str) != 0 )
    {
      fprintf(stderr, "error:[%d] ", errno);
      perror("acl_free()");
    }
  }
  /*************************************************************/


  /* ファイルにACLをセット */
  if( acl_set_fd( fd, file_acl ) != 0)
  {
    fprintf(stderr, "error:[%d] ", errno);
    perror("acl_set_fd() ");
  }

  acl_free( (void*)file_acl );

  write(fd, filename, strlen(filename));
  write(fd, "\n", 1);

  /* クローズ、別名で作成した場合、正規の名前に変更する */
  close(fd);
  if( strlen(filename) != 0 )
  {
    unlink(basename);
    rename(filename,basename);
  }

  return 0;
}
/* EOF */
Java6で追加されたFileクラスのsetReadable, setWritableを使ってアクセス権を設定します。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import java.io.*;

public class Sample {

    public static void main(String[] args) throws IOException {
        File f = new File(args[0]);
        if (!f.exists())
            f.createNewFile();            // 無ければ新規に作る
        f.setReadable(false, false);    // read権を全て削除
        f.setReadable(true, true);        // ownerのread権のみ設定
        f.setWritable(false, false);    // write権を全て削除
        f.setWritable(true, true);        // ownerのwrite権のみ設定
    }

}

お題には明記されていませんが、実行権を削除しろと言っているようにもとれるのでこちらも作ってみました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import java.io.*;

public class Sample {

    public static void main(String[] args) throws IOException {
        File f = new File(args[0]);
        if (!f.exists())
            f.createNewFile();            // 無ければ新規に作る
        f.setExecutable(false, false);    // 実行権を全て削除
        f.setReadable(false, false);    // read権を全て削除
        f.setReadable(true, true);        // ownerのread権のみ設定
        f.setWritable(false, false);    // write権を全て削除
        f.setWritable(true, true);        // ownerのwrite権のみ設定
    }

}

Squeak Smalltalk で。

…というより、外部コマンドの実行ですが…^^;

1
2
3
4
5
6
7
8
| path user |
path := '/path/to/a/file'.
(FileStream newFileNamed: path) close.
user := (OSProcess thisOSProcess waitForCommandOutput: 'echo $USER') withoutTrailingBlanks.
OSProcess thisOSProcess
    waitForCommand: 'chmod 600 ', path;
    waitForCommand: 'chmod +a "everyone deny read,write" ', path;
    waitForCommand: 'chmod +a# 0 "', user, ' allow read,write" ', path
1
2
3
4
5
<?php
$file_name = '/tmp/test.txt';
touch($file_name);
chmod($file_name, 0600);
?>
chmodを使って単純に権限を変更してみました。
a.txtファイルが存在すればそのファイルの権限を変更するのにも対応してます。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
use strict;
use warnings;
my $n;

if(-e "a.txt")
{
    print "a.txt file is already exist.\n";
    $n = chmod 0600, "a.txt";
    print "change permission to 0600\n";
} else{
    print "make new file a.txt\n";
    open NEW, ">a.txt";
    close NEW;
    $n = chmod 0600, "a.txt";
    print "change permission to 0600\n";
以下の投稿を参考にさせていただき、
groovyに書き直して見ました。

どう書く?org 8736 匿名: Java6で追加されたFileクラスのs...(ACLの制御) - 投稿の詳細 <http://ja.doukaku.org/comment/8736/>
1
2
3
4
5
6
7
8
9
new File(args[0]).with{
    if (exists())
        System.exit(0);
    createNewFile()
    setReadable(false, false)
    setReadable(true, true)
    setWritable(false, false)
    setWritable(true, true)
}

Windows APIです。Cで投稿されているのがいくつかあるので、無理やりC++化しました。まあCオンリーの人も見てくださいな。

大まかに言うと、目的の状態のDACLを作成→それを指定してファイルを作成 or ファイルを開いてDACLを設定の手順です。

CreateFileでREAD_CONTROL | WRITE_DACを指定しているのは、既存ファイルに設定するためです。新しくファイルを作るなら0で構いません(もちろんRead/WriteFileなどで読み書きするならGENERIC_READ/WRITEなど適宜指定してください)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <iostream>
#include <windows.h>
#include <aclapi.h>

// エラーメッセージ出力関数
void print_error(DWORD code)
{
    LPTSTR msg;
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        0, code, LANG_USER_DEFAULT, reinterpret_cast<LPTSTR>(&msg), 0, 0);
    std::cerr << msg << std::endl;
    LocalFree(reinterpret_cast<HLOCAL>(msg));
}

int main()
{
    PACL pacl;
    // 所有者のみ読み書きの設定
    EXPLICIT_ACCESS ea =
    {
        /* .grfAccessPermissions = */ GENERIC_READ | GENERIC_WRITE,
        /* .grfAccessMode = */ SET_ACCESS,
        /* .grfInheritance = */ NO_INHERITANCE,
        /* .Trustee = */
        {
            /* .pMultipleTrustee = */ 0,
            /* .MultipleTrusteeOperation = */ NO_MULTIPLE_TRUSTEE,
            /* .TrusteeForm = */ TRUSTEE_IS_NAME,
            /* .TrusteeType = */ TRUSTEE_IS_WELL_KNOWN_GROUP,
            /* .ptstrName = */ TEXT("CREATOR OWNER")
        },
    };
    DWORD res = SetEntriesInAcl(1, &ea, 0, &pacl);
    if (res != 0)
    {
        print_error(res);
        return 1;
    }
    // DACLを保持するセキュリティディスクプリタの作成
    PSECURITY_DESCRIPTOR psd = static_cast<PSECURITY_DESCRIPTOR>(
        malloc(SECURITY_DESCRIPTOR_MIN_LENGTH));
    if (InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION) == FALSE)
    {
        print_error(GetLastError());
        return 1;
    }

    if (SetSecurityDescriptorDacl(psd, TRUE, pacl, FALSE) == FALSE)
    {
        print_error(GetLastError());
        return 1;
    }
    // 上記設定を指定してファイルを作成
    SECURITY_ATTRIBUTES sa =
    {
        /*.nLength =*/ sizeof sa,
        /*.lpSecurityDescriptor =*/ psd,
        /*.bInheritHandle =*/ FALSE,
    };
    HANDLE hFile = CreateFile(TEXT("D:\\TEMP\\t.bat"), READ_CONTROL | WRITE_DAC,
        FILE_SHARE_WRITE, &sa, CREATE_ALWAYS, 0, 0);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        print_error(GetLastError());
        return 1;
    }
    // ファイルが既に開かれていた場合、sa.lpSecurityDescriptorの指定が反映されないので、ここで設定している
    res = SetSecurityInfo(hFile, SE_FILE_OBJECT,
        DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
        0, 0, pacl, 0);
    if (res != 0)
    {
        print_error(res);
        return 1;
    }
    CloseHandle(hFile);
    LocalFree(pacl);
    free(psd);
    return 0;
}

Common Lisp版です。短いけどosicatを使っているのでOSが限定されるのがちとイタイ。

1
2
3
4
5
(asdf:operate 'asdf:load-op :osicat)
(let ((filename "foo"))
  (with-open-file (dummy filename :direction :output :if-exists :supersede)
                  (princ "bar" dummy))
  (setf (osicat:file-permissions filename) (list :user-read :user-write)))

file.utilのfile-exists?をつかって存在確認し、 sys-chmodをつかって権限変更しました。

1
2
3
4
5
(use file.util)
(define (main args)
  (if (file-exists? "./a.txt")
      (begin (sys-chmod "./a.txt" #o600))
      (begin (touch-file "./a.txt") (sys-chmod "./a.txt" #o600))))
chmodでパーミッションを0600に変更しています。
1
2
3
filename = 'a.txt'
open(filename, 'w') { } unless File.exist?(filename)
File.chmod(0600, filename)

.NET Framework限定 (Monoでは動きそうにない)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;

class P
{
  static void Main(string[] args)
  {
    if (args.Length == 0) return;
    using (Stream s = File.Open
      (args[0],
      FileMode.CreateNew,
      FileAccess.Write,
      FileShare.ReadWrite))
    {
      FileSecurity sec = File.GetAccessControl(args[0]);
      IdentityReference owner = sec.GetOwner(typeof(NTAccount));
      sec.SetAccessRuleProtection(true, false);
      foreach (AuthorizationRule r
        in sec.GetAccessRules(true, true, typeof(NTAccount)))
      {
        sec.RemoveAccessRule((FileSystemAccessRule)r);
      }
      sec.AddAccessRule
        (new FileSystemAccessRule
          (owner,
          FileSystemRights.Read | FileSystemRights.Write,
          AccessControlType.Allow));
      File.SetAccessControl(args[0], sec);
    }
  }
}
1
2
3
4
let file = Sys.argv.(1) in 
let f = try open_out_gen [Open_wronly; Open_creat; Open_trunc; Open_text] 0o600 file
with Sys_error msg -> failwith ("Couldn't write to " ^ msg) in 
output_string f "OCaml\n";close_out f;;

Index

Feed

Other

Link

Pathtraq

loading...