ansible/roles/ldap_server/files/ldapspn.py

135 lines
3.9 KiB
Python
Executable file

#!/usr/bin/python3
import sys
import argparse
from time import time
from struct import pack
import ldap3
def ldap_connect():
server = ldap3.Server("ldapi:///var/run/ldapi")
conn = ldap3.Connection(
server,
authentication=ldap3.SASL,
sasl_mechanism=ldap3.EXTERNAL,
sasl_credentials="",
client_strategy=ldap3.SYNC,
)
conn.bind()
conn.search(
search_base="",
search_filter="(objectClass=*)",
search_scope=ldap3.BASE,
attributes=["namingContexts"],
)
basedn = conn.response[0]["attributes"]["namingContexts"][0]
return (conn, basedn)
def get_last_modified(principal):
""" create krbExtraData value with KRB5_TL_MOD_PRINC type """
data = pack(
"<ccL%dsc" % len(principal),
b"\x00",
b"\x02",
int(time()),
bytes(principal, "UTF-8"),
b"\x00",
)
return data
def get_kvno():
""" return krbExtraData value with KRB_TL_MKVNO type with value 0 """
return pack("<cccc", b"\x00", b"\x08", b"\x01", b"\x00")
def principal_create(args, conn, basedn):
if not args.container:
(principal, realm) = args.principal.split("@", 2)
(service, hostname) = principal.split("/", 2)
conn.search(
search_base=basedn,
search_filter=f"(&(cn={hostname})(objectClass=device))",
)
if len(conn.response) != 1:
print(
"ERROR: Could not get container dn for principal '{args.principal}'",
file=sys.stderr,
)
sys.exit(1)
args.container = conn.response[0]["dn"]
conn.add(
f"krbPrincipalName={args.principal},{args.container}",
attributes={
"objectClass": [
"krbPrincipal",
"krbPrincipalAux",
"krbTicketPolicyAux",
],
"krbPrincipalName": args.principal,
"krbExtraData": [get_last_modified("root/admin@FOO.SH"), get_kvno()],
},
)
if conn.result["result"] != 0:
print(
f"ERROR: Failed to add principal: {conn.result['description']}",
file=sys.stderr,
)
sys.exit(1)
def principal_delete(args, conn, basedn):
conn.search(
search_base=basedn,
search_filter=f"(&(krbPrincipalName={args.principal})(objectClass=krbPrincipalAux))",
attributes=[],
)
if len(conn.response) != 1:
print(f"ERROR: Could not find principal '{args.principal}'", file=sys.stderr)
sys.exit(1)
conn.delete(conn.response[0]["dn"])
if conn.result["result"] != 0:
print(
f"ERROR: Failed to delete principal: {conn.result['description']}",
file=sys.stderr,
)
sys.exit(1)
def principal_list(args, conn, baesdn):
conn.search(
search_base=basedn,
search_filter="(objectClass=krbPrincipalAux)",
attributes=["krbPrincipalName"],
)
for result in conn.response:
for name in result["attributes"]["krbPrincipalName"]:
print(name)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
create = subparsers.add_parser("add")
create.set_defaults(func=principal_create)
create.add_argument("--principal", metavar="PRINCIPAL", type=str, required=True)
create.add_argument("--container", metavar="CONTAINER", type=str, required=False)
delete = subparsers.add_parser("del")
delete.set_defaults(func=principal_delete)
delete.add_argument("--principal", metavar="PRINCIPAL", type=str, required=True)
show = subparsers.add_parser("list")
show.set_defaults(func=principal_list)
parser.set_defaults(func=principal_list)
args = parser.parse_args()
try:
(conn, basedn) = ldap_connect()
args.func(args, conn, basedn)
except KeyboardInterrupt:
sys.exit(1)