0

My scenario is the following: I've got a C++ app, which accesses a radio receiver to get a time signal from it and then updates system time according to the time from the radio signal. For the sake of security, the app shall not run with root privileges. After having read this advice from a forum thread, I tried out a code snippet like this:

tm              current_time;
struct timeval *epoch         = new timeval;

// current_time is filled with time data from the radio tuner here.

epoch -> tv_sec  = mktime (&current_time);
epoch -> tv_usec = 0;

if (difftime (epoch -> tv_sec, mktime (&this -> last_system_time_update)) > (time_t) receiver::SYSTEM_TIME_UPDATE_INTERVAL) {
  retval += setgid       ((uid_t) 0);
  retval += setuid       ((uid_t) 0);
  retval += prctl        (PR_SET_KEEPCAPS, 1L, 0L, 0L, 0L);
  retval += setgid       (group_id);
  retval += setuid       (user_id);
  retval += settimeofday (epoch, NULL);
}

Contrary to the advice, this snippet won't work when I'm not running it as root. I'd always get errno = 1.

What's wrong here? And: Is there a workaround?

Zoe
  • 27,060
  • 21
  • 118
  • 148
Neppomuk
  • 1,102
  • 1
  • 11
  • 24
  • 1
    Does your process have the `CAP_SYS_TIME` capability bit set? – Lightness Races in Orbit Nov 23 '18 at 18:00
  • 1
    Does that mean you have followed the advice of giving your executable file the CAP_SYS_TIME capability ? – nos Nov 23 '18 at 18:00
  • I don't even reach the point where I can set the system time cap as already the first syscall (`setgid`) returns errno = 1. – Neppomuk Nov 23 '18 at 18:10
  • The code snippet assumes that you are root, the executable is root-owned with SUID bit set or CAP_SETUID is set. Completely without any privileges you cannot change the system time. It also keeps all the capabilities instead of only the needed one. Instead of using this code set CAP_SYS_TIME on your executable. –  Nov 23 '18 at 18:35

1 Answers1

1

You're still trying to get root privileges. If you have CAP_SYS_TIME capability, all you need is settimeofday().

if (difftime (epoch -> tv_sec, mktime (&this -> last_system_time_update)) > 
(time_t) receiver::SYSTEM_TIME_UPDATE_INTERVAL) {
  retval += settimeofday (epoch, NULL);
}
Michael Surette
  • 701
  • 1
  • 4
  • 12
  • And can I acquire this capability by using prctl ()? – Neppomuk Nov 23 '18 at 23:10
  • 1
    @Neppomuk You set the capability on your program as root using the setcap utility. See https://wiki.archlinux.org/index.php/capabilities for more information. – Michael Surette Nov 24 '18 at 03:01
  • So I'd have to issue the following command before executing my app: `sudo setcap cap_sys_time+ep /home/jacek/autoradio` And I'd have to do it only once 'cause the caps are stored in the file system. Right? – Neppomuk Nov 24 '18 at 22:52
  • From my understanding. I've never actually done this. – Michael Surette Nov 26 '18 at 21:19
  • No use: `pi@autoradio:/import/valen/autoradio $ sudo setcap cap_sys_time+ep autoradio Failed to set capabilities on file `autoradio' (Invalid argument) The value of the capability argument is not permitted for a file. Or the file is not a regular (non-symlink) file` – Neppomuk Dec 03 '18 at 20:19