1258945Sroberto#include <config.h>
2258945Sroberto#include "networking.h"
3280849Scy#include "ntp_debug.h"
4258945Sroberto
5258945Sroberto
6258945Sroberto/* Send a packet */
7280849Scyint
8258945Srobertosendpkt (
9258945Sroberto	SOCKET rsock,
10258945Sroberto	sockaddr_u *dest,
11258945Sroberto	struct pkt *pkt,
12258945Sroberto	int len
13258945Sroberto	)
14258945Sroberto{
15258945Sroberto	int cc;
16258945Sroberto
17258945Sroberto#ifdef DEBUG
18280849Scy	if (debug > 2) {
19280849Scy		printf("sntp sendpkt: Packet data:\n");
20280849Scy		pkt_output(pkt, len, stdout);
21280849Scy	}
22258945Sroberto#endif
23280849Scy	TRACE(1, ("sntp sendpkt: Sending packet to %s ...\n",
24280849Scy		  sptoa(dest)));
25258945Sroberto
26280849Scy	cc = sendto(rsock, (void *)pkt, len, 0, &dest->sa,
27280849Scy		    SOCKLEN(dest));
28280849Scy	if (cc == SOCKET_ERROR) {
29280849Scy		msyslog(LOG_ERR, "Send to %s failed, %m",
30280849Scy			sptoa(dest));
31280849Scy		return FALSE;
32258945Sroberto	}
33280849Scy	TRACE(1, ("Packet sent.\n"));
34258945Sroberto
35280849Scy	return TRUE;
36258945Sroberto}
37258945Sroberto
38280849Scy
39258945Sroberto/* Receive raw data */
40258945Srobertoint
41258945Srobertorecvdata(
42280849Scy	SOCKET		rsock,
43280849Scy	sockaddr_u *	sender,
44280849Scy	void *		rdata,
45280849Scy	int		rdata_length
46258945Sroberto	)
47258945Sroberto{
48258945Sroberto	GETSOCKNAME_SOCKLEN_TYPE slen;
49258945Sroberto	int recvc;
50258945Sroberto
51258945Sroberto	slen = sizeof(*sender);
52280849Scy	recvc = recvfrom(rsock, rdata, rdata_length, 0,
53258945Sroberto			 &sender->sa, &slen);
54280849Scy	if (recvc < 0)
55280849Scy		return recvc;
56258945Sroberto#ifdef DEBUG
57280849Scy	if (debug > 2) {
58280849Scy		printf("Received %d bytes from %s:\n", recvc, sptoa(sender));
59280849Scy		pkt_output((struct pkt *)rdata, recvc, stdout);
60258945Sroberto	}
61258945Sroberto#endif
62258945Sroberto	return recvc;
63258945Sroberto}
64258945Sroberto
65280849Scy/* Parsing from a short 'struct pkt' directly is bound to create
66280849Scy * coverity warnings. These are hard to avoid, as the formal declaration
67280849Scy * does not reflect the true layout in the presence of autokey extension
68280849Scy * fields. Parsing and skipping the extension fields of a received packet
69280849Scy * until there's only the MAC left is better done in this separate
70280849Scy * function.
71280849Scy */
72280849Scystatic void*
73280849Scyskip_efields(
74280849Scy	u_int32 *head,	/* head of extension chain 	*/
75280849Scy	u_int32 *tail	/* tail/end of extension chain	*/
76258945Sroberto	)
77258945Sroberto{
78280849Scy
79280849Scy	u_int nlen;	/* next extension length */
80280849Scy	while ((tail - head) > 6) {
81280849Scy		nlen = ntohl(*head++) & 0xffff;
82280849Scy		nlen = (nlen + 3) >> 2;
83280849Scy		if (nlen > (u_int)(tail - head) || nlen < 4)
84280849Scy			return NULL;	/* Blooper! Inconsistent! */
85280849Scy		head += nlen;
86258945Sroberto	}
87280849Scy	return head;
88258945Sroberto}
89258945Sroberto
90280849Scy/*
91280849Scy** Check if it's data for us and whether it's useable or not.
92280849Scy**
93280849Scy** If not, return a failure code so we can delete this server from our list
94280849Scy** and continue with another one.
95280849Scy*/
96258945Srobertoint
97258945Srobertoprocess_pkt (
98258945Sroberto	struct pkt *rpkt,
99280849Scy	sockaddr_u *sender,
100258945Sroberto	int pkt_len,
101258945Sroberto	int mode,
102258945Sroberto	struct pkt *spkt,
103280849Scy	const char * func_name
104258945Sroberto	)
105258945Sroberto{
106280849Scy	u_int		key_id;
107280849Scy	struct key *	pkt_key;
108280849Scy	int		is_authentic;
109280849Scy	int		mac_size;
110280849Scy	u_int		exten_len;
111280849Scy	u_int32 *       exten_end;
112280849Scy	u_int32 *       packet_end;
113280849Scy	l_fp		sent_xmt;
114280849Scy	l_fp		resp_org;
115280849Scy
116290000Sglebius	// key_id = 0;
117280849Scy	pkt_key = NULL;
118280849Scy	is_authentic = (HAVE_OPT(AUTHENTICATION)) ? 0 : -1;
119280849Scy
120258945Sroberto	/*
121258945Sroberto	 * Parse the extension field if present. We figure out whether
122258945Sroberto	 * an extension field is present by measuring the MAC size. If
123258945Sroberto	 * the number of words following the packet header is 0, no MAC
124258945Sroberto	 * is present and the packet is not authenticated. If 1, the
125258945Sroberto	 * packet is a crypto-NAK; if 3, the packet is authenticated
126258945Sroberto	 * with DES; if 5, the packet is authenticated with MD5; if 6,
127258945Sroberto	 * the packet is authenticated with SHA. If 2 or 4, the packet
128258945Sroberto	 * is a runt and discarded forthwith. If greater than 6, an
129258945Sroberto	 * extension field is present, so we subtract the length of the
130258945Sroberto	 * field and go around again.
131258945Sroberto	 */
132280849Scy	if (pkt_len < (int)LEN_PKT_NOMAC || (pkt_len & 3) != 0) {
133280849Scy		msyslog(LOG_ERR,
134280849Scy			"%s: Incredible packet length: %d.  Discarding.",
135280849Scy			func_name, pkt_len);
136258945Sroberto		return PACKET_UNUSEABLE;
137258945Sroberto	}
138280849Scy	/* Note: pkt_len must be a multiple of 4 at this point! */
139293894Sglebius	packet_end = (void*)((char*)rpkt + pkt_len);
140280849Scy	exten_end = skip_efields(rpkt->exten, packet_end);
141280849Scy	if (NULL == exten_end) {
142280849Scy		msyslog(LOG_ERR,
143280849Scy			"%s: Missing extension field.  Discarding.",
144280849Scy			func_name);
145280849Scy		return PACKET_UNUSEABLE;
146258945Sroberto	}
147280849Scy	/* get size of MAC in cells; can be zero */
148280849Scy	exten_len = (u_int)(packet_end - exten_end);
149258945Sroberto
150280849Scy	/* deduce action required from remaining length */
151280849Scy	switch (exten_len) {
152280849Scy
153280849Scy	case 0:	/* no MAC at all */
154280849Scy		break;
155280849Scy
156280849Scy	case 1:	/* crypto NAK */
157280849Scy		key_id = ntohl(*exten_end);
158258945Sroberto		printf("Crypto NAK = 0x%08x\n", key_id);
159258945Sroberto		break;
160280849Scy
161280849Scy	case 3: /* key ID + 3DES MAC -- unsupported! */
162280849Scy		msyslog(LOG_ERR,
163280849Scy			"%s: Key ID + 3DES MAC is unsupported.  Discarding.",
164280849Scy			func_name);
165280849Scy		return PACKET_UNUSEABLE;
166280849Scy
167280849Scy	case 5:	/* key ID + MD5 MAC */
168280849Scy	case 6:	/* key ID + SHA MAC */
169280849Scy		/*
170280849Scy		** Look for the key used by the server in the specified
171280849Scy		** keyfile and if existent, fetch it or else leave the
172280849Scy		** pointer untouched
173280849Scy		*/
174280849Scy		key_id = ntohl(*exten_end);
175258945Sroberto		get_key(key_id, &pkt_key);
176258945Sroberto		if (!pkt_key) {
177258945Sroberto			printf("unrecognized key ID = 0x%08x\n", key_id);
178258945Sroberto			break;
179258945Sroberto		}
180280849Scy		/*
181280849Scy		** Seems like we've got a key with matching keyid.
182280849Scy		**
183280849Scy		** Generate a md5sum of the packet with the key from our
184280849Scy		** keyfile and compare those md5sums.
185280849Scy		*/
186280849Scy		mac_size = exten_len << 2;
187294904Sdelphij		if (!auth_md5(rpkt, pkt_len - mac_size,
188280849Scy			      mac_size - 4, pkt_key)) {
189280849Scy			is_authentic = FALSE;
190258945Sroberto			break;
191258945Sroberto		}
192258945Sroberto		/* Yay! Things worked out! */
193280849Scy		is_authentic = TRUE;
194280849Scy		TRACE(1, ("sntp %s: packet from %s authenticated using key id %d.\n",
195280849Scy			  func_name, stoa(sender), key_id));
196258945Sroberto		break;
197280849Scy
198258945Sroberto	default:
199280849Scy		msyslog(LOG_ERR,
200280849Scy			"%s: Unexpected extension length: %d.  Discarding.",
201280849Scy			func_name, exten_len);
202280849Scy		return PACKET_UNUSEABLE;
203280849Scy	}
204280849Scy
205280849Scy	switch (is_authentic) {
206280849Scy
207280849Scy	case -1:	/* unknown */
208258945Sroberto		break;
209280849Scy
210280849Scy	case 0:		/* not authentic */
211280849Scy		return SERVER_AUTH_FAIL;
212280849Scy		break;
213280849Scy
214280849Scy	case 1:		/* authentic */
215280849Scy		break;
216280849Scy
217280849Scy	default:	/* error */
218280849Scy		break;
219258945Sroberto	}
220280849Scy
221258945Sroberto	/* Check for server's ntp version */
222258945Sroberto	if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION ||
223258945Sroberto		PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) {
224280849Scy		msyslog(LOG_ERR,
225280849Scy			"%s: Packet shows wrong version (%d)",
226280849Scy			func_name, PKT_VERSION(rpkt->li_vn_mode));
227258945Sroberto		return SERVER_UNUSEABLE;
228258945Sroberto	}
229258945Sroberto	/* We want a server to sync with */
230258945Sroberto	if (PKT_MODE(rpkt->li_vn_mode) != mode &&
231258945Sroberto	    PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) {
232280849Scy		msyslog(LOG_ERR,
233280849Scy			"%s: mode %d stratum %d", func_name,
234280849Scy			PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
235258945Sroberto		return SERVER_UNUSEABLE;
236258945Sroberto	}
237258945Sroberto	/* Stratum is unspecified (0) check what's going on */
238258945Sroberto	if (STRATUM_PKT_UNSPEC == rpkt->stratum) {
239258945Sroberto		char *ref_char;
240280849Scy
241280849Scy		TRACE(1, ("%s: Stratum unspecified, going to check for KOD (stratum: %d)\n",
242280849Scy			  func_name, rpkt->stratum));
243258945Sroberto		ref_char = (char *) &rpkt->refid;
244280849Scy		TRACE(1, ("%s: Packet refid: %c%c%c%c\n", func_name,
245280849Scy			  ref_char[0], ref_char[1], ref_char[2], ref_char[3]));
246258945Sroberto		/* If it's a KOD packet we'll just use the KOD information */
247258945Sroberto		if (ref_char[0] != 'X') {
248258945Sroberto			if (strncmp(ref_char, "DENY", 4) == 0)
249258945Sroberto				return KOD_DEMOBILIZE;
250258945Sroberto			if (strncmp(ref_char, "RSTR", 4) == 0)
251258945Sroberto				return KOD_DEMOBILIZE;
252258945Sroberto			if (strncmp(ref_char, "RATE", 4) == 0)
253258945Sroberto				return KOD_RATE;
254280849Scy			/*
255280849Scy			** There are other interesting kiss codes which
256280849Scy			** might be interesting for authentication.
257280849Scy			*/
258258945Sroberto		}
259258945Sroberto	}
260258945Sroberto	/* If the server is not synced it's not really useable for us */
261258945Sroberto	if (LEAP_NOTINSYNC == PKT_LEAP(rpkt->li_vn_mode)) {
262280849Scy		msyslog(LOG_ERR,
263280849Scy			"%s: %s not in sync, skipping this server",
264280849Scy			func_name, stoa(sender));
265258945Sroberto		return SERVER_UNUSEABLE;
266258945Sroberto	}
267258945Sroberto
268258945Sroberto	/*
269258945Sroberto	 * Decode the org timestamp and make sure we're getting a response
270258945Sroberto	 * to our last request, but only if we're not in broadcast mode.
271258945Sroberto	 */
272280849Scy	if (MODE_BROADCAST == mode)
273280849Scy		return pkt_len;
274258945Sroberto
275280849Scy	if (!L_ISEQU(&rpkt->org, &spkt->xmt)) {
276280849Scy		NTOHL_FP(&rpkt->org, &resp_org);
277280849Scy		NTOHL_FP(&spkt->xmt, &sent_xmt);
278280849Scy		msyslog(LOG_ERR,
279280849Scy			"%s response org expected to match sent xmt",
280280849Scy			stoa(sender));
281280849Scy		msyslog(LOG_ERR, "resp org: %s", prettydate(&resp_org));
282280849Scy		msyslog(LOG_ERR, "sent xmt: %s", prettydate(&sent_xmt));
283258945Sroberto		return PACKET_UNUSEABLE;
284258945Sroberto	}
285258945Sroberto
286258945Sroberto	return pkt_len;
287258945Sroberto}
288