win32-security 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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