1 /* $NetBSD: networking.c,v 1.16 2020/05/25 20:47:32 christos Exp $ */
2
3 #include <config.h>
4 #include "networking.h"
5 #include "ntp_debug.h"
6
7
8 /* Send a packet */
9 int
sendpkt(SOCKET rsock,sockaddr_u * dest,struct pkt * pkt,int len)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, "sendpkt: sendto(%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
recvdata(SOCKET rsock,sockaddr_u * sender,void * rdata,int rdata_length)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*
skip_efields(u_int32 * head,u_int32 * tail)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 ++head;
85 nlen = (nlen + 3) >> 2;
86 if (nlen > (u_int)(tail - head) || nlen < 4)
87 return NULL; /* Blooper! Inconsistent! */
88 head += nlen;
89 }
90 return head;
91 }
92
93 /*
94 ** Check if it's data for us and whether it's useable or not.
95 **
96 ** If not, return a failure code so we can delete this server from our list
97 ** and continue with another one.
98 */
99 int
process_pkt(struct pkt * rpkt,sockaddr_u * sender,int pkt_len,int mode,struct pkt * spkt,const char * func_name)100 process_pkt (
101 struct pkt *rpkt,
102 sockaddr_u *sender,
103 int pkt_len,
104 int mode,
105 struct pkt *spkt,
106 const char * func_name
107 )
108 {
109 u_int key_id;
110 struct key * pkt_key;
111 int is_authentic;
112 int mac_size;
113 u_int exten_len;
114 u_int32 * exten_end;
115 u_int32 * packet_end;
116 l_fp sent_xmt;
117 l_fp resp_org;
118
119 // key_id = 0;
120 pkt_key = NULL;
121 is_authentic = (HAVE_OPT(AUTHENTICATION)) ? 0 : -1;
122
123 /*
124 * Parse the extension field if present. We figure out whether
125 * an extension field is present by measuring the MAC size. If
126 * the number of words following the packet header is 0, no MAC
127 * is present and the packet is not authenticated. If 1, the
128 * packet is a crypto-NAK; if 3, the packet is authenticated
129 * with DES; if 5, the packet is authenticated with MD5; if 6,
130 * the packet is authenticated with SHA. If 2 or 4, the packet
131 * is a runt and discarded forthwith. If greater than 6, an
132 * extension field is present, so we subtract the length of the
133 * field and go around again.
134 */
135 if (pkt_len < (int)LEN_PKT_NOMAC || (pkt_len & 3) != 0) {
136 msyslog(LOG_ERR,
137 "%s: Incredible packet length: %d. Discarding.",
138 func_name, pkt_len);
139 return PACKET_UNUSEABLE;
140 }
141
142 /* HMS: the following needs a bit of work */
143 /* Note: pkt_len must be a multiple of 4 at this point! */
144 packet_end = (void*)((char*)rpkt + pkt_len);
145 exten_end = skip_efields(rpkt->exten, packet_end);
146 if (NULL == exten_end) {
147 msyslog(LOG_ERR,
148 "%s: Missing extension field. Discarding.",
149 func_name);
150 return PACKET_UNUSEABLE;
151 }
152
153 /* get size of MAC in cells; can be zero */
154 exten_len = (u_int)(packet_end - exten_end);
155
156 /* deduce action required from remaining length */
157 switch (exten_len) {
158
159 case 0: /* no Legacy MAC */
160 break;
161
162 case 1: /* crypto NAK */
163 /* Only if the keyID is 0 and there were no EFs */
164 key_id = ntohl(*exten_end);
165 printf("Crypto NAK = 0x%08x from %s\n", key_id, stoa(sender));
166 break;
167
168 case 3: /* key ID + 3DES MAC -- unsupported! */
169 msyslog(LOG_ERR,
170 "%s: Key ID + 3DES MAC is unsupported. Discarding.",
171 func_name);
172 return PACKET_UNUSEABLE;
173
174 case 5: /* key ID + MD5 MAC */
175 case 6: /* key ID + SHA MAC */
176 /*
177 ** Look for the key used by the server in the specified
178 ** keyfile and if existent, fetch it or else leave the
179 ** pointer untouched
180 */
181 key_id = ntohl(*exten_end);
182 get_key(key_id, &pkt_key);
183 if (!pkt_key) {
184 printf("unrecognized key ID = 0x%08x\n", key_id);
185 break;
186 }
187 /*
188 ** Seems like we've got a key with matching keyid.
189 **
190 ** Generate a md5sum of the packet with the key from our
191 ** keyfile and compare those md5sums.
192 */
193 mac_size = exten_len << 2;
194 if (!auth_md5(rpkt, pkt_len - mac_size,
195 mac_size - 4, pkt_key)) {
196 is_authentic = FALSE;
197 break;
198 }
199 /* Yay! Things worked out! */
200 is_authentic = TRUE;
201 TRACE(1, ("sntp %s: packet from %s authenticated using key id %d.\n",
202 func_name, stoa(sender), key_id));
203 break;
204
205 default:
206 msyslog(LOG_ERR,
207 "%s: Unexpected extension length: %d. Discarding.",
208 func_name, exten_len);
209 return PACKET_UNUSEABLE;
210 }
211
212 switch (is_authentic) {
213
214 case -1: /* unknown */
215 break;
216
217 case 0: /* not authentic */
218 return SERVER_AUTH_FAIL;
219 break;
220
221 case 1: /* authentic */
222 break;
223
224 default: /* error */
225 break;
226 }
227
228 /* Check for server's ntp version */
229 if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION ||
230 PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) {
231 msyslog(LOG_ERR,
232 "%s: Packet shows wrong version (%d)",
233 func_name, PKT_VERSION(rpkt->li_vn_mode));
234 return SERVER_UNUSEABLE;
235 }
236 /* We want a server to sync with */
237 if (PKT_MODE(rpkt->li_vn_mode) != mode &&
238 PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) {
239 msyslog(LOG_ERR,
240 "%s: mode %d stratum %d", func_name,
241 PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
242 return SERVER_UNUSEABLE;
243 }
244 /* Stratum is unspecified (0) check what's going on */
245 if (STRATUM_PKT_UNSPEC == rpkt->stratum) {
246 char *ref_char;
247
248 TRACE(1, ("%s: Stratum unspecified, going to check for KOD (stratum: %d)\n",
249 func_name, rpkt->stratum));
250 ref_char = (char *) &rpkt->refid;
251 TRACE(1, ("%s: Packet refid: %c%c%c%c\n", func_name,
252 ref_char[0], ref_char[1], ref_char[2], ref_char[3]));
253 /* If it's a KOD packet we'll just use the KOD information */
254 if (ref_char[0] != 'X') {
255 if (strncmp(ref_char, "DENY", 4) == 0)
256 return KOD_DEMOBILIZE;
257 if (strncmp(ref_char, "RSTR", 4) == 0)
258 return KOD_DEMOBILIZE;
259 if (strncmp(ref_char, "RATE", 4) == 0)
260 return KOD_RATE;
261 /*
262 ** There are other interesting kiss codes which
263 ** might be interesting for authentication.
264 */
265 }
266 }
267 /* If the server is not synced it's not really useable for us */
268 if (LEAP_NOTINSYNC == PKT_LEAP(rpkt->li_vn_mode)) {
269 msyslog(LOG_ERR,
270 "%s: %s not in sync, skipping this server",
271 func_name, stoa(sender));
272 return SERVER_UNUSEABLE;
273 }
274
275 /*
276 * Decode the org timestamp and make sure we're getting a response
277 * to our last request, but only if we're not in broadcast mode.
278 */
279 if (MODE_BROADCAST == mode)
280 return pkt_len;
281
282 if (!L_ISEQU(&rpkt->org, &spkt->xmt)) {
283 NTOHL_FP(&rpkt->org, &resp_org);
284 NTOHL_FP(&spkt->xmt, &sent_xmt);
285 msyslog(LOG_ERR,
286 "%s response org expected to match sent xmt",
287 stoa(sender));
288 msyslog(LOG_ERR, "resp org: %s", prettydate(&resp_org));
289 msyslog(LOG_ERR, "sent xmt: %s", prettydate(&sent_xmt));
290 return PACKET_UNUSEABLE;
291 }
292
293 return pkt_len;
294 }
295