CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/rex/proto/ldap/client.rb
Views: 11704
1
require 'net/ldap'
2
3
module Rex
4
module Proto
5
module LDAP
6
# This is a Rex Proto wrapper around the Net::LDAP client which is currently coming from the 'net-ldap' gem.
7
# The purpose of this wrapper is to provide 'peerhost' and 'peerport' methods to ensure the client interfaces
8
# are consistent between various session clients.
9
class Client < Net::LDAP
10
11
# @return [Rex::Socket]
12
attr_reader :socket
13
14
# [Time] The last time an interaction occurred on the connection (for keep-alive purposes)
15
attr_reader :last_interaction
16
17
# [Mutex] Control access to the connection. One at a time.
18
attr_reader :connection_use_mutex
19
20
def initialize(args)
21
@base_dn = args[:base]
22
@last_interaction = nil
23
@connection_use_mutex = Mutex.new
24
super
25
end
26
27
def register_interaction
28
@last_interaction = Process.clock_gettime(Process::CLOCK_MONOTONIC)
29
end
30
31
# @return [Array<String>] LDAP servers naming contexts
32
def naming_contexts
33
@naming_contexts ||= search_root_dse[:namingcontexts]
34
end
35
36
# @return [String] LDAP servers Base DN
37
def base_dn
38
@base_dn ||= discover_base_dn
39
end
40
41
# @return [String, nil] LDAP servers Schema DN, nil if one isn't found
42
def schema_dn
43
@schema_dn ||= discover_schema_naming_context
44
end
45
46
# @return [String] The remote IP address that LDAP is running on
47
def peerhost
48
host
49
end
50
51
# @return [Integer] The remote port that LDAP is running on
52
def peerport
53
port
54
end
55
56
# @return [String] The remote peer information containing IP and port
57
def peerinfo
58
"#{peerhost}:#{peerport}"
59
end
60
61
def use_connection(args)
62
@connection_use_mutex.synchronize do
63
return super(args)
64
ensure
65
register_interaction
66
end
67
end
68
69
# https://github.com/ruby-ldap/ruby-net-ldap/issues/11
70
# We want to keep the ldap connection open to use later
71
# but there's no built in way within the `Net::LDAP` library to do that
72
# so we're adding this function to do it instead
73
# @param connect_opts [Hash] Options for the LDAP connection.
74
def self._open(connect_opts)
75
client = new(connect_opts)
76
client._open
77
end
78
79
# https://github.com/ruby-ldap/ruby-net-ldap/issues/11
80
def _open
81
raise Net::LDAP::AlreadyOpenedError, 'Open already in progress' if @open_connection
82
83
instrument 'open.net_ldap' do |payload|
84
@open_connection = new_connection
85
@socket = @open_connection.socket
86
payload[:connection] = @open_connection
87
payload[:bind] = @result = @open_connection.bind(@auth)
88
register_interaction
89
return self
90
end
91
end
92
93
def discover_schema_naming_context
94
result = search(base: '', attributes: [:schemanamingcontext], scope: Net::LDAP::SearchScope_BaseObject)
95
if result.first && !result.first[:schemanamingcontext].empty?
96
schema_dn = result.first[:schemanamingcontext].first
97
ilog("#{peerinfo} Discovered Schema DN: #{schema_dn}")
98
return schema_dn
99
end
100
wlog("#{peerinfo} Could not discover Schema DN")
101
nil
102
end
103
104
def discover_base_dn
105
unless naming_contexts
106
elog("#{peerinfo} Base DN cannot be determined, no naming contexts available")
107
return
108
end
109
110
# NOTE: Find the first entry that starts with `DC=` as this will likely be the base DN.
111
result = naming_contexts.select { |context| context =~ /^([Dd][Cc]=[A-Za-z0-9-]+,?)+$/ }
112
.reject { |context| context =~ /(Configuration)|(Schema)|(ForestDnsZones)/ }
113
if result.blank?
114
elog("#{peerinfo} A base DN matching the expected format could not be found!")
115
return
116
end
117
base_dn = result[0]
118
119
dlog("#{peerinfo} Discovered base DN: #{base_dn}")
120
base_dn
121
end
122
123
# Monkeypatch upstream library to support the extended Whoami request. Delete
124
# this after https://github.com/ruby-ldap/ruby-net-ldap/pull/425 is landed.
125
# This is not the only occurrence of a patch for this functionality.
126
def ldapwhoami(args = {})
127
instrument "ldapwhoami.net_ldap", args do |payload|
128
@result = use_connection(args, &:ldapwhoami)
129
@result.success? ? @result.extended_response : nil
130
end
131
end
132
end
133
end
134
end
135
end
136
137