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