Skip to content

Commit b4dbb19

Browse files
authored
LDAP: add/modifydn (#4652)
1 parent 8fef083 commit b4dbb19

File tree

1 file changed

+123
-2
lines changed

1 file changed

+123
-2
lines changed

scapy/layers/ldap.py

+123-2
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,10 @@
9999

100100
# Typing imports
101101
from typing import (
102+
Any,
103+
Dict,
102104
List,
105+
Union,
103106
)
104107

105108
# Elements of protocol
@@ -891,6 +894,29 @@ class LDAP_DelResponse(ASN1_Packet):
891894
)
892895

893896

897+
# Modify DN Operation
898+
# https://datatracker.ietf.org/doc/html/rfc4511#section-4.9
899+
900+
901+
class LDAP_ModifyDNRequest(ASN1_Packet):
902+
ASN1_codec = ASN1_Codecs.BER
903+
ASN1_root = ASN1F_SEQUENCE(
904+
LDAPDN("entry", ""),
905+
LDAPDN("newrdn", ""),
906+
ASN1F_BOOLEAN("deleteoldrdn", ASN1_BOOLEAN(False)),
907+
ASN1F_optional(LDAPDN("newSuperior", None, implicit_tag=0xA0)),
908+
implicit_tag=ASN1_Class_LDAP.ModifyDNRequest,
909+
)
910+
911+
912+
class LDAP_ModifyDNResponse(ASN1_Packet):
913+
ASN1_codec = ASN1_Codecs.BER
914+
ASN1_root = ASN1F_SEQUENCE(
915+
*LDAPResult,
916+
implicit_tag=ASN1_Class_LDAP.ModifyDNResponse,
917+
)
918+
919+
894920
# Abandon Operation
895921
# https://datatracker.ietf.org/doc/html/rfc4511#section-4.11
896922

@@ -1024,6 +1050,8 @@ class LDAP(ASN1_Packet):
10241050
LDAP_AddResponse,
10251051
LDAP_DelRequest,
10261052
LDAP_DelResponse,
1053+
LDAP_ModifyDNRequest,
1054+
LDAP_ModifyDNResponse,
10271055
LDAP_UnbindRequest,
10281056
LDAP_ExtendedResponse,
10291057
),
@@ -2128,7 +2156,7 @@ def search(
21282156
attrsOnly=0,
21292157
attributes: List[str] = [],
21302158
controls: List[LDAP_Control] = [],
2131-
):
2159+
) -> Dict[str, List[Any]]:
21322160
"""
21332161
Perform a LDAP search.
21342162
@@ -2189,7 +2217,12 @@ def search(
21892217
resp.show()
21902218
raise TimeoutError("Search timed out.")
21912219
# Now, reassemble the results
2192-
_s = lambda x: x.decode(errors="backslashreplace")
2220+
2221+
def _s(x):
2222+
try:
2223+
return x.decode()
2224+
except UnicodeDecodeError:
2225+
return x
21932226

21942227
def _ssafe(x):
21952228
if self._TEXT_REG.match(x):
@@ -2272,6 +2305,94 @@ def modify(
22722305
resp=resp,
22732306
)
22742307

2308+
def add(
2309+
self,
2310+
entry: str,
2311+
attributes: Union[Dict[str, List[Any]], List[ASN1_Packet]],
2312+
controls: List[LDAP_Control] = [],
2313+
):
2314+
"""
2315+
Perform a LDAP add request.
2316+
2317+
:param attributes: the attributes to add. We support two formats:
2318+
- a list of LDAP_Attribute (or LDAP_PartialAttribute)
2319+
- a dict following {attribute: [list of values]}
2320+
2321+
:returns:
2322+
"""
2323+
# We handle the two cases in the type of attributes
2324+
if isinstance(attributes, dict):
2325+
attributes = [
2326+
LDAP_Attribute(
2327+
type=ASN1_STRING(k),
2328+
values=[
2329+
LDAP_AttributeValue(
2330+
value=ASN1_STRING(x),
2331+
)
2332+
for x in v
2333+
],
2334+
)
2335+
for k, v in attributes.items()
2336+
]
2337+
2338+
resp = self.sr1(
2339+
LDAP_AddRequest(
2340+
entry=ASN1_STRING(entry),
2341+
attributes=attributes,
2342+
),
2343+
controls=controls,
2344+
timeout=3,
2345+
)
2346+
if LDAP_AddResponse not in resp.protocolOp or resp.protocolOp.resultCode != 0:
2347+
raise LDAP_Exception(
2348+
"LDAP add failed !",
2349+
resp=resp,
2350+
)
2351+
2352+
def modifydn(
2353+
self,
2354+
entry: str,
2355+
newdn: str,
2356+
deleteoldrdn=True,
2357+
controls: List[LDAP_Control] = [],
2358+
):
2359+
"""
2360+
Perform a LDAP modify DN request.
2361+
2362+
..note:: This functions calculates the relative DN and superior required for
2363+
LDAP ModifyDN automatically.
2364+
2365+
:param entry: the DN of the entry to rename.
2366+
:param newdn: the new FULL DN of the entry.
2367+
:returns:
2368+
"""
2369+
# RFC4511 sect 4.9
2370+
# Calculate the newrdn (relative DN) and superior
2371+
newrdn, newSuperior = newdn.split(",", 1)
2372+
_, cur_superior = entry.split(",", 1)
2373+
# If the superior hasn't changed, don't update it.
2374+
if cur_superior == newSuperior:
2375+
newSuperior = None
2376+
# Send the request
2377+
resp = self.sr1(
2378+
LDAP_ModifyDNRequest(
2379+
entry=entry,
2380+
newrdn=newrdn,
2381+
newSuperior=newSuperior,
2382+
deleteoldrdn=deleteoldrdn,
2383+
),
2384+
controls=controls,
2385+
timeout=3,
2386+
)
2387+
if (
2388+
LDAP_ModifyDNResponse not in resp.protocolOp
2389+
or resp.protocolOp.resultCode != 0
2390+
):
2391+
raise LDAP_Exception(
2392+
"LDAP modify failed !",
2393+
resp=resp,
2394+
)
2395+
22752396
def close(self):
22762397
if self.verb:
22772398
print("X Connection closed\n")

0 commit comments

Comments
 (0)