xref: /netbsd-src/external/bsd/ntp/dist/sntp/networking.c (revision dd255ccea4286b0c44fa8fd48a9a19a768afe8e1)
1 /*	$NetBSD: networking.c,v 1.7 2013/12/28 03:20:15 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 
68 /*
69 ** Check if it's data for us and whether it's useable or not.
70 **
71 ** If not, return a failure code so we can delete this server from our list
72 ** and continue with another one.
73 */
74 int
75 process_pkt (
76 	struct pkt *rpkt,
77 	sockaddr_u *sender,
78 	int pkt_len,
79 	int mode,
80 	struct pkt *spkt,
81 	const char * func_name
82 	)
83 {
84 	u_int		key_id;
85 	struct key *	pkt_key;
86 	int		is_authentic;
87 	u_int		exten_words;
88 	u_int		exten_words_used;
89 	int		mac_size;
90 	u_int		exten_len;
91 	l_fp		sent_xmt;
92 	l_fp		resp_org;
93 
94 	key_id = 0;
95 	pkt_key = NULL;
96 	exten_words_used = 0;
97 	is_authentic = (HAVE_OPT(AUTHENTICATION)) ? 0 : -1;
98 
99 	/*
100 	 * Parse the extension field if present. We figure out whether
101 	 * an extension field is present by measuring the MAC size. If
102 	 * the number of words following the packet header is 0, no MAC
103 	 * is present and the packet is not authenticated. If 1, the
104 	 * packet is a crypto-NAK; if 3, the packet is authenticated
105 	 * with DES; if 5, the packet is authenticated with MD5; if 6,
106 	 * the packet is authenticated with SHA. If 2 or 4, the packet
107 	 * is a runt and discarded forthwith. If greater than 6, an
108 	 * extension field is present, so we subtract the length of the
109 	 * field and go around again.
110 	 */
111 	if (pkt_len < (int)LEN_PKT_NOMAC || (pkt_len & 3) != 0) {
112 		msyslog(LOG_ERR,
113 			"%s: Incredible packet length: %d.  Discarding.",
114 			func_name, pkt_len);
115 		return PACKET_UNUSEABLE;
116 	}
117 	/* skip past the extensions, if any */
118 	exten_words = ((unsigned)pkt_len - LEN_PKT_NOMAC) >> 2;
119 	while (exten_words > 6) {
120 		exten_len = ntohl(rpkt->exten[exten_words_used]) & 0xffff;
121 		exten_len = (exten_len + 7) >> 2; /* convert to words, add 1 */
122 		if (exten_len > exten_words || exten_len < 5)
123 			goto unusable;
124 		exten_words -= exten_len;
125 		exten_words_used += exten_len;
126 	}
127 
128 	switch (exten_words) {
129 
130 	case 0:
131 		break;
132 
133 	case 1:
134 		key_id = ntohl(rpkt->exten[exten_words_used]);
135 		printf("Crypto NAK = 0x%08x\n", key_id);
136 		break;
137 
138 	case 5:
139 	case 6:
140 		/*
141 		** Look for the key used by the server in the specified
142 		** keyfile and if existent, fetch it or else leave the
143 		** pointer untouched
144 		*/
145 		key_id = ntohl(rpkt->exten[exten_words_used]);
146 		get_key(key_id, &pkt_key);
147 		if (!pkt_key) {
148 			printf("unrecognized key ID = 0x%08x\n", key_id);
149 			break;
150 		}
151 		/*
152 		** Seems like we've got a key with matching keyid.
153 		**
154 		** Generate a md5sum of the packet with the key from our
155 		** keyfile and compare those md5sums.
156 		*/
157 		mac_size = exten_words << 2;
158 		if (!auth_md5((char *)rpkt, pkt_len - mac_size,
159 			      mac_size - 4, pkt_key)) {
160 			is_authentic = FALSE;
161 			break;
162 		}
163 		/* Yay! Things worked out! */
164 		is_authentic = TRUE;
165 		TRACE(1, ("sntp %s: packet from %s authenticated using key id %d.\n",
166 			  func_name, stoa(sender), key_id));
167 		break;
168 
169 	default:
170 		goto unusable;
171 		break;
172 	}
173 
174 	switch (is_authentic) {
175 
176 	case -1:	/* unknown */
177 		break;
178 
179 	case 0:		/* not authentic */
180 		return SERVER_AUTH_FAIL;
181 		break;
182 
183 	case 1:		/* authentic */
184 		break;
185 
186 	default:	/* error */
187 		break;
188 	}
189 
190 	/* Check for server's ntp version */
191 	if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION ||
192 		PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) {
193 		msyslog(LOG_ERR,
194 			"%s: Packet shows wrong version (%d)",
195 			func_name, PKT_VERSION(rpkt->li_vn_mode));
196 		return SERVER_UNUSEABLE;
197 	}
198 	/* We want a server to sync with */
199 	if (PKT_MODE(rpkt->li_vn_mode) != mode &&
200 	    PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) {
201 		msyslog(LOG_ERR,
202 			"%s: mode %d stratum %d", func_name,
203 			PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
204 		return SERVER_UNUSEABLE;
205 	}
206 	/* Stratum is unspecified (0) check what's going on */
207 	if (STRATUM_PKT_UNSPEC == rpkt->stratum) {
208 		char *ref_char;
209 
210 		TRACE(1, ("%s: Stratum unspecified, going to check for KOD (stratum: %d)\n",
211 			  func_name, rpkt->stratum));
212 		ref_char = (char *) &rpkt->refid;
213 		TRACE(1, ("%s: Packet refid: %c%c%c%c\n", func_name,
214 			  ref_char[0], ref_char[1], ref_char[2], ref_char[3]));
215 		/* If it's a KOD packet we'll just use the KOD information */
216 		if (ref_char[0] != 'X') {
217 			if (strncmp(ref_char, "DENY", 4) == 0)
218 				return KOD_DEMOBILIZE;
219 			if (strncmp(ref_char, "RSTR", 4) == 0)
220 				return KOD_DEMOBILIZE;
221 			if (strncmp(ref_char, "RATE", 4) == 0)
222 				return KOD_RATE;
223 			/*
224 			** There are other interesting kiss codes which
225 			** might be interesting for authentication.
226 			*/
227 		}
228 	}
229 	/* If the server is not synced it's not really useable for us */
230 	if (LEAP_NOTINSYNC == PKT_LEAP(rpkt->li_vn_mode)) {
231 		msyslog(LOG_ERR,
232 			"%s: %s not in sync, skipping this server",
233 			func_name, stoa(sender));
234 unusable:
235 		return SERVER_UNUSEABLE;
236 	}
237 
238 	/*
239 	 * Decode the org timestamp and make sure we're getting a response
240 	 * to our last request, but only if we're not in broadcast mode.
241 	 */
242 	if (MODE_BROADCAST == mode)
243 		return pkt_len;
244 
245 	if (!L_ISEQU(&rpkt->org, &spkt->xmt)) {
246 		NTOHL_FP(&rpkt->org, &resp_org);
247 		NTOHL_FP(&spkt->xmt, &sent_xmt);
248 		msyslog(LOG_ERR,
249 			"%s response org expected to match sent xmt",
250 			stoa(sender));
251 		msyslog(LOG_ERR, "resp org: %s", prettydate(&resp_org));
252 		msyslog(LOG_ERR, "sent xmt: %s", prettydate(&sent_xmt));
253 		return PACKET_UNUSEABLE;
254 	}
255 
256 	return pkt_len;
257 }
258