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