diff --git a/pkg/ldaputils/actions.go b/pkg/ldaputils/actions.go index b76505a..56d5393 100644 --- a/pkg/ldaputils/actions.go +++ b/pkg/ldaputils/actions.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "fmt" "net" + "strconv" "strings" "github.com/Macmod/godap/v2/pkg/adidns" @@ -551,55 +552,126 @@ func (lc *LDAPConn) DeleteObject(targetDN string) error { return nil } -func (lc *LDAPConn) AddGroup(objectName string, parentDN string) error { +// Basic templates for object creation +type AttrEntries map[string][]string + +func GetGroupTemplate(objectName string) AttrEntries { + return AttrEntries{ + "objectClass": []string{"top", "group"}, + "cn": []string{objectName}, + "sAMAccountName": []string{objectName}, + } +} + +func GetOUTemplate(objectName string) AttrEntries { + return AttrEntries{ + "objectClass": []string{"top", "organizationalUnit"}, + "cn": []string{objectName}, + } +} + +func GetContainerTemplate(objectName string) AttrEntries { + return AttrEntries{ + "objectClass": []string{"top", "container"}, + } +} + +func GetComputerTemplate(objectName string) AttrEntries { + return AttrEntries{ + "objectClass": []string{"top", "computer"}, + "cn": []string{objectName}, + "sAMAccountName": []string{objectName + "$"}, + "userAccountControl": []string{"4096"}, + } +} + +func GetUserTemplate(objectName string, rootFQDN string) AttrEntries { + entries := AttrEntries{} + + if rootFQDN != "" { + userPrincipalName := fmt.Sprintf("%s@%s", objectName, strings.ToLower(rootFQDN)) + entries["userPrincipalName"] = []string{userPrincipalName} + } + + return AttrEntries{ + "objectClass": []string{"top", "person", "organizationalPerson", "user"}, + "cn": []string{objectName}, + "sAMAccountName": []string{objectName}, + } +} + +func AddEntriesToRequest(req *ldap.AddRequest, entries AttrEntries) { + for key, value := range entries { + req.Attribute(key, value) + } +} + +func (lc *LDAPConn) AddGroup(objectName string, parentDN string, dynamicTTL int) error { addRequest := ldap.NewAddRequest("CN="+objectName+","+parentDN, nil) - addRequest.Attribute("objectClass", []string{"top", "group"}) - addRequest.Attribute("cn", []string{objectName}) - addRequest.Attribute("sAMAccountName", []string{objectName}) + groupTemplate := GetGroupTemplate(objectName) + if dynamicTTL > 0 { + groupTemplate["entryTTL"] = []string{strconv.Itoa(dynamicTTL)} + groupTemplate["objectClass"] = append(groupTemplate["objectClass"], "dynamicObject") + } + AddEntriesToRequest(addRequest, groupTemplate) return lc.Conn.Add(addRequest) } -func (lc *LDAPConn) AddOrganizationalUnit(objectName string, parentDN string) error { +func (lc *LDAPConn) AddOrganizationalUnit(objectName string, parentDN string, dynamicTTL int) error { addRequest := ldap.NewAddRequest("OU="+objectName+","+parentDN, nil) - addRequest.Attribute("objectClass", []string{"top", "organizationalUnit"}) - addRequest.Attribute("ou", []string{objectName}) + ouTemplate := GetOUTemplate(objectName) + if dynamicTTL > 0 { + ouTemplate["entryTTL"] = []string{strconv.Itoa(dynamicTTL)} + ouTemplate["objectClass"] = append(ouTemplate["objectClass"], "dynamicObject") + } + AddEntriesToRequest(addRequest, ouTemplate) return lc.Conn.Add(addRequest) } -func (lc *LDAPConn) AddContainer(objectName string, parentDN string) error { +func (lc *LDAPConn) AddContainer(objectName string, parentDN string, dynamicTTL int) error { addRequest := ldap.NewAddRequest("CN="+objectName+","+parentDN, nil) - addRequest.Attribute("objectClass", []string{"top", "container"}) + containerTemplate := GetContainerTemplate(objectName) + if dynamicTTL > 0 { + containerTemplate["entryTTL"] = []string{strconv.Itoa(dynamicTTL)} + containerTemplate["objectClass"] = append(containerTemplate["objectClass"], "dynamicObject") + } + AddEntriesToRequest(addRequest, containerTemplate) return lc.Conn.Add(addRequest) } -func (lc *LDAPConn) AddComputer(objectName string, parentDN string) error { +func (lc *LDAPConn) AddComputer(objectName string, parentDN string, dynamicTTL int) error { addRequest := ldap.NewAddRequest("CN="+objectName+","+parentDN, nil) - addRequest.Attribute("objectClass", []string{"top", "computer"}) - addRequest.Attribute("cn", []string{objectName}) - addRequest.Attribute("sAMAccountName", []string{objectName + "$"}) - addRequest.Attribute("userAccountControl", []string{"4096"}) + computerTemplate := GetComputerTemplate(objectName) + if dynamicTTL > 0 { + computerTemplate["entryTTL"] = []string{strconv.Itoa(dynamicTTL)} + computerTemplate["objectClass"] = append(computerTemplate["objectClass"], "dynamicObject") + } + AddEntriesToRequest(addRequest, computerTemplate) return lc.Conn.Add(addRequest) } -func (lc *LDAPConn) AddUser(objectName string, parentDN string) error { +func (lc *LDAPConn) AddUser(objectName string, parentDN string, dynamicTTL int) error { addRequest := ldap.NewAddRequest("CN="+objectName+","+parentDN, nil) - addRequest.Attribute("objectClass", []string{"top", "person", "organizationalPerson", "user"}) - addRequest.Attribute("cn", []string{objectName}) - addRequest.Attribute("sAMAccountName", []string{objectName}) + rootFQDN, err := lc.FindRootFQDN() + var userTemplate AttrEntries if err == nil { - userPrincipalName := fmt.Sprintf("%s@%s", objectName, strings.ToLower(rootFQDN)) - addRequest.Attribute( - "userPrincipalName", - []string{userPrincipalName}, - ) + userTemplate = GetUserTemplate(objectName, rootFQDN) + } else { + userTemplate = GetUserTemplate(objectName, "") + } + + if dynamicTTL > 0 { + userTemplate["entryTTL"] = []string{strconv.Itoa(dynamicTTL)} + userTemplate["objectClass"] = append(userTemplate["objectClass"], "dynamicObject") } + AddEntriesToRequest(addRequest, userTemplate) return lc.Conn.Add(addRequest) } diff --git a/tui/explorer.go b/tui/explorer.go index ef210e1..1343b25 100644 --- a/tui/explorer.go +++ b/tui/explorer.go @@ -278,6 +278,7 @@ func openCreateObjectForm(node *tview.TreeNode, done func()) { createObjectForm := NewXForm(). AddDropDown("Object Type", []string{"OrganizationalUnit", "Container", "User", "Group", "Computer"}, 0, nil). AddInputField("Object Name", "", 0, nil, nil). + AddInputField("Entry TTL", "-1", 0, nil, nil). AddInputField("Parent DN", baseDN, 0, nil, nil) createObjectForm. SetInputCapture(handleEscape(treePanel)) @@ -304,19 +305,25 @@ func openCreateObjectForm(node *tview.TreeNode, done func()) { objectName := createObjectForm.GetFormItemByLabel("Object Name").(*tview.InputField).GetText() - var err error = nil + entryTTL := createObjectForm.GetFormItemByLabel("Entry TTL").(*tview.InputField).GetText() + entryTTLInt, err := strconv.Atoi(entryTTL) + if err != nil { + entryTTLInt = -1 + } switch objectType { case "OrganizationalUnit": - err = lc.AddOrganizationalUnit(objectName, baseDN) + err = lc.AddOrganizationalUnit(objectName, baseDN, entryTTLInt) case "Container": - err = lc.AddContainer(objectName, baseDN) + err = lc.AddContainer(objectName, baseDN, entryTTLInt) case "User": - err = lc.AddUser(objectName, baseDN) + err = lc.AddUser(objectName, baseDN, entryTTLInt) case "Group": - err = lc.AddGroup(objectName, baseDN) + err = lc.AddGroup(objectName, baseDN, entryTTLInt) case "Computer": - err = lc.AddComputer(objectName, baseDN) + err = lc.AddComputer(objectName, baseDN, entryTTLInt) + default: + err = fmt.Errorf("Invalid object type") } if err != nil {