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