xref: /netbsd-src/external/bsd/ntp/dist/sntp/networking.c (revision cdfa2a7ef92791ba9db70a584a1d904730e6fb46)
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