[B<-b>E<nbsp>I<base_dir>E<nbsp>[B<-B>]]
[B<-F>]
[B<-f>E<nbsp>I<timeout>]
+[B<-G>E<nbsp>I<group>]]
[B<-g>]
[B<-j>E<nbsp>I<journal_dir>]
[B<-L>]
[B<-R>]
[B<-s>E<nbsp>I<group>]
[B<-t>E<nbsp>I<write_threads>]
+[B<-U>E<nbsp>I<user>]]
[B<-w>E<nbsp>I<timeout>]
[B<-z>E<nbsp>I<delay>]
=item B<-O>
-Preven the CREATE command from overwriting existing files, even when it is
+Prevent the CREATE command from overwriting existing files, even when it is
instructed to do so. This is for added security.
+=item B<-G> -I<group>
+
+When running as daemon and invoked from a privileged account, reset
+group privileges to those of I<group>. The group may be specified as
+a name or as a group ID. The daemon will exit with a diagnostic if
+it cannot successfully transition to the specified group.
+
+=item B<-U> -I<user>
+
+When running as daemon and invoked from a privileged account, reset
+user privileges to those of I<user>. The user may be specified as
+a name or as a user ID. The daemon will exit with a diagnostic if
+it cannot successfully transition to the specified user.
+
=back
=head1 AFFECTED RRDTOOL COMMANDS
#include <time.h>
#include <libgen.h>
#include <grp.h>
+#include <pwd.h>
#ifdef HAVE_LIBWRAP
#include <tcpd.h>
*/
static int stay_foreground = 0;
static uid_t daemon_uid;
+static gid_t daemon_gid;
static listen_socket_t *listen_fds = NULL;
static size_t listen_fds_num = 0;
int pid_fd;
char *base_dir;
- daemon_uid = geteuid();
-
pid_fd = open_pidfile("create", O_CREAT|O_EXCL|O_WRONLY);
if (pid_fd < 0)
pid_fd = check_pidfile();
openlog ("rrdcached", LOG_PID, LOG_DAEMON);
RRDD_LOG(LOG_INFO, "starting up");
- install_signal_receiver();
cache_tree = g_tree_new_full ((GCompareDataFunc) strcmp, NULL, NULL,
(GDestroyNotify) free_cache_item);
goto error;
}
- return write_pidfile (pid_fd);
+ if (0 == write_pidfile (pid_fd))
+ {
+ /* Writing the pid file was the last act that might require privileges.
+ * Attempt to change to the desired runtime privilege level. */
+ if (getegid() != daemon_gid)
+ {
+ if (0 != setgid(daemon_gid))
+ {
+ RRDD_LOG (LOG_ERR, "daemonize: failed to setgid(%u)", daemon_gid);
+ goto error;
+ }
+ RRDD_LOG(LOG_INFO, "setgid(%u) succeeded", daemon_gid);
+ }
+ if (geteuid() != daemon_uid)
+ {
+ if (0 != setuid(daemon_uid))
+ {
+ RRDD_LOG (LOG_ERR, "daemonize: failed to setuid(%u)", daemon_uid);
+ goto error;
+ }
+ RRDD_LOG(LOG_INFO, "setuid(%u) succeeded", daemon_uid);
+ }
+ /* Delay creation of threads until the final privilege level has
+ * been reached. */
+ install_signal_receiver();
+ return 0;
+ }
+ /*FALLTHRU*/
error:
remove_pidfile();
return -1;
socket_permission_clear (&default_socket);
+ daemon_uid = geteuid();
+ daemon_gid = getegid();
default_socket.socket_group = (gid_t)-1;
default_socket.socket_permissions = (mode_t)-1;
- while ((option = getopt(argc, argv, "?a:Bb:Ff:ghj:Ll:m:OP:p:Rs:t:w:z:")) != -1)
+ while ((option = getopt(argc, argv, "?a:Bb:Ff:gG:hj:Ll:m:OP:p:Rs:t:U:w:z:")) != -1)
{
switch (option)
{
stay_foreground=1;
break;
+ case 'G':
+#if defined(HAVE_GETGRNAM) && defined(HAVE_GRP_H) && defined(HAVE_SETGID)
+ {
+ gid_t group_gid;
+ char * ep;
+ struct group *grp;
+
+ group_gid = strtoul(optarg, &ep, 10);
+ if (0 == *ep)
+ {
+ /* we were passed a number */
+ grp = getgrgid(group_gid);
+ }
+ else
+ {
+ grp = getgrnam(optarg);
+ }
+ if (NULL == grp)
+ {
+ fprintf (stderr, "read_options: couldn't map \"%s\" to a group, Sorry\n", optarg);
+ return (5);
+ }
+ daemon_gid = grp->gr_gid;
+ break;
+ }
+#else
+ fprintf(stderr, "read_options: -G not supported.\n");
+ return 5;
+#endif
+
+ case 'U':
+#if defined(HAVE_GETPWNAM) && defined(HAVE_PWD_H) && defined(HAVE_SETUID)
+ {
+ uid_t uid;
+ char * ep;
+ struct passwd *pw;
+
+ uid = strtoul(optarg, &ep, 10);
+ if (0 == *ep)
+ {
+ /* we were passed a number */
+ pw = getpwuid(uid);
+ }
+ else
+ {
+ pw = getpwnam(optarg);
+ }
+ if (NULL == pw)
+ {
+ fprintf (stderr, "read_options: couldn't map \"%s\" to a user, Sorry\n", optarg);
+ return (5);
+ }
+ daemon_uid = pw->pw_uid;
+ break;
+ }
+#else
+ fprintf(stderr, "read_options: -U not supported.\n");
+ return 5;
+#endif
+
case 'L':
case 'l':
{
}
break;
- case 'R':
+ case 'R':
config_allow_recursive_mkdir = 1;
break;
" -b <dir> Base directory to change to.\n"
" -F Always flush all updates at shutdown\n"
" -f <seconds> Interval in which to flush dead data.\n"
+ " -G <group> Unprivileged group used when running.\n"
" -g Do not fork and run in the foreground.\n"
" -j <dir> Directory in which to create the journal files.\n"
" -L Open sockets on all INET interfaces using default port.\n"
" (the socket will also have read/write permissions "
"for that group)\n"
" -t <threads> Number of write threads.\n"
+ " -U <user> Unprivileged user account used when running.\n"
" -w <seconds> Interval in which to write data.\n"
" -z <delay> Delay writes up to <delay> seconds to spread load\n"
"\n"