win32-security 0.1.2 → 0.1.3

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