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

ITS#98 'user' patch for BSD systems



Attached are three small patch files that implement a 'user'
configuration
command to address ITS#98.  They have received minimal testing under
FreeBSD 3.1R.

Note that giving up root permissions means that the databases must
be accessable by the new user/group(s).   (The uid/gid change occurs
just after binding the socket; so it should be before any of the
databases
are actually opened.  On some systems, this may prevent access to
the encrypted passwords for passwd back-ends.  Shell back-ends
must also be able to work under the new uid/gid.


I've included the possibility to set a list of groups to allow for the
possibility that different databases/back-ends may be set up to
require different group permissions.


The new global configuration command takes the form:

    user    <new-user>    [ <new-group> ... ]

Where <new-user> is either a numeric uid or a username to be looked
up via getpwnam().   Similarly, each <new-group> is either a numeric
group id or a group name to be looked up via getgrnam().  NOTE that
if <new-user> is numeric, then at least one <new-group> must be
specified.

If no groups are specified then the group access list will be determined

from the <user-name> via getgrouplist().  (SEE PORTABILITY NOTE
BELOW.)

If no user command is given, then no attempt will be made to change
the uid/gid of the process.  (Note that 'user root' may give slightly
different results since it will re-initialize the group access list.)


PORTABILITY NOTE:

These patches should work as given for any reasonably modern release
of FreeBSD, NetBSD, or OpenBSD; and probably for other BSD-derived
systems.   I have not made any serious attempt to handle portability to
non-BSD systems.   (The patches are in the form expected by the
FreeBSD ports system.  FreeBSD users can simply create a 'patches'
directory in /usr/ports/net/openbsd, and copy these patches in then
'make' as usual.)

SysV-derived systems (e.g., Solaris 2.5.1, Linux) appear to lack the
getgrouplist(3) system call which is used to determine the group access
list associated with a given username.  An autoconfig check and #ifdef
will be required around that code block.  I found no immediately obvious

way to cleanly extract the same information from these systems;  and
I don't have the cycles to investigate it in depth now.  So I leave it
to
someone with more time and/or experience on those systems.  If there
is no clean way to do it, I suggest that the alternative be to simply
fall
back to only the base gid from the passwd info.   (Replacing the if
block
that uses getgrouplist() with 'ngids=1;run_gids[0]=user->pw_gid;'
clean-compiles and passes the tests suite under Solaris 2.5.1; but I
haven't tested it beyond that.)

I have absolutely no idea how portable this code is to BeOS, NT, or
other non-unixish systems.  I suspect that it may need an autoconfig/
#ifdef to disable the whole thing for some systems.



-Pat
--- doc/man/man5/slapd.conf.5	Sat Jan 23 13:24:59 1999
+++ doc/man/man5/slapd.conf.5	Wed Mar 10 18:30:38 1999
@@ -204,6 +204,21 @@
 for the backend in which they are defined.  They are supported by every
 type of backend.
 .TP
+.B user <uid or name> [<gid or name> ...]
+Specify the UID, and optionally GID(s), under which slapd is to run after
+binding to the socket.  These values may be either numeric uid (gid)
+values or names to be looked up via getpwnam(3) (getgrnam(3)).  If not
+specified here or at compile time, the uid and gid will not be changed.
+If a user is specified, but no group, then initgroups(3) will be used to
+determine the appropriate groups for that user.  NOTE that if the user is
+given as a uid number, at least one group 
+.B must
+be specified.
+.LP
+Note that on some systems, running as a non-privileged user will prevent
+passwd back-ends from accessing the encrypted passwords.  Note also that
+any shell back-ends will run as the specified non-privileged user.
+.TP
 .B database <databasetype>
 Mark the beginning of a new database instance definition. <databasetype>
 should be one of
--- servers/slapd/config.c	Sun Jan 31 10:01:35 1999
+++ servers/slapd/config.c	Wed Mar 10 18:36:20 1999
@@ -3,10 +3,13 @@
 #include "portable.h"
 
 #include <stdio.h>
+#include <limits.h>
 
 #include <ac/string.h>
 #include <ac/ctype.h>
 #include <ac/socket.h>
+#include <pwd.h>
+#include <grp.h>
 
 #include "ldapconfig.h"
 #include "slap.h"
@@ -23,6 +26,9 @@
 char		*replogfile;
 int		global_lastmod;
 char		*ldap_srvtab = "";
+uid_t		run_uid = 0;
+int		ngids = 0;
+gid_t		run_gids[NGROUPS_MAX];
 
 char   *slapd_pid_file  = NULL;
 char   *slapd_args_file = NULL;
@@ -453,6 +459,77 @@
 				exit( 1 );
 			}
 			ldap_srvtab = ch_strdup( cargv[1] );
+
+		/* UID to run as after binding to the socket */
+		} else if ( strcasecmp( cargv[0], "user" ) == 0 ) {
+		        struct passwd *user = NULL ;
+
+			if ( cargc < 2) {
+				Debug( LDAP_DEBUG_ANY,
+"%s: line %d: missing user name or id in \"user <uid-or-name> [<gid-or-name> ...]\" line\n",
+				       fname, lineno, 0 );
+				exit( 1 );
+			}
+			if ( be != NULL ) {
+				Debug( LDAP_DEBUG_ANY,
+"%s: line %d: user line must appear before the database definitions (ignored)\n",
+				       fname, lineno, 0 );
+				continue;
+			} else if ( isdigit( *(cargv[1]) )) {
+				run_uid = atoi( *(cargv[1]) );
+				if  ( cargc <= 2 ) {
+				        Debug( LDAP_DEBUG_ANY,
+    "%s: line %d: numeric user id must be followed by at least one group id\n",
+					       fname, lineno, 0);
+					exit( 1 );
+				}
+			} else {
+				user = getpwnam( cargv[1] );
+
+				if ( user == NULL ) {
+					Debug( LDAP_DEBUG_ANY,
+		"%s: line %d: could not find password entry for user \"%s\"\n",
+					       fname, lineno, cargv[1] );
+					exit( 1 );
+				}
+				run_uid = user->pw_uid;
+			}
+
+			/* Any further args are group ids. */
+			if  (cargc > 2) {
+			        if  ((cargc - 2) > (sizeof(run_gids)/sizeof(gid_t))) {
+				        Debug( LDAP_DEBUG_ANY,
+		      "%s: line %d: too many groups on user line - max = %d\n",
+					       fname, lineno,
+					       sizeof(run_gids)/sizeof(gid_t));
+					exit( 1 );
+				}
+
+				ngids = 0 ;
+			        for (i = 2  ;  i < cargc  ;  i++) {
+					if  ( isdigit( *(cargv[i]) )) {
+					        run_gids[ngids++] = atoi( *(cargv[i]) );
+					} else {
+						struct group* grp = getgrnam( cargv[i] );
+
+						if ( grp == NULL ) {
+							Debug( LDAP_DEBUG_ANY,
+			"%s: line %d: could not find group entry for \"%s\"\n",
+							       fname, lineno, cargv[i] );
+							exit( 1 );
+						}
+						run_gids[ngids++] = grp->gr_gid ;
+					}
+				}
+			} else {
+			        /* NOTE: We only get here if the user was given as a name */
+			        if ( getgrouplist( cargv[1], user->pw_gid, &ngids, &run_gids ) != 0) {
+				        Debug( LDAP_DEBUG_ANY,
+		  "%s: line %d: could not get group id list for user \"%s\"\n",
+					       fname, lineno, cargv[1] );
+					exit( 1 ) ;
+				}
+			}
 
 		/* pass anything else to the current backend config routine */
 		} else {
--- servers/slapd/daemon.c	Sun Feb  7 10:42:24 1999
+++ servers/slapd/daemon.c	Wed Mar 10 18:33:19 1999
@@ -33,6 +33,10 @@
 int deny_severity = LOG_NOTICE;
 #endif /* TCP Wrappers */
 
+extern uid_t		run_uid;
+extern int		ngids;
+extern gid_t		run_gids[];
+
 int		dtblsize;
 Connection	*c;
 
@@ -125,6 +129,42 @@
 		    errno, errno > -1 && errno < sys_nerr ? sys_errlist[errno] :
 		    "unknown", 0 );
 		exit( 1 );
