[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 );