1258945Sroberto#include <config.h> 2258945Sroberto#include "networking.h" 3280849Scy#include "ntp_debug.h" 4258945Sroberto 5258945Sroberto 6258945Sroberto/* Send a packet */ 7280849Scyint 8258945Srobertosendpkt ( 9258945Sroberto SOCKET rsock, 10258945Sroberto sockaddr_u *dest, 11258945Sroberto struct pkt *pkt, 12258945Sroberto int len 13258945Sroberto ) 14258945Sroberto{ 15258945Sroberto int cc; 16258945Sroberto 17258945Sroberto#ifdef DEBUG 18280849Scy if (debug > 2) { 19280849Scy printf("sntp sendpkt: Packet data:\n"); 20280849Scy pkt_output(pkt, len, stdout); 21280849Scy } 22258945Sroberto#endif 23280849Scy TRACE(1, ("sntp sendpkt: Sending packet to %s ...\n", 24280849Scy sptoa(dest))); 25258945Sroberto 26280849Scy cc = sendto(rsock, (void *)pkt, len, 0, &dest->sa, 27280849Scy SOCKLEN(dest)); 28280849Scy if (cc == SOCKET_ERROR) { 29280849Scy msyslog(LOG_ERR, "Send to %s failed, %m", 30280849Scy sptoa(dest)); 31280849Scy return FALSE; 32258945Sroberto } 33280849Scy TRACE(1, ("Packet sent.\n")); 34258945Sroberto 35280849Scy return TRUE; 36258945Sroberto} 37258945Sroberto 38280849Scy 39258945Sroberto/* Receive raw data */ 40258945Srobertoint 41258945Srobertorecvdata( 42280849Scy SOCKET rsock, 43280849Scy sockaddr_u * sender, 44280849Scy void * rdata, 45280849Scy int rdata_length 46258945Sroberto ) 47258945Sroberto{ 48258945Sroberto GETSOCKNAME_SOCKLEN_TYPE slen; 49258945Sroberto int recvc; 50258945Sroberto 51258945Sroberto slen = sizeof(*sender); 52280849Scy recvc = recvfrom(rsock, rdata, rdata_length, 0, 53258945Sroberto &sender->sa, &slen); 54280849Scy if (recvc < 0) 55280849Scy return recvc; 56258945Sroberto#ifdef DEBUG 57280849Scy if (debug > 2) { 58280849Scy printf("Received %d bytes from %s:\n", recvc, sptoa(sender)); 59280849Scy pkt_output((struct pkt *)rdata, recvc, stdout); 60258945Sroberto } 61258945Sroberto#endif 62258945Sroberto return recvc; 63258945Sroberto} 64258945Sroberto 65280849Scy/* Parsing from a short 'struct pkt' directly is bound to create 66280849Scy * coverity warnings. These are hard to avoid, as the formal declaration 67280849Scy * does not reflect the true layout in the presence of autokey extension 68280849Scy * fields. Parsing and skipping the extension fields of a received packet 69280849Scy * until there's only the MAC left is better done in this separate 70280849Scy * function. 71280849Scy */ 72280849Scystatic void* 73280849Scyskip_efields( 74280849Scy u_int32 *head, /* head of extension chain */ 75280849Scy u_int32 *tail /* tail/end of extension chain */ 76258945Sroberto ) 77258945Sroberto{ 78280849Scy 79280849Scy u_int nlen; /* next extension length */ 80280849Scy while ((tail - head) > 6) { 81280849Scy nlen = ntohl(*head++) & 0xffff; 82280849Scy nlen = (nlen + 3) >> 2; 83280849Scy if (nlen > (u_int)(tail - head) || nlen < 4) 84280849Scy return NULL; /* Blooper! Inconsistent! */ 85280849Scy head += nlen; 86258945Sroberto } 87280849Scy return head; 88258945Sroberto} 89258945Sroberto 90280849Scy/* 91280849Scy** Check if it's data for us and whether it's useable or not. 92280849Scy** 93280849Scy** If not, return a failure code so we can delete this server from our list 94280849Scy** and continue with another one. 95280849Scy*/ 96258945Srobertoint 97258945Srobertoprocess_pkt ( 98258945Sroberto struct pkt *rpkt, 99280849Scy sockaddr_u *sender, 100258945Sroberto int pkt_len, 101258945Sroberto int mode, 102258945Sroberto struct pkt *spkt, 103280849Scy const char * func_name 104258945Sroberto ) 105258945Sroberto{ 106280849Scy u_int key_id; 107280849Scy struct key * pkt_key; 108280849Scy int is_authentic; 109280849Scy int mac_size; 110280849Scy u_int exten_len; 111280849Scy u_int32 * exten_end; 112280849Scy u_int32 * packet_end; 113280849Scy l_fp sent_xmt; 114280849Scy l_fp resp_org; 115280849Scy 116290000Sglebius // key_id = 0; 117280849Scy pkt_key = NULL; 118280849Scy is_authentic = (HAVE_OPT(AUTHENTICATION)) ? 0 : -1; 119280849Scy 120258945Sroberto /* 121258945Sroberto * Parse the extension field if present. We figure out whether 122258945Sroberto * an extension field is present by measuring the MAC size. If 123258945Sroberto * the number of words following the packet header is 0, no MAC 124258945Sroberto * is present and the packet is not authenticated. If 1, the 125258945Sroberto * packet is a crypto-NAK; if 3, the packet is authenticated 126258945Sroberto * with DES; if 5, the packet is authenticated with MD5; if 6, 127258945Sroberto * the packet is authenticated with SHA. If 2 or 4, the packet 128258945Sroberto * is a runt and discarded forthwith. If greater than 6, an 129258945Sroberto * extension field is present, so we subtract the length of the 130258945Sroberto * field and go around again. 131258945Sroberto */ 132280849Scy if (pkt_len < (int)LEN_PKT_NOMAC || (pkt_len & 3) != 0) { 133280849Scy msyslog(LOG_ERR, 134280849Scy "%s: Incredible packet length: %d. Discarding.", 135280849Scy func_name, pkt_len); 136258945Sroberto return PACKET_UNUSEABLE; 137258945Sroberto } 138280849Scy /* Note: pkt_len must be a multiple of 4 at this point! */ 139293894Sglebius packet_end = (void*)((char*)rpkt + pkt_len); 140280849Scy exten_end = skip_efields(rpkt->exten, packet_end); 141280849Scy if (NULL == exten_end) { 142280849Scy msyslog(LOG_ERR, 143280849Scy "%s: Missing extension field. Discarding.", 144280849Scy func_name); 145280849Scy return PACKET_UNUSEABLE; 146258945Sroberto } 147280849Scy /* get size of MAC in cells; can be zero */ 148280849Scy exten_len = (u_int)(packet_end - exten_end); 149258945Sroberto 150280849Scy /* deduce action required from remaining length */ 151280849Scy switch (exten_len) { 152280849Scy 153280849Scy case 0: /* no MAC at all */ 154280849Scy break; 155280849Scy 156280849Scy case 1: /* crypto NAK */ 157280849Scy key_id = ntohl(*exten_end); 158258945Sroberto printf("Crypto NAK = 0x%08x\n", key_id); 159258945Sroberto break; 160280849Scy 161280849Scy case 3: /* key ID + 3DES MAC -- unsupported! */ 162280849Scy msyslog(LOG_ERR, 163280849Scy "%s: Key ID + 3DES MAC is unsupported. Discarding.", 164280849Scy func_name); 165280849Scy return PACKET_UNUSEABLE; 166280849Scy 167280849Scy case 5: /* key ID + MD5 MAC */ 168280849Scy case 6: /* key ID + SHA MAC */ 169280849Scy /* 170280849Scy ** Look for the key used by the server in the specified 171280849Scy ** keyfile and if existent, fetch it or else leave the 172280849Scy ** pointer untouched 173280849Scy */ 174280849Scy key_id = ntohl(*exten_end); 175258945Sroberto get_key(key_id, &pkt_key); 176258945Sroberto if (!pkt_key) { 177258945Sroberto printf("unrecognized key ID = 0x%08x\n", key_id); 178258945Sroberto break; 179258945Sroberto } 180280849Scy /* 181280849Scy ** Seems like we've got a key with matching keyid. 182280849Scy ** 183280849Scy ** Generate a md5sum of the packet with the key from our 184280849Scy ** keyfile and compare those md5sums. 185280849Scy */ 186280849Scy mac_size = exten_len << 2; 187294904Sdelphij if (!auth_md5(rpkt, pkt_len - mac_size, 188280849Scy mac_size - 4, pkt_key)) { 189280849Scy is_authentic = FALSE; 190258945Sroberto break; 191258945Sroberto } 192258945Sroberto /* Yay! Things worked out! */ 193280849Scy is_authentic = TRUE; 194280849Scy TRACE(1, ("sntp %s: packet from %s authenticated using key id %d.\n", 195280849Scy func_name, stoa(sender), key_id)); 196258945Sroberto break; 197280849Scy 198258945Sroberto default: 199280849Scy msyslog(LOG_ERR, 200280849Scy "%s: Unexpected extension length: %d. Discarding.", 201280849Scy func_name, exten_len); 202280849Scy return PACKET_UNUSEABLE; 203280849Scy } 204280849Scy 205280849Scy switch (is_authentic) { 206280849Scy 207280849Scy case -1: /* unknown */ 208258945Sroberto break; 209280849Scy 210280849Scy case 0: /* not authentic */ 211280849Scy return SERVER_AUTH_FAIL; 212280849Scy break; 213280849Scy 214280849Scy case 1: /* authentic */ 215280849Scy break; 216280849Scy 217280849Scy default: /* error */ 218280849Scy break; 219258945Sroberto } 220280849Scy 221258945Sroberto /* Check for server's ntp version */ 222258945Sroberto if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION || 223258945Sroberto PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) { 224280849Scy msyslog(LOG_ERR, 225280849Scy "%s: Packet shows wrong version (%d)", 226280849Scy func_name, PKT_VERSION(rpkt->li_vn_mode)); 227258945Sroberto return SERVER_UNUSEABLE; 228258945Sroberto } 229258945Sroberto /* We want a server to sync with */ 230258945Sroberto if (PKT_MODE(rpkt->li_vn_mode) != mode && 231258945Sroberto PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) { 232280849Scy msyslog(LOG_ERR, 233280849Scy "%s: mode %d stratum %d", func_name, 234280849Scy PKT_MODE(rpkt->li_vn_mode), rpkt->stratum); 235258945Sroberto return SERVER_UNUSEABLE; 236258945Sroberto } 237258945Sroberto /* Stratum is unspecified (0) check what's going on */ 238258945Sroberto if (STRATUM_PKT_UNSPEC == rpkt->stratum) { 239258945Sroberto char *ref_char; 240280849Scy 241280849Scy TRACE(1, ("%s: Stratum unspecified, going to check for KOD (stratum: %d)\n", 242280849Scy func_name, rpkt->stratum)); 243258945Sroberto ref_char = (char *) &rpkt->refid; 244280849Scy TRACE(1, ("%s: Packet refid: %c%c%c%c\n", func_name, 245280849Scy ref_char[0], ref_char[1], ref_char[2], ref_char[3])); 246258945Sroberto /* If it's a KOD packet we'll just use the KOD information */ 247258945Sroberto if (ref_char[0] != 'X') { 248258945Sroberto if (strncmp(ref_char, "DENY", 4) == 0) 249258945Sroberto return KOD_DEMOBILIZE; 250258945Sroberto if (strncmp(ref_char, "RSTR", 4) == 0) 251258945Sroberto return KOD_DEMOBILIZE; 252258945Sroberto if (strncmp(ref_char, "RATE", 4) == 0) 253258945Sroberto return KOD_RATE; 254280849Scy /* 255280849Scy ** There are other interesting kiss codes which 256280849Scy ** might be interesting for authentication. 257280849Scy */ 258258945Sroberto } 259258945Sroberto } 260258945Sroberto /* If the server is not synced it's not really useable for us */ 261258945Sroberto if (LEAP_NOTINSYNC == PKT_LEAP(rpkt->li_vn_mode)) { 262280849Scy msyslog(LOG_ERR, 263280849Scy "%s: %s not in sync, skipping this server", 264280849Scy func_name, stoa(sender)); 265258945Sroberto return SERVER_UNUSEABLE; 266258945Sroberto } 267258945Sroberto 268258945Sroberto /* 269258945Sroberto * Decode the org timestamp and make sure we're getting a response 270258945Sroberto * to our last request, but only if we're not in broadcast mode. 271258945Sroberto */ 272280849Scy if (MODE_BROADCAST == mode) 273280849Scy return pkt_len; 274258945Sroberto 275280849Scy if (!L_ISEQU(&rpkt->org, &spkt->xmt)) { 276280849Scy NTOHL_FP(&rpkt->org, &resp_org); 277280849Scy NTOHL_FP(&spkt->xmt, &sent_xmt); 278280849Scy msyslog(LOG_ERR, 279280849Scy "%s response org expected to match sent xmt", 280280849Scy stoa(sender)); 281280849Scy msyslog(LOG_ERR, "resp org: %s", prettydate(&resp_org)); 282280849Scy msyslog(LOG_ERR, "sent xmt: %s", prettydate(&sent_xmt)); 283258945Sroberto return PACKET_UNUSEABLE; 284258945Sroberto } 285258945Sroberto 286258945Sroberto return pkt_len; 287258945Sroberto} 288