+	}
+
+	if ( ngids != 0 ) {
+	        if ( setgroups( ngids, run_gids) != 0) {
+			Debug (LDAP_DEBUG_ANY,
+			       "Could not set the group access (gid) list",
+			       0, 0, 0) ;
+			exit( 1 );
+		}
+		    
+		if ( setgid( run_gids[0] ) != 0 ) {
+			Debug (LDAP_DEBUG_ANY,
+			       "Could not set real group id to %d",
+			       run_gids[0], 0, 0) ;
+			exit( 1 );
+		}
+		if ( setegid( run_gids[0] ) != 0 ) {
+			Debug (LDAP_DEBUG_ANY,
+			       "Could not set effective group id to %d",
+			       run_gids[0], 0, 0) ;
+			exit( 1 );
+		}
+	}
+	if ( run_uid != 0 ) {
+		if ( setuid( run_uid ) != 0 ) {
+			Debug (LDAP_DEBUG_ANY,
+			       "Could not set effective user id to %d",
+			       run_uid, 0, 0) ;
+			exit( 1 );
+		}
+		if ( seteuid( run_uid ) != 0) {
+			Debug (LDAP_DEBUG_ANY,
+			       "Could not set real UID to %d",
+			       run_uid, 0, 0) ;
+			exit( 1 );
+		}
 	}
 
 	Debug( LDAP_DEBUG_ANY, "slapd starting\n", 0, 0, 0 );