1290001Sglebius#include <config.h> 2290001Sglebius#include "networking.h" 3290001Sglebius#include "ntp_debug.h" 4290001Sglebius 5290001Sglebius 6290001Sglebius/* Send a packet */ 7290001Sglebiusint 8290001Sglebiussendpkt ( 9290001Sglebius SOCKET rsock, 10290001Sglebius sockaddr_u *dest, 11290001Sglebius struct pkt *pkt, 12290001Sglebius int len 13290001Sglebius ) 14290001Sglebius{ 15290001Sglebius int cc; 16290001Sglebius 17290001Sglebius#ifdef DEBUG 18290001Sglebius if (debug > 2) { 19290001Sglebius printf("sntp sendpkt: Packet data:\n"); 20290001Sglebius pkt_output(pkt, len, stdout); 21290001Sglebius } 22290001Sglebius#endif 23290001Sglebius TRACE(1, ("sntp sendpkt: Sending packet to %s ...\n", 24290001Sglebius sptoa(dest))); 25290001Sglebius 26290001Sglebius cc = sendto(rsock, (void *)pkt, len, 0, &dest->sa, 27290001Sglebius SOCKLEN(dest)); 28290001Sglebius if (cc == SOCKET_ERROR) { 29290001Sglebius msyslog(LOG_ERR, "Send to %s failed, %m", 30290001Sglebius sptoa(dest)); 31290001Sglebius return FALSE; 32290001Sglebius } 33290001Sglebius TRACE(1, ("Packet sent.\n")); 34290001Sglebius 35290001Sglebius return TRUE; 36290001Sglebius} 37290001Sglebius 38290001Sglebius 39290001Sglebius/* Receive raw data */ 40290001Sglebiusint 41290001Sglebiusrecvdata( 42290001Sglebius SOCKET rsock, 43290001Sglebius sockaddr_u * sender, 44290001Sglebius void * rdata, 45290001Sglebius int rdata_length 46290001Sglebius ) 47290001Sglebius{ 48290001Sglebius GETSOCKNAME_SOCKLEN_TYPE slen; 49290001Sglebius int recvc; 50290001Sglebius 51290001Sglebius slen = sizeof(*sender); 52290001Sglebius recvc = recvfrom(rsock, rdata, rdata_length, 0, 53290001Sglebius &sender->sa, &slen); 54290001Sglebius if (recvc < 0) 55290001Sglebius return recvc; 56290001Sglebius#ifdef DEBUG 57290001Sglebius if (debug > 2) { 58290001Sglebius printf("Received %d bytes from %s:\n", recvc, sptoa(sender)); 59290001Sglebius pkt_output((struct pkt *)rdata, recvc, stdout); 60290001Sglebius } 61290001Sglebius#endif 62290001Sglebius return recvc; 63290001Sglebius} 64290001Sglebius 65290001Sglebius/* Parsing from a short 'struct pkt' directly is bound to create 66290001Sglebius * coverity warnings. These are hard to avoid, as the formal declaration 67290001Sglebius * does not reflect the true layout in the presence of autokey extension 68290001Sglebius * fields. Parsing and skipping the extension fields of a received packet 69290001Sglebius * until there's only the MAC left is better done in this separate 70290001Sglebius * function. 71290001Sglebius */ 72290001Sglebiusstatic void* 73290001Sglebiusskip_efields( 74290001Sglebius u_int32 *head, /* head of extension chain */ 75290001Sglebius u_int32 *tail /* tail/end of extension chain */ 76290001Sglebius ) 77290001Sglebius{ 78290001Sglebius 79290001Sglebius u_int nlen; /* next extension length */ 80290001Sglebius while ((tail - head) > 6) { 81290001Sglebius nlen = ntohl(*head++) & 0xffff; 82290001Sglebius nlen = (nlen + 3) >> 2; 83290001Sglebius if (nlen > (u_int)(tail - head) || nlen < 4) 84290001Sglebius return NULL; /* Blooper! Inconsistent! */ 85290001Sglebius head += nlen; 86290001Sglebius } 87290001Sglebius return head; 88290001Sglebius} 89290001Sglebius 90290001Sglebius/* 91290001Sglebius** Check if it's data for us and whether it's useable or not. 92290001Sglebius** 93290001Sglebius** If not, return a failure code so we can delete this server from our list 94290001Sglebius** and continue with another one. 95290001Sglebius*/ 96290001Sglebiusint 97290001Sglebiusprocess_pkt ( 98290001Sglebius struct pkt *rpkt, 99290001Sglebius sockaddr_u *sender, 100290001Sglebius int pkt_len, 101290001Sglebius int mode, 102290001Sglebius struct pkt *spkt, 103290001Sglebius const char * func_name 104290001Sglebius ) 105290001Sglebius{ 106290001Sglebius u_int key_id; 107290001Sglebius struct key * pkt_key; 108290001Sglebius int is_authentic; 109290001Sglebius int mac_size; 110290001Sglebius u_int exten_len; 111290001Sglebius u_int32 * exten_end; 112290001Sglebius u_int32 * packet_end; 113290001Sglebius l_fp sent_xmt; 114290001Sglebius l_fp resp_org; 115290001Sglebius 116290001Sglebius // key_id = 0; 117290001Sglebius pkt_key = NULL; 118290001Sglebius is_authentic = (HAVE_OPT(AUTHENTICATION)) ? 0 : -1; 119290001Sglebius 120290001Sglebius /* 121290001Sglebius * Parse the extension field if present. We figure out whether 122290001Sglebius * an extension field is present by measuring the MAC size. If 123290001Sglebius * the number of words following the packet header is 0, no MAC 124290001Sglebius * is present and the packet is not authenticated. If 1, the 125290001Sglebius * packet is a crypto-NAK; if 3, the packet is authenticated 126290001Sglebius * with DES; if 5, the packet is authenticated with MD5; if 6, 127290001Sglebius * the packet is authenticated with SHA. If 2 or 4, the packet 128290001Sglebius * is a runt and discarded forthwith. If greater than 6, an 129290001Sglebius * extension field is present, so we subtract the length of the 130290001Sglebius * field and go around again. 131290001Sglebius */ 132290001Sglebius if (pkt_len < (int)LEN_PKT_NOMAC || (pkt_len & 3) != 0) { 133290001Sglebius msyslog(LOG_ERR, 134290001Sglebius "%s: Incredible packet length: %d. Discarding.", 135290001Sglebius func_name, pkt_len); 136290001Sglebius return PACKET_UNUSEABLE; 137290001Sglebius } 138290001Sglebius /* Note: pkt_len must be a multiple of 4 at this point! */ 139293896Sglebius packet_end = (void*)((char*)rpkt + pkt_len); 140290001Sglebius exten_end = skip_efields(rpkt->exten, packet_end); 141290001Sglebius if (NULL == exten_end) { 142290001Sglebius msyslog(LOG_ERR, 143290001Sglebius "%s: Missing extension field. Discarding.", 144290001Sglebius func_name); 145290001Sglebius return PACKET_UNUSEABLE; 146290001Sglebius } 147290001Sglebius /* get size of MAC in cells; can be zero */ 148290001Sglebius exten_len = (u_int)(packet_end - exten_end); 149290001Sglebius 150290001Sglebius /* deduce action required from remaining length */ 151290001Sglebius switch (exten_len) { 152290001Sglebius 153290001Sglebius case 0: /* no MAC at all */ 154290001Sglebius break; 155290001Sglebius 156290001Sglebius case 1: /* crypto NAK */ 157290001Sglebius key_id = ntohl(*exten_end); 158290001Sglebius printf("Crypto NAK = 0x%08x\n", key_id); 159290001Sglebius break; 160290001Sglebius 161290001Sglebius case 3: /* key ID + 3DES MAC -- unsupported! */ 162290001Sglebius msyslog(LOG_ERR, 163290001Sglebius "%s: Key ID + 3DES MAC is unsupported. Discarding.", 164290001Sglebius func_name); 165290001Sglebius return PACKET_UNUSEABLE; 166290001Sglebius 167290001Sglebius case 5: /* key ID + MD5 MAC */ 168290001Sglebius case 6: /* key ID + SHA MAC */ 169290001Sglebius /* 170290001Sglebius ** Look for the key used by the server in the specified 171290001Sglebius ** keyfile and if existent, fetch it or else leave the 172290001Sglebius ** pointer untouched 173290001Sglebius */ 174290001Sglebius key_id = ntohl(*exten_end); 175290001Sglebius get_key(key_id, &pkt_key); 176290001Sglebius if (!pkt_key) { 177290001Sglebius printf("unrecognized key ID = 0x%08x\n", key_id); 178290001Sglebius break; 179290001Sglebius } 180290001Sglebius /* 181290001Sglebius ** Seems like we've got a key with matching keyid. 182290001Sglebius ** 183290001Sglebius ** Generate a md5sum of the packet with the key from our 184290001Sglebius ** keyfile and compare those md5sums. 185290001Sglebius */ 186290001Sglebius mac_size = exten_len << 2; 187294905Sdelphij if (!auth_md5(rpkt, pkt_len - mac_size, 188290001Sglebius mac_size - 4, pkt_key)) { 189290001Sglebius is_authentic = FALSE; 190290001Sglebius break; 191290001Sglebius } 192290001Sglebius /* Yay! Things worked out! */ 193290001Sglebius is_authentic = TRUE; 194290001Sglebius TRACE(1, ("sntp %s: packet from %s authenticated using key id %d.\n", 195290001Sglebius func_name, stoa(sender), key_id)); 196290001Sglebius break; 197290001Sglebius 198290001Sglebius default: 199290001Sglebius msyslog(LOG_ERR, 200290001Sglebius "%s: Unexpected extension length: %d. Discarding.", 201290001Sglebius func_name, exten_len); 202290001Sglebius return PACKET_UNUSEABLE; 203290001Sglebius } 204290001Sglebius 205290001Sglebius switch (is_authentic) { 206290001Sglebius 207290001Sglebius case -1: /* unknown */ 208290001Sglebius break; 209290001Sglebius 210290001Sglebius case 0: /* not authentic */ 211290001Sglebius return SERVER_AUTH_FAIL; 212290001Sglebius break; 213290001Sglebius 214290001Sglebius case 1: /* authentic */ 215290001Sglebius break; 216290001Sglebius 217290001Sglebius default: /* error */ 218290001Sglebius break; 219290001Sglebius } 220290001Sglebius 221290001Sglebius /* Check for server's ntp version */ 222290001Sglebius if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION || 223290001Sglebius PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) { 224290001Sglebius msyslog(LOG_ERR, 225290001Sglebius "%s: Packet shows wrong version (%d)", 226290001Sglebius func_name, PKT_VERSION(rpkt->li_vn_mode)); 227290001Sglebius return SERVER_UNUSEABLE; 228290001Sglebius } 229290001Sglebius /* We want a server to sync with */ 230290001Sglebius if (PKT_MODE(rpkt->li_vn_mode) != mode && 231290001Sglebius PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) { 232290001Sglebius msyslog(LOG_ERR, 233290001Sglebius "%s: mode %d stratum %d", func_name, 234290001Sglebius PKT_MODE(rpkt->li_vn_mode), rpkt->stratum); 235290001Sglebius return SERVER_UNUSEABLE; 236290001Sglebius } 237290001Sglebius /* Stratum is unspecified (0) check what's going on */ 238290001Sglebius if (STRATUM_PKT_UNSPEC == rpkt->stratum) { 239290001Sglebius char *ref_char; 240290001Sglebius 241290001Sglebius TRACE(1, ("%s: Stratum unspecified, going to check for KOD (stratum: %d)\n", 242290001Sglebius func_name, rpkt->stratum)); 243290001Sglebius ref_char = (char *) &rpkt->refid; 244290001Sglebius TRACE(1, ("%s: Packet refid: %c%c%c%c\n", func_name, 245290001Sglebius ref_char[0], ref_char[1], ref_char[2], ref_char[3])); 246290001Sglebius /* If it's a KOD packet we'll just use the KOD information */ 247290001Sglebius if (ref_char[0] != 'X') { 248290001Sglebius if (strncmp(ref_char, "DENY", 4) == 0) 249290001Sglebius return KOD_DEMOBILIZE; 250290001Sglebius if (strncmp(ref_char, "RSTR", 4) == 0) 251290001Sglebius return KOD_DEMOBILIZE; 252290001Sglebius if (strncmp(ref_char, "RATE", 4) == 0) 253290001Sglebius return KOD_RATE; 254290001Sglebius /* 255290001Sglebius ** There are other interesting kiss codes which 256290001Sglebius ** might be interesting for authentication. 257290001Sglebius */ 258290001Sglebius } 259290001Sglebius } 260290001Sglebius /* If the server is not synced it's not really useable for us */ 261290001Sglebius if (LEAP_NOTINSYNC == PKT_LEAP(rpkt->li_vn_mode)) { 262290001Sglebius msyslog(LOG_ERR, 263290001Sglebius "%s: %s not in sync, skipping this server", 264290001Sglebius func_name, stoa(sender)); 265290001Sglebius return SERVER_UNUSEABLE; 266290001Sglebius } 267290001Sglebius 268290001Sglebius /* 269290001Sglebius * Decode the org timestamp and make sure we're getting a response 270290001Sglebius * to our last request, but only if we're not in broadcast mode. 271290001Sglebius */ 272290001Sglebius if (MODE_BROADCAST == mode) 273290001Sglebius return pkt_len; 274290001Sglebius 275290001Sglebius if (!L_ISEQU(&rpkt->org, &spkt->xmt)) { 276290001Sglebius NTOHL_FP(&rpkt->org, &resp_org); 277290001Sglebius NTOHL_FP(&spkt->xmt, &sent_xmt); 278290001Sglebius msyslog(LOG_ERR, 279290001Sglebius "%s response org expected to match sent xmt", 280290001Sglebius stoa(sender)); 281290001Sglebius msyslog(LOG_ERR, "resp org: %s", prettydate(&resp_org)); 282290001Sglebius msyslog(LOG_ERR, "sent xmt: %s", prettydate(&sent_xmt)); 283290001Sglebius return PACKET_UNUSEABLE; 284290001Sglebius } 285290001Sglebius 286290001Sglebius return pkt_len; 287290001Sglebius} 288