xref: /freebsd/contrib/ntp/sntp/networking.c (revision 2e3507c25e42292b45a5482e116d278f5515d04d)
1 #include <config.h>
2 #include "networking.h"
3 #include "ntp_debug.h"
4 
5 
6 /* Send a packet */
7 int
8 sendpkt (
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, "sendpkt: sendto(%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 */
40 int
41 recvdata(
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  */
72 static void*
73 skip_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 */
97 int
98 process_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