[Win32utils-devel] File.owned? custom implementation

Daniel Berger djberg96 at gmail.com
Sat Dec 29 03:08:16 UTC 2012


On Tue, Dec 25, 2012 at 8:02 AM, Heesob Park <phasis at gmail.com> wrote:
> Hi,
>
> 2012/12/25 Daniel Berger <djberg96 at gmail.com>
>>
>> On Mon, Dec 24, 2012 at 6:42 PM, Heesob Park <phasis at gmail.com> wrote:
>> > Hi,
>> >
>> > 2012/12/25 Daniel Berger <djberg96 at gmail.com>:
>> >> Hi,
>> >>
>> >> I've added what I think is a working File.owned? method to the
>> >> win32-file-security gem. However, I'm not 100% sure that my method of
>> >> comparison is correct - I'm comparing sid strings (where I sort of
>> >> artifically chop the size of one of them) instead of using EqualSid
>> >> with pointers.
>> >>
>> >> Can someone please validate? Just pull the latest git master.
>> >>
>> >> Suggestions welcome.
>> >>
>> >> Thanks,
>> >>
>> >> Dan
>> >>
>> >> PS - Heesob, I thought maybe you implemented this already at some
>> >> point, but I couldn't find it. If you did and I missed it then I'm
>> >> sorry!
>> > The File.owned? method works fine on some tests.
>> >
>> > I can't remember I have implemented this method.
>> > But I found some related messages.
>> >
>> > http://rubyforge.org/pipermail/win32utils-devel/2008-June/thread.html#1163
>>
>> Ok, I just wanted to make sure bit of code wasn't problematic:
>>
>> if string_sid == sid.read_string(string_sid.size)
>>   return_value = true
>> end
>>
>> I'm not sure how to implement File.chown. The links from those 2008
>> messages seem to be dead. I found this:
>>
>> http://www.perlmonks.org/?node_id=70562
>>
>> Would that work?
>>
> Did you mean you cannot reach the link?
> http://rubyforge.org/pipermail/win32utils-devel/2008-June/001170.html
>
> Following is the content of the message:
> =========================================================
> Hi,
> ----- Original Message -----
> From: "Berger, Daniel" <Daniel.Berger at qwest.com>
> To: "Development and ideas for win32utils projects"
> <win32utils-devel at rubyforge.org>
> Sent: Saturday, June 28, 2008 12:27 AM
> Subject: Re: [Win32utils-devel] User sid
>
>
>> Hi,
>>
>> <snip>
>>
>>> > What do you think about the idea of having Process.uid
>>> return a SID, and
>>> > a Process.uid= method that would take a SID? I think this
>>> is possible
>>> > with SetSecurityInfo, but I haven't tried yet.
>>> >
>>> > Possible enhancement for win32-process 0.6.0?
>>> >
>>> I'm not sure it is possible. but it is worthwhile.
>>
>> Here's one possible solution I came across.
>>
>> http://hockeythoughts.net/forums/printthread.php?t=1210
>>
>> There might be better ones, though.
>>
>>> In relation to the SID,
>>>  Finding the Owner of a File Object
>>> (http://msdn.microsoft.com/en-us/library/aa446629(VS.85).aspx)
>>> and Taking Object Ownership
>>> (http://msdn.microsoft.com/en-us/library/aa379620(VS.85).aspx)
>>> is also possible enhancement for win32-file, right?
>>
>> That looks like a good idea. With that we could implement a working
>> File.chown (and File.owned?, too, I think), correct?
>>
>> Please go ahead and implement this if you have the desire and the time,
>> as I will be out next week on vacation.
>>
> Here is a working code for change_owner and get_owner :
>
> require 'windows/process'
> require 'windows/handle'
> require 'windows/file'
> require 'windows/error'
> require 'windows/security'
> require 'windows/msvcrt/string'
> require 'windows/msvcrt/buffer'
>
> include Windows::Process
> include Windows::Handle
> include Windows::File
> include Windows::Error
> include Windows::Security
> include Windows::MSVCRT::String
> include Windows::MSVCRT::Buffer
>
> SE_KERNEL_OBJECT = 6
> SE_FILE_OBJECT = 1
> OWNER_SECURITY_INFORMATION = 1
> SECURITY_DESCRIPTOR_REVISION = 1
>
> def get_sid(account)
>    sid = 0.chr * 28
>    sid_cb = [sid.size].pack('L')
>
>    name_buf = 0.chr * 80
>    name_cch = [name_buf.size].pack('L')
>
>    domain_buf = 0.chr * 80
>    domain_cch = [domain_buf.size].pack('L')
>
>    sid_name_use = 0.chr * 4
>    bool = LookupAccountName(
>      nil,
>      account,
>      sid,
>      sid_cb,
>      domain_buf,
>      domain_cch,
>      sid_name_use
>    )
>    unless bool
>        raise get_last_error
>    end
>    sid_addr = [sid].pack('p*').unpack('L').first
>    sid_buf = 0.chr * 80
>    sid_ptr = 0.chr * 4
>    unless ConvertSidToStringSid(sid_addr,sid_ptr)
>       raise get_last_error
>    end
>    strcpy(sid_buf,sid_ptr.unpack('L').first)
>    sid
> end
>
> def enable_privilege(privilege)
>    modify_state(privilege, true)
> end
>
> def disable_privilege(privilege)
>    modify_state(privilege, false)
> end
>
> def modify_state(privilege,enable)
>    token_handle = [0].pack('L')
>
>    unless OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY |
>     TOKEN_ADJUST_PRIVILEGES, token_handle)
>       raise get_last_error
>    end
>
>    token_handle  = token_handle.unpack('L')[0]
>    return_length = [0].pack('L')
>
>    luid = 0.chr * 8
>    LookupPrivilegeValue("", privilege, luid)
>    unless LookupPrivilegeValue("", privilege, luid)
>       raise get_last_error
>    end
>
>    luid = luid.unpack('LL')
>    if enable
>      tkp1 = [ 1, luid[0], luid[1], 2 ].pack('LLLL')
>    else
>      tkp1 = [ 1, luid[0], luid[1], 0 ].pack('LLLL')
>    end
>
>    unless AdjustTokenPrivileges(token_handle, 0, tkp1, tkp1.size, nil, nil)
>        raise get_last_error
>    end
>
>    CloseHandle(token_handle)
>    true
> end
>
> def get_owner(file)
>    handle = CreateFile(
>             file,
>             GENERIC_READ ,
>             FILE_SHARE_READ,
>             0,
>             OPEN_EXISTING,
>             0,
>             0
>          )
>    if handle == INVALID_HANDLE_VALUE
>             raise ArgumentError, get_last_error
>          end
>    sid = 0.chr * 28
>    sid_ptr = [0].pack('L')
>    ret = GetSecurityInfo(
>              handle,
>              SE_FILE_OBJECT,
>              OWNER_SECURITY_INFORMATION,
>              sid_ptr,nil,nil,nil,nil)
>    if(ret!=0)
>      raise get_last_error
>    end
>
>    name_buf = 0.chr * 80
>    name_cch = [name_buf.size].pack('L')
>
>    domain_buf = 0.chr * 80
>    domain_cch = [domain_buf.size].pack('L')
>    sid_name_use = 0.chr * 4
>
>    bool = LookupAccountSid(
>        nil,
>        sid_ptr.unpack('L').first,
>        name_buf,
>        name_cch,
>        domain_buf,
>        domain_cch,
>        sid_name_use
>    )
>    unless bool
>        raise get_last_error
>    end
>    name_buf.strip
> end
>
> def change_owner(file,account)
>    enable_privilege("SeRestorePrivilege")
>
>    sid = get_sid(account)
>    sec_desc = 0.chr * SECURITY_DESCRIPTOR_MIN_LENGTH
>    unless InitializeSecurityDescriptor(sec_desc, 1)
>       raise get_last_error
>    end
>
>    unless SetSecurityDescriptorOwner(sec_desc, sid, 0)
>       raise get_last_error
>    end
>
>    unless SetFileSecurity(file, OWNER_SECURITY_INFORMATION, sec_desc)
>       raise get_last_error
>    end
>
>    disable_privilege ("SeRestorePrivilege")
> end
>
> You can test it with
>  change_owner('c:/test.txt','user')
> or
>  get_owner('c:/test.txt')
> ==============================================================

Thanks for this. It's similar in many ways to what I was already
working on, so I was definitely using your code as a reference point,
so that helped.

I think I have a working File.owner and File.chown implementation now
in github. The only odd thing is that the explorer pop-up properties
window doesn't seem to reflect the ownership change (in the "details"
tab), but a "dir /q" on the command line does show it, as does the
File.owner method. Perhaps I did something wrong?

Also, I'm not sure what to do with the "group" parameter for the
File.chown method (to match the Ruby API spec). For now I'm just
ignoring it. Technically it's not in line with the spec anyway, since
we're accepting a userid (string) instead of a numeric id for the
owner. I suppose we could alter that, but I kinda like it that way.

Anyway, please feel free to take a look and see what you think.

Regards,

Dan


More information about the win32utils-devel mailing list