X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Ftimesync%2Ftimesyncd.c;h=19e6d67c63d6203c9221455420852b3eb5ce97cc;hp=6e38a2cb89c6162a9ef1b3b03dc10bee918140d4;hb=f864fd1b4b4546244dfb59bb59196598742fa29c;hpb=e8af69739a629708d2c155ebcd4fa57b8fd5006a diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c index 6e38a2cb8..19e6d67c6 100644 --- a/src/timesync/timesyncd.c +++ b/src/timesync/timesyncd.c @@ -33,6 +33,9 @@ #include #include #include +#include +#include +#include #include "missing.h" #include "util.h" @@ -46,6 +49,10 @@ #include "sd-event.h" #include "sd-resolve.h" #include "sd-daemon.h" +#include "sd-network.h" +#include "event-util.h" +#include "network-util.h" +#include "capability.h" #include "timesyncd.h" #define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1) @@ -202,7 +209,7 @@ static int manager_send_request(Manager *m) { return manager_connect(m); } - /* re-arm timer with incresing timeout, in case the packets never arrive back */ + /* re-arm timer with increasing timeout, in case the packets never arrive back */ if (m->retry_interval > 0) { if (m->retry_interval < NTP_POLL_INTERVAL_MAX_SEC * USEC_PER_SEC) m->retry_interval *= 2; @@ -483,7 +490,6 @@ static bool sockaddr_equal(union sockaddr_union *a, union sockaddr_union *b) { } static int manager_receive_response(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - _cleanup_free_ char *pretty = NULL; Manager *m = userdata; struct ntp_msg ntpmsg; @@ -573,12 +579,18 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re m->event_timeout = sd_event_source_unref(m->event_timeout); + if (be32toh(ntpmsg.recv_time.sec) < TIME_EPOCH + OFFSET_1900_1970 || + be32toh(ntpmsg.trans_time.sec) < TIME_EPOCH + OFFSET_1900_1970) { + log_debug("Invalid reply, returned times before epoch. Ignoring."); + return manager_connect(m); + } + if (NTP_FIELD_LEAP(ntpmsg.field) == NTP_LEAP_NOTINSYNC) { log_debug("Server is not synchronized. Disconnecting."); return manager_connect(m); } - if (NTP_FIELD_VERSION(ntpmsg.field) != 4 && NTP_FIELD_VERSION(ntpmsg.field) != 3) { + if (!IN_SET(NTP_FIELD_VERSION(ntpmsg.field), 3, 4)) { log_debug("Response NTPv%d. Disconnecting.", NTP_FIELD_VERSION(ntpmsg.field)); return manager_connect(m); } @@ -661,9 +673,8 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re log_error("Failed to call clock_adjtime(): %m"); } - sockaddr_pretty(&m->current_server_address->sockaddr.sa, m->current_server_address->socklen, true, &pretty); - log_info("%s (%s): interval/delta/delay/jitter/drift %llus/%+.3fs/%.3fs/%.3fs/%+ippm%s", - strna(pretty), m->current_server_name->string, m->poll_interval_usec / USEC_PER_SEC, offset, delay, m->samples_jitter, m->drift_ppm, + log_info("interval/delta/delay/jitter/drift %llus/%+.3fs/%.3fs/%.3fs/%+ippm%s", + m->poll_interval_usec / USEC_PER_SEC, offset, delay, m->samples_jitter, m->drift_ppm, spike ? " (ignored)" : ""); r = manager_arm_timer(m, m->poll_interval_usec); @@ -768,7 +779,7 @@ static int manager_resolve_handler(sd_resolve_query *q, int ret, const struct ad m->resolve_query = sd_resolve_query_unref(m->resolve_query); if (ret != 0) { - log_error("Failed to resolve %s: %s", m->current_server_name->string, gai_strerror(ret)); + log_info("Failed to resolve %s: %s", m->current_server_name->string, gai_strerror(ret)); /* Try next host */ return manager_connect(m); @@ -965,8 +976,10 @@ static int manager_new(Manager **ret) { if (r < 0) return r; - sd_event_add_signal(m->event, &m->sigterm, SIGTERM, NULL, NULL); - sd_event_add_signal(m->event, &m->sigint, SIGINT, NULL, NULL); + sd_event_set_watchdog(m->event, true); + + sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL); + sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL); r = sd_resolve_default(&m->resolve); if (r < 0) @@ -974,7 +987,7 @@ static int manager_new(Manager **ret) { r = sd_resolve_attach_event(m->resolve, m->event, 0); if (r < 0) - return 0; + return r; r = manager_clock_watch_setup(m); if (r < 0) @@ -993,11 +1006,11 @@ static void manager_free(Manager *m) { manager_disconnect(m); manager_flush_names(m); - sd_event_source_unref(m->sigint); - sd_event_source_unref(m->sigterm); - sd_event_source_unref(m->event_retry); + sd_event_source_unref(m->network_event_source); + sd_network_monitor_unref(m->network_monitor); + sd_resolve_unref(m->resolve); sd_event_unref(m->event); @@ -1052,6 +1065,156 @@ static int manager_parse_config_file(Manager *m) { return r; } +static bool network_is_online(void) { + _cleanup_free_ char *state = NULL; + int r; + + r = sd_network_get_operational_state(&state); + if (r >= 0 && streq("carrier", state)) + return true; + else + return false; +} + +static int manager_network_event_handler(sd_event_source *s, int fd, uint32_t revents, + void *userdata) { + Manager *m = userdata; + bool connected, online; + int r; + + assert(m); + + /* check if the machine is online */ + online = network_is_online(); + + /* check if the client is currently connected */ + connected = (m->server_socket != -1); + + if (connected && !online) { + log_info("No network connectivity. Suspending."); + manager_disconnect(m); + } else if (!connected && online) { + log_info("Network connectivity detected. Resuming."); + if (m->current_server_address) { + r = manager_begin(m); + if (r < 0) + return r; + } else { + r = manager_connect(m); + if (r < 0) + return r; + } + } + + sd_network_monitor_flush(m->network_monitor); + + return 0; +} + +static int manager_network_monitor_listen(Manager *m) { + _cleanup_event_source_unref_ sd_event_source *event_source = NULL; + _cleanup_network_monitor_unref_ sd_network_monitor *monitor = NULL; + int r, fd, events; + + r = sd_network_monitor_new(NULL, &monitor); + if (r < 0) + return r; + + fd = sd_network_monitor_get_fd(monitor); + if (fd < 0) + return fd; + + events = sd_network_monitor_get_events(monitor); + if (events < 0) + return events; + + r = sd_event_add_io(m->event, &event_source, fd, events, + &manager_network_event_handler, m); + if (r < 0) + return r; + + m->network_monitor = monitor; + m->network_event_source = event_source; + monitor = NULL; + event_source = NULL; + + return 0; +} + +static int drop_priviliges(void) { + static const cap_value_t bits[] = { + CAP_SYS_TIME, + }; + + _cleanup_cap_free_ cap_t d = NULL; + const char *name = "systemd-timesync"; + uid_t uid; + gid_t gid; + int r; + + /* Unfortunately we cannot leave privilige dropping to PID 1 + * here, since we want to run as user but want to keep te + * CAP_SYS_TIME capability. Since file capabilities have been + * introduced this cannot be done across exec() anymore, + * unless our binary has the capability configured in the file + * system, which we want to avoid. */ + + r = get_user_creds(&name, &uid, &gid, NULL, NULL); + if (r < 0) { + log_error("Cannot resolve user name %s: %s", name, strerror(-r)); + return r; + } + + if (setresgid(gid, gid, gid) < 0) { + log_error("Failed change group ID: %m"); + return -errno; + } + + if (setgroups(0, NULL) < 0) { + log_error("Failed to drop auxiliary groups list: %m"); + return -errno; + } + + if (prctl(PR_SET_KEEPCAPS, 1) < 0) { + log_error("Failed to enable keep capabilities flag: %m"); + return -errno; + } + + r = setresuid(uid, uid, uid); + if (r < 0) { + log_error("Failed change user ID: %m"); + return -errno; + } + + if (prctl(PR_SET_KEEPCAPS, 0) < 0) { + log_error("Failed to disable keep capabilities flag: %m"); + return -errno; + } + + r = capability_bounding_set_drop(~(1ULL << CAP_SYS_TIME), true); + if (r < 0) { + log_error("Failed to drop capabilities: %s", strerror(-r)); + return r; + } + + d = cap_init(); + if (!d) + return log_oom(); + + if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 || + cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0) { + log_error("Failed to enable capabilities bits: %m"); + return -errno; + } + + if (cap_set_proc(d) < 0) { + log_error("Failed to increase capabilities: %m"); + return -errno; + } + + return 0; +} + int main(int argc, char *argv[]) { _cleanup_manager_free_ Manager *m = NULL; int r; @@ -1068,6 +1231,10 @@ int main(int argc, char *argv[]) { umask(0022); + r = drop_priviliges(); + if (r < 0) + goto out; + assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0); r = manager_new(&m); @@ -1079,12 +1246,20 @@ int main(int argc, char *argv[]) { manager_add_server_string(m, NTP_SERVERS); manager_parse_config_file(m); + r = manager_network_monitor_listen(m); + if (r < 0) { + log_error("Failed to listen to networkd events: %s", strerror(-r)); + goto out; + } + log_debug("systemd-timesyncd running as pid %lu", (unsigned long) getpid()); sd_notify(false, "READY=1"); - r = manager_connect(m); - if (r < 0) - goto out; + if (network_is_online()) { + r = manager_connect(m); + if (r < 0) + goto out; + } r = sd_event_loop(m->event); if (r < 0) {