networking.c revision 330106
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, "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 */ 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 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*/ 96int 97process_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} 292