networking.c revision 344884
1#include <config.h>
2#include "networking.h"
3#include "ntp_debug.h"
4
5
6/* Send a packet */
7int
8sendpkt (
9	SOCKET rsock,
10	sockaddr_u *dest,
11	struct pkt *pkt,
12	int len
13	)
14{
15	int cc;
16
17#ifdef DEBUG
18	if (debug > 2) {
19		printf("sntp sendpkt: Packet data:\n");
20		pkt_output(pkt, len, stdout);
21	}
22#endif
23	TRACE(1, ("sntp sendpkt: Sending packet to %s ...\n",
24		  sptoa(dest)));
25
26	cc = sendto(rsock, (void *)pkt, len, 0, &dest->sa,
27		    SOCKLEN(dest));
28	if (cc == SOCKET_ERROR) {
29		msyslog(LOG_ERR, "Send to %s failed, %m",
30			sptoa(dest));
31		return FALSE;
32	}
33	TRACE(1, ("Packet sent.\n"));
34
35	return TRUE;
36}
37
38
39/* Receive raw data */
40int
41recvdata(
42	SOCKET		rsock,
43	sockaddr_u *	sender,
44	void *		rdata,
45	int		rdata_length
46	)
47{
48	GETSOCKNAME_SOCKLEN_TYPE slen;
49	int recvc;
50
51	slen = sizeof(*sender);
52	recvc = recvfrom(rsock, rdata, rdata_length, 0,
53			 &sender->sa, &slen);
54	if (recvc < 0)
55		return recvc;
56#ifdef DEBUG
57	if (debug > 2) {
58		printf("Received %d bytes from %s:\n", recvc, sptoa(sender));
59		pkt_output((struct pkt *)rdata, recvc, stdout);
60	}
61#endif
62	return recvc;
63}
64
65/* Parsing from a short 'struct pkt' directly is bound to create
66 * coverity warnings. These are hard to avoid, as the formal declaration
67 * does not reflect the true layout in the presence of autokey extension
68 * fields. Parsing and skipping the extension fields of a received packet
69 * until there's only the MAC left is better done in this separate
70 * function.
71 */
72static void*
73skip_efields(
74	u_int32 *head,	/* head of extension chain 	*/
75	u_int32 *tail	/* tail/end of extension chain	*/
76	)
77{
78
79	u_int nlen;	/* next extension length */
80	while ((tail - head) > 6) {
81		nlen = ntohl(*head) & 0xffff;
82		++head;
83		nlen = (nlen + 3) >> 2;
84		if (nlen > (u_int)(tail - head) || nlen < 4)
85			return NULL;	/* Blooper! Inconsistent! */
86		head += nlen;
87	}
88	return head;
89}
90
91/*
92** Check if it's data for us and whether it's useable or not.
93**
94** If not, return a failure code so we can delete this server from our list
95** and continue with another one.
96*/
97int
98process_pkt (
99	struct pkt *rpkt,
100	sockaddr_u *sender,
101	int pkt_len,
102	int mode,
103	struct pkt *spkt,
104	const char * func_name
105	)
106{
107	u_int		key_id;
108	struct key *	pkt_key;
109	int		is_authentic;
110	int		mac_size;
111	u_int		exten_len;
112	u_int32 *       exten_end;
113	u_int32 *       packet_end;
114	l_fp		sent_xmt;
115	l_fp		resp_org;
116
117	// key_id = 0;
118	pkt_key = NULL;
119	is_authentic = (HAVE_OPT(AUTHENTICATION)) ? 0 : -1;
120
121	/*
122	 * Parse the extension field if present. We figure out whether
123	 * an extension field is present by measuring the MAC size. If
124	 * the number of words following the packet header is 0, no MAC
125	 * is present and the packet is not authenticated. If 1, the
126	 * packet is a crypto-NAK; if 3, the packet is authenticated
127	 * with DES; if 5, the packet is authenticated with MD5; if 6,
128	 * the packet is authenticated with SHA. If 2 or 4, the packet
129	 * is a runt and discarded forthwith. If greater than 6, an
130	 * extension field is present, so we subtract the length of the
131	 * field and go around again.
132	 */
133	if (pkt_len < (int)LEN_PKT_NOMAC || (pkt_len & 3) != 0) {
134		msyslog(LOG_ERR,
135			"%s: Incredible packet length: %d.  Discarding.",
136			func_name, pkt_len);
137		return PACKET_UNUSEABLE;
138	}
139
140	/* HMS: the following needs a bit of work */
141	/* Note: pkt_len must be a multiple of 4 at this point! */
142	packet_end = (void*)((char*)rpkt + pkt_len);
143	exten_end = skip_efields(rpkt->exten, packet_end);
144	if (NULL == exten_end) {
145		msyslog(LOG_ERR,
146			"%s: Missing extension field.  Discarding.",
147			func_name);
148		return PACKET_UNUSEABLE;
149	}
150
151	/* get size of MAC in cells; can be zero */
152	exten_len = (u_int)(packet_end - exten_end);
153
154	/* deduce action required from remaining length */
155	switch (exten_len) {
156
157	case 0:	/* no Legacy MAC */
158		break;
159
160	case 1:	/* crypto NAK */
161		/* Only if the keyID is 0 and there were no EFs */
162		key_id = ntohl(*exten_end);
163		printf("Crypto NAK = 0x%08x from %s\n", key_id, stoa(sender));
164		break;
165
166	case 3: /* key ID + 3DES MAC -- unsupported! */
167		msyslog(LOG_ERR,
168			"%s: Key ID + 3DES MAC is unsupported.  Discarding.",
169			func_name);
170		return PACKET_UNUSEABLE;
171
172	case 5:	/* key ID + MD5 MAC */
173	case 6:	/* key ID + SHA MAC */
174		/*
175		** Look for the key used by the server in the specified
176		** keyfile and if existent, fetch it or else leave the
177		** pointer untouched
178		*/
179		key_id = ntohl(*exten_end);
180		get_key(key_id, &pkt_key);
181		if (!pkt_key) {
182			printf("unrecognized key ID = 0x%08x\n", key_id);
183			break;
184		}
185		/*
186		** Seems like we've got a key with matching keyid.
187		**
188		** Generate a md5sum of the packet with the key from our
189		** keyfile and compare those md5sums.
190		*/
191		mac_size = exten_len << 2;
192		if (!auth_md5(rpkt, pkt_len - mac_size,
193			      mac_size - 4, pkt_key)) {
194			is_authentic = FALSE;
195			break;
196		}
197		/* Yay! Things worked out! */
198		is_authentic = TRUE;
199		TRACE(1, ("sntp %s: packet from %s authenticated using key id %d.\n",
200			  func_name, stoa(sender), key_id));
201		break;
202
203	default:
204		msyslog(LOG_ERR,
205			"%s: Unexpected extension length: %d.  Discarding.",
206			func_name, exten_len);
207		return PACKET_UNUSEABLE;
208	}
209
210	switch (is_authentic) {
211
212	case -1:	/* unknown */
213		break;
214
215	case 0:		/* not authentic */
216		return SERVER_AUTH_FAIL;
217		break;
218
219	case 1:		/* authentic */
220		break;
221
222	default:	/* error */
223		break;
224	}
225
226	/* Check for server's ntp version */
227	if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION ||
228		PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) {
229		msyslog(LOG_ERR,
230			"%s: Packet shows wrong version (%d)",
231			func_name, PKT_VERSION(rpkt->li_vn_mode));
232		return SERVER_UNUSEABLE;
233	}
234	/* We want a server to sync with */
235	if (PKT_MODE(rpkt->li_vn_mode) != mode &&
236	    PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) {
237		msyslog(LOG_ERR,
238			"%s: mode %d stratum %d", func_name,
239			PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
240		return SERVER_UNUSEABLE;
241	}
242	/* Stratum is unspecified (0) check what's going on */
243	if (STRATUM_PKT_UNSPEC == rpkt->stratum) {
244		char *ref_char;
245
246		TRACE(1, ("%s: Stratum unspecified, going to check for KOD (stratum: %d)\n",
247			  func_name, rpkt->stratum));
248		ref_char = (char *) &rpkt->refid;
249		TRACE(1, ("%s: Packet refid: %c%c%c%c\n", func_name,
250			  ref_char[0], ref_char[1], ref_char[2], ref_char[3]));
251		/* If it's a KOD packet we'll just use the KOD information */
252		if (ref_char[0] != 'X') {
253			if (strncmp(ref_char, "DENY", 4) == 0)
254				return KOD_DEMOBILIZE;
255			if (strncmp(ref_char, "RSTR", 4) == 0)
256				return KOD_DEMOBILIZE;
257			if (strncmp(ref_char, "RATE", 4) == 0)
258				return KOD_RATE;
259			/*
260			** There are other interesting kiss codes which
261			** might be interesting for authentication.
262			*/
263		}
264	}
265	/* If the server is not synced it's not really useable for us */
266	if (LEAP_NOTINSYNC == PKT_LEAP(rpkt->li_vn_mode)) {
267		msyslog(LOG_ERR,
268			"%s: %s not in sync, skipping this server",
269			func_name, stoa(sender));
270		return SERVER_UNUSEABLE;
271	}
272
273	/*
274	 * Decode the org timestamp and make sure we're getting a response
275	 * to our last request, but only if we're not in broadcast mode.
276	 */
277	if (MODE_BROADCAST == mode)
278		return pkt_len;
279
280	if (!L_ISEQU(&rpkt->org, &spkt->xmt)) {
281		NTOHL_FP(&rpkt->org, &resp_org);
282		NTOHL_FP(&spkt->xmt, &sent_xmt);
283		msyslog(LOG_ERR,
284			"%s response org expected to match sent xmt",
285			stoa(sender));
286		msyslog(LOG_ERR, "resp org: %s", prettydate(&resp_org));
287		msyslog(LOG_ERR, "sent xmt: %s", prettydate(&sent_xmt));
288		return PACKET_UNUSEABLE;
289	}
290
291	return pkt_len;
292}
293