292 lines
7.3 KiB
C
292 lines
7.3 KiB
C
#include <config.h>
|
|
#include "networking.h"
|
|
#include "ntp_debug.h"
|
|
|
|
|
|
/* Send a packet */
|
|
int
|
|
sendpkt (
|
|
SOCKET rsock,
|
|
sockaddr_u *dest,
|
|
struct pkt *pkt,
|
|
int len
|
|
)
|
|
{
|
|
int cc;
|
|
|
|
#ifdef DEBUG
|
|
if (debug > 2) {
|
|
printf("sntp sendpkt: Packet data:\n");
|
|
pkt_output(pkt, len, stdout);
|
|
}
|
|
#endif
|
|
TRACE(1, ("sntp sendpkt: Sending packet to %s ...\n",
|
|
sptoa(dest)));
|
|
|
|
cc = sendto(rsock, (void *)pkt, len, 0, &dest->sa,
|
|
SOCKLEN(dest));
|
|
if (cc == SOCKET_ERROR) {
|
|
msyslog(LOG_ERR, "Send to %s failed, %m",
|
|
sptoa(dest));
|
|
return FALSE;
|
|
}
|
|
TRACE(1, ("Packet sent.\n"));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* Receive raw data */
|
|
int
|
|
recvdata(
|
|
SOCKET rsock,
|
|
sockaddr_u * sender,
|
|
void * rdata,
|
|
int rdata_length
|
|
)
|
|
{
|
|
GETSOCKNAME_SOCKLEN_TYPE slen;
|
|
int recvc;
|
|
|
|
slen = sizeof(*sender);
|
|
recvc = recvfrom(rsock, rdata, rdata_length, 0,
|
|
&sender->sa, &slen);
|
|
if (recvc < 0)
|
|
return recvc;
|
|
#ifdef DEBUG
|
|
if (debug > 2) {
|
|
printf("Received %d bytes from %s:\n", recvc, sptoa(sender));
|
|
pkt_output((struct pkt *)rdata, recvc, stdout);
|
|
}
|
|
#endif
|
|
return recvc;
|
|
}
|
|
|
|
/* Parsing from a short 'struct pkt' directly is bound to create
|
|
* coverity warnings. These are hard to avoid, as the formal declaration
|
|
* does not reflect the true layout in the presence of autokey extension
|
|
* fields. Parsing and skipping the extension fields of a received packet
|
|
* until there's only the MAC left is better done in this separate
|
|
* function.
|
|
*/
|
|
static void*
|
|
skip_efields(
|
|
u_int32 *head, /* head of extension chain */
|
|
u_int32 *tail /* tail/end of extension chain */
|
|
)
|
|
{
|
|
|
|
u_int nlen; /* next extension length */
|
|
while ((tail - head) > 6) {
|
|
nlen = ntohl(*head++) & 0xffff;
|
|
nlen = (nlen + 3) >> 2;
|
|
if (nlen > (u_int)(tail - head) || nlen < 4)
|
|
return NULL; /* Blooper! Inconsistent! */
|
|
head += nlen;
|
|
}
|
|
return head;
|
|
}
|
|
|
|
/*
|
|
** Check if it's data for us and whether it's useable or not.
|
|
**
|
|
** If not, return a failure code so we can delete this server from our list
|
|
** and continue with another one.
|
|
*/
|
|
int
|
|
process_pkt (
|
|
struct pkt *rpkt,
|
|
sockaddr_u *sender,
|
|
int pkt_len,
|
|
int mode,
|
|
struct pkt *spkt,
|
|
const char * func_name
|
|
)
|
|
{
|
|
u_int key_id;
|
|
struct key * pkt_key;
|
|
int is_authentic;
|
|
int mac_size;
|
|
u_int exten_len;
|
|
u_int32 * exten_end;
|
|
u_int32 * packet_end;
|
|
l_fp sent_xmt;
|
|
l_fp resp_org;
|
|
|
|
// key_id = 0;
|
|
pkt_key = NULL;
|
|
is_authentic = (HAVE_OPT(AUTHENTICATION)) ? 0 : -1;
|
|
|
|
/*
|
|
* Parse the extension field if present. We figure out whether
|
|
* an extension field is present by measuring the MAC size. If
|
|
* the number of words following the packet header is 0, no MAC
|
|
* is present and the packet is not authenticated. If 1, the
|
|
* packet is a crypto-NAK; if 3, the packet is authenticated
|
|
* with DES; if 5, the packet is authenticated with MD5; if 6,
|
|
* the packet is authenticated with SHA. If 2 or 4, the packet
|
|
* is a runt and discarded forthwith. If greater than 6, an
|
|
* extension field is present, so we subtract the length of the
|
|
* field and go around again.
|
|
*/
|
|
if (pkt_len < (int)LEN_PKT_NOMAC || (pkt_len & 3) != 0) {
|
|
msyslog(LOG_ERR,
|
|
"%s: Incredible packet length: %d. Discarding.",
|
|
func_name, pkt_len);
|
|
return PACKET_UNUSEABLE;
|
|
}
|
|
|
|
/* HMS: the following needs a bit of work */
|
|
/* Note: pkt_len must be a multiple of 4 at this point! */
|
|
packet_end = (void*)((char*)rpkt + pkt_len);
|
|
exten_end = skip_efields(rpkt->exten, packet_end);
|
|
if (NULL == exten_end) {
|
|
msyslog(LOG_ERR,
|
|
"%s: Missing extension field. Discarding.",
|
|
func_name);
|
|
return PACKET_UNUSEABLE;
|
|
}
|
|
|
|
/* get size of MAC in cells; can be zero */
|
|
exten_len = (u_int)(packet_end - exten_end);
|
|
|
|
/* deduce action required from remaining length */
|
|
switch (exten_len) {
|
|
|
|
case 0: /* no Legacy MAC */
|
|
break;
|
|
|
|
case 1: /* crypto NAK */
|
|
/* Only if the keyID is 0 and there were no EFs */
|
|
key_id = ntohl(*exten_end);
|
|
printf("Crypto NAK = 0x%08x from %s\n", key_id, stoa(sender));
|
|
break;
|
|
|
|
case 3: /* key ID + 3DES MAC -- unsupported! */
|
|
msyslog(LOG_ERR,
|
|
"%s: Key ID + 3DES MAC is unsupported. Discarding.",
|
|
func_name);
|
|
return PACKET_UNUSEABLE;
|
|
|
|
case 5: /* key ID + MD5 MAC */
|
|
case 6: /* key ID + SHA MAC */
|
|
/*
|
|
** Look for the key used by the server in the specified
|
|
** keyfile and if existent, fetch it or else leave the
|
|
** pointer untouched
|
|
*/
|
|
key_id = ntohl(*exten_end);
|
|
get_key(key_id, &pkt_key);
|
|
if (!pkt_key) {
|
|
printf("unrecognized key ID = 0x%08x\n", key_id);
|
|
break;
|
|
}
|
|
/*
|
|
** Seems like we've got a key with matching keyid.
|
|
**
|
|
** Generate a md5sum of the packet with the key from our
|
|
** keyfile and compare those md5sums.
|
|
*/
|
|
mac_size = exten_len << 2;
|
|
if (!auth_md5(rpkt, pkt_len - mac_size,
|
|
mac_size - 4, pkt_key)) {
|
|
is_authentic = FALSE;
|
|
break;
|
|
}
|
|
/* Yay! Things worked out! */
|
|
is_authentic = TRUE;
|
|
TRACE(1, ("sntp %s: packet from %s authenticated using key id %d.\n",
|
|
func_name, stoa(sender), key_id));
|
|
break;
|
|
|
|
default:
|
|
msyslog(LOG_ERR,
|
|
"%s: Unexpected extension length: %d. Discarding.",
|
|
func_name, exten_len);
|
|
return PACKET_UNUSEABLE;
|
|
}
|
|
|
|
switch (is_authentic) {
|
|
|
|
case -1: /* unknown */
|
|
break;
|
|
|
|
case 0: /* not authentic */
|
|
return SERVER_AUTH_FAIL;
|
|
break;
|
|
|
|
case 1: /* authentic */
|
|
break;
|
|
|
|
default: /* error */
|
|
break;
|
|
}
|
|
|
|
/* Check for server's ntp version */
|
|
if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION ||
|
|
PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) {
|
|
msyslog(LOG_ERR,
|
|
"%s: Packet shows wrong version (%d)",
|
|
func_name, PKT_VERSION(rpkt->li_vn_mode));
|
|
return SERVER_UNUSEABLE;
|
|
}
|
|
/* We want a server to sync with */
|
|
if (PKT_MODE(rpkt->li_vn_mode) != mode &&
|
|
PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) {
|
|
msyslog(LOG_ERR,
|
|
"%s: mode %d stratum %d", func_name,
|
|
PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
|
|
return SERVER_UNUSEABLE;
|
|
}
|
|
/* Stratum is unspecified (0) check what's going on */
|
|
if (STRATUM_PKT_UNSPEC == rpkt->stratum) {
|
|
char *ref_char;
|
|
|
|
TRACE(1, ("%s: Stratum unspecified, going to check for KOD (stratum: %d)\n",
|
|
func_name, rpkt->stratum));
|
|
ref_char = (char *) &rpkt->refid;
|
|
TRACE(1, ("%s: Packet refid: %c%c%c%c\n", func_name,
|
|
ref_char[0], ref_char[1], ref_char[2], ref_char[3]));
|
|
/* If it's a KOD packet we'll just use the KOD information */
|
|
if (ref_char[0] != 'X') {
|
|
if (strncmp(ref_char, "DENY", 4) == 0)
|
|
return KOD_DEMOBILIZE;
|
|
if (strncmp(ref_char, "RSTR", 4) == 0)
|
|
return KOD_DEMOBILIZE;
|
|
if (strncmp(ref_char, "RATE", 4) == 0)
|
|
return KOD_RATE;
|
|
/*
|
|
** There are other interesting kiss codes which
|
|
** might be interesting for authentication.
|
|
*/
|
|
}
|
|
}
|
|
/* If the server is not synced it's not really useable for us */
|
|
if (LEAP_NOTINSYNC == PKT_LEAP(rpkt->li_vn_mode)) {
|
|
msyslog(LOG_ERR,
|
|
"%s: %s not in sync, skipping this server",
|
|
func_name, stoa(sender));
|
|
return SERVER_UNUSEABLE;
|
|
}
|
|
|
|
/*
|
|
* Decode the org timestamp and make sure we're getting a response
|
|
* to our last request, but only if we're not in broadcast mode.
|
|
*/
|
|
if (MODE_BROADCAST == mode)
|
|
return pkt_len;
|
|
|
|
if (!L_ISEQU(&rpkt->org, &spkt->xmt)) {
|
|
NTOHL_FP(&rpkt->org, &resp_org);
|
|
NTOHL_FP(&spkt->xmt, &sent_xmt);
|
|
msyslog(LOG_ERR,
|
|
"%s response org expected to match sent xmt",
|
|
stoa(sender));
|
|
msyslog(LOG_ERR, "resp org: %s", prettydate(&resp_org));
|
|
msyslog(LOG_ERR, "sent xmt: %s", prettydate(&sent_xmt));
|
|
return PACKET_UNUSEABLE;
|
|
}
|
|
|
|
return pkt_len;
|
|
}
|