ldapmodify - can't add members to AD group - linux

I'm wanting to add members to an AD distribution group. I enter the following command (putting in dummy values in some places):
ldapmodify -v -h 111.111.111.11 -D "CN=binding_user,DC=example,DC=com" -x -w password -f entrymods
The file 'entrymods' contains:
dn: CN=group_name, OU=Groups, DC=example, DC=com
changetype: modify
add: member
member: CN=Smith\, John, OU=Users, DC=example, DC=com
I'm getting the following output:
ldap_initialize( ldap://111.111.111.11 )
warning: no attributes to change or add (entry="CN=group_name, OU=Groups, DC=example, DC=comchangetype: modifyadd: membermember: CN=Smith\, John, OU=Users, DC=example, DC=com")
modifying entry "CN=group_name, OU=Groups, DC=example, DC=comchangetype: modifyadd: membermember: CN=Smith\, John, OU=Users, DC=example, DC=com"
modify complete
ldap_modify: Invalid DN syntax (34)
additional info: 00000057: LdapErr: DSID-0C090A5B, comment: Error processing name, data 0, vece

Find a user without a Comma in their DN and try it again that way. It is saying the referenced object does not exist. Now I am not sure if it is the DN:
CN=Smith\, John, OU=Users, DC=example, DC=com (The member being added)
or the group DN:
CN=group_name, OU=Groups, DC=example, DC=com
Looking at the member DN being added, the comma (properly escaped, I would say) is easy enough to test and eliminate from being an issue if it was.

Related

LDAP - ldapmodify - add two attributes in one operation doesn't work

I read this book : "Mastering OpenLDAP". It is an old book - 2007 - the version of Openldap is the 2.3 in this book.
I use Openldap 2.5 (Ubuntu 22.04).
It is written that I can add two attributes by using ldapmodify this way:
dn: uid=nicholas,ou=Users,dc=example,dc=com
changetype: modify
add: description title
description: This is a test
title: Cartesian philosopher
but when I try it, it doesn't work:
thierry#thierry-VirtualBox:~$ ldapmodify -x -w secret -D 'cn=Manager,dc=example,dc=com'
dn: uid=nicholas,ou=Users,dc=example,dc=com
changetype: modify
add: description title
description: This is a test
title: Cartesian philosopher
ldapmodify: wrong attributeType at line 4, entry "uid=nicholas,ou=Users,dc=example,dc=com"
Although it works well if I add one attribute, and then another. Why ?
thierry#thierry-VirtualBox:~$ ldapmodify -x -w secret -D 'cn=Manager,dc=example,dc=com'
dn: uid=nicholas,ou=Users,dc=example,dc=com
changetype: modify
add: description
description: This is a test
modifying entry "uid=nicholas,ou=Users,dc=example,dc=com"
^C
thierry#thierry-VirtualBox:~$ ldapmodify -x -w secret -D 'cn=Manager,dc=example,dc=com'
dn: uid=nicholas,ou=Users,dc=example,dc=com
changetype: modify
add: title
title: Cartesian philosopher
modifying entry "uid=nicholas,ou=Users,dc=example,dc=com"
^C
thierry#thierry-VirtualBox:~$ ldapsearch -x -w secret -D 'cn=Manager,dc=example,dc=com' -LLL '(uid=nicholas)'
dn: uid=nicholas,ou=Users,dc=example,dc=com
cn: Nicholas Malebranche
sn: Malebranche
uid: nicholas
ou: Users
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
title: Cartesian philosopher
description: This is a test
Thank you for your help.
The manpage has an example of making multiple changes:
dn: cn=Modify Me,dc=example,dc=com
changetype: modify
replace: mail
mail: modme#example.com
-
add: title
title: Grand Poobah
-
add: jpegPhoto
jpegPhoto:< file:///tmp/modme.jpeg
-
delete: description
-
So in your case, yours should look something like this:
dn: uid=nicholas,ou=Users,dc=example,dc=com
changetype: modify
add: description
description: This is a test
-
add: title
title: Cartesian philosopher
-

Did I import inetOrgPerson schema correctly for OpenLDAP on Alpine Linux?

I'm trying to import a user into OpenLDAP on Alpine Linux. Here's the LDIF that I named searchUser.ldif:
# Search account
dn: uid=search,dc=home
changetype: add
objectClass: top
objectClass: person
objectClass: inetOrgPerson
cn: search
sn: search
uid: search
The command I used to import it is:
ldapadd -x -D "cn=Manager,dc=home" -w supersecret -f searchUser.ldif
The error I get is:
ldap_add: Invalid syntax (21) additional info: objectClass: value #2 invalid per syntax
My understanding of this is the objectClasses are numbered, starting with 0, and that #2 indicates the problem is with inetOrgPerson.
I've done this successfully using OpenLDAP on Raspberry Pi OS (debian). However, I get the feeling the Debian package automates some configuration steps that the Alpine package does not. One of those steps I think Debian does during the package install is to import inetOrgPerson schema.
I've tried to do the schema import manually. Here are the steps I took prior to trying the LDIF import...
I scripted my install of OpenLDAP on Alpine, like so:
export DOMAIN="dc=home"
echo "Installing packages..."
apk add openldap openldap-back-mdb openldap-clients
echo "Configuring for v2.3+ style slapd.d config directory..."
install -m 755 -o ldap -g ldap -d /etc/openldap/slapd.d
sed -i~ \
-e 's/^cfgfile=/#cfgfile=/' \
-e 's/^#cfgdir=.*/cfgdir=\"\/etc\/openldap\/slapd.d\"/' \
/etc/conf.d/slapd
rm /etc/openldap/slapd.conf
echo "Customizing for domain: ${DOMAIN}..."
sed -i~ \
-e 's/\.la$/.so/' \
-e "s/dc=my-domain,dc=com/${DOMAIN}/" /etc/openldap/slapd.ldif
echo "Importing configuration..."
slapadd -n 0 -F /etc/openldap/slapd.d -l /etc/openldap/slapd.ldif
chown -R ldap:ldap /etc/openldap/slapd.d/*
echo "Configuring slapd service..."
install -m 755 -o ldap -g ldap -d /var/lib/openldap/run
service slapd start
rc-update add slapd
The slapd service started and I could connect to it with command-line tools and from a client over port 389. So far, so good.
The next thing I did was to import schema for cosine and inetOrgPerson. I believe the Debian package did this automatically, because I don't recall having to do this previously.
Here's what I did on Alpine to import the schema:
slapadd -n 0 -F /etc/openldap/slapd.d -l /etc/openldap/schema/cosine.ldif
slapadd -n 0 -F /etc/openldap/slapd.d -l /etc/openldap/schema/inetorgperson.ldif
There were no errors.
I then created an organization using the command ldapadd -x -D "cn=Manager,dc=home" -w secret -f org.ldif and this LDIF as org.ldif:
dn: dc=home
objectclass: dcObject
objectclass: organization
o: Home
dc: home
dn: cn=Manager,dc=home
objectclass: organizationalRole
cn: Manager
This too was successful.
I can also create organizational units with this LDIF:
# Organizational unit for users
dn: ou=People,dc=home
changetype: add
objectClass: organizationalUnit
ou: People
# Organizational unit for groups.
dn: ou=Groups,dc=home
changetype: add
objectClass: organizationalUnit
ou: Groups
So I think my server is okay, but I may have done something wrong with the inetOrgPerson schema import that's causing the Invalid syntax (21) error.
Is the way I'm importing the inetOrgPerson schema correct? Is there a way to verify it?
I believe the problem was due to incorrect ownership for the new files in the /etc/openldap/slapd.d/cn=config/cn=schema directory. Once I fixed that, I was able to import the search user.
Because I ran the slapd commands as the root user, the resulting schema config files were owned by root. I discovered this when I restarted the slapd service and it failed with this error in /var/log/messages:
ldif_read_file: Permission denied for "/etc/openldap/slapd.d/cn=config/cn=schema/cn={1}cosine.ldif"
The solution was to change ownership on the files. This is the correct ownership:
alpine:/etc/openldap/slapd.d/cn=config/cn=schema# ls -l
total 32
-rw------- 1 ldap ldap 15575 May 5 12:43 cn={0}core.ldif
-rw------- 1 ldap ldap 11361 May 5 14:53 cn={1}cosine.ldif
-rw------- 1 ldap ldap 2855 May 5 14:53 cn={2}inetorgperson.ldif
So the answer to this question is...
Yes, importing with slapadd -n 0 -F /etc/openldap/slapd.d -l /etc/openldap/schema/inetorgperson.ldif worked fine, but the command should have been run as the ldap user so the ownership is correct. (Or run as root and change ownership after.)
One way to verify the schema is to look inside the /etc/openldap/slapd.d directory. Specifically, /etc/openldap/slapd.d/cn=config/cn=schema shows evidence of the schema I added.
Even with the potential for incorrect file ownership, I see this as a much easier way to add schema than some of the other tutorials I've found that involve creating and editing a temporary slapd.conf file.

Parsing ldap sid in node

Im trying to get all the groups of a user including nested groups, to make the query faster I'm trying to use the tokenGroups attribute so to not recursively query the groups of groups.
I got the tokenGroups with contains the sid of all the groups of the user.
The problem is entry.object gives me an array of weird string that I'm unable to use and object.raw gives me an array of jsons with type: "buffer" and data containing an array with numbers that I don't know what to do with.
Given what I have how do I parse the sid or get more info about each group?
tokenGroups is a binary attribute, returned in a base64-encoded string. In order to use it in a query, you would need to decode it and convert to an escaped string, which may be used in a query. To reduce the network load and increase performance, you may query all the groups in a single query and since objectSid is indexed, the result is blazing fast.
Let me give a command line example:
# Preparation
# Domain controller
$ ad_host=w2k8-dc.internal.local
# Get domain context automatically (or set manually)
$ base_dn=$(ldapsearch -LLL -Q -N -o ldif-wrap=no -h $ad_host -s base defaultNamingContext | grep '^defaultNamingContext:' | cut -f2- -d\ )
$ echo $base_dn
DC=internal,DC=local
# User to check
$ user=Administrator
$ user_dn=$(ldapsearch -LLL -Q -N -o ldif-wrap=no -h $ad_host -b "$base_dn" sAMAccountName=$user dn | grep '^dn:' | cut -f2- -d\ )
$ echo $user_dn
CN=Administrator,CN=Users,DC=internal,DC=local
# Actual query
# Get token groups
$ token_groups=$(ldapsearch -LLL -Q -N -o ldif-wrap=no -h $ad_host -b "$user_dn" -s base tokenGroups | grep '^tokenGroups:' | cut -f2- -d\ )
$ echo $token_groups
AQIAAAAAAAUgAAAAIQIAAA== AQIAAAAAAAUgAAAAIAIAAA== AQUAAAAAAAUVAAAAClus3m3pCzBhhoO3PAIAAA== AQUAAAAAAAUVAAAAClus3m3pCzBhhoO3BgIAAA== AQUAAAAAAAUVAAAAClus3m3pCzBhhoO3BwIAAA== AQUAAAAAAAUVAAAAClus3m3pCzBhhoO3AQIAAA== AQUAAAAAAAUVAAAAClus3m3pCzBhhoO3CAIAAA== AQUAAAAAAAUVAAAAClus3m3pCzBhhoO3AAIAAA==
# Decode SIDs and prepare query
$ sid_query=$(for sid in $token_groups; do printf "(objectSid=%s)" $(echo $sid | base64 -d | hexdump -ve '"\\" /1 "%02x"'); done)
$ echo $sid_query
(objectSid=\01\02\00\00\00\00\00\05\20\00\00\00\21\02\00\00)(objectSid=\01\02\00\00\00\00\00\05\20\00\00\00\20\02\00\00)(objectSid=\01\05\00\00\00\00\00\05\15\00\00\00\0a\5b\ac\de\6d\e9\0b\30\61\86\83\b7\3c\02\00\00)(objectSid=\01\05\00\00\00\00\00\05\15\00\00\00\0a\5b\ac\de\6d\e9\0b\30\61\86\83\b7\06\02\00\00)(objectSid=\01\05\00\00\00\00\00\05\15\00\00\00\0a\5b\ac\de\6d\e9\0b\30\61\86\83\b7\07\02\00\00)(objectSid=\01\05\00\00\00\00\00\05\15\00\00\00\0a\5b\ac\de\6d\e9\0b\30\61\86\83\b7\01\02\00\00)(objectSid=\01\05\00\00\00\00\00\05\15\00\00\00\0a\5b\ac\de\6d\e9\0b\30\61\86\83\b7\08\02\00\00)(objectSid=\01\05\00\00\00\00\00\05\15\00\00\00\0a\5b\ac\de\6d\e9\0b\30\61\86\83\b7\00\02\00\00)
# Get group information
$ ldapsearch -LLL -Q -N -o ldif-wrap=no -h $ad_host -b "$base_dn" "(|$sid_query)" dn cn
dn: CN=Schema Admins,CN=Users,DC=internal,DC=local
cn: Schema Admins
dn: CN=Enterprise Admins,CN=Users,DC=internal,DC=local
cn: Enterprise Admins
dn: CN=Administrators,CN=Builtin,DC=internal,DC=local
cn: Administrators
dn: CN=Users,CN=Builtin,DC=internal,DC=local
cn: Users
dn: CN=Domain Admins,CN=Users,DC=internal,DC=local
cn: Domain Admins
dn: CN=Domain Users,CN=Users,DC=internal,DC=local
cn: Domain Users
dn: CN=Group Policy Creator Owners,CN=Users,DC=internal,DC=local
cn: Group Policy Creator Owners
dn: CN=Denied RODC Password Replication Group,CN=Users,DC=internal,DC=local
cn: Denied RODC Password Replication Group
Note that using tokenGroups does not provide information about distribution groups. It includes however user's primary group (most probably Domain Users), which is NOT included in memberOf (nor the user is included in group's member attribute).
That would make a difference in results as the regular recursive member query will not return primary group nor the groups that a user would be a member through their primary group. For example, if user's primary group is Domain Users, and Domain Users is a member of group my-security-group, the latter will not be found through a
member:1.2.840.113556.1.4.1941: query, but will be returned in tokenGroups.
Last but not least, member:1.2.840.113556.1.4.1941: can be very expensive computationally and depending on domain's size, base DN and domain controller's load can take much time to resolve. For my real company's domain it is more or less 3 times longer than the SID query:
$ time ldapsearch -LLL -Q -N -o ldif-wrap=no -h $ad_host -b "$base_dn" "(&(objectClass=group)(member:1.2.840.113556.1.4.1941:=$user_dn))" dn | grep '^dn:' | wc -l
92
real 0m11.038s
user 0m0.017s
sys 0m0.024s
$ time ldapsearch -LLL -Q -N -o ldif-wrap=no -h $ad_host -b "$base_dn" "(|$sid_query)" dn | grep '^dn:' | wc -l
111
real 0m2.851s
user 0m0.014s
sys 0m0.033s
Note the difference in result count; the SID query returns some built-in groups and those groups that a user is member of through Domain Users.
If you need help parsing the tokenGroups, then you'll have to show your code and what output you're actually getting.
But yes, tokenGroups gives you the SID of every security group that the user should be considered a member of (recursively). But there are caveats:
If you want the name of each group, you have to look up the group based on the SID. That's basically one network request per group. If you want "faster", then this is bad. More network requests = slower.
It will not include distribution groups (groups where the Group Type is "Distribution"). That may or may not matter to you.
My first question is always: Why do you want to know this? Do you really need to list every group the user is a member of? Or are you looking for one specific group. If you're looking for one group, then you can find the SID of that group and look for that SID in the tokenGroups.
If you really do want the name of every group, or you need to include distribution groups, then you're better off doing this differently. You can perform a search for all groups that have this user as a member:
(&(objectClass=group)(member:1.2.840.113556.1.4.1941:=userDN))
Where userDN is the distinguishedName of the user. That weird looking number is the object identifier (OID) for LDAP_MATCHING_RULE_IN_CHAIN, which you can read more about on this page. Basically, it tells AD to do a recursive search. So it will return every group that has the user as a direct member, or nested member.
I wrote a couple articles on my website about this, which you might benefit from reading, especially if you have more than one domain in your environment. My code examples are C#, but it's really just LDAP in the background anyway, so the principles can be applied to any language.
Finding all of a user’s groups
Find out if one user is a member of a group

Test group membership on FreeIPA server

I need to check users for membership in a group on FreeIPA. (Currently I'm testing on the command line to get the search right before writing the actual code in Node). Based on searches, I'm using the following query:
ldapsearch -x -b "uid=testuser,cn=users,cn=accounts,dc=smnet,dc=com" '(memberof=cn=testgroup,cn=groups,cn=accounts,dc=smnet,dc=com)'
But the result I get is:
# extended LDIF
#
# LDAPv3
# base <uid=testuser,cn=users,cn=accounts,dc=smnet,dc=com> with scope subtree
# filter: (memberof=cn=testgroup,cn=groups,cn=accounts,dc=smnet,dc=com)
# requesting: ALL
#
# search result
search: 2
result: 0 Success
# numResponses: 1`
However, if I leave off the filter:
ldapsearch -x -b "uid=testuser,cn=users,cn=accounts,dc=smnet,dc=com"
I get:
# extended LDIF
#
# LDAPv3
# base <uid=testuser,cn=users,cn=accounts,dc=smnet,dc=com> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#
# testuser, users, accounts, smnet.com
dn: uid=testuser,cn=users,cn=accounts,dc=smnet,dc=com
givenName: test
sn: user
uid: testuser
cn: test user
displayName: test user
initials: tu
gecos: test user
objectClass: top
objectClass: person
objectClass: organizationalperson
objectClass: inetorgperson
objectClass: inetuser
objectClass: posixaccount
objectClass: krbprincipalaux
objectClass: krbticketpolicyaux
objectClass: ipaobject
objectClass: ipasshuser
objectClass: ipaSshGroupOfPubKeys
objectClass: mepOriginEntry
loginShell: /bin/sh
homeDirectory: /home/testuser
uidNumber: 253000005
gidNumber: 253000005
# search result
search: 2
result: 0 Success
# numResponses: 2
# numEntries: 1
Which is what I'm expecting. Is my query wrong? Or am I misinterpreting the results?
In case it matters, this is with the latest Fedora server, with Free IPA included in the install process, running in a VirtualBox VM.
Both your queries are done with anonymous bind to LDAP (-x switch to ldapsearch). FreeIPA does not allow to see membership information unless you are authenticated. Create a user and use its credentials to authenticate in your searches, then you'll get both member and memberof attributes visible.

Ldap create user with custom attributes and objectclasses

I am trying to add custom attributes and object classes in my LDAP(ODSEE) but having issues.
Initially, I had issues with adding the custom attributes and classes, but was finally it worked. My custom definitions look like below(got the test code from one of the sites for testing)
dn: cn=schema
changetype: modify
add: attributeTypes
attributeTypes: ( 1.3.6.1.4.1.4203.666.1.90
NAME ( 'personStatus' )
DESC 'person Status'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
MULTI-VALUE )
dn: cn=schema
changetype: modify
add: objectClasses
objectClasses: ( 1.3.6.1.4.1.4203.666.1.100
NAME 'YoLinuxPerson'
DESC 'Yo Linux Person’
SUP inetOrgPerson
AUXILIARY
MAY personStatus )
My user ldif is as below
dn: uid=acc, ou=People, dc=example,dc=com
**objectClass: YoLinuxPerson*******************Custom
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: aaaaa Vaughan
sn: Vaughan
facsimileTelephoneNumber: +1 408 555 3372
givenName: Kirsten
l: Sunnyvale
mail: aaaaa#example.com
ou: Human Resources
ou: People
roomNumber: 2871
telephoneNumber: +1 408 555 5625
uid: aaaaa
**personStatus: abcdefghij**********************Custom
userPassword:: e1NTSEF9WhgljhlWxzZUdDUVVFUW05OTg1bmJSUkpoNHdYUVgrZkE9P
I tried multiple ways but everything fails. If I remove the "personStatus: abcdefghij" the ldapadd command works, but if I have it there, it fails saying
[config]$ ldapadd -h -p -D "cn=" -w -f User.ldif
adding new entry uid=acc, ou=People, dc=example,dc=com
ldap_add: Object class violation
I also did a ldap search
- ldapsearch -D -w -h -p -b "cn=schema" -s base 'objectclass=*'
and it displayed all the results including the custom attribute (which I am having issues with), but i could not see the custom objectClass(which I can add to the user).
I tried with Apache Studio also, it fails with the same message. I am not sure what I am missing. Any help is appreciated. Thanks.
There's a problem with the objectClass definition :
Either it extends inetOrgPerson, and thus it's a STRUCTURAL objectClass,
Or it is AUXILIARY and doesn't need to extend inetOrgPerson.
But your issue may be that you need to restart DSEE to be able to set the entry with the new objectClass and attribute (I vaguely remember an issue in that domain, but it's been more than 5 years since I last played with DSEE).

Resources