diff options
Diffstat (limited to 'contrib/ntp/sntp/networking.c')
-rw-r--r-- | contrib/ntp/sntp/networking.c | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/contrib/ntp/sntp/networking.c b/contrib/ntp/sntp/networking.c new file mode 100644 index 0000000..bef7352 --- /dev/null +++ b/contrib/ntp/sntp/networking.c @@ -0,0 +1,287 @@ +#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; + } + /* Note: pkt_len must be a multiple of 4 at this point! */ + packet_end = (u_int32*)((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 MAC at all */ + break; + + case 1: /* crypto NAK */ + key_id = ntohl(*exten_end); + printf("Crypto NAK = 0x%08x\n", key_id); + 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((char *)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; +} |