Example #1
0
static void
read_from_cmd_socket(int sock_fd, int event, void *anything)
{
  CMD_Request rx_message;
  CMD_Reply tx_message;
  int status, read_length, expected_length, rx_message_length;
  int localhost, allowed, log_index;
  union sockaddr_all where_from;
  socklen_t from_length;
  IPAddr remote_ip;
  unsigned short remote_port, rx_command;
  struct timeval now, cooked_now;

  rx_message_length = sizeof(rx_message);
  from_length = sizeof(where_from);

  status = recvfrom(sock_fd, (char *)&rx_message, rx_message_length, 0,
                    &where_from.sa, &from_length);

  if (status < 0) {
    LOG(LOGS_WARN, LOGF_CmdMon, "Error [%s] reading from control socket %d",
        strerror(errno), sock_fd);
    return;
  }

  if (from_length > sizeof (where_from) ||
      from_length <= sizeof (where_from.sa.sa_family)) {
    DEBUG_LOG(LOGF_CmdMon, "Read command packet without source address");
    return;
  }

  read_length = status;

  /* Get current time cheaply */
  SCH_GetLastEventTime(&cooked_now, NULL, &now);

  UTI_SockaddrToIPAndPort(&where_from.sa, &remote_ip, &remote_port);

  /* Check if it's from localhost (127.0.0.1, ::1, or Unix domain) */
  switch (remote_ip.family) {
    case IPADDR_INET4:
      assert(sock_fd == sock_fd4);
      localhost = remote_ip.addr.in4 == INADDR_LOOPBACK;
      break;
#ifdef FEAT_IPV6
    case IPADDR_INET6:
      assert(sock_fd == sock_fd6);
      localhost = !memcmp(remote_ip.addr.in6, &in6addr_loopback,
                          sizeof (in6addr_loopback));
      break;
#endif
    case IPADDR_UNSPEC:
      /* This should be the Unix domain socket */
      if (where_from.sa.sa_family != AF_UNIX)
        return;
      assert(sock_fd == sock_fdu);
      localhost = 1;
      break;
    default:
      assert(0);
  }

  DEBUG_LOG(LOGF_CmdMon, "Received %d bytes from %s fd %d",
            status, UTI_SockaddrToString(&where_from.sa), sock_fd);

  if (!(localhost || ADF_IsAllowed(access_auth_table, &remote_ip))) {
    /* The client is not allowed access, so don't waste any more time
       on him.  Note that localhost is always allowed access
       regardless of the defined access rules - otherwise, we could
       shut ourselves out completely! */
    return;
  }

  if (read_length < offsetof(CMD_Request, data) ||
      read_length < offsetof(CMD_Reply, data) ||
      rx_message.pkt_type != PKT_TYPE_CMD_REQUEST ||
      rx_message.res1 != 0 ||
      rx_message.res2 != 0) {

    /* We don't know how to process anything like this or an error reply
       would be larger than the request */
    DEBUG_LOG(LOGF_CmdMon, "Command packet dropped");
    return;
  }

  expected_length = PKL_CommandLength(&rx_message);
  rx_command = ntohs(rx_message.command);

  tx_message.version = PROTO_VERSION_NUMBER;
  tx_message.pkt_type = PKT_TYPE_CMD_REPLY;
  tx_message.res1 = 0;
  tx_message.res2 = 0;
  tx_message.command = rx_message.command;
  tx_message.reply = htons(RPY_NULL);
  tx_message.status = htons(STT_SUCCESS);
  tx_message.pad1 = 0;
  tx_message.pad2 = 0;
  tx_message.pad3 = 0;
  tx_message.sequence = rx_message.sequence;
  tx_message.pad4 = 0;
  tx_message.pad5 = 0;

  if (rx_message.version != PROTO_VERSION_NUMBER) {
    DEBUG_LOG(LOGF_CmdMon, "Command packet has invalid version (%d != %d)",
              rx_message.version, PROTO_VERSION_NUMBER);

    if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) {
      tx_message.status = htons(STT_BADPKTVERSION);
      transmit_reply(&tx_message, &where_from);
    }
    return;
  }

  if (rx_command >= N_REQUEST_TYPES ||
      expected_length < (int)offsetof(CMD_Request, data)) {
    DEBUG_LOG(LOGF_CmdMon, "Command packet has invalid command %d", rx_command);

    tx_message.status = htons(STT_INVALID);
    transmit_reply(&tx_message, &where_from);
    return;
  }

  if (read_length < expected_length) {
    DEBUG_LOG(LOGF_CmdMon, "Command packet is too short (%d < %d)", read_length,
              expected_length);

    tx_message.status = htons(STT_BADPKTLENGTH);
    transmit_reply(&tx_message, &where_from);
    return;
  }

  /* OK, we have a valid message.  Now dispatch on message type and process it. */

  log_index = CLG_LogCommandAccess(&remote_ip, &cooked_now);

  /* Don't reply to all requests from hosts other than localhost if the rate
     is excessive */
  if (!localhost && log_index >= 0 && CLG_LimitCommandResponseRate(log_index)) {
      DEBUG_LOG(LOGF_CmdMon, "Command packet discarded to limit response rate");
      return;
  }

  if (rx_command >= N_REQUEST_TYPES) {
    /* This should be already handled */
    assert(0);
  } else {
    /* Check level of authority required to issue the command.  All commands
       from the Unix domain socket (which is accessible only by the root and
       chrony user/group) are allowed. */
    if (where_from.sa.sa_family == AF_UNIX) {
      assert(sock_fd == sock_fdu);
      allowed = 1;
    } else {
      switch (permissions[rx_command]) {
        case PERMIT_AUTH:
          allowed = 0;
          break;
        case PERMIT_LOCAL:
          allowed = localhost;
          break;
        case PERMIT_OPEN:
          allowed = 1;
          break;
        default:
          assert(0);
          allowed = 0;
      }
    }

    if (allowed) {
      switch(rx_command) {
        case REQ_NULL:
          /* Do nothing */
          break;

        case REQ_DUMP:
          handle_dump(&rx_message, &tx_message);
          break;

        case REQ_ONLINE:
          handle_online(&rx_message, &tx_message);
          break;

        case REQ_OFFLINE:
          handle_offline(&rx_message, &tx_message);
          break;

        case REQ_BURST:
          handle_burst(&rx_message, &tx_message);
          break;

        case REQ_MODIFY_MINPOLL:
          handle_modify_minpoll(&rx_message, &tx_message);
          break;

        case REQ_MODIFY_MAXPOLL:
          handle_modify_maxpoll(&rx_message, &tx_message);
          break;

        case REQ_MODIFY_MAXDELAY:
          handle_modify_maxdelay(&rx_message, &tx_message);
          break;

        case REQ_MODIFY_MAXDELAYRATIO:
          handle_modify_maxdelayratio(&rx_message, &tx_message);
          break;

        case REQ_MODIFY_MAXDELAYDEVRATIO:
          handle_modify_maxdelaydevratio(&rx_message, &tx_message);
          break;

        case REQ_MODIFY_MAXUPDATESKEW:
          handle_modify_maxupdateskew(&rx_message, &tx_message);
          break;

        case REQ_MODIFY_MAKESTEP:
          handle_modify_makestep(&rx_message, &tx_message);
          break;

        case REQ_LOGON:
          /* Authentication is no longer supported, log-on always fails */
          tx_message.status = htons(STT_FAILED);
          break;

        case REQ_SETTIME:
          handle_settime(&rx_message, &tx_message);
          break;
        
        case REQ_LOCAL2:
          handle_local(&rx_message, &tx_message);
          break;

        case REQ_MANUAL:
          handle_manual(&rx_message, &tx_message);
          break;

        case REQ_N_SOURCES:
          handle_n_sources(&rx_message, &tx_message);
          break;

        case REQ_SOURCE_DATA:
          handle_source_data(&rx_message, &tx_message);
          break;

        case REQ_REKEY:
          handle_rekey(&rx_message, &tx_message);
          break;

        case REQ_ALLOW:
          handle_allowdeny(&rx_message, &tx_message, 1, 0);
          break;

        case REQ_ALLOWALL:
          handle_allowdeny(&rx_message, &tx_message, 1, 1);
          break;

        case REQ_DENY:
          handle_allowdeny(&rx_message, &tx_message, 0, 0);
          break;

        case REQ_DENYALL:
          handle_allowdeny(&rx_message, &tx_message, 0, 1);
          break;

        case REQ_CMDALLOW:
          handle_cmdallowdeny(&rx_message, &tx_message, 1, 0);
          break;

        case REQ_CMDALLOWALL:
          handle_cmdallowdeny(&rx_message, &tx_message, 1, 1);
          break;

        case REQ_CMDDENY:
          handle_cmdallowdeny(&rx_message, &tx_message, 0, 0);
          break;

        case REQ_CMDDENYALL:
          handle_cmdallowdeny(&rx_message, &tx_message, 0, 1);
          break;

        case REQ_ACCHECK:
          handle_accheck(&rx_message, &tx_message);
          break;

        case REQ_CMDACCHECK:
          handle_cmdaccheck(&rx_message, &tx_message);
          break;

        case REQ_ADD_SERVER:
          handle_add_source(NTP_SERVER, &rx_message, &tx_message);
          break;

        case REQ_ADD_PEER:
          handle_add_source(NTP_PEER, &rx_message, &tx_message);
          break;

        case REQ_DEL_SOURCE:
          handle_del_source(&rx_message, &tx_message);
          break;

        case REQ_WRITERTC:
          handle_writertc(&rx_message, &tx_message);
          break;
          
        case REQ_DFREQ:
          handle_dfreq(&rx_message, &tx_message);
          break;

        case REQ_DOFFSET:
          handle_doffset(&rx_message, &tx_message);
          break;

        case REQ_TRACKING:
          handle_tracking(&rx_message, &tx_message);
          break;

        case REQ_SMOOTHING:
          handle_smoothing(&rx_message, &tx_message);
          break;

        case REQ_SMOOTHTIME:
          handle_smoothtime(&rx_message, &tx_message);
          break;

        case REQ_SOURCESTATS:
          handle_sourcestats(&rx_message, &tx_message);
          break;

        case REQ_RTCREPORT:
          handle_rtcreport(&rx_message, &tx_message);
          break;
          
        case REQ_TRIMRTC:
          handle_trimrtc(&rx_message, &tx_message);
          break;

        case REQ_CYCLELOGS:
          handle_cyclelogs(&rx_message, &tx_message);
          break;

        case REQ_CLIENT_ACCESSES_BY_INDEX2:
          handle_client_accesses_by_index(&rx_message, &tx_message);
          break;

        case REQ_MANUAL_LIST:
          handle_manual_list(&rx_message, &tx_message);
          break;

        case REQ_MANUAL_DELETE:
          handle_manual_delete(&rx_message, &tx_message);
          break;

        case REQ_MAKESTEP:
          handle_make_step(&rx_message, &tx_message);
          break;

        case REQ_ACTIVITY:
          handle_activity(&rx_message, &tx_message);
          break;

        case REQ_RESELECTDISTANCE:
          handle_reselect_distance(&rx_message, &tx_message);
          break;

        case REQ_RESELECT:
          handle_reselect(&rx_message, &tx_message);
          break;

        case REQ_MODIFY_MINSTRATUM:
          handle_modify_minstratum(&rx_message, &tx_message);
          break;

        case REQ_MODIFY_POLLTARGET:
          handle_modify_polltarget(&rx_message, &tx_message);
          break;

        case REQ_REFRESH:
          handle_refresh(&rx_message, &tx_message);
          break;

        case REQ_SERVER_STATS:
          handle_server_stats(&rx_message, &tx_message);
          break;

        default:
          DEBUG_LOG(LOGF_CmdMon, "Unhandled command %d", rx_command);
          tx_message.status = htons(STT_FAILED);
          break;
      }
    } else {
      tx_message.status = htons(STT_UNAUTH);
    }
  }

  /* Transmit the response */
  {
    /* Include a simple way to lose one message in three to test resend */

    static int do_it=1;

    if (do_it) {
      transmit_reply(&tx_message, &where_from);
    }

#if 0
    do_it = ((do_it + 1) % 3);
#endif
  }
}
Example #2
0
void
test_unit(void)
{
  int i, j, index;
  struct timespec ts;
  IPAddr ip;
  char conf[][100] = {
    "clientloglimit 10000",
    "ratelimit interval 3 burst 4 leak 3",
    "cmdratelimit interval 3 burst 4 leak 3",
  };

  CNF_Initialise(0, 0);
  for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
    CNF_ParseLine(NULL, i + 1, conf[i]);

  CLG_Initialise();

  TEST_CHECK(ARR_GetSize(records) == 16);

  for (i = 0; i < 500; i++) {
    DEBUG_LOG("iteration %d", i);

    ts.tv_sec = (time_t)random() & 0x0fffffff;
    ts.tv_nsec = 0;

    for (j = 0; j < 1000; j++) {
      TST_GetRandomAddress(&ip, IPADDR_UNSPEC, i % 8 ? -1 : i / 8 % 9);
      DEBUG_LOG("address %s", UTI_IPToString(&ip));

      if (random() % 2) {
        index = CLG_LogNTPAccess(&ip, &ts);
        TEST_CHECK(index >= 0);
        CLG_LimitNTPResponseRate(index);
      } else {
        index = CLG_LogCommandAccess(&ip, &ts);
        TEST_CHECK(index >= 0);
        CLG_LimitCommandResponseRate(index);
      }

      UTI_AddDoubleToTimespec(&ts, (1 << random() % 14) / 100.0, &ts);
    }
  }

  DEBUG_LOG("records %u", ARR_GetSize(records));
  TEST_CHECK(ARR_GetSize(records) == 64);

  for (i = j = 0; i < 10000; i++) {
    ts.tv_sec += 1;
    index = CLG_LogNTPAccess(&ip, &ts);
    TEST_CHECK(index >= 0);
    if (!CLG_LimitNTPResponseRate(index))
      j++;
  }

  DEBUG_LOG("requests %d responses %d", i, j);
  TEST_CHECK(j * 4 < i && j * 6 > i);

  CLG_Finalise();
  CNF_Finalise();
}