client: randomize sequence number in requests

Don't rely on random source port of a connected socket alone as a
protection against spoofed packets in chronyc. Generate a fully random
32-bit sequence number for each request and modify the code to not send
a new request until the timeout expires or a valid response is received.
For a monitoring protocol this should be more than good enough.
This commit is contained in:
Miroslav Lichvar 2016-11-11 17:20:38 +01:00
parent 07aa54b183
commit 74f581e7ab

View file

@ -1263,7 +1263,6 @@ give_help(void)
/* ================================================== */ /* ================================================== */
static unsigned long sequence = 0;
static int max_retries = 2; static int max_retries = 2;
static int initial_timeout = 1000; static int initial_timeout = 1000;
static int proto_version = PROTO_VERSION_NUMBER; static int proto_version = PROTO_VERSION_NUMBER;
@ -1276,7 +1275,6 @@ static int proto_version = PROTO_VERSION_NUMBER;
static int static int
submit_request(CMD_Request *request, CMD_Reply *reply) submit_request(CMD_Request *request, CMD_Reply *reply)
{ {
unsigned long tx_sequence;
int bad_length, bad_sequence, bad_header; int bad_length, bad_sequence, bad_header;
int select_status; int select_status;
int recv_status; int recv_status;
@ -1284,31 +1282,43 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
int expected_length; int expected_length;
int command_length; int command_length;
int padding_length; int padding_length;
struct timespec ts_now, ts_start;
struct timeval tv; struct timeval tv;
int timeout; int n_attempts, new_attempt;
int n_attempts; double timeout;
fd_set rdfd, wrfd, exfd; fd_set rdfd, wrfd, exfd;
request->pkt_type = PKT_TYPE_CMD_REQUEST; request->pkt_type = PKT_TYPE_CMD_REQUEST;
request->res1 = 0; request->res1 = 0;
request->res2 = 0; request->res2 = 0;
tx_sequence = sequence++;
request->sequence = htonl(tx_sequence);
request->pad1 = 0; request->pad1 = 0;
request->pad2 = 0; request->pad2 = 0;
timeout = initial_timeout;
n_attempts = 0; n_attempts = 0;
new_attempt = 1;
do { do {
request->version = proto_version; if (new_attempt) {
new_attempt = 0;
if (n_attempts > max_retries)
return 0;
if (gettimeofday(&tv, NULL))
return 0;
UTI_TimevalToTimespec(&tv, &ts_start);
UTI_GetRandomBytes(&request->sequence, sizeof (request->sequence));
request->attempt = htons(n_attempts); request->attempt = htons(n_attempts);
request->version = proto_version;
command_length = PKL_CommandLength(request); command_length = PKL_CommandLength(request);
padding_length = PKL_CommandPaddingLength(request); padding_length = PKL_CommandPaddingLength(request);
assert(command_length > 0 && command_length > padding_length); assert(command_length > 0 && command_length > padding_length);
/* Zero the padding to avoid sending uninitialized data */ n_attempts++;
/* Zero the padding to not send any uninitialized data */
memset(((char *)request) + command_length - padding_length, 0, padding_length); memset(((char *)request) + command_length - padding_length, 0, padding_length);
if (sock_fd < 0) { if (sock_fd < 0) {
@ -1323,10 +1333,21 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
} }
DEBUG_LOG(LOGF_Client, "Sent %d bytes", command_length); DEBUG_LOG(LOGF_Client, "Sent %d bytes", command_length);
}
tv.tv_sec = timeout / 1000; if (gettimeofday(&tv, NULL))
tv.tv_usec = timeout % 1000 * 1000; return 0;
timeout *= 2;
UTI_TimevalToTimespec(&tv, &ts_now);
/* Check if the clock wasn't stepped back */
if (UTI_CompareTimespecs(&ts_now, &ts_start) < 0)
ts_start = ts_now;
timeout = initial_timeout / 1000.0 * (1U << (n_attempts - 1)) -
UTI_DiffTimespecsToDouble(&ts_now, &ts_start);
UTI_DoubleToTimeval(timeout, &tv);
DEBUG_LOG(LOGF_Client, "Timeout %f seconds", timeout);
FD_ZERO(&rdfd); FD_ZERO(&rdfd);
FD_ZERO(&wrfd); FD_ZERO(&wrfd);
@ -1343,14 +1364,7 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
DEBUG_LOG(LOGF_Client, "select failed : %s", strerror(errno)); DEBUG_LOG(LOGF_Client, "select failed : %s", strerror(errno));
} else if (select_status == 0) { } else if (select_status == 0) {
/* Timeout must have elapsed, try a resend? */ /* Timeout must have elapsed, try a resend? */
n_attempts ++; new_attempt = 1;
if (n_attempts > max_retries) {
return 0;
}
/* Back to top of loop to do resend */
continue;
} else { } else {
recv_status = recv(sock_fd, (void *)reply, sizeof(CMD_Reply), 0); recv_status = recv(sock_fd, (void *)reply, sizeof(CMD_Reply), 0);
@ -1358,11 +1372,7 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
/* If we get connrefused here, it suggests the sendto is /* If we get connrefused here, it suggests the sendto is
going to a dead port */ going to a dead port */
DEBUG_LOG(LOGF_Client, "Could not receive : %s", strerror(errno)); DEBUG_LOG(LOGF_Client, "Could not receive : %s", strerror(errno));
new_attempt = 1;
n_attempts++;
if (n_attempts > max_retries) {
return 0;
}
} else { } else {
DEBUG_LOG(LOGF_Client, "Received %d bytes", recv_status); DEBUG_LOG(LOGF_Client, "Received %d bytes", recv_status);
@ -1377,16 +1387,12 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
expected_length < offsetof(CMD_Reply, data)); expected_length < offsetof(CMD_Reply, data));
if (!bad_length) { if (!bad_length) {
bad_sequence = (ntohl(reply->sequence) != tx_sequence); bad_sequence = reply->sequence != request->sequence;
} else { } else {
bad_sequence = 0; bad_sequence = 0;
} }
if (bad_length || bad_sequence) { if (bad_length || bad_sequence) {
n_attempts++;
if (n_attempts > max_retries) {
return 0;
}
continue; continue;
} }
@ -1399,10 +1405,6 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
(reply->command != request->command)); (reply->command != request->command));
if (bad_header) { if (bad_header) {
n_attempts++;
if (n_attempts > max_retries) {
return 0;
}
continue; continue;
} }
@ -1413,6 +1415,8 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
if (proto_version == PROTO_VERSION_NUMBER && if (proto_version == PROTO_VERSION_NUMBER &&
reply->version == PROTO_VERSION_NUMBER - 1) { reply->version == PROTO_VERSION_NUMBER - 1) {
proto_version = PROTO_VERSION_NUMBER - 1; proto_version = PROTO_VERSION_NUMBER - 1;
n_attempts--;
new_attempt = 1;
continue; continue;
} }
#else #else
@ -1420,9 +1424,8 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
#endif #endif
/* Good packet received, print out results */ /* Good packet received, print out results */
DEBUG_LOG(LOGF_Client, "Reply cmd=%d reply=%d stat=%d seq=%d", DEBUG_LOG(LOGF_Client, "Reply cmd=%d reply=%d stat=%d",
ntohs(reply->command), ntohs(reply->reply), ntohs(reply->status), ntohs(reply->command), ntohs(reply->reply), ntohs(reply->status));
ntohl(reply->sequence));
break; break;
} }
} }