m/n/time: add time service

This adds a bare-minimum time service based on chrony/NTP for keeping
the system clock and RTC on Metropolis nodes accurate.

It also introduces a UID/GID registry in the Metropolis node code
as this is the first unprivileged service to run on the node itself.

It does not yet use a secure time source, this is tracked as #73.

Change-Id: I873971e6d3825709bc8c696e227bece4cfbda93a
Reviewed-on: https://review.monogon.dev/c/monogon/+/319
Reviewed-by: Sergiusz Bazanski <serge@monogon.tech>
diff --git a/third_party/chrony/patches/BUILD b/third_party/chrony/patches/BUILD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/chrony/patches/BUILD
diff --git a/third_party/chrony/patches/disable_defaults.patch b/third_party/chrony/patches/disable_defaults.patch
new file mode 100644
index 0000000..2a3dacf
--- /dev/null
+++ b/third_party/chrony/patches/disable_defaults.patch
@@ -0,0 +1,13 @@
+diff --git a/conf.c b/conf.c
+index ce2ff00..7ce7fd2 100644
+--- a/conf.c
++++ b/conf.c
+@@ -403,8 +403,6 @@ CNF_Initialise(int r, int client_only)
+   if (client_only) {
+     cmd_port = ntp_port = 0;
+   } else {
+-    bind_cmd_path = Strdup(DEFAULT_COMMAND_SOCKET);
+-    pidfile = Strdup(DEFAULT_PID_FILE);
+   }
+
+   SCK_GetAnyLocalIPAddress(IPADDR_INET4, &bind_address4);
\ No newline at end of file
diff --git a/third_party/chrony/patches/support_fixed_uids.patch b/third_party/chrony/patches/support_fixed_uids.patch
new file mode 100644
index 0000000..f9aecb7
--- /dev/null
+++ b/third_party/chrony/patches/support_fixed_uids.patch
@@ -0,0 +1,52 @@
+--- a/main.c
++++ b/main.c
+@@ -439,6 +439,7 @@ int main
+   int scfilter_level = 0, lock_memory = 0, sched_priority = 0;
+   int clock_control = 1, system_log = 1, log_severity = LOGS_INFO;
+   int user_check = 1, config_args = 0, print_config = 0;
++  int uid = -1, gid = -1;
+ 
+   do_platform_checks();
+ 
+@@ -458,7 +459,7 @@ int main
+   optind = 1;
+ 
+   /* Parse short command-line options */
+-  while ((opt = getopt(argc, argv, "46df:F:hl:L:mnpP:qQrRst:u:Uvx")) != -1) {
++  while ((opt = getopt(argc, argv, "46df:F:g:hi:l:L:mnpP:qQrRst:u:Uvx")) != -1) {
+     switch (opt) {
+       case '4':
+       case '6':
+@@ -475,6 +476,12 @@ int main
+       case 'F':
+         scfilter_level = parse_int_arg(optarg);
+         break;
++      case 'g':
++        gid = parse_int_arg(optarg);
++        break;
++      case 'i': // u and U were alredy used, so i for id
++        uid = parse_int_arg(optarg);
++        break;
+       case 'l':
+         log_file = optarg;
+         break;
+@@ -583,9 +590,13 @@ int main
+   if (!user)
+     user = CNF_GetUser();
+ 
+-  pw = getpwnam(user);
+-  if (!pw)
+-    LOG_FATAL("Could not get user/group ID of %s", user);
++  if (uid != -1 && gid != -1) {
++    pw = &(struct passwd) { .pw_uid = uid, .pw_gid = gid };
++  } else {
++    pw = getpwnam(user);
++    if (!pw)
++      LOG_FATAL("Could not get user/group ID of %s", user);
++  }
+ 
+   /* Create directories for sockets, log files, and dump files */
+   CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
+-- 
+2.25.1
+