1290001Sglebius#include <config.h>
2290001Sglebius#include "networking.h"
3290001Sglebius#include "ntp_debug.h"
4290001Sglebius
5290001Sglebius
6290001Sglebius/* Send a packet */
7290001Sglebiusint
8290001Sglebiussendpkt (
9290001Sglebius	SOCKET rsock,
10290001Sglebius	sockaddr_u *dest,
11290001Sglebius	struct pkt *pkt,
12290001Sglebius	int len
13290001Sglebius	)
14290001Sglebius{
15290001Sglebius	int cc;
16290001Sglebius
17290001Sglebius#ifdef DEBUG
18290001Sglebius	if (debug > 2) {
19290001Sglebius		printf("sntp sendpkt: Packet data:\n");
20290001Sglebius		pkt_output(pkt, len, stdout);
21290001Sglebius	}
22290001Sglebius#endif
23290001Sglebius	TRACE(1, ("sntp sendpkt: Sending packet to %s ...\n",
24290001Sglebius		  sptoa(dest)));
25290001Sglebius
26290001Sglebius	cc = sendto(rsock, (void *)pkt, len, 0, &dest->sa,
27290001Sglebius		    SOCKLEN(dest));
28290001Sglebius	if (cc == SOCKET_ERROR) {
29290001Sglebius		msyslog(LOG_ERR, "Send to %s failed, %m",
30290001Sglebius			sptoa(dest));
31290001Sglebius		return FALSE;
32290001Sglebius	}
33290001Sglebius	TRACE(1, ("Packet sent.\n"));
34290001Sglebius
35290001Sglebius	return TRUE;
36290001Sglebius}
37290001Sglebius
38290001Sglebius
39290001Sglebius/* Receive raw data */
40290001Sglebiusint
41290001Sglebiusrecvdata(
42290001Sglebius	SOCKET		rsock,
43290001Sglebius	sockaddr_u *	sender,
44290001Sglebius	void *		rdata,
45290001Sglebius	int		rdata_length
46290001Sglebius	)
47290001Sglebius{
48290001Sglebius	GETSOCKNAME_SOCKLEN_TYPE slen;
49290001Sglebius	int recvc;
50290001Sglebius
51290001Sglebius	slen = sizeof(*sender);
52290001Sglebius	recvc = recvfrom(rsock, rdata, rdata_length, 0,
53290001Sglebius			 &sender->sa, &slen);
54290001Sglebius	if (recvc < 0)
55290001Sglebius		return recvc;
56290001Sglebius#ifdef DEBUG
57290001Sglebius	if (debug > 2) {
58290001Sglebius		printf("Received %d bytes from %s:\n", recvc, sptoa(sender));
59290001Sglebius		pkt_output((struct pkt *)rdata, recvc, stdout);
60290001Sglebius	}
61290001Sglebius#endif
62290001Sglebius	return recvc;
63290001Sglebius}
64290001Sglebius
65290001Sglebius/* Parsing from a short 'struct pkt' directly is bound to create
66290001Sglebius * coverity warnings. These are hard to avoid, as the formal declaration
67290001Sglebius * does not reflect the true layout in the presence of autokey extension
68290001Sglebius * fields. Parsing and skipping the extension fields of a received packet
69290001Sglebius * until there's only the MAC left is better done in this separate
70290001Sglebius * function.
71290001Sglebius */
72290001Sglebiusstatic void*
73290001Sglebiusskip_efields(
74290001Sglebius	u_int32 *head,	/* head of extension chain 	*/
75290001Sglebius	u_int32 *tail	/* tail/end of extension chain	*/
76290001Sglebius	)
77290001Sglebius{
78290001Sglebius
79290001Sglebius	u_int nlen;	/* next extension length */
80290001Sglebius	while ((tail - head) > 6) {
81290001Sglebius		nlen = ntohl(*head++) & 0xffff;
82290001Sglebius		nlen = (nlen + 3) >> 2;
83290001Sglebius		if (nlen > (u_int)(tail - head) || nlen < 4)
84290001Sglebius			return NULL;	/* Blooper! Inconsistent! */
85290001Sglebius		head += nlen;
86290001Sglebius	}
87290001Sglebius	return head;
88290001Sglebius}
89290001Sglebius
90290001Sglebius/*
91290001Sglebius** Check if it's data for us and whether it's useable or not.
92290001Sglebius**
93290001Sglebius** If not, return a failure code so we can delete this server from our list
94290001Sglebius** and continue with another one.
95290001Sglebius*/
96290001Sglebiusint
97290001Sglebiusprocess_pkt (
98290001Sglebius	struct pkt *rpkt,
99290001Sglebius	sockaddr_u *sender,
100290001Sglebius	int pkt_len,
101290001Sglebius	int mode,
102290001Sglebius	struct pkt *spkt,
103290001Sglebius	const char * func_name
104290001Sglebius	)
105290001Sglebius{
106290001Sglebius	u_int		key_id;
107290001Sglebius	struct key *	pkt_key;
108290001Sglebius	int		is_authentic;
109290001Sglebius	int		mac_size;
110290001Sglebius	u_int		exten_len;
111290001Sglebius	u_int32 *       exten_end;
112290001Sglebius	u_int32 *       packet_end;
113290001Sglebius	l_fp		sent_xmt;
114290001Sglebius	l_fp		resp_org;
115290001Sglebius
116290001Sglebius	// key_id = 0;
117290001Sglebius	pkt_key = NULL;
118290001Sglebius	is_authentic = (HAVE_OPT(AUTHENTICATION)) ? 0 : -1;
119290001Sglebius
120290001Sglebius	/*
121290001Sglebius	 * Parse the extension field if present. We figure out whether
122290001Sglebius	 * an extension field is present by measuring the MAC size. If
123290001Sglebius	 * the number of words following the packet header is 0, no MAC
124290001Sglebius	 * is present and the packet is not authenticated. If 1, the
125290001Sglebius	 * packet is a crypto-NAK; if 3, the packet is authenticated
126290001Sglebius	 * with DES; if 5, the packet is authenticated with MD5; if 6,
127290001Sglebius	 * the packet is authenticated with SHA. If 2 or 4, the packet
128290001Sglebius	 * is a runt and discarded forthwith. If greater than 6, an
129290001Sglebius	 * extension field is present, so we subtract the length of the
130290001Sglebius	 * field and go around again.
131290001Sglebius	 */
132290001Sglebius	if (pkt_len < (int)LEN_PKT_NOMAC || (pkt_len & 3) != 0) {
133290001Sglebius		msyslog(LOG_ERR,
134290001Sglebius			"%s: Incredible packet length: %d.  Discarding.",
135290001Sglebius			func_name, pkt_len);
136290001Sglebius		return PACKET_UNUSEABLE;
137290001Sglebius	}
138290001Sglebius	/* Note: pkt_len must be a multiple of 4 at this point! */
139293896Sglebius	packet_end = (void*)((char*)rpkt + pkt_len);
140290001Sglebius	exten_end = skip_efields(rpkt->exten, packet_end);
141290001Sglebius	if (NULL == exten_end) {
142290001Sglebius		msyslog(LOG_ERR,
143290001Sglebius			"%s: Missing extension field.  Discarding.",
144290001Sglebius			func_name);
145290001Sglebius		return PACKET_UNUSEABLE;
146290001Sglebius	}
147290001Sglebius	/* get size of MAC in cells; can be zero */
148290001Sglebius	exten_len = (u_int)(packet_end - exten_end);
149290001Sglebius
150290001Sglebius	/* deduce action required from remaining length */
151290001Sglebius	switch (exten_len) {
152290001Sglebius
153290001Sglebius	case 0:	/* no MAC at all */
154290001Sglebius		break;
155290001Sglebius
156290001Sglebius	case 1:	/* crypto NAK */
157290001Sglebius		key_id = ntohl(*exten_end);
158290001Sglebius		printf("Crypto NAK = 0x%08x\n", key_id);
159290001Sglebius		break;
160290001Sglebius
161290001Sglebius	case 3: /* key ID + 3DES MAC -- unsupported! */
162290001Sglebius		msyslog(LOG_ERR,
163290001Sglebius			"%s: Key ID + 3DES MAC is unsupported.  Discarding.",
164290001Sglebius			func_name);
165290001Sglebius		return PACKET_UNUSEABLE;
166290001Sglebius
167290001Sglebius	case 5:	/* key ID + MD5 MAC */
168290001Sglebius	case 6:	/* key ID + SHA MAC */
169290001Sglebius		/*
170290001Sglebius		** Look for the key used by the server in the specified
171290001Sglebius		** keyfile and if existent, fetch it or else leave the
172290001Sglebius		** pointer untouched
173290001Sglebius		*/
174290001Sglebius		key_id = ntohl(*exten_end);
175290001Sglebius		get_key(key_id, &pkt_key);
176290001Sglebius		if (!pkt_key) {
177290001Sglebius			printf("unrecognized key ID = 0x%08x\n", key_id);
178290001Sglebius			break;
179290001Sglebius		}
180290001Sglebius		/*
181290001Sglebius		** Seems like we've got a key with matching keyid.
182290001Sglebius		**
183290001Sglebius		** Generate a md5sum of the packet with the key from our
184290001Sglebius		** keyfile and compare those md5sums.
185290001Sglebius		*/
186290001Sglebius		mac_size = exten_len << 2;
187294905Sdelphij		if (!auth_md5(rpkt, pkt_len - mac_size,
188290001Sglebius			      mac_size - 4, pkt_key)) {
189290001Sglebius			is_authentic = FALSE;
190290001Sglebius			break;
191290001Sglebius		}
192290001Sglebius		/* Yay! Things worked out! */
193290001Sglebius		is_authentic = TRUE;
194290001Sglebius		TRACE(1, ("sntp %s: packet from %s authenticated using key id %d.\n",
195290001Sglebius			  func_name, stoa(sender), key_id));
196290001Sglebius		break;
197290001Sglebius
198290001Sglebius	default:
199290001Sglebius		msyslog(LOG_ERR,
200290001Sglebius			"%s: Unexpected extension length: %d.  Discarding.",
201290001Sglebius			func_name, exten_len);
202290001Sglebius		return PACKET_UNUSEABLE;
203290001Sglebius	}
204290001Sglebius
205290001Sglebius	switch (is_authentic) {
206290001Sglebius
207290001Sglebius	case -1:	/* unknown */
208290001Sglebius		break;
209290001Sglebius
210290001Sglebius	case 0:		/* not authentic */
211290001Sglebius		return SERVER_AUTH_FAIL;
212290001Sglebius		break;
213290001Sglebius
214290001Sglebius	case 1:		/* authentic */
215290001Sglebius		break;
216290001Sglebius
217290001Sglebius	default:	/* error */
218290001Sglebius		break;
219290001Sglebius	}
220290001Sglebius
221290001Sglebius	/* Check for server's ntp version */
222290001Sglebius	if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION ||
223290001Sglebius		PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) {
224290001Sglebius		msyslog(LOG_ERR,
225290001Sglebius			"%s: Packet shows wrong version (%d)",
226290001Sglebius			func_name, PKT_VERSION(rpkt->li_vn_mode));
227290001Sglebius		return SERVER_UNUSEABLE;
228290001Sglebius	}
229290001Sglebius	/* We want a server to sync with */
230290001Sglebius	if (PKT_MODE(rpkt->li_vn_mode) != mode &&
231290001Sglebius	    PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) {
232290001Sglebius		msyslog(LOG_ERR,
233290001Sglebius			"%s: mode %d stratum %d", func_name,
234290001Sglebius			PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
235290001Sglebius		return SERVER_UNUSEABLE;
236290001Sglebius	}
237290001Sglebius	/* Stratum is unspecified (0) check what's going on */
238290001Sglebius	if (STRATUM_PKT_UNSPEC == rpkt->stratum) {
239290001Sglebius		char *ref_char;
240290001Sglebius
241290001Sglebius		TRACE(1, ("%s: Stratum unspecified, going to check for KOD (stratum: %d)\n",
242290001Sglebius			  func_name, rpkt->stratum));
243290001Sglebius		ref_char = (char *) &rpkt->refid;
244290001Sglebius		TRACE(1, ("%s: Packet refid: %c%c%c%c\n", func_name,
245290001Sglebius			  ref_char[0], ref_char[1], ref_char[2], ref_char[3]));
246290001Sglebius		/* If it's a KOD packet we'll just use the KOD information */
247290001Sglebius		if (ref_char[0] != 'X') {
248290001Sglebius			if (strncmp(ref_char, "DENY", 4) == 0)
249290001Sglebius				return KOD_DEMOBILIZE;
250290001Sglebius			if (strncmp(ref_char, "RSTR", 4) == 0)
251290001Sglebius				return KOD_DEMOBILIZE;
252290001Sglebius			if (strncmp(ref_char, "RATE", 4) == 0)
253290001Sglebius				return KOD_RATE;
254290001Sglebius			/*
255290001Sglebius			** There are other interesting kiss codes which
256290001Sglebius			** might be interesting for authentication.
257290001Sglebius			*/
258290001Sglebius		}
259290001Sglebius	}
260290001Sglebius	/* If the server is not synced it's not really useable for us */
261290001Sglebius	if (LEAP_NOTINSYNC == PKT_LEAP(rpkt->li_vn_mode)) {
262290001Sglebius		msyslog(LOG_ERR,
263290001Sglebius			"%s: %s not in sync, skipping this server",
264290001Sglebius			func_name, stoa(sender));
265290001Sglebius		return SERVER_UNUSEABLE;
266290001Sglebius	}
267290001Sglebius
268290001Sglebius	/*
269290001Sglebius	 * Decode the org timestamp and make sure we're getting a response
270290001Sglebius	 * to our last request, but only if we're not in broadcast mode.
271290001Sglebius	 */
272290001Sglebius	if (MODE_BROADCAST == mode)
273290001Sglebius		return pkt_len;
274290001Sglebius
275290001Sglebius	if (!L_ISEQU(&rpkt->org, &spkt->xmt)) {
276290001Sglebius		NTOHL_FP(&rpkt->org, &resp_org);
277290001Sglebius		NTOHL_FP(&spkt->xmt, &sent_xmt);
278290001Sglebius		msyslog(LOG_ERR,
279290001Sglebius			"%s response org expected to match sent xmt",
280290001Sglebius			stoa(sender));
281290001Sglebius		msyslog(LOG_ERR, "resp org: %s", prettydate(&resp_org));
282290001Sglebius		msyslog(LOG_ERR, "sent xmt: %s", prettydate(&sent_xmt));
283290001Sglebius		return PACKET_UNUSEABLE;
284290001Sglebius	}
285290001Sglebius
286290001Sglebius	return pkt_len;
287290001Sglebius}
288