win32-security 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,394 +1,394 @@
1
- require 'socket'
2
-
3
- # The Win32 module serves as a namespace only.
4
- module Win32
5
-
6
- # The Security class serves as a toplevel class namespace.
7
- class Security
8
-
9
- # The SID class encapsulates a Security Identifier.
10
- class SID
11
- include Windows::Security::Constants
12
- include Windows::Security::Functions
13
- include Windows::Security::Structs
14
- extend Windows::Security::Functions
15
-
16
- # The version of the Win32::Security::SID class.
17
- VERSION = '0.2.1'
18
-
19
- # Some constant SID's for your convenience, in string format.
20
- # See http://support.microsoft.com/kb/243330 for details.
21
-
22
- Null = 'S-1-0'
23
- Nobody = 'S-1-0-0'
24
- World = 'S-1-1'
25
- Everyone = 'S-1-1-0'
26
- Local = 'S-1-2'
27
- Creator = 'S-1-3'
28
- CreatorOwner = 'S-1-3-0'
29
- CreatorGroup = 'S-1-3-1'
30
- CreatorOwnerServer = 'S-1-3-2'
31
- CreatorGroupServer = 'S-1-3-3'
32
- NonUnique = 'S-1-4'
33
- Nt = 'S-1-5'
34
- Dialup = 'S-1-5-1'
35
- Network = 'S-1-5-2'
36
- Batch = 'S-1-5-3'
37
- Interactive = 'S-1-5-4'
38
- Service = 'S-1-5-6'
39
- Anonymous = 'S-1-5-7'
40
- Proxy = 'S-1-5-8'
41
- EnterpriseDomainControllers = 'S-1-5-9'
42
- PrincipalSelf = 'S-1-5-10'
43
- AuthenticatedUsers = 'S-1-5-11'
44
- RestrictedCode = 'S-1-5-12'
45
- TerminalServerUsers = 'S-1-5-13'
46
- LocalSystem = 'S-1-5-18'
47
- NtLocal = 'S-1-5-19'
48
- NtNetwork = 'S-1-5-20'
49
- BuiltinAdministrators = 'S-1-5-32-544'
50
- BuiltinUsers = 'S-1-5-32-545'
51
- Guests = 'S-1-5-32-546'
52
- PowerUsers = 'S-1-5-32-547'
53
- AccountOperators = 'S-1-5-32-548'
54
- ServerOperators = 'S-1-5-32-549'
55
- PrintOperators = 'S-1-5-32-550'
56
- BackupOperators = 'S-1-5-32-551'
57
- Replicators = 'S-1-5-32-552'
58
-
59
- # The binary SID object itself.
60
- attr_reader :sid
61
-
62
- # The account name passed to the constructor.
63
- attr_reader :account
64
-
65
- # The SID account type, e.g. 'user, 'group', etc.
66
- attr_reader :account_type
67
-
68
- # The domain the SID is on.
69
- attr_reader :domain
70
-
71
- # The host passed to the constructor, or the localhost if none
72
- # was specified.
73
- attr_reader :host
74
-
75
- # Converts a binary SID to a string in S-R-I-S-S... format.
76
- #
77
- def self.sid_to_string(sid)
78
- result = nil
79
-
80
- FFI::MemoryPointer.new(:pointer) do |string_sid|
81
- unless ConvertSidToStringSid(sid, string_sid)
82
- raise SystemCallError.new("ConvertSidToStringSid", FFI.errno)
83
- end
84
-
85
- result = string_sid.read_pointer.read_string
86
- end
87
-
88
- result
89
- end
90
-
91
- # Converts a string in S-R-I-S-S... format back to a binary SID.
92
- #
93
- def self.string_to_sid(string)
94
- result = nil
95
-
96
- FFI::MemoryPointer.new(:pointer) do |sid|
97
- unless ConvertStringSidToSid(string, sid)
98
- raise SystemCallError.new("ConvertStringSidToSid", FFI.errno)
99
- end
100
-
101
- ptr = sid.read_pointer
102
-
103
- result = ptr.read_bytes(GetLengthSid(ptr))
104
- end
105
-
106
- result
107
- end
108
-
109
- # Creates a new SID with +authority+ and up to 8 +subauthorities+,
110
- # and returns new Win32::Security::SID object.
111
- #
112
- # Example:
113
- #
114
- # sec = Security::SID.create(
115
- # Security::SID::SECURITY_WORLD_SID_AUTHORITY,
116
- # Security::SID::SECURITY_WORLD_RID
117
- # )
118
- #
119
- # p sec
120
- #
121
- # #<Win32::Security::SID:0x2c5a95c
122
- # @host="your_host",
123
- # @account="Everyone",
124
- # @account_type="well known group",
125
- # @sid="\001\001\000\000\000\000\000\001\000\000\000\000",
126
- # @domain=""
127
- # >
128
- #
129
- def self.create(authority, *sub_authorities)
130
- if sub_authorities.length > 8
131
- raise ArgumentError, "maximum of 8 subauthorities allowed"
132
- end
133
-
134
- size = GetSidLengthRequired(sub_authorities.length)
135
- new_obj = nil
136
-
137
- FFI::MemoryPointer.new(:uchar, size) do |sid|
138
- auth = SID_IDENTIFIER_AUTHORITY.new
139
- auth[:Value][5] = authority
140
-
141
- unless InitializeSid(sid, auth, sub_authorities.length)
142
- raise SystemCallError.new("InitializeSid", FFI.errno)
143
- end
144
-
145
- sub_authorities.each_index do |i|
146
- ptr = GetSidSubAuthority(sid, i)
147
- ptr.write_ulong(sub_authorities[i])
148
- end
149
-
150
- new_obj = new(sid.read_string(size)) # Pass a binary string
151
- end
152
-
153
- new_obj
154
- end
155
-
156
- # Creates and returns a new Win32::Security::SID object, based on
157
- # the account name, which may also be a binary SID. If a host is
158
- # provided, then the information is retrieved from that host.
159
- # Otherwise, the local host is used.
160
- #
161
- # If no account is provided then it retrieves information for the
162
- # user account associated with the calling thread and the host argument
163
- # is ignored.
164
- #
165
- # Note that this does NOT create a new SID, but merely retrieves
166
- # information for an existing SID. To create a new SID, use the
167
- # SID.create method.
168
- #
169
- # Examples:
170
- #
171
- # # Current user
172
- # Win32::Security::SID.new
173
- #
174
- # # User 'john' on the localhost
175
- # Win32::Security::SID.new('john')
176
- #
177
- # # User 'jane' on a remote machine
178
- # Win32::Security::SID.new('jane', 'some_host')
179
- #
180
- # # Binary SID
181
- # Win32::Security::SID.new("\001\000\000\000\000\000\001\000\000\000\000")
182
- #
183
- def initialize(account=nil, host=Socket.gethostname)
184
- if account.nil?
185
- begin
186
- if RUBY_PLATFORM == 'java' && ENV_JAVA['sun.arch.data.model'] == '64'
187
- ptr_type = :ulong_long
188
- else
189
- ptr_type = :uintptr_t
190
- end
191
-
192
- ptoken = FFI::MemoryPointer.new(ptr_type)
193
-
194
- # Try the thread token first, default to the process token.
195
- bool = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, true, ptoken)
196
-
197
- if !bool && FFI.errno != ERROR_NO_TOKEN
198
- raise SystemCallError.new("OpenThreadToken", FFI.errno)
199
- else
200
- ptoken.clear
201
- unless OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, ptoken)
202
- raise SystemCallError.new("OpenProcessToken", FFI.errno)
203
- end
204
- end
205
-
206
- token = ptoken.read_pointer.to_i
207
-
208
- pinfo = FFI::MemoryPointer.new(:pointer)
209
- plength = FFI::MemoryPointer.new(:ulong)
210
-
211
- # First pass, just get the size needed (1 is TokenOwner)
212
- GetTokenInformation(token, 1, pinfo, pinfo.size, plength)
213
-
214
- pinfo = FFI::MemoryPointer.new(plength.read_ulong)
215
- plength.clear
216
-
217
- # Second pass, actual call (1 is TokenOwner)
218
- unless GetTokenInformation(token, 1, pinfo, pinfo.size, plength)
219
- raise SystemCallError.new("GetTokenInformation", FFI.errno)
220
- end
221
-
222
- token_info = pinfo.read_pointer
223
- ensure
224
- CloseHandle(token) if token
225
- end
226
- end
227
-
228
- if account
229
- ordinal_val = account[0]
230
- ordinal_val = ordinal_val.ord if RUBY_VERSION.to_f >= 1.9
231
- else
232
- ordinal_val = nil
233
- end
234
-
235
- sid = FFI::MemoryPointer.new(:uchar, 260)
236
- sid_size = FFI::MemoryPointer.new(:ulong)
237
- sid_size.write_ulong(sid.size)
238
-
239
- domain = FFI::MemoryPointer.new(:uchar, 260)
240
- domain_size = FFI::MemoryPointer.new(:ulong)
241
- domain_size.write_ulong(domain.size)
242
-
243
- use_ptr = FFI::MemoryPointer.new(:ulong)
244
-
245
- if ordinal_val.nil?
246
- bool = LookupAccountSid(
247
- nil,
248
- token_info,
249
- sid,
250
- sid_size,
251
- domain,
252
- domain_size,
253
- use_ptr
254
- )
255
- unless bool
256
- raise SystemCallError.new("LookupAccountSid", FFI.errno)
257
- end
258
- elsif ordinal_val < 10 # Assume it's a binary SID.
259
- account_ptr = FFI::MemoryPointer.from_string(account)
260
-
261
- bool = LookupAccountSid(
262
- host,
263
- account_ptr,
264
- sid,
265
- sid_size,
266
- domain,
267
- domain_size,
268
- use_ptr
269
- )
270
-
271
- unless bool
272
- raise SystemCallError.new("LookupAccountSid", FFI.errno)
273
- end
274
-
275
- account_ptr.free
276
- else
277
- bool = LookupAccountName(
278
- host,
279
- account,
280
- sid,
281
- sid_size,
282
- domain,
283
- domain_size,
284
- use_ptr
285
- )
286
- unless bool
287
- raise SystemCallError.new("LookupAccountName", FFI.errno)
288
- end
289
- end
290
-
291
- # The arguments are flipped depending on which path we took
292
- if ordinal_val.nil?
293
- @sid = token_info.read_string
294
- @account = sid.read_string(sid.size).strip
295
- elsif ordinal_val < 10
296
- @sid = account
297
- @account = sid.read_string(sid.size).strip
298
- else
299
- length = GetLengthSid(sid)
300
- @sid = sid.read_string(length)
301
- @account = account
302
- end
303
-
304
- @host = host
305
- @domain = domain.read_string
306
-
307
- @account_type = get_account_type(use_ptr.read_ulong)
308
- end
309
-
310
- # Synonym for SID.new.
311
- #
312
- def self.open(account=nil, host=Socket.gethostname)
313
- new(account, host)
314
- end
315
-
316
- # Returns the binary SID in string format suitable for display,
317
- # storage or transmission.
318
- #
319
- def to_s
320
- string = nil
321
-
322
- FFI::MemoryPointer.new(:pointer) do |ptr|
323
- unless ConvertSidToStringSid(@sid, ptr)
324
- raise SystemCallError.new("ConvertSidToStringSid", FFI.errno)
325
- end
326
-
327
- string = ptr.read_pointer.read_string
328
- end
329
-
330
- string
331
- end
332
-
333
- alias to_str to_s
334
-
335
- # Returns whether or not the SID object is equal to +other+.
336
- #
337
- def ==(other)
338
- EqualSid(@sid, other.sid)
339
- end
340
-
341
- # Returns whether or not the SID is a valid sid.
342
- #
343
- def valid?
344
- IsValidSid(@sid)
345
- end
346
-
347
- # Returns whether or not the SID is a well known SID.
348
- #
349
- # Requires Windows XP or later. Earlier versions will raise a
350
- # NoMethodError.
351
- #
352
- def well_known?
353
- if defined? IsWellKnownSid
354
- IsWellKnownSid(@sid)
355
- else
356
- raise NoMethodError, 'requires Windows XP or later'
357
- end
358
- end
359
-
360
- # Returns the length of the SID object, in bytes.
361
- #
362
- def length
363
- GetLengthSid(@sid)
364
- end
365
-
366
- private
367
-
368
- # Converts a numeric account type into a human readable string.
369
- #
370
- def get_account_type(value)
371
- case value
372
- when SidTypeUser
373
- 'user'
374
- when SidTypeGroup
375
- 'group'
376
- when SidTypeDomain
377
- 'domain'
378
- when SidTypeAlias
379
- 'alias'
380
- when SidTypeWellKnownGroup
381
- 'well known group'
382
- when SidTypeDeletedAccount
383
- 'deleted account'
384
- when SidTypeInvalid
385
- 'invalid'
386
- when SidTypeUnknown
387
- 'unknown'
388
- when SidComputer
389
- 'computer'
390
- end
391
- end
392
- end
393
- end
394
- end
1
+ require 'socket'
2
+
3
+ # The Win32 module serves as a namespace only.
4
+ module Win32
5
+
6
+ # The Security class serves as a toplevel class namespace.
7
+ class Security
8
+
9
+ # The SID class encapsulates a Security Identifier.
10
+ class SID
11
+ include Windows::Security::Constants
12
+ include Windows::Security::Functions
13
+ include Windows::Security::Structs
14
+ extend Windows::Security::Functions
15
+
16
+ # The version of the Win32::Security::SID class.
17
+ VERSION = '0.2.2'
18
+
19
+ # Some constant SID's for your convenience, in string format.
20
+ # See http://support.microsoft.com/kb/243330 for details.
21
+
22
+ Null = 'S-1-0'
23
+ Nobody = 'S-1-0-0'
24
+ World = 'S-1-1'
25
+ Everyone = 'S-1-1-0'
26
+ Local = 'S-1-2'
27
+ Creator = 'S-1-3'
28
+ CreatorOwner = 'S-1-3-0'
29
+ CreatorGroup = 'S-1-3-1'
30
+ CreatorOwnerServer = 'S-1-3-2'
31
+ CreatorGroupServer = 'S-1-3-3'
32
+ NonUnique = 'S-1-4'
33
+ Nt = 'S-1-5'
34
+ Dialup = 'S-1-5-1'
35
+ Network = 'S-1-5-2'
36
+ Batch = 'S-1-5-3'
37
+ Interactive = 'S-1-5-4'
38
+ Service = 'S-1-5-6'
39
+ Anonymous = 'S-1-5-7'
40
+ Proxy = 'S-1-5-8'
41
+ EnterpriseDomainControllers = 'S-1-5-9'
42
+ PrincipalSelf = 'S-1-5-10'
43
+ AuthenticatedUsers = 'S-1-5-11'
44
+ RestrictedCode = 'S-1-5-12'
45
+ TerminalServerUsers = 'S-1-5-13'
46
+ LocalSystem = 'S-1-5-18'
47
+ NtLocal = 'S-1-5-19'
48
+ NtNetwork = 'S-1-5-20'
49
+ BuiltinAdministrators = 'S-1-5-32-544'
50
+ BuiltinUsers = 'S-1-5-32-545'
51
+ Guests = 'S-1-5-32-546'
52
+ PowerUsers = 'S-1-5-32-547'
53
+ AccountOperators = 'S-1-5-32-548'
54
+ ServerOperators = 'S-1-5-32-549'
55
+ PrintOperators = 'S-1-5-32-550'
56
+ BackupOperators = 'S-1-5-32-551'
57
+ Replicators = 'S-1-5-32-552'
58
+
59
+ # The binary SID object itself.
60
+ attr_reader :sid
61
+
62
+ # The account name passed to the constructor.
63
+ attr_reader :account
64
+
65
+ # The SID account type, e.g. 'user, 'group', etc.
66
+ attr_reader :account_type
67
+
68
+ # The domain the SID is on.
69
+ attr_reader :domain
70
+
71
+ # The host passed to the constructor, or the localhost if none
72
+ # was specified.
73
+ attr_reader :host
74
+
75
+ # Converts a binary SID to a string in S-R-I-S-S... format.
76
+ #
77
+ def self.sid_to_string(sid)
78
+ result = nil
79
+
80
+ FFI::MemoryPointer.new(:pointer) do |string_sid|
81
+ unless ConvertSidToStringSid(sid, string_sid)
82
+ raise SystemCallError.new("ConvertSidToStringSid", FFI.errno)
83
+ end
84
+
85
+ result = string_sid.read_pointer.read_string
86
+ end
87
+
88
+ result
89
+ end
90
+
91
+ # Converts a string in S-R-I-S-S... format back to a binary SID.
92
+ #
93
+ def self.string_to_sid(string)
94
+ result = nil
95
+
96
+ FFI::MemoryPointer.new(:pointer) do |sid|
97
+ unless ConvertStringSidToSid(string, sid)
98
+ raise SystemCallError.new("ConvertStringSidToSid", FFI.errno)
99
+ end
100
+
101
+ ptr = sid.read_pointer
102
+
103
+ result = ptr.read_bytes(GetLengthSid(ptr))
104
+ end
105
+
106
+ result
107
+ end
108
+
109
+ # Creates a new SID with +authority+ and up to 8 +subauthorities+,
110
+ # and returns new Win32::Security::SID object.
111
+ #
112
+ # Example:
113
+ #
114
+ # sec = Security::SID.create(
115
+ # Security::SID::SECURITY_WORLD_SID_AUTHORITY,
116
+ # Security::SID::SECURITY_WORLD_RID
117
+ # )
118
+ #
119
+ # p sec
120
+ #
121
+ # #<Win32::Security::SID:0x2c5a95c
122
+ # @host="your_host",
123
+ # @account="Everyone",
124
+ # @account_type="well known group",
125
+ # @sid="\001\001\000\000\000\000\000\001\000\000\000\000",
126
+ # @domain=""
127
+ # >
128
+ #
129
+ def self.create(authority, *sub_authorities)
130
+ if sub_authorities.length > 8
131
+ raise ArgumentError, "maximum of 8 subauthorities allowed"
132
+ end
133
+
134
+ size = GetSidLengthRequired(sub_authorities.length)
135
+ new_obj = nil
136
+
137
+ FFI::MemoryPointer.new(:uchar, size) do |sid|
138
+ auth = SID_IDENTIFIER_AUTHORITY.new
139
+ auth[:Value][5] = authority
140
+
141
+ unless InitializeSid(sid, auth, sub_authorities.length)
142
+ raise SystemCallError.new("InitializeSid", FFI.errno)
143
+ end
144
+
145
+ sub_authorities.each_index do |i|
146
+ ptr = GetSidSubAuthority(sid, i)
147
+ ptr.write_ulong(sub_authorities[i])
148
+ end
149
+
150
+ new_obj = new(sid.read_string(size)) # Pass a binary string
151
+ end
152
+
153
+ new_obj
154
+ end
155
+
156
+ # Creates and returns a new Win32::Security::SID object, based on
157
+ # the account name, which may also be a binary SID. If a host is
158
+ # provided, then the information is retrieved from that host.
159
+ # Otherwise, the local host is used.
160
+ #
161
+ # If no account is provided then it retrieves information for the
162
+ # user account associated with the calling thread and the host argument
163
+ # is ignored.
164
+ #
165
+ # Note that this does NOT create a new SID, but merely retrieves
166
+ # information for an existing SID. To create a new SID, use the
167
+ # SID.create method.
168
+ #
169
+ # Examples:
170
+ #
171
+ # # Current user
172
+ # Win32::Security::SID.new
173
+ #
174
+ # # User 'john' on the localhost
175
+ # Win32::Security::SID.new('john')
176
+ #
177
+ # # User 'jane' on a remote machine
178
+ # Win32::Security::SID.new('jane', 'some_host')
179
+ #
180
+ # # Binary SID
181
+ # Win32::Security::SID.new("\001\000\000\000\000\000\001\000\000\000\000")
182
+ #
183
+ def initialize(account=nil, host=Socket.gethostname)
184
+ if account.nil?
185
+ begin
186
+ if RUBY_PLATFORM == 'java' && ENV_JAVA['sun.arch.data.model'] == '64'
187
+ ptr_type = :ulong_long
188
+ else
189
+ ptr_type = :uintptr_t
190
+ end
191
+
192
+ ptoken = FFI::MemoryPointer.new(ptr_type)
193
+
194
+ # Try the thread token first, default to the process token.
195
+ bool = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, 1, ptoken)
196
+
197
+ if !bool && FFI.errno != ERROR_NO_TOKEN
198
+ raise SystemCallError.new("OpenThreadToken", FFI.errno)
199
+ else
200
+ ptoken.clear
201
+ unless OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, ptoken)
202
+ raise SystemCallError.new("OpenProcessToken", FFI.errno)
203
+ end
204
+ end
205
+
206
+ token = ptoken.read_pointer.to_i
207
+
208
+ pinfo = FFI::MemoryPointer.new(:pointer)
209
+ plength = FFI::MemoryPointer.new(:ulong)
210
+
211
+ # First pass, just get the size needed (1 is TokenOwner)
212
+ GetTokenInformation(token, 1, pinfo, pinfo.size, plength)
213
+
214
+ pinfo = FFI::MemoryPointer.new(plength.read_ulong)
215
+ plength.clear
216
+
217
+ # Second pass, actual call (1 is TokenOwner)
218
+ unless GetTokenInformation(token, 1, pinfo, pinfo.size, plength)
219
+ raise SystemCallError.new("GetTokenInformation", FFI.errno)
220
+ end
221
+
222
+ token_info = pinfo.read_pointer
223
+ ensure
224
+ CloseHandle(token) if token
225
+ end
226
+ end
227
+
228
+ if account
229
+ ordinal_val = account[0]
230
+ ordinal_val = ordinal_val.ord if RUBY_VERSION.to_f >= 1.9
231
+ else
232
+ ordinal_val = nil
233
+ end
234
+
235
+ sid = FFI::MemoryPointer.new(:uchar, 260)
236
+ sid_size = FFI::MemoryPointer.new(:ulong)
237
+ sid_size.write_ulong(sid.size)
238
+
239
+ domain = FFI::MemoryPointer.new(:uchar, 260)
240
+ domain_size = FFI::MemoryPointer.new(:ulong)
241
+ domain_size.write_ulong(domain.size)
242
+
243
+ use_ptr = FFI::MemoryPointer.new(:ulong)
244
+
245
+ if ordinal_val.nil?
246
+ bool = LookupAccountSid(
247
+ nil,
248
+ token_info,
249
+ sid,
250
+ sid_size,
251
+ domain,
252
+ domain_size,
253
+ use_ptr
254
+ )
255
+ unless bool
256
+ raise SystemCallError.new("LookupAccountSid", FFI.errno)
257
+ end
258
+ elsif ordinal_val < 10 # Assume it's a binary SID.
259
+ account_ptr = FFI::MemoryPointer.from_string(account)
260
+
261
+ bool = LookupAccountSid(
262
+ host,
263
+ account_ptr,
264
+ sid,
265
+ sid_size,
266
+ domain,
267
+ domain_size,
268
+ use_ptr
269
+ )
270
+
271
+ unless bool
272
+ raise SystemCallError.new("LookupAccountSid", FFI.errno)
273
+ end
274
+
275
+ account_ptr.free
276
+ else
277
+ bool = LookupAccountName(
278
+ host,
279
+ account,
280
+ sid,
281
+ sid_size,
282
+ domain,
283
+ domain_size,
284
+ use_ptr
285
+ )
286
+ unless bool
287
+ raise SystemCallError.new("LookupAccountName", FFI.errno)
288
+ end
289
+ end
290
+
291
+ # The arguments are flipped depending on which path we took
292
+ if ordinal_val.nil?
293
+ @sid = token_info.read_string
294
+ @account = sid.read_string(sid.size).strip
295
+ elsif ordinal_val < 10
296
+ @sid = account
297
+ @account = sid.read_string(sid.size).strip
298
+ else
299
+ length = GetLengthSid(sid)
300
+ @sid = sid.read_string(length)
301
+ @account = account
302
+ end
303
+
304
+ @host = host
305
+ @domain = domain.read_string
306
+
307
+ @account_type = get_account_type(use_ptr.read_ulong)
308
+ end
309
+
310
+ # Synonym for SID.new.
311
+ #
312
+ def self.open(account=nil, host=Socket.gethostname)
313
+ new(account, host)
314
+ end
315
+
316
+ # Returns the binary SID in string format suitable for display,
317
+ # storage or transmission.
318
+ #
319
+ def to_s
320
+ string = nil
321
+
322
+ FFI::MemoryPointer.new(:pointer) do |ptr|
323
+ unless ConvertSidToStringSid(@sid, ptr)
324
+ raise SystemCallError.new("ConvertSidToStringSid", FFI.errno)
325
+ end
326
+
327
+ string = ptr.read_pointer.read_string
328
+ end
329
+
330
+ string
331
+ end
332
+
333
+ alias to_str to_s
334
+
335
+ # Returns whether or not the SID object is equal to +other+.
336
+ #
337
+ def ==(other)
338
+ EqualSid(@sid, other.sid)
339
+ end
340
+
341
+ # Returns whether or not the SID is a valid sid.
342
+ #
343
+ def valid?
344
+ IsValidSid(@sid)
345
+ end
346
+
347
+ # Returns whether or not the SID is a well known SID.
348
+ #
349
+ # Requires Windows XP or later. Earlier versions will raise a
350
+ # NoMethodError.
351
+ #
352
+ def well_known?
353
+ if defined? IsWellKnownSid
354
+ IsWellKnownSid(@sid)
355
+ else
356
+ raise NoMethodError, 'requires Windows XP or later'
357
+ end
358
+ end
359
+
360
+ # Returns the length of the SID object, in bytes.
361
+ #
362
+ def length
363
+ GetLengthSid(@sid)
364
+ end
365
+
366
+ private
367
+
368
+ # Converts a numeric account type into a human readable string.
369
+ #
370
+ def get_account_type(value)
371
+ case value
372
+ when SidTypeUser
373
+ 'user'
374
+ when SidTypeGroup
375
+ 'group'
376
+ when SidTypeDomain
377
+ 'domain'
378
+ when SidTypeAlias
379
+ 'alias'
380
+ when SidTypeWellKnownGroup
381
+ 'well known group'
382
+ when SidTypeDeletedAccount
383
+ 'deleted account'
384
+ when SidTypeInvalid
385
+ 'invalid'
386
+ when SidTypeUnknown
387
+ 'unknown'
388
+ when SidComputer
389
+ 'computer'
390
+ end
391
+ end
392
+ end
393
+ end
394
+ end