[Date Prev][Date Next] [Chronological] [Thread] [Top]

(ITS#7714) Making slapd easier to jail (enhancement?)

Full_Name: Barry Lance
Version: 2.4.35
OS: Linux (Debian 7)
URL: ftp://ftp.openldap.org/incoming/
Submission from: (NULL) (

Operating System: 	Debian 7.1.0 (Wheezy) 64-bit
Openldap version: 	2.4.35

Configure Options:	--prefix=/usr/local 

config:			slapd.conf from make install

Jail directory:		/var/chroot/openldap

Files copied into jail:	

/etc/openldap -> <jaildir>/etc/openldap
/etc/nsswitch.cond -> <jaildir>/etc
/etc/pam.d -> <jaildir>/etc/pam.d
/etc/passwd (or fragment of) -> <jaildir>/etc/passwd
/etc/groups (or fragment of) -> <jaildir>/etc/groups
/etc/shadow -> <jaildir>/etc/shadow
all other libs referenced by ldd slapd into respective <jaildir>/dir
/usr/local/libexec/openldap -> <jaildir>//usr/local/libexec/openldap
/lib/x86_64-linux-gnu/libnss_* -> <jaildir>/lib/x86_64-linux-gnu
commandline: /usr/local/libexec/slapd -d -1 -f /etc/openldap/slapd.conf -h
"ldap:/// ldapi:///" -n slapd -r /var/chroot/openldap -u ldap -g ldap

The behavior I have experienced is as follows:

1, Launch slapd without user (-u), group (-g), and jail dir (-r) options is
successful.  Slapd is running under the current user id (root).

2. Launch slapd with user and group parameters, but without a jail directory
successful and the root privilege is dropped to the username given

Takeaway - slapd is able to read passwd and groups outside jail. This is
definitely expected.

3, Launch slapd with user, group and jail dir options, slapd fails with a
message that no such user exists in passwd.

4.  Launching slapd given a jail directory, but no user or group options
succeeds with the daemon jailed in jail dir, butrunning as root (undesirable). 

Takeaway - chroot code works, but passwd/groups cannot be accessed after it (as
seen in (3)).  (4) is expected given the code in servers/slapd/main.c attempts
to get the real uid/gid and drop root permission only if the -u and -g options
are given on the command line.

Comparing servers/slapd/main.c to the code for a few other daemons (ntpd, named,
isc-dhcp), the jailing process follows the chdir/chroot process as expected. 
The difference in these other daemon is that, in all cases, the real uid/gid are
retrieved before the chroot code.  By doing this, nsswitch.conf, passwd, groups,
etc are all still available outside the jail.  Once the chroot is completed,
they then drop root permission to the non privileged user given on the command

The code in servers/slapd/main.c gets the real uid/gid AFTER the chroot.  As
such, the authentication infrastructure (nsswitch.conf, passwd, groups, etc)
must be duplicated in the jail.  In my opinion, this makes jailing slapd more
difficult (inconvenient) than the other daemons mentioned.  The jailing code in
slapd may work, but I was unable to make it go as a non-root user.  Didn't find
very much useful information available via Google with respect to
troubleshooting my chroot issue.

To test a few theories, I added some scaffolding code in main.c and user.c to
see where the process was going bad for me.  As expected after looking at the
source, the failure was happening in the slap_init_user function of user.c. 
More specifically, the call to getpwnam was returning NULL (failure) and causing
the corresponding Debug statement to print the error message I am seeing when
attempting to jail as a non-privileged user.  No surprise there.

Not being that familiar with the code in these two files, I am reluctant to
modify too much for fear of introducing unintentional side effects. But in
testing I found a few ways of working around the issue. 

Initially, I thought it might be easiest to move the call to slap_init_user
before the chroot code.  But then I realized that cannot work, because this
function drops root permission before returning which will then cause the chroot
code to fail.

The first, and simplest, workaround I found was that by making an initial call
to getpwnam and discarding the result before hitting the chroot code seemed to
make the subsequent call in slap_init_user succeed.  Wierd.  I can only
speculate two possibilities for that.  The most reasonable is that the getpwnam
call before the chroot code loads some shared library(ies) into memory that I'm
missing in my jail allowing the later call after the chroot call to succeed. 
The second, and least reasonable, is that the man page for getpwnam states the
returned pointer is to a static passwd struct which when initialized before the
chroot code, is returned despite the later failure in slap_init_user.  Like I
said, least likely.  I don't have the knowledge to test either theory and it
just works despite the wasteful additional call to getpwnam.  This workaround
seems to have the least amount of potential side-effects elsewhere in the code.

The second workaround involved moving the code blocks for the getpwnam and
getgrnam calls out of slap_init_user and into main just prior to the the chroot
code.  I would also then have to drop the root permission just after the chroot
code block based on got_uid/gid variable values.  I didn't dig far enough into
the code to know if there is anywhere else that the code drops root permission. 
If so, this may cause one of those unintentional side effects.  This also
worked, but seemed like bad coding practice.

My third, and final, work around I think fits in the best with the spirit of the
existing code.  In this workaround I split up the slap_init_user code in user.c
into four seperate functions: slap_init_user, slap_init_group, slap_set_user,
slap_set_group.  The slap_init_xxx functions use two variables added to main of
type uid_t and gid_t that are passed in by reference.  Each function performs
the respective passwd/group calls in slap_init_user,  store the result, if
successful, into the byref parameter(s) and return a got_uid/gid value back as
an integer return value indicating if the uid/gid parameters contain useful
id's.  The slapd_set_xxx functions perform the actual drop of root permission. 
Back in main, the slap_init_user/group functions get called prior to the chroot
code and the corresponding slap_set_xxx calls are added in the relevant code
block that follows the chroot call if the "got" variables indicate the uid/gid
values are useful.  This allows the uid/gid to be looked up prior to chroot
eliminating the need for security info in the jail, root permission is still
held to chroot, and the dropping of root authority sill occurs immediately after
the chroot call.

At the end of the day, I think the third workaround allow the chroot code for
slapd to perform in the same way as the other daemons I mentioned creating a
more predictable user experience.  I certainly can't speak for everyone, but to
me, a predictable user experience is more definitely more convenient.  In the
same light, I wonder if moving the creation of the pid and args files before the
chroot adds value for the user when dealing with the daemon via an init script. 
The init script might be a bit more simple without having to worry about
prepending a jail path to the pid file detected from the config when the daemon
is being run as such.  Not a big issue.  

If you think the code I used in my workarounds add value to the project, I will
gladly send them along.  But, honestly, it is so rudimentary that I doubt my
copy offers you much value since I'm sure you could duplicate it in less than 30


Barry Lance