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