Originally from ftp://ftp.redhat.com/pub/redhat/linux/7.2/en/os/i386/SRPMS/ntp-4.1.0-4.src.rpm See for details. --- ntp-4.0.99m-rc2/html/ntpd.htm.droproot Thu May 24 08:04:57 2001 +++ ntp-4.0.99m-rc2/html/ntpd.htm Thu Aug 30 12:29:04 2001 @@ -22,7 +22,7 @@ driftfile ] [ -g ] [ -k keyfile ] [ -l logfile ] [ -N high ] [ -p pidfile ] [ -r broadcastdelay ] [ -s statsdir ] [ -t key ] [ -v variable ] [ -V -variable ] [ -x ] +variable ] [ -T chroot_dir ] [ -U server_user ] [ -x ]

Description

@@ -379,6 +379,19 @@
Add a system variable listed by default.
+ +
-T chroot_dir
+
Chroot the ntpd server process into chroot_dir. To use this +option you have to copy all the files that ntpd process needs into the +chroot directory. This option adds security only if the server also drops +root privileges (see -U option).
+ +
-U server_user
+
Ntpd process drops root privileges and changes user ID to +server_user and group ID to the primary group of server_user. +To use this option you need libcap-library. +
+
-x
Normally, the time is slewed if the offset is less than the --- ntp-4.0.99m-rc2/html/ntpdate.htm.droproot Tue Apr 3 05:43:05 2001 +++ ntp-4.0.99m-rc2/html/ntpdate.htm Thu Aug 30 12:29:04 2001 @@ -26,6 +26,7 @@ ntpdate [ -bBdoqsuv ] [ -a key ] [ -e authdelay ] [ -k keyfile ] [ -o version ] [ -p samples ] [ +-U user_name ] [ -t timeout ] server [ ... ]

Description

@@ -161,6 +162,12 @@
Be verbose. This option will cause ntpdate's version identification string to be logged.
+ +
-U user_name
+ +
ntpdate process drops root privileges and changes user ID to +user_name and group ID to the primary group of server_user. +To use this option you need libcap-library.

Files

