[Bug 1459] sntp MD5 authentication does not work with ntpd.
Many sntp fixes from Nelson Boyard:
1. sntp properly handles hex key strings in the keyfile, instead of
just treating the text as binary strings.
2. sntp handles SHA1 as well as MD5.
3. If the key file contains more than one key, sntp can use keys
other than the first one in the file.
4. sntp computes the MAC using the key, then the data (was other way).
5. sntp handles responses with extensions now.
6. sntp implements the -b (broadcast/multicast) option. Tested.
The -b option takes an argument which is the broadcast/multicast
address on which to listen for packets. The command still requires a
host address at the end of the command line. This is the address
expected to do the broadcasting/multicasting.
7. sntp correctly handles network-order key IDs on little-endian.
8. Eliminated a huge amount of code duplication between the sntp
unicast and multicast code paths, creating a single process_pkt()
function that is used for both paths after the packet is received.
9. The -t timeout option now works with both unicast and multicast.
Previously it worked with neither. It wasn't coded for unicast, and
was incorrectly coded for multicast.
10. In unicast mode, the requests are sent MACed using the key
specified with the -a command line option.
11. Cleaned up indentation consistent with the predominant style in
the files changed, namely, using tab stop characters and not spaces
for indentation. This way, you can set your tab stop spacing as you
like it and the indentation looks correct. This was not a big change.
The files were mostly consistent before.
12. Eliminated LOTS of blank lines.
sntp/configure.ac OpenSSL support now that sntp optionally uses it.
Escape unprintable characters in a refid in ntpq -p billboard.
bk: 4be6572580YE-GbhOQwPTAggM9bScQ
---
+* [Bug 1325] unreachable code in sntp recv_bcst_data().
+* [Bug 1459] sntp MD5 authentication does not work with ntpd.
* [Bug 1512] ntpsnmpd should connect to net-snmpd via a unix-domain
socket by default. Provide a command-line 'socket name' option.
* [Bug 1538] update refclock_nmea.c's call to getprotobyname().
* [Bug 1541] Fix wrong keyword for "maxclock".
+* Many sntp fixes from Nelson Boyard:
+ 1. sntp properly handles hex key strings in the keyfile, instead of
+ just treating the text as binary strings.
+ 2. sntp handles SHA1 as well as MD5.
+ 3. If the key file contains more than one key, sntp can use keys
+ other than the first one in the file.
+ 4. sntp computes the MAC using the key, then the data (was other way).
+ 5. sntp handles responses with extensions now.
+ 6. sntp implements the -b (broadcast/multicast) option. Tested.
+ The -b option takes an argument which is the broadcast/multicast
+ address on which to listen for packets. The command still requires a
+ host address at the end of the command line. This is the address
+ expected to do the broadcasting/multicasting.
+ 7. sntp correctly handles network-order key IDs on little-endian.
+ 8. Eliminated a huge amount of code duplication between the sntp
+ unicast and multicast code paths, creating a single process_pkt()
+ function that is used for both paths after the packet is received.
+ 9. The -t timeout option now works with both unicast and multicast.
+ Previously it worked with neither. It wasn't coded for unicast, and
+ was incorrectly coded for multicast.
+ 10. In unicast mode, the requests are sent MACed using the key
+ specified with the -a command line option.
+ 11. Cleaned up indentation consistent with the predominant style in
+ the files changed, namely, using tab stop characters and not spaces
+ for indentation. This way, you can set your tab stop spacing as you
+ like it and the indentation looks correct. This was not a big change.
+ The files were mostly consistent before.
+ 12. Eliminated LOTS of blank lines.
+* sntp/configure.ac OpenSSL support now that sntp optionally uses it.
+* Escape unprintable characters in a refid in ntpq -p billboard.
---
(4.2.6p2-RC2) 2010/04/27 Released by Harlan Stenn <stenn@ntp.org>
# AC_SUBST(LIBRSAREF)
# AC_SUBST(MAKE_LIBRSAREF)
-AC_SUBST(OPENSSL)
-AC_SUBST(OPENSSL_INC)
-AC_SUBST(OPENSSL_LIB)
-
-AC_MSG_CHECKING([for openssl library directory])
-AC_ARG_WITH(openssl-libdir,
- AC_HELP_STRING([--with-openssl-libdir], [+ =/something/reasonable]),
-[ans=$withval],
-[case "$build" in
- $host) ans=yes ;;
- *) ans=no ;;
-esac])
-case "$ans" in
- no) ;;
- yes) # Look in:
- ans="/usr/lib /usr/lib/openssl /usr/sfw/lib /usr/local/lib /usr/local/ssl/lib /lib"
- ;;
- *) # Look where they said
- ;;
-esac
-case "$ans" in
- no) ;;
- *) # Look for libcrypto.a and libssl.a:
- for i in $ans no
- do
- case "$host" in
- *-*-darwin*)
- test -f $i/libcrypto.dylib -a -f $i/libssl.dylib && break
- ;;
- *)
- test -f $i/libcrypto.so -a -f $i/libssl.so && break
- test -f $i/libcrypto.a -a -f $i/libssl.a && break
- ;;
- esac
- done
- case "$i" in
- no)
- ans=no
- OPENSSL_LIB=
- ;;
- *) ans=$i
- OPENSSL_LIB=$ans
- ;;
- esac
- ;;
-esac
-AC_MSG_RESULT([$ans])
-
-AC_MSG_CHECKING([for openssl include directory])
-AC_ARG_WITH(openssl-incdir,
- AC_HELP_STRING([--with-openssl-incdir], [+ =/something/reasonable]),
-[ans=$withval],
-[case "$build" in
- $host) ans=yes ;;
- *) ans=no ;;
-esac])
-case "$ans" in
- no) ;;
- yes) # look in:
- ans="/usr/include /usr/sfw/include /usr/local/include /usr/local/ssl/include"
- ;;
- *) # Look where they said
- ;;
-esac
-case "$ans" in
- no) ;;
- *) # look for openssl/opensslconf.h:
- for i in $ans no
- do
- test -f $i/openssl/opensslconf.h && break
- done
- case "$i" in
- no)
- ans=no
- OPENSSL_INC=
- ;;
- *) ans=$i
- OPENSSL_INC=$ans
- ;;
- esac
- ;;
-esac
-AC_MSG_RESULT([$ans])
-
-AC_MSG_CHECKING([if we will use crypto])
-AC_ARG_WITH(crypto,
- AC_HELP_STRING([--with-crypto], [+ =openssl]),
-[ans=$withval], [ans=yes])
-case "$ans" in
- no)
- ;;
- yes|openssl)
- if test -z "$OPENSSL_LIB" -o -z "$OPENSSL_INC"
- then
- ans=no
- else
- ans=yes
- fi
-esac
-ntp_openssl=$ans
-AC_MSG_RESULT([$ans])
-
-case "$ntp_openssl" in
- yes)
- # We have OpenSSL inc/lib - use them.
- CPPFLAGS="$CPPFLAGS -I$OPENSSL_INC"
- LDFLAGS="$LDFLAGS -L$OPENSSL_LIB"
- case "$need_dash_r" in
- 1) LDFLAGS="$LDFLAGS -R$OPENSSL_LIB"
- esac
- AC_SUBST(LCRYPTO, [-lcrypto])
- AC_DEFINE(OPENSSL, , [Use OpenSSL?])
-esac
-
-#
-# Older OpenSSL headers have a number of callback prototypes inside
-# other function prototypes which trigger copious warnings with gcc's
-# -Wstrict-prototypes, which is included in -Wall.
-#
-# An example:
-#
-# int i2d_RSA_NET(const RSA *a, unsigned char **pp,
-# int (*cb)(), int sgckey);
-# ^^^^^^^^^^^
-#
-#
-#
-openssl_triggers_warnings=unknown
-SAVED_CFLAGS="$CFLAGS"
-
-case "$GCC$ntp_openssl" in
- yesyes)
- CFLAGS="$CFLAGS -Werror"
- AC_COMPILE_IFELSE(
- AC_LANG_SOURCE([[ /* see if -Werror breaks gcc */ ]]),
- [gcc_handles_Werror=yes],
- [gcc_handles_Werror=no]
- )
- case "$gcc_handles_Werror" in
- no)
- # if this gcc doesn't do -Werror go ahead and use
- # -Wstrict-prototypes.
- openssl_triggers_warnings=yes
- ;;
- yes)
- CFLAGS="$CFLAGS -Wstrict-prototypes"
- AC_COMPILE_IFELSE(
- AC_LANG_PROGRAM(
- [[
- #include "openssl/asn1_mac.h"
- #include "openssl/bn.h"
- #include "openssl/err.h"
- #include "openssl/evp.h"
- #include "openssl/pem.h"
- #include "openssl/rand.h"
- #include "openssl/x509v3.h"
- ]],
- [[ /* empty body */ ]]
- ),
- [openssl_triggers_warnings=no],
- [openssl_triggers_warnings=yes]
- )
- esac
- case "$openssl_triggers_warnings" in
- yes)
- CFLAGS="$SAVED_CFLAGS -Wno-strict-prototypes"
- ;;
- *)
- CFLAGS="$SAVED_CFLAGS -Wstrict-prototypes"
- esac
- ;;
- yesno)
- # gcc without OpenSSL
- CFLAGS="$SAVED_CFLAGS -Wstrict-prototypes"
-esac
+NTP_OPENSSL
AC_MSG_CHECKING([if we want to compile with ElectricFence])
AC_ARG_WITH(electricfence,
--- /dev/null
+dnl ######################################################################
+dnl OpenSSL support shared by top-level and sntp/configure.ac
+AC_DEFUN([NTP_OPENSSL], [
+
+AC_SUBST(OPENSSL)
+AC_SUBST(OPENSSL_INC)
+AC_SUBST(OPENSSL_LIB)
+
+AC_MSG_CHECKING([for openssl library directory])
+AC_ARG_WITH(openssl-libdir,
+ AC_HELP_STRING([--with-openssl-libdir], [+ =/something/reasonable]),
+[ans=$withval],
+[case "$build" in
+ $host) ans=yes ;;
+ *) ans=no ;;
+esac])
+case "$ans" in
+ no) ;;
+ yes) # Look in:
+ ans="/usr/lib /usr/lib/openssl /usr/sfw/lib /usr/local/lib /usr/local/ssl/lib /lib"
+ ;;
+ *) # Look where they said
+ ;;
+esac
+case "$ans" in
+ no) ;;
+ *) # Look for libcrypto.a and libssl.a:
+ for i in $ans no
+ do
+ case "$host" in
+ *-*-darwin*)
+ test -f $i/libcrypto.dylib -a -f $i/libssl.dylib && break
+ ;;
+ *)
+ test -f $i/libcrypto.so -a -f $i/libssl.so && break
+ test -f $i/libcrypto.a -a -f $i/libssl.a && break
+ ;;
+ esac
+ done
+ case "$i" in
+ no)
+ ans=no
+ OPENSSL_LIB=
+ ;;
+ *) ans=$i
+ OPENSSL_LIB=$ans
+ ;;
+ esac
+ ;;
+esac
+AC_MSG_RESULT([$ans])
+
+AC_MSG_CHECKING([for openssl include directory])
+AC_ARG_WITH(openssl-incdir,
+ AC_HELP_STRING([--with-openssl-incdir], [+ =/something/reasonable]),
+[ans=$withval],
+[case "$build" in
+ $host) ans=yes ;;
+ *) ans=no ;;
+esac])
+case "$ans" in
+ no) ;;
+ yes) # look in:
+ ans="/usr/include /usr/sfw/include /usr/local/include /usr/local/ssl/include"
+ ;;
+ *) # Look where they said
+ ;;
+esac
+case "$ans" in
+ no) ;;
+ *) # look for openssl/opensslconf.h:
+ for i in $ans no
+ do
+ test -f $i/openssl/opensslconf.h && break
+ done
+ case "$i" in
+ no)
+ ans=no
+ OPENSSL_INC=
+ ;;
+ *) ans=$i
+ OPENSSL_INC=$ans
+ ;;
+ esac
+ ;;
+esac
+AC_MSG_RESULT([$ans])
+
+AC_MSG_CHECKING([if we will use crypto])
+AC_ARG_WITH(crypto,
+ AC_HELP_STRING([--with-crypto], [+ =openssl]),
+[ans=$withval], [ans=yes])
+case "$ans" in
+ no)
+ ;;
+ yes|openssl)
+ if test -z "$OPENSSL_LIB" -o -z "$OPENSSL_INC"
+ then
+ ans=no
+ else
+ ans=yes
+ fi
+esac
+ntp_openssl=$ans
+AC_MSG_RESULT([$ans])
+
+case "$ntp_openssl" in
+ yes)
+ # We have OpenSSL inc/lib - use them.
+ CPPFLAGS="$CPPFLAGS -I$OPENSSL_INC"
+ LDFLAGS="$LDFLAGS -L$OPENSSL_LIB"
+ case "$need_dash_r" in
+ 1) LDFLAGS="$LDFLAGS -R$OPENSSL_LIB"
+ esac
+ AC_SUBST(LCRYPTO, [-lcrypto])
+ AC_DEFINE(OPENSSL, , [Use OpenSSL?])
+esac
+
+#
+# Older OpenSSL headers have a number of callback prototypes inside
+# other function prototypes which trigger copious warnings with gcc's
+# -Wstrict-prototypes, which is included in -Wall.
+#
+# An example:
+#
+# int i2d_RSA_NET(const RSA *a, unsigned char **pp,
+# int (*cb)(), int sgckey);
+# ^^^^^^^^^^^
+#
+#
+#
+openssl_triggers_warnings=unknown
+SAVED_CFLAGS="$CFLAGS"
+
+case "$GCC$ntp_openssl" in
+ yesyes)
+ CFLAGS="$CFLAGS -Werror"
+ AC_COMPILE_IFELSE(
+ AC_LANG_SOURCE([[ /* see if -Werror breaks gcc */ ]]),
+ [gcc_handles_Werror=yes],
+ [gcc_handles_Werror=no]
+ )
+ case "$gcc_handles_Werror" in
+ no)
+ # if this gcc doesn't do -Werror go ahead and use
+ # -Wstrict-prototypes.
+ openssl_triggers_warnings=yes
+ ;;
+ yes)
+ CFLAGS="$CFLAGS -Wstrict-prototypes"
+ AC_COMPILE_IFELSE(
+ AC_LANG_PROGRAM(
+ [[
+ #include "openssl/asn1_mac.h"
+ #include "openssl/bn.h"
+ #include "openssl/err.h"
+ #include "openssl/evp.h"
+ #include "openssl/pem.h"
+ #include "openssl/rand.h"
+ #include "openssl/x509v3.h"
+ ]],
+ [[ /* empty body */ ]]
+ ),
+ [openssl_triggers_warnings=no],
+ [openssl_triggers_warnings=yes]
+ )
+ esac
+ case "$openssl_triggers_warnings" in
+ yes)
+ CFLAGS="$SAVED_CFLAGS -Wno-strict-prototypes"
+ ;;
+ *)
+ CFLAGS="$SAVED_CFLAGS -Wstrict-prototypes"
+ esac
+ ;;
+ yesno)
+ # gcc without OpenSSL
+ CFLAGS="$SAVED_CFLAGS -Wstrict-prototypes"
+esac
+
+])
+dnl ======================================================================
{
char *name;
char *value = NULL;
+ size_t len;
int i;
int c;
havevar[HAVE_REFID] = 1;
if (*value == '\0') {
dstadr_refid = "0.0.0.0";
- } else if ((int)strlen(value) <= 4) {
+ } else if ((i = (int)strlen(value)) <= 4) {
refid_string[0] = '.';
(void) strcpy(&refid_string[1], value);
i = strlen(refid_string);
fprintf(fp, "%-*s ", maxhostlen, currenthost);
if (af == 0 || AF(&srcadr) == af) {
strncpy(clock_name, nntohost(&srcadr), sizeof(clock_name));
+ fprintf(fp, "%c%-15.15s ", c, clock_name);
+ len = strlen(dstadr_refid);
+ makeascii(len, dstadr_refid, fp);
+ while (len++ < 15)
+ fputc(' ', fp);
fprintf(fp,
- "%c%-15.15s %-15.15s %2ld %c %4.4s %4.4s %3lo %7.7s %8.7s %7.7s\n",
- c, clock_name, dstadr_refid, stratum, type,
+ " %2ld %c %4.4s %4.4s %3lo %7.7s %8.7s %7.7s\n",
+ stratum, type,
prettyinterval(whenbuf, sizeof(whenbuf),
when(&ts, &rec, &reftime)),
prettyinterval(pollbuf, sizeof(pollbuf),
static void error (const char *, const char *, const char *);
static u_long getkeyid (const char *);
static void atoascii (const char *, size_t, char *, size_t);
-static void makeascii (int, char *, FILE *);
static void cookedprint (int, int, char *, int, int, FILE *);
static void rawprint (int, int, char *, int, int, FILE *);
static void startoutput (void);
* makeascii - print possibly ascii data using the character
* transformations that cat -v uses.
*/
-static void
+void
makeascii(
int length,
char *data,
extern void printvars (int, char *, int, int, int, FILE *);
extern int decodeint (char *, long *);
extern int findvar (char *, struct ctl_var *, int code);
+extern void makeascii (int, char *, FILE *);
-I$(top_srcdir)/../lib/isc/nothreads/include \
-I$(top_srcdir)/../lib/isc/unix/include
-LDADD= $(LIBOPTS_LDADD) -lm ../libntp/libntp.a
+LDADD= $(LIBOPTS_LDADD) -lm ../libntp/libntp.a @LCRYPTO@
run_ag= cd $(srcdir) && env PATH="$(abs_builddir):$(PATH)" \
autogen -L ../include --writable
yes)
CFLAGS="$CFLAGS -Wstrict-overflow"
esac
+ # -W[no-]strict-prototypes is added later depending on OpenSSL
esac
# HMS: These need to be moved to AM_CPPFLAGS and/or AM_CFLAGS
esac
AC_TYPE_UID_T
+NTP_OPENSSL
+
AC_MSG_CHECKING([type of socklen arg for getsockname()])
AC_CACHE_VAL(ac_cv_func_getsockname_arg2,dnl
[AC_CACHE_VAL(ac_cv_func_getsockname_socklen_type,dnl
#include "crypto.h"
+#include <ctype.h>
struct key *key_ptr;
int key_cnt = 0;
-/* Generates a md5 digest of the ntp packet (exluding the MAC) concatinated
- * with the key specified in keyid and compares this digest to the digest in
- * the packet's MAC. If they're equal this function returns 1 (packet is
- * authentic) or else 0 (not authentic).
- */
+static int
+s_keytype_from_text(
+ const char *text
+ )
+{
+ int key_type;
+#ifdef OPENSSL
+ char upcased[EVP_MAX_MD_SIZE];
+ char * pch;
+
+ /*
+ * OpenSSL digest short names are capitalized, so uppercase the
+ * digest name before passing to OBJ_sn2nid(). If it is not
+ * recognized but begins with 'M' use NID_md5 to be consistent
+ * with past behavior.
+ */
+ strncpy(upcased, text, sizeof upcased);
+ for (pch = upcased; '\0' != *pch; pch++)
+ *pch = (char)toupper(*pch);
+ key_type = OBJ_sn2nid(upcased);
+#else
+ key_type = 0;
+#endif
+
+ if (!key_type && 'm' == tolower(text[0]))
+ key_type = NID_md5;
+ return key_type;
+}
+
int
-auth_md5(
+make_mac(
char *pkt_data,
+ int pkt_size,
int mac_size,
- struct key *cmp_key
+ struct key *cmp_key,
+ char * digest
)
{
- register int a;
- char digest[16];
+ unsigned int len = mac_size;
+#ifdef OPENSSL
+ int key_type;
+ EVP_MD_CTX ctx;
+#else
MD5_CTX ctx;
- char *digest_data;
-
- if (cmp_key->type != 'M')
- return -1;
+#endif /* OPENSSL */
+ if (cmp_key->key_len > 64)
+ return 0;
+ if (pkt_size % 4 != 0)
+ return 0;
+#ifdef OPENSSL
+ INIT_SSL();
+ key_type = s_keytype_from_text(cmp_key->type);
+ EVP_DigestInit(&ctx, EVP_get_digestbynid(key_type));
+ EVP_DigestUpdate(&ctx, (u_char *)cmp_key->key_seq, (u_int)cmp_key->key_len);
+ EVP_DigestUpdate(&ctx, (u_char *)pkt_data, (u_int)pkt_size);
+ EVP_DigestFinal(&ctx, (u_char *)digest, &len);
+#else /* OPENSSL */
+ if ((cmp_key->type[0] | 0x20) != 'm')
+ return 0;
+ if (mac_size < 16)
+ return 0;
MD5Init(&ctx);
+ MD5Update(&ctx, (u_char *)cmp_key->key_seq, (u_int)cmp_key->key_len);
+ MD5Update(&ctx, (u_char *)pkt_data, (u_int)pkt_size);
+ MD5Final((u_char *)digest, &ctx);
+ len = 16;
+#endif /* OPENSSL */
- digest_data = emalloc(sizeof(char) * (LEN_PKT_NOMAC + cmp_key->key_len));
-
- for (a = 0; a < LEN_PKT_NOMAC; a++)
- digest_data[a] = pkt_data[a];
+ return (int)len;
+}
- for (a = 0; a < cmp_key->key_len; a++)
- digest_data[LEN_PKT_NOMAC + a] = cmp_key->key_seq[a];
- MD5Update(&ctx, (u_char *)digest_data, LEN_PKT_NOMAC + cmp_key->key_len);
- MD5Final((u_char *)digest, &ctx);
+/* Generates a md5 digest of the key specified in keyid concatinated with the
+ * ntp packet (exluding the MAC) and compares this digest to the digest in
+ * the packet's MAC. If they're equal this function returns 1 (packet is
+ * authentic) or else 0 (not authentic).
+ */
+int
+auth_md5(
+ char *pkt_data,
+ int pkt_size,
+ int mac_size,
+ struct key *cmp_key
+ )
+{
+ int rv;
+ char digest[20];
- free(digest_data);
+ if (mac_size > sizeof digest)
+ return 0;
+ rv = make_mac(pkt_data, pkt_size, sizeof digest, cmp_key, digest);
+ return rv ? !memcmp(digest, pkt_data + pkt_size + 4, rv) : rv;
+}
- for (a = 0; a < 16; a++)
- if (digest[a] != pkt_data[LEN_PKT_MAC + a])
- return 0;
+static int
+hex_val( unsigned char x)
+{
+ static const short vals [] =
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15 };
- return 1;
+ return (x < '0' || x > 'f') ? -1 : vals[x - '0'];
}
/* Load keys from the specified keyfile into the key structures.
{
FILE *keyf = fopen(keyfile, "r");
struct key *prev = NULL;
- register int a, line_limit;
int scan_cnt, line_cnt = 0;
- char kbuf[96];
+ char kbuf[200];
+ char keystring[129];
if (keyf == NULL) {
if (ENABLED_OPT(NORMALVERBOSE))
printf("sntp auth_init: Couldn't open key file %s for reading!\n", keyfile);
-
return -1;
}
-
- line_cnt = 0;
-
if (feof(keyf)) {
if (ENABLED_OPT(NORMALVERBOSE))
printf("sntp auth_init: Key file %s is empty!\n", keyfile);
fclose(keyf);
-
return -1;
}
-
+ key_cnt = 0;
while (!feof(keyf)) {
+ char * octothorpe;
struct key *act = emalloc(sizeof(struct key));
- line_limit = 0;
+ int goodline = 0;
fgets(kbuf, sizeof(kbuf), keyf);
-
- for (a = 0; a < strlen(kbuf) && a < sizeof(kbuf); a++) {
- if (kbuf[a] == '#') {
- line_limit = a;
- break;
- }
- }
-
- if (line_limit != 0)
- kbuf[line_limit] = '\0';
-
+ kbuf[199] = '\0';
+ octothorpe = strchr(kbuf, '#');
+ if (octothorpe)
+ *octothorpe = '\0';
#ifdef DEBUG
printf("sntp auth_init: fgets: %s", kbuf);
#endif
-
-
- if ((scan_cnt = sscanf(kbuf, "%i %c %16s", &act->key_id, &act->type, act->key_seq)) == 3) {
- act->key_len = strlen(act->key_seq);
+ scan_cnt = sscanf(kbuf, "%i %10s %128s", &act->key_id, act->type, keystring);
+ if (scan_cnt == 3) {
+ int len = strlen(keystring);
+ if (len <= 20) {
+ act->key_len = len;
+ memcpy(act->key_seq, keystring, len+1);
+ goodline = 1;
+ } else if ((len & 1) != 0) {
+ goodline = 0; /* it's bad */
+ } else {
+ int j;
+ goodline = 1;
+ act->key_len = len >> 1;
+ for (j = 0; j < len; j+=2) {
+ int val;
+ val = (hex_val(keystring[j]) << 4) |
+ hex_val(keystring[j+1]);
+ if (val < 0) {
+ goodline = 0; /* it's bad */
+ break;
+ }
+ act->key_seq[j>>1] = (char)val;
+ }
+ }
+ }
+ if (goodline) {
act->next = NULL;
-
if (NULL == prev)
*keys = act;
else
prev->next = act;
prev = act;
-
key_cnt++;
-
#ifdef DEBUG
- printf("sntp auth_init: key_id %i type %c with key %s\n", act->key_id, act->type, act->key_seq);
+ printf("sntp auth_init: key_id %i type %s with key %s\n", act->key_id, act->type, act->key_seq);
#endif
} else {
#ifdef DEBUG
printf("sntp auth_init: scanf read %i items, doesn't look good, skipping line %i.\n", scan_cnt, line_cnt);
#endif
-
free(act);
}
-
line_cnt++;
}
-
fclose(keyf);
#ifdef DEBUG
STDLINE
printf("sntp auth_init: Read %i keys from file %s:\n", line_cnt, keyfile);
-
{
struct key *kptr = *keys;
+ register int a;
for (a = 0; a < key_cnt; a++) {
- printf("key_id %i type %c with key %s (key length: %i)\n",
+ printf("key_id %i type %s with key %s (key length: %i)\n",
kptr->key_id, kptr->type, kptr->key_seq, kptr->key_len);
kptr = kptr->next;
}
}
STDLINE
#endif
-
- key_cnt = line_cnt;
key_ptr = *keys;
-
- return line_cnt;
+ return key_cnt;
}
/* Looks for the key with keyid key_id and sets the d_key pointer to the
struct key **d_key
)
{
- register int a;
- struct key *itr_key = key_ptr;
+ struct key *itr_key;
if (key_cnt == 0)
return;
-
- for (a = 0; a < key_cnt && itr_key != NULL; a++) {
+ for (itr_key = key_ptr; itr_key; itr_key = itr_key->next) {
if (itr_key->key_id == key_id) {
*d_key = itr_key;
- return;
+ break;
}
}
-
return;
}
#include <ntp_fp.h>
#include <ntp.h>
-#include <ntp_md5.h>
+
+#ifdef OPENSSL
+#include "openssl/evp.h"
+#else
+#include "ntp_md5.h"
+#endif /* OPENSSSL */
+
#include <ntp_stdlib.h>
#include "utilities.h"
/* #include "sntp-opts.h" */
struct key {
+ struct key *next;
int key_id;
int key_len;
- char type;
- char key_seq[16];
- struct key *next;
+ char type[10];
+ char key_seq[64];
};
-int auth_md5(char *pkt_data, int mac_size, struct key *cmp_key);
int auth_init(const char *keyfile, struct key **keys);
void get_key(int key_id, struct key **d_key);
-
+int make_mac(char *pkt_data, int pkt_size, int mac_size, struct key *cmp_key, char *digest);
+int auth_md5(char *pkt_data, int pkt_size, int mac_size, struct key *cmp_key);
#endif
void set_li_vn_mode (struct pkt *spkt, char leap, char version, char mode);
int sntp_main (int argc, char **argv);
-int on_wire (struct addrinfo *host);
+int on_wire (struct addrinfo *host, struct addrinfo *bcastaddr);
int set_time (double offset);
int
main (
- int argc,
- char **argv
- )
+ int argc,
+ char **argv
+ )
{
return sntp_main(argc, argv);
}
*/
int
sntp_main (
- int argc,
- char **argv
- )
+ int argc,
+ char **argv
+ )
{
register int c;
struct kod_entry *reason = NULL;
int optct;
int sync_data_suc = 0;
+ struct addrinfo **bcastaddr = NULL;
struct addrinfo **resh = NULL;
struct addrinfo *ai;
int resc;
int kodc;
int ow_ret;
+ int bcast = 0;
char *hostname;
/* IPv6 available? */
#ifdef DEBUG
printf("No ipv6 support available, forcing ipv4\n");
#endif
- }
- else {
+ } else {
/* Check for options -4 and -6 */
if (HAVE_OPT(IPV4))
ai_fam_pref = AF_INET;
/* Considering employing a variable that prevents functions of doing anything until
* everything is initialized properly
*/
- resc = resolve_hosts(argv, argc, &resh, ai_fam_pref);
-
+ resc = resolve_hosts((const char **)argv, argc, &resh, ai_fam_pref);
if (resc < 1) {
printf("Unable to resolve hostname(s)\n");
return -1;
}
+ bcast = ENABLED_OPT(BROADCAST);
+ if (bcast) {
+ const char * myargv[2];
+
+ myargv[0] = OPT_ARG(BROADCAST);
+ myargv[1] = NULL;
+ bcast = resolve_hosts(myargv, 1, &bcastaddr, ai_fam_pref);
+ }
/* Select a certain ntp server according to simple criteria? For now
* let's just pay attention to previous KoDs.
ai = resh[c];
do {
hostname = addrinfo_to_str(ai);
-
if ((kodc = search_entry(hostname, &reason)) == 0) {
if (is_reachable(ai)) {
- ow_ret = on_wire(ai);
+ ow_ret = on_wire(ai, bcast ? bcastaddr[0] : NULL);
if (ow_ret < 0)
printf("on_wire failed for server %s!\n", hostname);
else
freeaddrinfo(resh[c]);
}
free(resh);
-
return 0;
}
+static union {
+ struct pkt pkt;
+ char buf[1500];
+} rbuf;
+
+#define r_pkt rbuf.pkt
+
/* The heart of (S)NTP, exchange NTP packets and compute values to correct the local clock */
int
on_wire (
- struct addrinfo *host
- )
+ struct addrinfo *host,
+ struct addrinfo *bcast
+ )
{
char logmsg[32 + INET6_ADDRSTRLEN];
char addr_buf[INET6_ADDRSTRLEN];
register int try;
SOCKET sock;
struct pkt x_pkt;
- struct pkt r_pkt;
char *ref;
+ struct key *pkt_key = NULL;
+ int key_id = 0;
+ if (ENABLED_OPT(AUTHENTICATION)) {
+ key_id = (int) OPT_ARG(AUTHENTICATION);
+ get_key(key_id, &pkt_key);
+ }
for(try=0; try<5; try++) {
struct timeval tv_xmt, tv_dst;
double t21, t34, delta, offset, precision, root_dispersion;
u_fp p_rdly, p_rdsp;
l_fp p_rec, p_xmt, p_ref, p_org, xmt, tmp, dst;
- memset(&r_pkt, 0, sizeof(r_pkt));
+ memset(&r_pkt, 0, sizeof rbuf);
memset(&x_pkt, 0, sizeof(x_pkt));
error = GETTIMEOFDAY(&tv_xmt, (struct timezone *)NULL);
-
tv_xmt.tv_sec += JAN_1970;
#ifdef DEBUG
(unsigned int) tv_xmt.tv_usec);
#endif
- TVTOTS(&tv_xmt, &xmt);
- HTONL_FP(&xmt, &(x_pkt.xmt));
-
- x_pkt.stratum = STRATUM_TO_PKT(STRATUM_UNSPEC);
- x_pkt.ppoll = 8;
- /* FIXME! Modus broadcast + adr. check -> bdr. pkt */
- set_li_vn_mode(&x_pkt, LEAP_NOTINSYNC, 4, 3);
-
- create_socket(&sock, (sockaddr_u *)host->ai_addr);
-
- sendpkt(sock, (sockaddr_u *)host->ai_addr, &x_pkt, LEN_PKT_NOMAC);
- rpktl = recvpkt(sock, &r_pkt, &x_pkt);
-
- closesocket(sock);
+ if (bcast) {
+ create_socket(&sock, (sockaddr_u *)bcast->ai_addr);
+ rpktl = recv_bcst_pkt(sock, &r_pkt, sizeof rbuf, (sockaddr_u *)bcast->ai_addr);
+ closesocket(sock);
+ } else {
+ int pkt_len = LEN_PKT_NOMAC;
+ TVTOTS(&tv_xmt, &xmt);
+ HTONL_FP(&xmt, &(x_pkt.xmt));
+ x_pkt.stratum = STRATUM_TO_PKT(STRATUM_UNSPEC);
+ x_pkt.ppoll = 8;
+ /* FIXME! Modus broadcast + adr. check -> bdr. pkt */
+ set_li_vn_mode(&x_pkt, LEAP_NOTINSYNC, 4, 3);
+ if (pkt_key != NULL) {
+ int mac_size = 20; /* max room for MAC */
+ x_pkt.exten[0] = htonl(key_id);
+ mac_size = make_mac((char *)&x_pkt, pkt_len, mac_size, pkt_key, (char *)&x_pkt.exten[1]);
+ if (mac_size)
+ pkt_len += mac_size + 4;
+ }
+ create_socket(&sock, (sockaddr_u *)host->ai_addr);
+ sendpkt(sock, (sockaddr_u *)host->ai_addr, &x_pkt, pkt_len);
+ rpktl = recvpkt(sock, &r_pkt, sizeof rbuf, &x_pkt);
+ closesocket(sock);
+ }
if(rpktl > 0)
sw_case = 1;
sw_case = rpktl;
switch(sw_case) {
- case SERVER_UNUSEABLE:
- return -1;
- break;
-
- case PACKET_UNUSEABLE:
- break;
-
- case SERVER_AUTH_FAIL:
- break;
-
- case KOD_DEMOBILIZE:
- /* Received a DENY or RESTR KOD packet */
- hostname = addrinfo_to_str(host);
- ref = (char *)&r_pkt.refid;
- add_entry(hostname, ref);
-
- if (ENABLED_OPT(NORMALVERBOSE))
- printf("sntp on_wire: Received KOD packet with code: %c%c%c%c from %s, demobilizing all connections\n",
- ref[0], ref[1], ref[2], ref[3],
- hostname);
-
- log_str = emalloc(INET6_ADDRSTRLEN + 72);
- snprintf(log_str, INET6_ADDRSTRLEN + 72,
- "Received a KOD packet with code %c%c%c%c from %s, demobilizing all connections",
- ref[0], ref[1], ref[2], ref[3],
- hostname);
- log_msg(log_str, 2);
- free(log_str);
- break;
-
- case KOD_RATE:
- /* Hmm... probably we should sleep a bit here */
- break;
-
- case 1:
-
+ case SERVER_UNUSEABLE:
+ return -1;
+ break;
+
+ case PACKET_UNUSEABLE:
+ break;
+
+ case SERVER_AUTH_FAIL:
+ break;
+
+ case KOD_DEMOBILIZE:
+ /* Received a DENY or RESTR KOD packet */
+ hostname = addrinfo_to_str(host);
+ ref = (char *)&r_pkt.refid;
+ add_entry(hostname, ref);
+
+ if (ENABLED_OPT(NORMALVERBOSE))
+ printf("sntp on_wire: Received KOD packet with code: %c%c%c%c from %s, demobilizing all connections\n",
+ ref[0], ref[1], ref[2], ref[3],
+ hostname);
+
+ log_str = emalloc(INET6_ADDRSTRLEN + 72);
+ snprintf(log_str, INET6_ADDRSTRLEN + 72,
+ "Received a KOD packet with code %c%c%c%c from %s, demobilizing all connections",
+ ref[0], ref[1], ref[2], ref[3],
+ hostname);
+ log_msg(log_str, 2);
+ free(log_str);
+ break;
+
+ case KOD_RATE:
+ /* Hmm... probably we should sleep a bit here */
+ break;
+
+ case 1:
/* Convert timestamps from network to host byte order */
p_rdly = NTOHS_FP(r_pkt.rootdelay);
p_rdsp = NTOHS_FP(r_pkt.rootdisp);
if (ENABLED_OPT(NORMALVERBOSE)) {
getnameinfo(host->ai_addr, host->ai_addrlen, addr_buf,
sizeof(addr_buf), NULL, 0, NI_NUMERICHOST);
-
printf("sntp on_wire: Received %i bytes from %s\n", rpktl, addr_buf);
}
/* Compute offset etc. */
GETTIMEOFDAY(&tv_dst, (struct timezone *)NULL);
-
tv_dst.tv_sec += JAN_1970;
-
tmp = p_rec;
L_SUB(&tmp, &p_org);
-
LFPTOD(&tmp, t21);
-
TVTOTS(&tv_dst, &dst);
-
tmp = dst;
L_SUB(&tmp, &p_xmt);
-
LFPTOD(&tmp, t34);
-
offset = (t21 + t34) / 2.;
delta = t21 - t34;
t21, t34, delta, offset);
ts_str = tv_to_str(&tv_dst);
-
printf("%s ", ts_str);
-
if(offset > 0)
printf("+");
-
printf("%.*f", digits, offset);
-
if (root_dispersion > 0.)
printf(" +/- %f secs", root_dispersion);
-
printf("\n");
-
free(ts_str);
if(ENABLED_OPT(SETTOD) || ENABLED_OPT(ADJTIME))
/* Compute the 8 bits for li_vn_mode */
void
set_li_vn_mode (
- struct pkt *spkt,
- char leap,
- char version,
- char mode
- )
+ struct pkt *spkt,
+ char leap,
+ char version,
+ char mode
+ )
{
if(leap > 3) {
* with adjtime()/adjusttimeofday().
*/
int
-set_time (
- double offset
- )
+set_time(
+ double offset
+ )
{
struct timeval tp;
strerror(errno));
return -1;
}
- else {
- return 0;
- }
+ return 0;
}
- else {
- tp.tv_sec = (int) offset;
- tp.tv_usec = offset - (double)((int)offset);
+ tp.tv_sec = (int) offset;
+ tp.tv_usec = offset - (double)((int)offset);
- if(ADJTIMEOFDAY(&tp, NULL) < 0) {
- printf("set_time: adjtime(): Time not set: %s\n",
- strerror(errno));
- return -1;
- }
- else {
- return 0;
- }
+ if(ADJTIMEOFDAY(&tp, NULL) < 0) {
+ printf("set_time: adjtime(): Time not set: %s\n",
+ strerror(errno));
+ return -1;
}
+ return 0;
}
*/
int
resolve_hosts (
- char **hosts,
+ const char **hosts,
int hostc,
struct addrinfo ***res,
int pref_family
return 0;
tres = emalloc(sizeof(struct addrinfo *) * hostc);
-
for (a = 0, resc = 0; a < hostc; a++) {
struct addrinfo hints;
int error;
tres[resc] = NULL;
-
#ifdef DEBUG
printf("sntp resolve_hosts: Starting host resolution for %s...\n", hosts[a]);
#endif
-
memset(&hints, 0, sizeof(hints));
-
if (AF_UNSPEC == pref_family)
hints.ai_family = PF_UNSPEC;
else
hints.ai_family = pref_family;
-
hints.ai_socktype = SOCK_DGRAM;
-
error = getaddrinfo(hosts[a], "123", &hints, &tres[resc]);
-
if (error) {
size_t msg_length = strlen(hosts[a]) + 21;
char *logmsg = (char *) emalloc(sizeof(char) * msg_length);
#ifdef DEBUG
printf("%s\n", logmsg);
#endif
-
log_msg(logmsg, 1);
free(logmsg);
} else {
free(tres);
*res = NULL;
}
-
return resc;
}
if (ENABLED_OPT(NORMALVERBOSE)) {
getnameinfo(&dest->sa, SOCKLEN(dest), adr_buf, sizeof(adr_buf), NULL, 0, NI_NUMERICHOST);
-
printf("sntp sendpkt: Sending packet to %s... ", adr_buf);
}
cc = sendto(rsock, (void *)pkt, len, 0, &dest->sa, SOCKLEN(dest));
-
if (cc == SOCKET_ERROR) {
#ifdef DEBUG
printf("\n sntp sendpkt: Socket error: %i. Couldn't send packet!\n", cc);
#endif
-
if (errno != EWOULDBLOCK && errno != ENOBUFS) {
-
+ /* oh well */
}
- } else if (ENABLED_OPT(NORMALVERBOSE))
+ } else if (ENABLED_OPT(NORMALVERBOSE)) {
printf("Packet sent.\n");
+ }
}
/* Receive raw data */
int
-recvdata (
- SOCKET rsock,
- sockaddr_u *sender,
- char *rdata,
- int rdata_length
- )
+recvdata(
+ SOCKET rsock,
+ sockaddr_u *sender,
+ char *rdata,
+ int rdata_length
+ )
{
GETSOCKNAME_SOCKLEN_TYPE slen;
int recvc;
#ifdef DEBUG
if (recvc > 0) {
printf("Received %d bytes from %s:\n", recvc, stoa(sender));
-
pkt_output((struct pkt *) rdata, recvc, stdout);
- }
- else {
+ } else {
saved_errno = errno;
printf("recvfrom error %d (%s)\n", errno, strerror(errno));
errno = saved_errno;
}
#endif
-
return recvc;
}
* here, especially for protocol independence and IPv6 multicast */
int
recv_bcst_data (
- SOCKET rsock,
- char *rdata,
- int rdata_len,
- sockaddr_u *sas,
- sockaddr_u *ras
- )
+ SOCKET rsock,
+ char *rdata,
+ int rdata_len,
+ sockaddr_u *sas,
+ sockaddr_u *ras
+ )
{
- struct timeval timeout_tv;
- fd_set bcst_fd;
char *buf;
int btrue = 1;
int recv_bytes = 0;
+ int rdy_socks;
+ GETSOCKNAME_SOCKLEN_TYPE ss_len;
+ struct timeval timeout_tv;
+ fd_set bcst_fd;
-
setsockopt(rsock, SOL_SOCKET, SO_REUSEADDR, &btrue, sizeof(btrue));
-
if (IS_IPV4(sas)) {
-#ifndef MCAST
- return BROADCAST_FAILED;
-#else
- struct ip_mreq mdevadr;
- TYPEOF_IP_MULTICAST_LOOP mtrue = 1;
-
-
if (bind(rsock, &sas->sa, SOCKLEN(sas)) < 0) {
if (ENABLED_OPT(NORMALVERBOSE))
- printf("sntp recv_bcst_data: Couldn't bind() address.\n");
+ printf("sntp recv_bcst_data: Couldn't bind() address %s:%d.\n",
+ stoa(sas), SRCPORT(sas));
}
+#ifdef MCAST
+ struct ip_mreq mdevadr;
+ TYPEOF_IP_MULTICAST_LOOP mtrue = 1;
if (setsockopt(rsock, IPPROTO_IP, IP_MULTICAST_LOOP, &mtrue, sizeof(mtrue)) < 0) {
/* some error message regarding setting up multicast loop */
return BROADCAST_FAILED;
}
-
mdevadr.imr_multiaddr.s_addr = NSRCADR(sas);
mdevadr.imr_interface.s_addr = htonl(INADDR_ANY);
-
if (mdevadr.imr_multiaddr.s_addr == -1) {
if (ENABLED_OPT(NORMALVERBOSE)) {
- printf("sntp recv_bcst_data: %s is not a broad-/multicast address, aborting...\n",
- stoa(sas));
+ printf("sntp recv_bcst_data: %s:%d is not a broad-/multicast address, aborting...\n",
+ stoa(sas), SRCPORT(sas));
}
-
return BROADCAST_FAILED;
}
-
if (setsockopt(rsock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mdevadr, sizeof(mdevadr)) < 0) {
if (ENABLED_OPT(NORMALVERBOSE)) {
buf = ss_to_str(sas);
-
printf("sntp recv_bcst_data: Couldn't add IP membership for %s\n", buf);
-
free(buf);
-
- return BROADCAST_FAILED;
}
}
#endif /* MCAST */
}
#ifdef ISC_PLATFORM_HAVEIPV6
else if (IS_IPV6(sas)) {
-#ifndef INCLUDE_IPV6_MULTICAST_SUPPORT
- return BROADCAST_FAILED;
-#else
- struct ipv6_mreq mdevadr;
-
if (bind(rsock, &sas->sa, SOCKLEN(sas)) < 0) {
if (ENABLED_OPT(NORMALVERBOSE))
printf("sntp recv_bcst_data: Couldn't bind() address.\n");
}
-
+#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
+ struct ipv6_mreq mdevadr;
if (setsockopt(rsock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &btrue, sizeof (btrue)) < 0) {
/* some error message regarding setting up multicast loop */
return BROADCAST_FAILED;
}
-
memset(&mdevadr, 0, sizeof(mdevadr));
mdevadr.ipv6mr_multiaddr = SOCK_ADDR6(sas);
-
if(!IN6_IS_ADDR_MULTICAST(&mdevadr.ipv6mr_multiaddr)) {
if(ENABLED_OPT(NORMALVERBOSE)) {
buf = ss_to_str(sas);
-
printf("sntp recv_bcst_data: %s is not a broad-/multicast address, aborting...\n", buf);
-
free(buf);
}
-
return BROADCAST_FAILED;
}
-
if (setsockopt(rsock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mdevadr, sizeof(mdevadr)) < 0) {
if(ENABLED_OPT(NORMALVERBOSE)) {
buf = ss_to_str(sas);
-
printf("sntp recv_bcst_data: Couldn't join group for %s\n", buf);
-
free(buf);
-
- return BROADCAST_FAILED;
}
}
#endif /* INCLUDE_IPV6_MULTICAST_SUPPORT */
}
#endif /* ISC_PLATFORM_HAVEIPV6 */
-
FD_ZERO(&bcst_fd);
FD_SET(rsock, &bcst_fd);
-
if(ENABLED_OPT(TIMEOUT))
timeout_tv.tv_sec = (int) OPT_ARG(TIMEOUT);
else
timeout_tv.tv_sec = 68; /* ntpd broadcasts every 64s */
-
- switch(select(rsock + 1, &bcst_fd, 0, 0, &timeout_tv)) {
-
- case -1:
- if(ENABLED_OPT(NORMALVERBOSE))
- printf("sntp recv_bcst_data: select() returned -1, an error occured, aborting.\n");
-
- return BROADCAST_FAILED;
- break;
-
- case 0:
- if(ENABLED_OPT(NORMALVERBOSE))
- printf("sntp recv_bcst_data: select() reached timeout (%u sec), aborting.\n",
- (unsigned)timeout_tv.tv_sec);
-
- return BROADCAST_FAILED;
- break;
-
- default:
- {
- GETSOCKNAME_SOCKLEN_TYPE ss_len = sizeof(*ras);
-
- recv_bytes = recvfrom(rsock, rdata, rdata_len, 0, &ras->sa, &ss_len);
- }
+ timeout_tv.tv_usec = 0;
+ rdy_socks = select(rsock + 1, &bcst_fd, 0, 0, &timeout_tv);
+ switch(rdy_socks) {
+ case -1:
+ if(ENABLED_OPT(NORMALVERBOSE))
+ perror("sntp recv_bcst_data: select()");
+ return BROADCAST_FAILED;
+ break;
+ case 0:
+ if(ENABLED_OPT(NORMALVERBOSE))
+ printf("sntp recv_bcst_data: select() reached timeout (%u sec), aborting.\n",
+ (unsigned)timeout_tv.tv_sec);
+ return BROADCAST_FAILED;
+ break;
+ default:
+ ss_len = sizeof(*ras);
+ recv_bytes = recvfrom(rsock, rdata, rdata_len, 0, &ras->sa, &ss_len);
+ break;
}
-
if (recv_bytes == -1) {
if(ENABLED_OPT(NORMALVERBOSE))
- printf("sntp recv_bcst_data: Failed to receive from broad-/multicast\n");
-
- return BROADCAST_FAILED;
+ perror("sntp recv_bcst_data: recvfrom:");
+ recv_bytes = BROADCAST_FAILED;
}
-
+#ifdef MCAST
if (IS_IPV4(sas))
setsockopt(rsock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &btrue, sizeof(btrue));
+#endif
#ifdef INCLUDE_IPV6_MULTICAST_SUPPORT
- else if (IS_IPV6(sas))
+ if (IS_IPV6(sas))
setsockopt(rsock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &btrue, sizeof(btrue));
#endif
-
return recv_bytes;
}
-int
-recv_bcst_pkt (
- SOCKET rsock,
- struct pkt *rpkt,
- sockaddr_u *sas
- )
+static int
+process_pkt (
+ struct pkt *rpkt,
+ sockaddr_u *sas,
+ int pkt_len,
+ int mode,
+ char * func_name
+ )
{
- sockaddr_u sender;
- register int a;
- int is_authentic, has_mac = 0, orig_pkt_len;
-
- char *rdata = emalloc(sizeof(char) * 256);
-
- int pkt_len = recv_bcst_data(rsock, rdata, 256, sas, &sender);
-
-
- if (pkt_len < 0) {
- free(rdata);
-
- return BROADCAST_FAILED;
- }
-
- /* No MAC, no authentication */
- if (LEN_PKT_NOMAC == pkt_len)
- has_mac = 0;
-
- /* If there's more than just the NTP packet it should be a MAC */
- else if(pkt_len > LEN_PKT_NOMAC)
- has_mac = pkt_len - LEN_PKT_NOMAC;
- else
- if(ENABLED_OPT(NORMALVERBOSE)) {
- printf("sntp recv_bcst_pkt: Funny packet length: %i. Discarding package.\n", pkt_len);
- free(rdata);
-
- return PACKET_UNUSEABLE;
- }
-
- /* Packet too big */
- if(pkt_len > LEN_PKT_NOMAC + MAX_MAC_LEN) {
+ unsigned int key_id = 0;
+ struct key *pkt_key = NULL;
+ int is_authentic = 0;
+ unsigned int exten_words, exten_words_used = 0;
+ int mac_size;
+ /*
+ * Parse the extension field if present. We figure out whether
+ * an extension field is present by measuring the MAC size. If
+ * the number of words following the packet header is 0, no MAC
+ * is present and the packet is not authenticated. If 1, the
+ * packet is a crypto-NAK; if 3, the packet is authenticated
+ * with DES; if 5, the packet is authenticated with MD5; if 6,
+ * the packet is authenticated with SHA. If 2 or 4, the packet
+ * is a runt and discarded forthwith. If greater than 6, an
+ * extension field is present, so we subtract the length of the
+ * field and go around again.
+ */
+ if (pkt_len < LEN_PKT_NOMAC || (pkt_len & 3) != 0) {
+unusable:
if(ENABLED_OPT(NORMALVERBOSE))
- printf("sntp recv_bcst_pkt: Received packet is too big (%i bytes), trying again to get a useFable packet\n",
- pkt_len);
- free(rdata);
-
+ printf("sntp %s: Funny packet length: %i. Discarding package.\n", func_name, pkt_len);
return PACKET_UNUSEABLE;
}
-
- orig_pkt_len = pkt_len;
- pkt_len = min(pkt_len, sizeof(struct pkt));
-
- /* Let's copy the received data to the packet structure */
- for (a = 0; a < pkt_len; a++)
- if (a < orig_pkt_len)
- ((char *)rpkt)[a] = rdata[a];
- else
- ((char *)rpkt)[a] = 0;
-
- free(rdata);
+ /* skip past the extensions, if any */
+ exten_words = ((unsigned)pkt_len - LEN_PKT_NOMAC) >> 2;
+ while (exten_words > 6) {
+ unsigned int exten_len;
+ exten_len = ntohl(rpkt->exten[exten_words_used]) & 0xffff;
+ exten_len = (exten_len + 7) >> 2; /* convert to words, add 1 */
+ if (exten_len > exten_words || exten_len < 5)
+ goto unusable;
+ exten_words -= exten_len;
+ exten_words_used += exten_len;
+ }
- /* MAC could be useable for us */
- if (has_mac) {
- /* Two more things that the MAC must conform to */
- if (has_mac > MAX_MAC_LEN || has_mac % 4 != 0) {
- is_authentic = 0; /* Or should we discard this packet? */
+ switch (exten_words) {
+ case 1:
+ key_id = ntohl(rpkt->exten[exten_words_used]);
+ printf("Crypto NAK = 0x%08x\n", key_id);
+ break;
+ case 5:
+ case 6:
+ /* Look for the key used by the server in the specified keyfile
+ * and if existent, fetch it or else leave the pointer untouched */
+ key_id = ntohl(rpkt->exten[exten_words_used]);
+ get_key(key_id, &pkt_key);
+ if (!pkt_key) {
+ printf("unrecognized key ID = 0x%08x\n", key_id);
+ break;
}
- else {
- if (MAX_MAC_LEN == has_mac) {
- struct key *pkt_key = NULL;
-
- /* Look for the key used by the server in the specified keyfile
- * and if existent, fetch it or else leave the pointer untouched */
- get_key(rpkt->mac[0], &pkt_key);
-
- /* Seems like we've got a key with matching keyid */
- if (pkt_key != NULL) {
- /* Generate a md5sum of the packet with the key from our keyfile
- * and compare those md5sums */
- if (!auth_md5((char *) rpkt, has_mac, pkt_key)) {
- if (ENABLED_OPT(AUTHENTICATION)) {
- /* We want a authenticated packet */
- if (ENABLED_OPT(NORMALVERBOSE)) {
- char *hostname = ss_to_str(sas);
- printf("sntp recv_bcst_pkt: Broadcast packet received from %s is not authentic. Will discard this packet.\n",
- hostname);
-
- free(hostname);
- }
- return SERVER_AUTH_FAIL;
- }
- else {
- /* We don't know if the user wanted authentication so let's
- * use it anyways */
- if (ENABLED_OPT(NORMALVERBOSE)) {
- char *hostname = ss_to_str(sas);
- printf("sntp recv_bcst_pkt: Broadcast packet received from %s is not authentic. Authentication not enforced.\n",
- hostname);
-
- free(hostname);
- }
-
- is_authentic = 0;
- }
- }
- else {
- /* Yay! Things worked out! */
- if (ENABLED_OPT(NORMALVERBOSE)) {
- char *hostname = ss_to_str(sas);
- printf("sntp recv_bcst_pkt: Broadcast packet received from %s successfully authenticated using key id %i.\n",
- hostname, rpkt->mac[0]);
-
- free(hostname);
- }
-
- is_authentic = 1;
- }
- }
+ /* Seems like we've got a key with matching keyid */
+ /* Generate a md5sum of the packet with the key from our keyfile
+ * and compare those md5sums */
+ mac_size = exten_words << 2;
+ if (!auth_md5((char *)rpkt, pkt_len - mac_size, mac_size - 4, pkt_key)) {
+ break;
+ }
+ /* Yay! Things worked out! */
+ if (ENABLED_OPT(NORMALVERBOSE)) {
+ char *hostname = ss_to_str(sas);
+ printf("sntp %s: packet received from %s successfully authenticated using key id %i.\n",
+ func_name, hostname, key_id);
+ free(hostname);
+ }
+ is_authentic = 1;
+ break;
+ case 0:
+ break;
+ default:
+ goto unusable;
+ break;
+ }
+ if (!is_authentic) {
+ if (ENABLED_OPT(AUTHENTICATION)) {
+ /* We want a authenticated packet */
+ if (ENABLED_OPT(NORMALVERBOSE)) {
+ char *hostname = ss_to_str(sas);
+ printf("sntp %s: packet received from %s is not authentic. Will discard it.\n",
+ func_name, hostname);
+ free(hostname);
}
+ return SERVER_AUTH_FAIL;
+ }
+ /* We don't know if the user wanted authentication so let's
+ * use it anyways */
+ if (ENABLED_OPT(NORMALVERBOSE)) {
+ char *hostname = ss_to_str(sas);
+ printf("sntp %s: packet received from %s is not authentic. Authentication not enforced.\n",
+ func_name, hostname);
+ free(hostname);
}
}
-
/* Check for server's ntp version */
if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION ||
PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) {
if (ENABLED_OPT(NORMALVERBOSE))
- printf("sntp recv_bcst_pkt: Packet shows wrong version (%i)\n",
- PKT_VERSION(rpkt->li_vn_mode));
-
+ printf("sntp %s: Packet shows wrong version (%i)\n",
+ func_name, PKT_VERSION(rpkt->li_vn_mode));
return SERVER_UNUSEABLE;
}
-
/* We want a server to sync with */
- if (PKT_MODE(rpkt->li_vn_mode) != MODE_BROADCAST
- && PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) {
+ if (PKT_MODE(rpkt->li_vn_mode) != mode &&
+ PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) {
if (ENABLED_OPT(NORMALVERBOSE))
- printf("sntp recv_bcst_pkt: mode %d stratum %i\n",
- PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
-
+ printf("sntp %s: mode %d stratum %i\n", func_name,
+ PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
return SERVER_UNUSEABLE;
}
-
+ /* Stratum is unspecified (0) check what's going on */
if (STRATUM_PKT_UNSPEC == rpkt->stratum) {
char *ref_char;
-
if (ENABLED_OPT(NORMALVERBOSE))
- printf("sntp recv_bcst_pkt: Stratum unspecified, going to check for KOD (stratum: %i)\n", rpkt->stratum);
-
+ printf("sntp %s: Stratum unspecified, going to check for KOD (stratum: %i)\n",
+ func_name, rpkt->stratum);
ref_char = (char *) &rpkt->refid;
-
+ if (ENABLED_OPT(NORMALVERBOSE))
+ printf("sntp %s: Packet refid: %c%c%c%c\n", func_name,
+ ref_char[0], ref_char[1], ref_char[2], ref_char[3]);
/* If it's a KOD packet we'll just use the KOD information */
if (ref_char[0] != 'X') {
if (strncmp(ref_char, "DENY", 4))
return KOD_DEMOBILIZE;
-
if (strncmp(ref_char, "RSTR", 4))
return KOD_DEMOBILIZE;
-
if (strncmp(ref_char, "RATE", 4))
return KOD_RATE;
-
/* There are other interesting kiss codes which might be interesting for authentication */
}
}
-
/* If the server is not synced it's not really useable for us */
if (LEAP_NOTINSYNC == PKT_LEAP(rpkt->li_vn_mode)) {
if (ENABLED_OPT(NORMALVERBOSE))
- printf("recv_bcst_pkt: Server not in sync, skipping this server\n");
-
+ printf("sntp %s: Server not in sync, skipping this server\n", func_name);
return SERVER_UNUSEABLE;
}
-
return pkt_len;
}
-
-
-/* Fetch data, check if it's data for us and whether it's useable or not. If not, return
- * a failure code so we can delete this server from our list and continue with another one.
- */
int
-recvpkt (
- SOCKET rsock,
- struct pkt *rpkt,
- struct pkt *spkt
+recv_bcst_pkt (
+ SOCKET rsock,
+ struct pkt *rpkt,
+ unsigned int rsize,
+ sockaddr_u *sas
)
{
sockaddr_u sender;
- char *rdata /* , done */;
-
- register int a;
- int has_mac, is_authentic, pkt_len, orig_pkt_len;
-
-
- /* Much space, just to be sure */
- rdata = emalloc(sizeof(char) * 256);
-
- pkt_len = recvdata(rsock, &sender, rdata, 256);
-
-#if 0 /* done uninitialized */
- if (!done) {
- /* Do something about it, first check for a maximum length of ntp packets,
- * probably that's something we can avoid
- */
+ int pkt_len = recv_bcst_data(rsock, (char *)rpkt, rsize, sas, &sender);
+ if (pkt_len < 0) {
+ return BROADCAST_FAILED;
}
-#endif
-
- /* Some checks to see if that packet is intended for us */
-
- /* No MAC, no authentication */
- if (LEN_PKT_NOMAC == pkt_len)
- has_mac = 0;
+ pkt_len = process_pkt(rpkt, sas, pkt_len, MODE_BROADCAST, "recv_bcst_pkt");
+ return pkt_len;
+}
- /* If there's more than just the NTP packet it should be a MAC */
- else if (pkt_len > LEN_PKT_NOMAC)
- has_mac = pkt_len - LEN_PKT_NOMAC;
-
- else {
- if (ENABLED_OPT(NORMALVERBOSE))
- printf("sntp recvpkt: Funny packet length: %i. Discarding package.\n", pkt_len);
- free(rdata);
+/* Fetch data, check if it's data for us and whether it's useable or not. If not, return
+ * a failure code so we can delete this server from our list and continue with another one.
+ */
+int
+recvpkt (
+ SOCKET rsock,
+ struct pkt *rpkt, /* received packet (response) */
+ unsigned int rsize, /* size of rpkt buffer */
+ struct pkt *spkt /* sent packet (request) */
+ )
+{
+ int rdy_socks;
+ int pkt_len;
+ sockaddr_u sender;
+ struct timeval timeout_tv;
+ fd_set recv_fd;
+ FD_ZERO(&recv_fd);
+ FD_SET(rsock, &recv_fd);
+ if(ENABLED_OPT(TIMEOUT))
+ timeout_tv.tv_sec = (int) OPT_ARG(TIMEOUT);
+ else
+ timeout_tv.tv_sec = 68; /* ntpd broadcasts every 64s */
+ timeout_tv.tv_usec = 0;
+ rdy_socks = select(rsock + 1, &recv_fd, 0, 0, &timeout_tv);
+ switch(rdy_socks) {
+ case -1:
+ if(ENABLED_OPT(NORMALVERBOSE))
+ perror("sntp recvpkt: select()");
return PACKET_UNUSEABLE;
- }
-
- /* Packet too big */
- if (pkt_len > LEN_PKT_MAC) {
- if (ENABLED_OPT(NORMALVERBOSE))
- printf("sntp recvpkt: Received packet is too big (%i bytes), trying again to get a useable packet\n",
- pkt_len);
- free(rdata);
-
+ break;
+ case 0:
+ if(ENABLED_OPT(NORMALVERBOSE))
+ printf("sntp recvpkt: select() reached timeout (%u sec), aborting.\n",
+ (unsigned)timeout_tv.tv_sec);
return PACKET_UNUSEABLE;
+ break;
+ default:
+ break;
}
-
- orig_pkt_len = pkt_len;
- pkt_len = min(pkt_len, sizeof(struct pkt));
-
- for (a = 0; a < pkt_len; a++)
- /* FIXME! */
- if (a < orig_pkt_len)
- ((char *) rpkt)[a] = rdata[a];
- else
- ((char *) rpkt)[a] = 0;
-
- free(rdata);
- rdata = NULL;
-
- /* MAC could be useable for us */
- if (has_mac) {
- /* Two more things that the MAC must conform to */
- if(has_mac > MAX_MAC_LEN || has_mac % 4 != 0) {
- is_authentic = 0; /* Or should we discard this packet? */
- }
- else {
- if (MAX_MAC_LEN == has_mac) {
- struct key *pkt_key = NULL;
-
- /*
- * Look for the key used by the server in the specified keyfile
- * and if existent, fetch it or else leave the pointer untouched
- */
- get_key(rpkt->mac[0], &pkt_key);
-
- /* Seems like we've got a key with matching keyid */
- if (pkt_key != NULL) {
- /*
- * Generate a md5sum of the packet with the key from our keyfile
- * and compare those md5sums
- */
- if (!auth_md5((char *) rpkt, has_mac, pkt_key)) {
- if (ENABLED_OPT(AUTHENTICATION)) {
- /* We want a authenticated packet */
- if (ENABLED_OPT(NORMALVERBOSE)) {
- char *hostname = ss_to_str(&sender);
- printf("sntp recvpkt: Broadcast packet received from %s is not authentic. Will discard this packet.\n",
- hostname);
-
- free(hostname);
- }
- return SERVER_AUTH_FAIL;
- }
- else {
- /*
- * We don't know if the user wanted authentication so let's
- * use it anyways
- */
- if (ENABLED_OPT(NORMALVERBOSE)) {
- char *hostname = ss_to_str(&sender);
- printf("sntp recvpkt: Broadcast packet received from %s is not authentic. Authentication not enforced.\n",
- hostname);
-
- free(hostname);
- }
-
- is_authentic = 0;
- }
- }
- else {
- /* Yay! Things worked out! */
- if (ENABLED_OPT(NORMALVERBOSE)) {
- char *hostname = ss_to_str(&sender);
- printf("sntp recvpkt: Broadcast packet received from %s successfully authenticated using key id %i.\n",
- hostname, rpkt->mac[0]);
-
- free(hostname);
- }
-
- is_authentic = 1;
- }
- }
- }
- }
+ pkt_len = recvdata(rsock, &sender, (char *)rpkt, rsize);
+ if (pkt_len > 0) {
+ pkt_len = process_pkt(rpkt, &sender, pkt_len, MODE_SERVER, "recvpkt");
}
-
- /* Check for server's ntp version */
- if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION ||
- PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) {
- if (ENABLED_OPT(NORMALVERBOSE))
- printf("sntp recvpkt: Packet got wrong version (%i)\n", PKT_VERSION(rpkt->li_vn_mode));
-
- return SERVER_UNUSEABLE;
- }
-
- /* We want a server to sync with */
- if (PKT_MODE(rpkt->li_vn_mode) != MODE_SERVER &&
- PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE) {
- if (ENABLED_OPT(NORMALVERBOSE))
- printf("sntp recvpkt: mode %d stratum %i\n",
- PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
-
- return SERVER_UNUSEABLE;
- }
-
- /* Stratum is unspecified (0) check what's going on */
- if (STRATUM_PKT_UNSPEC == rpkt->stratum) {
- char *ref_char;
-
- if (ENABLED_OPT(NORMALVERBOSE))
- printf("sntp recvpkt: Stratum unspecified, going to check for KOD (stratum: %i)\n", rpkt->stratum);
-
-
- ref_char = (char *) &rpkt->refid;
-
- if (ENABLED_OPT(NORMALVERBOSE))
- printf("sntp recvpkt: Packet refid: %c%c%c%c\n", ref_char[0], ref_char[1], ref_char[2], ref_char[3]);
-
- /* If it's a KOD packet we'll just use the KOD information */
- if (ref_char[0] != 'X') {
- if (!strncmp(ref_char, "DENY", 4))
- return KOD_DEMOBILIZE;
-
- if (!strncmp(ref_char, "RSTR", 4))
- return KOD_DEMOBILIZE;
-
- if (!strncmp(ref_char, "RATE", 4))
- return KOD_RATE;
-
- /* There are other interesting kiss codes which might be interesting for authentication */
- }
- }
-
- /* If the server is not synced it's not really useable for us */
- if (LEAP_NOTINSYNC == PKT_LEAP(rpkt->li_vn_mode)) {
- if (ENABLED_OPT(NORMALVERBOSE))
- printf("sntp recvpkt: Server not in sync, skipping this server\n");
-
- return SERVER_UNUSEABLE;
- }
-
+ if (pkt_len < 0)
+ return pkt_len;
/*
* Decode the org timestamp and make sure we're getting a response
* to our last request.
*/
-
#ifdef DEBUG
printf("rpkt->org:\n");
l_fp_output(&rpkt->org, stdout);
printf("spkt->xmt:\n");
l_fp_output(&spkt->xmt, stdout);
#endif
-
if (!L_ISEQU(&rpkt->org, &spkt->xmt)) {
if (ENABLED_OPT(NORMALVERBOSE))
printf("sntp recvpkt: pkt.org and peer.xmt differ\n");
-
return PACKET_UNUSEABLE;
}
-
return pkt_len;
}
*/
int
is_reachable (
- struct addrinfo *dst
- )
+ struct addrinfo *dst
+ )
{
- SOCKET sockfd;
-
- sockfd = socket(dst->ai_family, SOCK_DGRAM, 0);
+ SOCKET sockfd = socket(dst->ai_family, SOCK_DGRAM, 0);
if (-1 == sockfd) {
#ifdef DEBUG
#endif
return 0;
}
-
if (connect(sockfd, dst->ai_addr, SOCKLEN((sockaddr_u *)dst->ai_addr))) {
closesocket(sockfd);
return 0;
}
-
closesocket(sockfd);
return 1;
}
/* From ntpdate.c */
int is_reachable (struct addrinfo *dst);
-int resolve_hosts (char **hosts, int hostc, struct addrinfo ***res, int pref_family);
+int resolve_hosts (const char **hosts, int hostc, struct addrinfo ***res, int pref_family);
void create_socket (SOCKET *rsock, sockaddr_u *dest);
int recvdata (SOCKET rsock, sockaddr_u *sender, char *rdata, int rdata_len);
-int recvpkt (SOCKET rsock, struct pkt *rpkt, struct pkt *spkt);
+int recvpkt (SOCKET rsock, struct pkt *rpkt, unsigned int rsize, struct pkt *spkt);
int recv_bcst_data (SOCKET rsock, char *rdata, int rdata_len, sockaddr_u *sas, sockaddr_u *ras);
-int recv_bcst_pkt (SOCKET rsock, struct pkt *rpkt, sockaddr_u *sas);
+int recv_bcst_pkt (SOCKET rsock, struct pkt *rpkt, unsigned int rsize, sockaddr_u *sas);
/* Shortened peer structure. Not absolutely necessary yet */
struct speer {