1*cdfa2a7eSchristos /* $NetBSD: networking.c,v 1.16 2020/05/25 20:47:32 christos Exp $ */
2abb0f93cSkardel
33123f114Skardel #include <config.h>
4abb0f93cSkardel #include "networking.h"
52950cc38Schristos #include "ntp_debug.h"
6abb0f93cSkardel
7abb0f93cSkardel
8abb0f93cSkardel /* Send a packet */
92950cc38Schristos int
sendpkt(SOCKET rsock,sockaddr_u * dest,struct pkt * pkt,int len)10abb0f93cSkardel sendpkt (
11abb0f93cSkardel SOCKET rsock,
12abb0f93cSkardel sockaddr_u *dest,
13abb0f93cSkardel struct pkt *pkt,
14abb0f93cSkardel int len
15abb0f93cSkardel )
16abb0f93cSkardel {
17abb0f93cSkardel int cc;
18abb0f93cSkardel
19abb0f93cSkardel #ifdef DEBUG
202950cc38Schristos if (debug > 2) {
21abb0f93cSkardel printf("sntp sendpkt: Packet data:\n");
22abb0f93cSkardel pkt_output(pkt, len, stdout);
23abb0f93cSkardel }
242950cc38Schristos #endif
252950cc38Schristos TRACE(1, ("sntp sendpkt: Sending packet to %s ...\n",
262950cc38Schristos sptoa(dest)));
27abb0f93cSkardel
282950cc38Schristos cc = sendto(rsock, (void *)pkt, len, 0, &dest->sa,
292950cc38Schristos SOCKLEN(dest));
30abb0f93cSkardel if (cc == SOCKET_ERROR) {
31*cdfa2a7eSchristos msyslog(LOG_ERR, "sendpkt: sendto(%s) failed: %m",
322950cc38Schristos sptoa(dest));
332950cc38Schristos return FALSE;
34abb0f93cSkardel }
352950cc38Schristos TRACE(1, ("Packet sent.\n"));
362950cc38Schristos
372950cc38Schristos return TRUE;
38abb0f93cSkardel }
392950cc38Schristos
40abb0f93cSkardel
41abb0f93cSkardel /* Receive raw data */
42abb0f93cSkardel int
recvdata(SOCKET rsock,sockaddr_u * sender,void * rdata,int rdata_length)43abb0f93cSkardel recvdata(
44abb0f93cSkardel SOCKET rsock,
45abb0f93cSkardel sockaddr_u * sender,
462950cc38Schristos void * rdata,
47abb0f93cSkardel int rdata_length
48abb0f93cSkardel )
49abb0f93cSkardel {
50abb0f93cSkardel GETSOCKNAME_SOCKLEN_TYPE slen;
51abb0f93cSkardel int recvc;
52abb0f93cSkardel
533123f114Skardel slen = sizeof(*sender);
54abb0f93cSkardel recvc = recvfrom(rsock, rdata, rdata_length, 0,
55abb0f93cSkardel &sender->sa, &slen);
562950cc38Schristos if (recvc < 0)
572950cc38Schristos return recvc;
58abb0f93cSkardel #ifdef DEBUG
592950cc38Schristos if (debug > 2) {
602950cc38Schristos printf("Received %d bytes from %s:\n", recvc, sptoa(sender));
61abb0f93cSkardel pkt_output((struct pkt *)rdata, recvc, stdout);
62abb0f93cSkardel }
63abb0f93cSkardel #endif
64abb0f93cSkardel return recvc;
65abb0f93cSkardel }
66abb0f93cSkardel
67ea66d795Schristos /* Parsing from a short 'struct pkt' directly is bound to create
68ea66d795Schristos * coverity warnings. These are hard to avoid, as the formal declaration
69ea66d795Schristos * does not reflect the true layout in the presence of autokey extension
70ea66d795Schristos * fields. Parsing and skipping the extension fields of a received packet
71ea66d795Schristos * until there's only the MAC left is better done in this separate
72ea66d795Schristos * function.
73ea66d795Schristos */
74ea66d795Schristos static void*
skip_efields(u_int32 * head,u_int32 * tail)75ea66d795Schristos skip_efields(
76ea66d795Schristos u_int32 *head, /* head of extension chain */
77ea66d795Schristos u_int32 *tail /* tail/end of extension chain */
78ea66d795Schristos )
79ea66d795Schristos {
80ea66d795Schristos
81ea66d795Schristos u_int nlen; /* next extension length */
82ea66d795Schristos while ((tail - head) > 6) {
83*cdfa2a7eSchristos nlen = ntohl(*head) & 0xffff;
84*cdfa2a7eSchristos ++head;
85ea66d795Schristos nlen = (nlen + 3) >> 2;
861104d021Suebayasi if (nlen > (u_int)(tail - head) || nlen < 4)
87ea66d795Schristos return NULL; /* Blooper! Inconsistent! */
88ea66d795Schristos head += nlen;
89ea66d795Schristos }
90ea66d795Schristos return head;
91ea66d795Schristos }
92abb0f93cSkardel
932950cc38Schristos /*
942950cc38Schristos ** Check if it's data for us and whether it's useable or not.
952950cc38Schristos **
962950cc38Schristos ** If not, return a failure code so we can delete this server from our list
972950cc38Schristos ** and continue with another one.
982950cc38Schristos */
99abb0f93cSkardel int
process_pkt(struct pkt * rpkt,sockaddr_u * sender,int pkt_len,int mode,struct pkt * spkt,const char * func_name)1003123f114Skardel process_pkt (
1013123f114Skardel struct pkt *rpkt,
1022950cc38Schristos sockaddr_u *sender,
1033123f114Skardel int pkt_len,
1043123f114Skardel int mode,
1053123f114Skardel struct pkt *spkt,
106a2545411Skardel const char * func_name
1073123f114Skardel )
1083123f114Skardel {
1092950cc38Schristos u_int key_id;
1102950cc38Schristos struct key * pkt_key;
1112950cc38Schristos int is_authentic;
1123123f114Skardel int mac_size;
1132950cc38Schristos u_int exten_len;
114ea66d795Schristos u_int32 * exten_end;
115ea66d795Schristos u_int32 * packet_end;
1162950cc38Schristos l_fp sent_xmt;
1172950cc38Schristos l_fp resp_org;
1182950cc38Schristos
119af12ab5eSchristos // key_id = 0;
1202950cc38Schristos pkt_key = NULL;
1212950cc38Schristos is_authentic = (HAVE_OPT(AUTHENTICATION)) ? 0 : -1;
1222950cc38Schristos
1233123f114Skardel /*
1243123f114Skardel * Parse the extension field if present. We figure out whether
1253123f114Skardel * an extension field is present by measuring the MAC size. If
1263123f114Skardel * the number of words following the packet header is 0, no MAC
1273123f114Skardel * is present and the packet is not authenticated. If 1, the
1283123f114Skardel * packet is a crypto-NAK; if 3, the packet is authenticated
1293123f114Skardel * with DES; if 5, the packet is authenticated with MD5; if 6,
1303123f114Skardel * the packet is authenticated with SHA. If 2 or 4, the packet
1313123f114Skardel * is a runt and discarded forthwith. If greater than 6, an
1323123f114Skardel * extension field is present, so we subtract the length of the
1333123f114Skardel * field and go around again.
1343123f114Skardel */
135a2545411Skardel if (pkt_len < (int)LEN_PKT_NOMAC || (pkt_len & 3) != 0) {
1362950cc38Schristos msyslog(LOG_ERR,
1372950cc38Schristos "%s: Incredible packet length: %d. Discarding.",
1382950cc38Schristos func_name, pkt_len);
1393123f114Skardel return PACKET_UNUSEABLE;
1403123f114Skardel }
1414eea345dSchristos
1424eea345dSchristos /* HMS: the following needs a bit of work */
143ea66d795Schristos /* Note: pkt_len must be a multiple of 4 at this point! */
1448b8da087Schristos packet_end = (void*)((char*)rpkt + pkt_len);
145ea66d795Schristos exten_end = skip_efields(rpkt->exten, packet_end);
1467476e6e4Schristos if (NULL == exten_end) {
1477476e6e4Schristos msyslog(LOG_ERR,
1487476e6e4Schristos "%s: Missing extension field. Discarding.",
1497476e6e4Schristos func_name);
1507476e6e4Schristos return PACKET_UNUSEABLE;
1517476e6e4Schristos }
1524eea345dSchristos
153ea66d795Schristos /* get size of MAC in cells; can be zero */
154ea66d795Schristos exten_len = (u_int)(packet_end - exten_end);
1553123f114Skardel
156ea66d795Schristos /* deduce action required from remaining length */
157ea66d795Schristos switch (exten_len) {
1582950cc38Schristos
1594eea345dSchristos case 0: /* no Legacy MAC */
1602950cc38Schristos break;
1612950cc38Schristos
162ea66d795Schristos case 1: /* crypto NAK */
1634eea345dSchristos /* Only if the keyID is 0 and there were no EFs */
164ea66d795Schristos key_id = ntohl(*exten_end);
1654eea345dSchristos printf("Crypto NAK = 0x%08x from %s\n", key_id, stoa(sender));
1663123f114Skardel break;
1672950cc38Schristos
168ea66d795Schristos case 3: /* key ID + 3DES MAC -- unsupported! */
1697476e6e4Schristos msyslog(LOG_ERR,
1707476e6e4Schristos "%s: Key ID + 3DES MAC is unsupported. Discarding.",
1717476e6e4Schristos func_name);
1727476e6e4Schristos return PACKET_UNUSEABLE;
173ea66d795Schristos
174ea66d795Schristos case 5: /* key ID + MD5 MAC */
175ea66d795Schristos case 6: /* key ID + SHA MAC */
1762950cc38Schristos /*
1772950cc38Schristos ** Look for the key used by the server in the specified
1782950cc38Schristos ** keyfile and if existent, fetch it or else leave the
1792950cc38Schristos ** pointer untouched
1802950cc38Schristos */
181ea66d795Schristos key_id = ntohl(*exten_end);
1823123f114Skardel get_key(key_id, &pkt_key);
1833123f114Skardel if (!pkt_key) {
1843123f114Skardel printf("unrecognized key ID = 0x%08x\n", key_id);
1853123f114Skardel break;
1863123f114Skardel }
1872950cc38Schristos /*
1882950cc38Schristos ** Seems like we've got a key with matching keyid.
1892950cc38Schristos **
1902950cc38Schristos ** Generate a md5sum of the packet with the key from our
1912950cc38Schristos ** keyfile and compare those md5sums.
1922950cc38Schristos */
193ea66d795Schristos mac_size = exten_len << 2;
19468dbbb44Schristos if (!auth_md5(rpkt, pkt_len - mac_size,
1952950cc38Schristos mac_size - 4, pkt_key)) {
1962950cc38Schristos is_authentic = FALSE;
1973123f114Skardel break;
1983123f114Skardel }
1993123f114Skardel /* Yay! Things worked out! */
2002950cc38Schristos is_authentic = TRUE;
2012950cc38Schristos TRACE(1, ("sntp %s: packet from %s authenticated using key id %d.\n",
2022950cc38Schristos func_name, stoa(sender), key_id));
2033123f114Skardel break;
2042950cc38Schristos
2053123f114Skardel default:
2067476e6e4Schristos msyslog(LOG_ERR,
2077476e6e4Schristos "%s: Unexpected extension length: %d. Discarding.",
2087476e6e4Schristos func_name, exten_len);
2097476e6e4Schristos return PACKET_UNUSEABLE;
2103123f114Skardel }
2112950cc38Schristos
2122950cc38Schristos switch (is_authentic) {
2132950cc38Schristos
2142950cc38Schristos case -1: /* unknown */
2152950cc38Schristos break;
2162950cc38Schristos
2172950cc38Schristos case 0: /* not authentic */
2183123f114Skardel return SERVER_AUTH_FAIL;
2192950cc38Schristos break;
2202950cc38Schristos
2212950cc38Schristos case 1: /* authentic */
2222950cc38Schristos break;
2232950cc38Schristos
2242950cc38Schristos default: /* error */
2252950cc38Schristos break;
2263123f114Skardel }
2272950cc38Schristos
2283123f114Skardel /* Check for server's ntp version */
2293123f114Skardel if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION ||
2303123f114Skardel PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) {
2312950cc38Schristos msyslog(LOG_ERR,
2322950cc38Schristos "%s: Packet shows wrong version (%d)",
2333123f114Skardel func_name, PKT_VERSION(rpkt->li_vn_mode));
2343123f114Skardel return SERVER_UNUSEABLE;
2353123f114Skardel }
2363123f114Skardel /* We want a server to sync with */
2373123f114Skardel if (PKT_MODE(rpkt->li_vn_mode) != mode &&
2383123f114Skardel PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) {
2392950cc38Schristos msyslog(LOG_ERR,
2402950cc38Schristos "%s: mode %d stratum %d", func_name,
2413123f114Skardel PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
2423123f114Skardel return SERVER_UNUSEABLE;
2433123f114Skardel }
2443123f114Skardel /* Stratum is unspecified (0) check what's going on */
2453123f114Skardel if (STRATUM_PKT_UNSPEC == rpkt->stratum) {
2463123f114Skardel char *ref_char;
2472950cc38Schristos
2482950cc38Schristos TRACE(1, ("%s: Stratum unspecified, going to check for KOD (stratum: %d)\n",
2492950cc38Schristos func_name, rpkt->stratum));
2503123f114Skardel ref_char = (char *) &rpkt->refid;
2512950cc38Schristos TRACE(1, ("%s: Packet refid: %c%c%c%c\n", func_name,
2522950cc38Schristos ref_char[0], ref_char[1], ref_char[2], ref_char[3]));
2533123f114Skardel /* If it's a KOD packet we'll just use the KOD information */
2543123f114Skardel if (ref_char[0] != 'X') {
2553123f114Skardel if (strncmp(ref_char, "DENY", 4) == 0)
2563123f114Skardel return KOD_DEMOBILIZE;
2573123f114Skardel if (strncmp(ref_char, "RSTR", 4) == 0)
2583123f114Skardel return KOD_DEMOBILIZE;
2593123f114Skardel if (strncmp(ref_char, "RATE", 4) == 0)
2603123f114Skardel return KOD_RATE;
2612950cc38Schristos /*
2622950cc38Schristos ** There are other interesting kiss codes which
2632950cc38Schristos ** might be interesting for authentication.
2642950cc38Schristos */
2653123f114Skardel }
2663123f114Skardel }
2673123f114Skardel /* If the server is not synced it's not really useable for us */
2683123f114Skardel if (LEAP_NOTINSYNC == PKT_LEAP(rpkt->li_vn_mode)) {
2692950cc38Schristos msyslog(LOG_ERR,
2702950cc38Schristos "%s: %s not in sync, skipping this server",
2712950cc38Schristos func_name, stoa(sender));
2723123f114Skardel return SERVER_UNUSEABLE;
2733123f114Skardel }
2743123f114Skardel
2753123f114Skardel /*
2763123f114Skardel * Decode the org timestamp and make sure we're getting a response
2773123f114Skardel * to our last request, but only if we're not in broadcast mode.
2783123f114Skardel */
2792950cc38Schristos if (MODE_BROADCAST == mode)
2802950cc38Schristos return pkt_len;
2812950cc38Schristos
2822950cc38Schristos if (!L_ISEQU(&rpkt->org, &spkt->xmt)) {
2832950cc38Schristos NTOHL_FP(&rpkt->org, &resp_org);
2842950cc38Schristos NTOHL_FP(&spkt->xmt, &sent_xmt);
2852950cc38Schristos msyslog(LOG_ERR,
2862950cc38Schristos "%s response org expected to match sent xmt",
2872950cc38Schristos stoa(sender));
2882950cc38Schristos msyslog(LOG_ERR, "resp org: %s", prettydate(&resp_org));
2892950cc38Schristos msyslog(LOG_ERR, "sent xmt: %s", prettydate(&sent_xmt));
2903123f114Skardel return PACKET_UNUSEABLE;
2913123f114Skardel }
2923123f114Skardel
2933123f114Skardel return pkt_len;
2943123f114Skardel }
295