Class: Net::LDAP::Connection
- Inherits:
-
Object
- Object
- Net::LDAP::Connection
- Defined in:
- lib/rex/proto/ldap.rb
Overview
Update Net::LDAP’s initialize and new_connection method to honor a tracking proxies setting
Defined Under Namespace
Modules: ConnectionSaslIO, SocketSaslIO, SynchronousRead
Instance Method Summary collapse
-
#initialize(server) {|_self| ... } ⇒ Connection
constructor
Initialize the LDAP connection using Rex::Socket::TCP, and optionally set up encryption on the connection if configured.
-
#ldapwhoami ⇒ Object
Monkeypatch upstream library to support the extended Whoami request.
-
#modify(args) ⇒ Object
Another monkeypatch to support :controls.
-
#search(args = nil) ⇒ Net::LDAP::PDU
Monkeypatch upstream library for now to support :controls hash option in ‘args` so that we can provide controls within searches.
Constructor Details
#initialize(server) {|_self| ... } ⇒ Connection
Initialize the LDAP connection using Rex::Socket::TCP, and optionally set up encryption on the connection if configured.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/rex/proto/ldap.rb', line 188 def initialize(server) begin @conn = Rex::Socket::Tcp.create( 'PeerHost' => server[:host], 'PeerPort' => server[:port], 'Proxies' => server[:proxies], 'Timeout' => server[:connect_timeout] ) @conn.extend(SynchronousRead) # Set up read/write wrapping self.extend(ConnectionSaslIO) rescue SocketError raise Net::LDAP::LdapError, 'No such address or other socket error.' rescue Errno::ECONNREFUSED raise Net::LDAP::LdapError, "Server #{server[:host]} refused connection on port #{server[:port]}." end if server[:encryption] setup_encryption server[:encryption] end yield self if block_given? end |
Instance Method Details
#ldapwhoami ⇒ Object
Monkeypatch upstream library to support the extended Whoami request. Delete this after github.com/ruby-ldap/ruby-net-ldap/pull/425 is landed. This is not the only occurrence of a patch for this functionality.
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 |
# File 'lib/rex/proto/ldap.rb', line 469 def ldapwhoami ext_seq = [Net::LDAP::WhoamiOid.to_ber_contextspecific(0)] request = ext_seq.to_ber_appsequence(Net::LDAP::PDU::ExtendedRequest) = next_msgid write(request, nil, ) pdu = queued_read() if !pdu || pdu.app_tag != Net::LDAP::PDU::ExtendedResponse raise Net::LDAP::ResponseMissingOrInvalidError, "response missing or invalid" end pdu end |
#modify(args) ⇒ Object
Another monkeypatch to support :controls
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 |
# File 'lib/rex/proto/ldap.rb', line 441 def modify(args) modify_dn = args[:dn] or raise "Unable to modify empty DN" ops = self.class.modify_ops args[:operations] = next_msgid request = [ modify_dn.to_ber, ops.to_ber_sequence, ].to_ber_appsequence(Net::LDAP::PDU::ModifyRequest) controls = args.fetch(:controls, nil) unless controls.nil? controls = controls.to_ber_contextspecific(0) end write(request, controls, ) pdu = queued_read() if !pdu || pdu.app_tag != Net::LDAP::PDU::ModifyResponse raise Net::LDAP::ResponseMissingOrInvalidError, "response missing or invalid" end pdu end |
#search(args = nil) ⇒ Net::LDAP::PDU
Monkeypatch upstream library for now to support :controls hash option in ‘args` so that we can provide controls within searches. Needed so we can specify the LDAP_SERVER_SD_FLAGS_OID flag for searches to prevent getting the SACL when querying for ntSecurityDescriptor, as this is retrieved by default and non-admin users are not allowed to retrieve SACLs for objects. Therefore by adjusting the search to not retrieve SACLs, non-admin users can still retrieve information about the security of objects without violating this rule.
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 |
# File 'lib/rex/proto/ldap.rb', line 228 def search(args = nil) args ||= {} # filtering, scoping, search base # filter: https://tools.ietf.org/html/rfc4511#section-4.5.1.7 # base: https://tools.ietf.org/html/rfc4511#section-4.5.1.1 # scope: https://tools.ietf.org/html/rfc4511#section-4.5.1.2 filter = args[:filter] || Net::LDAP::Filter.eq("objectClass", "*") base = args[:base] scope = args[:scope] || Net::LDAP::SearchScope_WholeSubtree # attr handling # attrs: https://tools.ietf.org/html/rfc4511#section-4.5.1.8 # attrs_only: https://tools.ietf.org/html/rfc4511#section-4.5.1.6 attrs = Array(args[:attributes]) attrs_only = args[:attributes_only] == true # references # refs: https://tools.ietf.org/html/rfc4511#section-4.5.3 # deref: https://tools.ietf.org/html/rfc4511#section-4.5.1.3 refs = args[:return_referrals] == true deref = args[:deref] || Net::LDAP::DerefAliases_Never # limiting, paging, sorting # size: https://tools.ietf.org/html/rfc4511#section-4.5.1.4 # time: https://tools.ietf.org/html/rfc4511#section-4.5.1.5 size = args[:size].to_i time = args[:time].to_i paged = args[:paged_searches_supported] sort = args.fetch(:sort_controls, false) # arg validation raise ArgumentError, "search base is required" unless base raise ArgumentError, "invalid search-size" unless size >= 0 raise ArgumentError, "invalid search scope" unless Net::LDAP::SearchScopes.include?(scope) raise ArgumentError, "invalid alias dereferencing value" unless Net::LDAP::DerefAliasesArray.include?(deref) # arg transforms filter = Net::LDAP::Filter.construct(filter) if filter.is_a?(String) ber_attrs = attrs.map { |attr| attr.to_s.to_ber } ber_sort = encode_sort_controls(sort) # An interesting value for the size limit would be close to A/D's # built-in page limit of 1000 records, but openLDAP newer than version # 2.2.0 chokes on anything bigger than 126. You get a silent error that # is easily visible by running slapd in debug mode. Go figure. # # Changed this around 06Sep06 to support a caller-specified search-size # limit. Because we ALWAYS do paged searches, we have to work around the # problem that it's not legal to specify a "normal" sizelimit (in the # body of the search request) that is larger than the page size we're # requesting. Unfortunately, I have the feeling that this will break # with LDAP servers that don't support paged searches!!! # # (Because we pass zero as the sizelimit on search rounds when the # remaining limit is larger than our max page size of 126. In these # cases, I think the caller's search limit will be ignored!) # # CONFIRMED: This code doesn't work on LDAPs that don't support paged # searches when the size limit is larger than 126. We're going to have # to do a root-DSE record search and not do a paged search if the LDAP # doesn't support it. Yuck. = [126, ""] result_pdu = nil n_results = 0 = next_msgid instrument "search.net_ldap_connection", message_id: , filter: filter, base: base, scope: scope, size: size, time: time, sort: sort, referrals: refs, deref: deref, attributes: attrs do |payload| loop do # should collect this into a private helper to clarify the structure query_limit = 0 if size > 0 query_limit = if paged (((size - n_results) < 126) ? (size - n_results) : 0) else size end end request = [ base.to_ber, scope.to_ber_enumerated, deref.to_ber_enumerated, query_limit.to_ber, # size limit time.to_ber, attrs_only.to_ber, filter.to_ber, ber_attrs.to_ber_sequence, ].to_ber_appsequence(Net::LDAP::PDU::SearchRequest) # rfc2696_cookie sometimes contains binary data from Microsoft Active Directory # this breaks when calling to_ber. (Can't force binary data to UTF-8) # we have to disable paging (even though server supports it) to get around this... user_controls = args.fetch(:controls, []) controls = [] controls << [ Net::LDAP::LDAPControls::PAGED_RESULTS.to_ber, # Criticality MUST be false to interoperate with normal LDAPs. false.to_ber, .map(&:to_ber).to_ber_sequence.to_s.to_ber, ].to_ber_sequence if paged controls << ber_sort if ber_sort if controls.empty? && user_controls.empty? controls = nil else controls += user_controls controls = controls.to_ber_contextspecific(0) end write(request, controls, ) result_pdu = nil controls = [] while pdu = queued_read() case pdu.app_tag when Net::LDAP::PDU::SearchReturnedData n_results += 1 yield pdu.search_entry if block_given? when Net::LDAP::PDU::SearchResultReferral if refs if block_given? se = Net::LDAP::Entry.new se[:search_referrals] = (pdu.search_referrals || []) yield se end end when Net::LDAP::PDU::SearchResult result_pdu = pdu controls = pdu.result_controls if refs && pdu.result_code == Net::LDAP::ResultCodeReferral if block_given? se = Net::LDAP::Entry.new se[:search_referrals] = (pdu.search_referrals || []) yield se end end break else raise Net::LDAP::ResponseTypeInvalidError, "invalid response-type in search: #{pdu.app_tag}" end end if result_pdu.nil? raise Net::LDAP::ResponseMissingOrInvalidError, "response missing" end # count number of pages of results payload[:page_count] ||= 0 payload[:page_count] += 1 # When we get here, we have seen a type-5 response. If there is no # error AND there is an RFC-2696 cookie, then query again for the next # page of results. If not, we're done. Don't screw this up or we'll # break every search we do. # # Noticed 02Sep06, look at the read_ber call in this loop, shouldn't # that have a parameter of AsnSyntax? Does this just accidentally # work? According to RFC-2696, the value expected in this position is # of type OCTET STRING, covered in the default syntax supported by # read_ber, so I guess we're ok. more_pages = false if result_pdu.result_code == Net::LDAP::ResultCodeSuccess and controls controls.each do |c| if c.oid == Net::LDAP::LDAPControls::PAGED_RESULTS # just in case some bogus server sends us more than 1 of these. more_pages = false if c.value and c.value.length > 0 = c.value.read_ber[1] if and .length > 0 [1] = more_pages = true end end end end end break unless more_pages end # loop # track total result count payload[:result_count] = n_results result_pdu || OpenStruct.new(:status => :failure, :result_code => Net::LDAP::ResultCodeOperationsError, :message => "Invalid search") end # instrument ensure # clean up message queue for this search = .delete() # in the exceptional case some messages were *not* consumed from the queue, # instrument the event but do not fail. if !.nil? && !.empty? instrument "search_messages_unread.net_ldap_connection", message_id: , messages: end end |