--- ntp-4.1.2/ntpd/Makefile.am.orig 2003-08-06 02:12:03.000000000 -0400 +++ ntp-4.1.2/ntpd/Makefile.am 2003-08-06 02:12:10.000000000 -0400 @@ -9,7 +9,7 @@ # sqrt ntp_control.o # floor refclock_wwv.o # which are (usually) provided by -lm. -ntpd_LDADD = $(LDADD) -lm +ntpd_LDADD = $(LDADD) -lm -lcap DISTCLEANFILES = .version version.c #EXTRA_DIST = ntpd.mak ETAGS_ARGS = Makefile.am --- ntp-4.0.99m-rc2/ntpd/ntpd.c.droproot Sat Apr 21 09:23:40 2001 +++ ntp-4.0.99m-rc2/ntpd/ntpd.c Thu Aug 30 12:32:54 2001 @@ -6,6 +6,11 @@ # include #endif +#include +#include +#include +#include + #include "ntp_machine.h" #include "ntpd.h" #include "ntp_io.h" @@ -96,6 +101,11 @@ #include "ntp_crypto.h" #endif /* PUBKEY */ +/* Username to run as */ +char *server_user=0; +/* Chroot to this dir */ +char *chroot_dir=0; + /* * Signals we catch for debugging. If not debugging we ignore them. */ @@ -188,10 +198,60 @@ char *argv[] ) { + server_user = NULL; + chroot_dir = NULL; return ntpdmain(argc, argv); } #endif +/* This patch is adapted (copied) from Chris Wings drop root patch + * for xntpd. + */ +void drop_root(uid_t server_uid, gid_t server_gid) +{ + cap_t caps; + + if (prctl(PR_SET_KEEPCAPS, 1)) { + msyslog(LOG_ERR, "prctl(PR_SET_KEEPCAPS, 1) failed"); + exit(1); + } + + if ( setgroups(0, NULL) == -1 ) { + msyslog(LOG_ERR, "setgroups failed."); + exit(1); + } + + if ( setegid(server_gid) == -1 || seteuid(server_uid) == -1 ) { + msyslog(LOG_ERR, "setegid/seteuid to uid=%d/gid=%d failed.", server_uid, + server_gid); + exit(1); + } + + caps = cap_from_text("cap_sys_time=epi"); + if (caps == NULL) { + msyslog(LOG_ERR, "cap_from_text failed."); + exit(1); + } + + if (cap_set_proc(caps) == -1) { + msyslog(LOG_ERR, "cap_set_proc failed."); + exit(1); + } + + /* Try to free the memory from cap_from_text */ + cap_free( caps ); + + if ( setregid(server_gid, server_gid) == -1 || + setreuid(server_uid, server_uid) == -1 ) { + msyslog(LOG_ERR, "setregid/setreuid to uid=%d/gid=%d failed.", server_uid, + server_gid); + exit(1); + } + + msyslog(LOG_DEBUG, "running as uid(%d)/gid(%d) euid(%d)/egid(%d).", + getuid(), getgid(), geteuid(), getegid()); +} + #ifdef _AIX /* * OK. AIX is different than solaris in how it implements plock(). @@ -337,6 +397,9 @@ #ifdef _AIX /* HMS: ifdef SIGDANGER? */ struct sigaction sa; #endif + struct passwd *pwd = NULL; + uid_t server_uid; + gid_t server_gid; initializing = 1; /* mark that we are initializing */ debug = 0; /* no debugging by default */ @@ -377,6 +440,29 @@ #endif getstartup(argc, argv); /* startup configuration, may set debug */ + /* Lookup server_user uid/gid before chroot/chdir */ + if ( server_user ) { + pwd = getpwnam( server_user ); + if ( pwd == NULL ) { + msyslog(LOG_ERR, "Failed to lookup user '%s'.", server_user); + exit(1); + } + server_uid = pwd->pw_uid; + server_gid = pwd->pw_gid; + } + + /* Try to chroot to chroot_dir. This probably makes sense only if + * the server drops root privileges. + */ + if ( chroot_dir ) { + if ( chroot(chroot_dir) == -1 || chdir("/") == -1 ) { + msyslog(LOG_ERR, "chroot/chdir to '%s' failed.", chroot_dir); + exit(1); + } + /* Close /dev/log */ + closelog(); + } + /* * Initialize random generator and public key pair */ @@ -745,6 +831,10 @@ #endif /* AUTOKEY */ initializing = 0; + if ( server_user ) { + drop_root( server_uid, server_gid ); + } + #if defined(SYS_WINNT) && !defined(NODETACH) # if defined(DEBUG) if(!debug) --- ntp-4.0.99m-rc2/ntpd/cmd_args.c.droproot Fri Apr 20 00:50:01 2001 +++ ntp-4.0.99m-rc2/ntpd/cmd_args.c Thu Aug 30 12:34:33 2001 @@ -15,7 +15,15 @@ extern char const *progname; int listen_to_virtual_ips = 0; +static const char *ntp_options = "aAbc:dD:f:gk:l:LmnN:p:P:qr:s:t:v:V:x-:U:T:"; + +/* Drop root patch */ +extern char *server_user; +extern char *chroot_dir; + +/* static const char *ntp_options = "aAbc:dD:f:gk:l:LmnN:p:P:qr:s:t:v:V:x-:"; +*/ #ifdef HAVE_NETINFO extern int check_netinfo; @@ -114,7 +122,31 @@ ++errflg; break; - default: + case 'U': + if ( !ntp_optarg ) { + fprintf(stderr, "Error: Need username with 'U' option\n"); + exit(1); + } + else { + if ( !server_user ) { + server_user = strdup(ntp_optarg); + } + } + break; + + case 'T': + if ( !ntp_optarg ) { + fprintf(stderr, "Error: Need directory with 'T' option\n"); + exit(1); + } + else { + if ( !chroot_dir ) { + chroot_dir = strdup(ntp_optarg); + } + } + break; + + default: break; } @@ -123,6 +155,7 @@ (void) fprintf(stderr, "\t\t[ -f freq_file ] [ -k key_file ] [ -l log_file ]\n"); (void) fprintf(stderr, "\t\t[ -p pid_file ] [ -r broad_delay ] [ -s statdir ]\n"); (void) fprintf(stderr, "\t\t[ -t trust_key ] [ -v sys_var ] [ -V default_sysvar ]\n"); + (void) fprintf(stderr, "\t\t[ -T chroot_dir ] [ -U server_user ]\n"); #if defined(HAVE_SCHED_SETSCHEDULER) (void) fprintf(stderr, "\t\t[ -P fixed_process_priority ]\n"); #endif @@ -293,6 +326,10 @@ allow_step = FALSE; break; + case 'U': /* already done at pre-scan */ + case 'T': /* already done at pre-scan */ + break; + default: errflg++; break; @@ -304,6 +341,7 @@ (void) fprintf(stderr, "\t\t[ -f freq_file ] [ -k key_file ] [ -l log_file ]\n"); (void) fprintf(stderr, "\t\t[ -p pid_file ] [ -r broad_delay ] [ -s statdir ]\n"); (void) fprintf(stderr, "\t\t[ -t trust_key ] [ -v sys_var ] [ -V default_sysvar ]\n"); + (void) fprintf(stderr, "\t\t[ -T chroot_dir ] [ -U server_user ]\n"); #if defined(HAVE_SCHED_SETSCHEDULER) (void) fprintf(stderr, "\t\t[ -P fixed_process_priority ]\n"); #endif --- ntp-4.1.2/ntpdate/Makefile.am.orig 2003-08-06 02:12:54.000000000 -0400 +++ ntp-4.1.2/ntpdate/Makefile.am 2003-08-06 02:13:59.000000000 -0400 @@ -5,6 +5,7 @@ INCLUDES = -I$(top_srcdir)/include # LDADD might need RESLIB and ADJLIB LDADD = version.o ../libntp/libntp.a @LIBRSAREF@ +ntpdate_LDADD = $(LDADD) -lcap DISTCLEANFILES = .version version.c stamp-v noinst_HEADERS = ntpdate.h #EXTRA_DIST = ntpdate.mak --- ntp-4.0.99m-rc2/ntpdate/ntpdate.c.droproot Sun Apr 22 11:42:48 2001 +++ ntp-4.0.99m-rc2/ntpdate/ntpdate.c Thu Aug 30 12:29:04 2001 @@ -41,6 +41,12 @@ # include #endif /* HAVE_SYS_RESOURCE_H */ +/* Linux capabilities */ +#include +#include +#include +#include + #ifdef SYS_VXWORKS # include "ioLib.h" # include "sockLib.h" @@ -127,6 +133,11 @@ int rate = 0; /* + * Use capabilities to drop privileges and switch uids + */ +char *server_user; + +/* * Program name. */ char *progname; @@ -273,6 +284,88 @@ static ni_namelist *getnetinfoservers P((void)); #endif +/* This patch is adapted (copied) from Chris Wings drop root patch + * for xntpd. + */ +void drop_root(uid_t server_uid, gid_t server_gid) +{ + cap_t caps; + + if (prctl(PR_SET_KEEPCAPS, 1)) { + if (syslogit) { + msyslog(LOG_ERR, "prctl(PR_SET_KEEPCAPS, 1) failed"); + } + else { + fprintf(stderr, "prctl(PR_SET_KEEPCAPS, 1) failed.\n"); + } + exit(1); + } + + if ( setgroups(0, NULL) == -1 ) { + if (syslogit) { + msyslog(LOG_ERR, "setgroups failed."); + } + else { + fprintf(stderr, "setgroups failed.\n"); + } + exit(1); + } + + if ( setegid(server_gid) == -1 || seteuid(server_uid) == -1 ) { + if (syslogit) { + msyslog(LOG_ERR, "setegid/seteuid to uid=%d/gid=%d failed.", server_uid, + server_gid); + } + else { + fprintf(stderr, "setegid/seteuid to uid=%d/gid=%d failed.\n", server_uid, + server_gid); + } + exit(1); + } + + caps = cap_from_text("cap_sys_time=epi"); + if (caps == NULL) { + if (syslogit) { + msyslog(LOG_ERR, "cap_from_text failed."); + } + else { + fprintf(stderr, "cap_from_text failed.\n"); + } + exit(1); + } + + if (cap_set_proc(caps) == -1) { + if (syslogit) { + msyslog(LOG_ERR, "cap_set_proc failed."); + } + else { + fprintf(stderr, "cap_set_proc failed.\n"); + } + exit(1); + } + + /* Try to free the memory from cap_from_text */ + cap_free( caps ); + + if ( setregid(server_gid, server_gid) == -1 || + setreuid(server_uid, server_uid) == -1 ) { + if (syslogit) { + msyslog(LOG_ERR, "setregid/setreuid to uid=%d/gid=%d failed.", + server_uid, server_gid); + } + else { + fprintf(stderr, "setregid/setreuid to uid=%d/gid=%d failed.\n", + server_uid, server_gid); + } + exit(1); + } + + if (syslogit) { + msyslog(LOG_DEBUG, "running as uid(%d)/gid(%d) euid(%d)/egid(%d).", + getuid(), getgid(), geteuid(), getegid()); + } +} + /* * Main program. Initialize us and loop waiting for I/O and/or * timer expiries. @@ -323,7 +416,7 @@ #ifdef NO_MAIN_ALLOWED clear_globals(); #endif - + server_user = NULL; errflg = 0; progname = argv[0]; syslogit = 0; @@ -331,7 +424,7 @@ /* * Decode argument list */ - while ((c = ntp_getopt(argc, argv, "a:bBde:k:o:p:qr:st:uv")) != EOF) + while ((c = ntp_getopt(argc, argv, "a:bBde:k:o:p:qr:st:uvU:")) != EOF) switch (c) { case 'a': @@ -417,13 +510,22 @@ case '?': ++errflg; break; + case 'U': + if (ntp_optarg) { + server_user = strdup(ntp_optarg); + } + else { + ++errflg; + } + break; + default: break; } if (errflg) { (void) fprintf(stderr, - "usage: %s [-bBdqsuv] [-a key#] [-e delay] [-k file] [-p samples] [-o version#] [-r rate] [-t timeo] server ...\n", + "usage: %s [-bBdqsuv] [-a key#] [-e delay] [-k file] [-p samples] [-o version#] [-r rate] [-t timeo] [-U username] server ...\n", progname); exit(2); } @@ -536,6 +638,24 @@ initializing = 0; was_alarmed = 0; + + if (server_user) { + struct passwd *pwd = NULL; + + /* Lookup server_user uid/gid before chroot/chdir */ + pwd = getpwnam( server_user ); + if ( pwd == NULL ) { + if (syslogit) { + msyslog(LOG_ERR, "Failed to lookup user '%s'.", server_user); + } + else { + fprintf(stderr, "Failed to lookup user '%s'.\n", server_user); + } + exit(1); + } + drop_root(pwd->pw_uid, pwd->pw_gid); + } + rbuflist = (struct recvbuf *)0; while (complete_servers < sys_numservers) { #ifdef HAVE_POLL_H