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 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 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* 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 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