Index: releng/10.1/UPDATING =================================================================== --- releng/10.1/UPDATING (revision 276158) +++ releng/10.1/UPDATING (revision 276159) @@ -1,2164 +1,2170 @@ Updating Information for FreeBSD current users This file is maintained and copyrighted by M. Warner Losh . See end of file for further details. For commonly done items, please see the COMMON ITEMS: section later in the file. These instructions assume that you basically know what you are doing. If not, then please consult the FreeBSD handbook: http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/makeworld.html Items affecting the ports and packages system can be found in /usr/ports/UPDATING. Please read that file before running portupgrade. NOTE: FreeBSD has switched from gcc to clang. If you have trouble bootstrapping from older versions of FreeBSD, try WITHOUT_CLANG to bootstrap to the tip of stable/10, and then rebuild without this option. The bootstrap process from older version of current is a bit fragile. +20141223: p3 FreeBSD-SA-14:31.ntp + FreeBSD-EN-14:13.freebsd-update + + Fix multiple vulnerabilities in NTP suite. [SA-14:31] + Fix directory deletion issue in freebsd-update. [EN-14:13] + 20141217: p2 FreeBSD-SA-14:30.unbound Fix unbound remote denial of service vulnerability. 20141210: p1 FreeBSD-SA-14:27.stdio FreeBSD-SA-14:28.file Fix buffer overflow in stdio. [SA-14:27] Fix multiple vulnerabilities in file(1) and libmagic(3). [SA-14:28] 20140904: The ofwfb driver, used to provide a graphics console on PowerPC when using vt(4), no longer allows mmap() of all of physical memory. This will prevent Xorg on PowerPC with some ATI graphics cards from initializing properly unless x11-servers/xorg-server is updated to 1.12.4_8 or newer. 20140831: The libatf-c and libatf-c++ major versions were downgraded to 0 and 1 respectively to match the upstream numbers. They were out of sync because, when they were originally added to FreeBSD, the upstream versions were not respected. These libraries are private and not yet built by default, so renumbering them should be a non-issue. However, unclean source trees will yield broken test programs once the operator executes "make delete-old-libs" after a "make installworld". Additionally, the atf-sh binary was made private by moving it into /usr/libexec/. Already-built shell test programs will keep the path to the old binary so they will break after "make delete-old" is run. If you are using WITH_TESTS=yes (not the default), wipe the object tree and rebuild from scratch to prevent spurious test failures. This is only needed once: the misnumbered libraries and misplaced binaries have been added to OptionalObsoleteFiles.inc so they will be removed during a clean upgrade. 20140814: The ixgbe tunables now match their sysctl counterparts, for example: hw.ixgbe.enable_aim => hw.ix.enable_aim Anyone using ixgbe tunables should ensure they update /boot/loader.conf. 20140801: The NFSv4.1 server committed by r269398 changes the internal function call interfaces used between the NFS and krpc modules. As such, __FreeBSD_version was bumped. 20140729: The default unbound configuration has been modified to address issues with reverse lookups on networks that use private address ranges. If you use the local_unbound service, run "service local_unbound setup" as root to regenerate your configuration, then "service local_unbound reload" to load the new configuration. 20140717: It is no longer necessary to include the dwarf version in your DEBUG options in your kernel config file. The bug that required it to be placed in the config file has bene fixed. DEBUG should now just contain -g. The build system will automatically update things to do the right thing. 20140715: Several ABI breaking changes were merged to CTL and new iSCSI code. All CTL and iSCSI-related tools, such as ctladm, ctld, iscsid and iscsictl need to be rebuilt to work with a new kernel. 20140708: The WITHOUT_VT_SUPPORT kernel config knob has been renamed WITHOUT_VT. (The other _SUPPORT knobs have a consistent meaning which differs from the behaviour controlled by this knob.) 20140608: On i386 and amd64 systems, the onifconsole flag is now set by default in /etc/ttys for ttyu0. This causes ttyu0 to be automatically enabled as a login TTY if it is set in the bootloader as an active kernel console. No changes in behavior should result otherwise. To revert to the previous behavior, set ttyu0 to "off" in /etc/ttys. 20140512: Clang and llvm have been upgraded to 3.4.1 release. 20140321: Clang and llvm have been upgraded to 3.4 release. 20140306: If a Makefile in a tests/ directory was auto-generating a Kyuafile instead of providing an explicit one, this would prevent such Makefile from providing its own Kyuafile in the future during NO_CLEAN builds. This has been fixed in the Makefiles but manual intervention is needed to clean an objdir if you use NO_CLEAN: # find /usr/obj -name Kyuafile | xargs rm -f 20140303: OpenSSH will now ignore errors caused by kernel lacking of Capsicum capability mode support. Please note that enabling the feature in kernel is still highly recommended. 20140227: OpenSSH is now built with sandbox support, and will use sandbox as the default privilege separation method. This requires Capsicum capability mode support in kernel. 20140216: The nve(4) driver for NVIDIA nForce MCP Ethernet adapters has been deprecated and will not be part of FreeBSD 11.0 and later releases. If you use this driver, please consider switching to the nfe(4) driver instead. 20140120: 10.0-RELEASE. 20131216: The behavior of gss_pseudo_random() for the krb5 mechanism has changed, for applications requesting a longer random string than produced by the underlying enctype's pseudo-random() function. In particular, the random string produced from a session key of enctype aes256-cts-hmac-sha1-96 or aes256-cts-hmac-sha1-96 will be different at the 17th octet and later, after this change. The counter used in the PRF+ construction is now encoded as a big-endian integer in accordance with RFC 4402. __FreeBSD_version is bumped to 1000701. 20131108: The WITHOUT_ATF build knob has been removed and its functionality has been subsumed into the more generic WITHOUT_TESTS. If you were using the former to disable the build of the ATF libraries, you should change your settings to use the latter. 20131031: The default version of mtree is nmtree which is obtained from NetBSD. The output is generally the same, but may vary slightly. If you found you need identical output adding "-F freebsd9" to the command line should do the trick. For the time being, the old mtree is available as fmtree. 20131014: libbsdyml has been renamed to libyaml and moved to /usr/lib/private. This will break ports-mgmt/pkg. Rebuild the port, or upgrade to pkg 1.1.4_8 and verify bsdyml not linked in, before running "make delete-old-libs": # make -C /usr/ports/ports-mgmt/pkg build deinstall install clean or # pkg install pkg; ldd /usr/local/sbin/pkg | grep bsdyml 20131010: The rc.d/jail script has been updated to support jail(8) configuration file. The "jail__*" rc.conf(5) variables for per-jail configuration are automatically converted to /var/run/jail..conf before the jail(8) utility is invoked. This is transparently backward compatible. See below about some incompatibilities and rc.conf(5) manual page for more details. These variables are now deprecated in favor of jail(8) configuration file. One can use "rc.d/jail config " command to generate a jail(8) configuration file in /var/run/jail..conf without running the jail(8) utility. The default pathname of the configuration file is /etc/jail.conf and can be specified by using $jail_conf or $jail__conf variables. Please note that jail_devfs_ruleset accepts an integer at this moment. Please consider to rewrite the ruleset name with an integer. 20130930: BIND has been removed from the base system. If all you need is a local resolver, simply enable and start the local_unbound service instead. Otherwise, several versions of BIND are available in the ports tree. The dns/bind99 port is one example. With this change, nslookup(1) and dig(1) are no longer in the base system. Users should instead use host(1) and drill(1) which are in the base system. Alternatively, nslookup and dig can be obtained by installing the dns/bind-tools port. 20130916: With the addition of unbound(8), a new unbound user is now required during installworld. "mergemaster -p" can be used to add the user prior to installworld, as documented in the handbook. 20130911: OpenSSH is now built with DNSSEC support, and will by default silently trust signed SSHFP records. This can be controlled with the VerifyHostKeyDNS client configuration setting. DNSSEC support can be disabled entirely with the WITHOUT_LDNS option in src.conf. 20130906: The GNU Compiler Collection and C++ standard library (libstdc++) are no longer built by default on platforms where clang is the system compiler. You can enable them with the WITH_GCC and WITH_GNUCXX options in src.conf. 20130905: The PROCDESC kernel option is now part of the GENERIC kernel configuration and is required for the rwhod(8) to work. If you are using custom kernel configuration, you should include 'options PROCDESC'. 20130905: The API and ABI related to the Capsicum framework was modified in backward incompatible way. The userland libraries and programs have to be recompiled to work with the new kernel. This includes the following libraries and programs, but the whole buildworld is advised: libc, libprocstat, dhclient, tcpdump, hastd, hastctl, kdump, procstat, rwho, rwhod, uniq. 20130903: AES-NI intrinsic support has been added to gcc. The AES-NI module has been updated to use this support. A new gcc is required to build the aesni module on both i386 and amd64. 20130821: The PADLOCK_RNG and RDRAND_RNG kernel options are now devices. Thus "device padlock_rng" and "device rdrand_rng" should be used instead of "options PADLOCK_RNG" & "options RDRAND_RNG". 20130813: WITH_ICONV has been split into two feature sets. WITH_ICONV now enables just the iconv* functionality and is now on by default. WITH_LIBICONV_COMPAT enables the libiconv api and link time compatability. Set WITHOUT_ICONV to build the old way. If you have been using WITH_ICONV before, you will very likely need to turn on WITH_LIBICONV_COMPAT. 20130806: INVARIANTS option now enables DEBUG for code with OpenSolaris and Illumos origin, including ZFS. If you have INVARIANTS in your kernel configuration, then there is no need to set DEBUG or ZFS_DEBUG explicitly. DEBUG used to enable witness(9) tracking of OpenSolaris (mostly ZFS) locks if WITNESS option was set. Because that generated a lot of witness(9) reports and all of them were believed to be false positives, this is no longer done. New option OPENSOLARIS_WITNESS can be used to achieve the previous behavior. 20130806: Timer values in IPv6 data structures now use time_uptime instead of time_second. Although this is not a user-visible functional change, userland utilities which directly use them---ndp(8), rtadvd(8), and rtsold(8) in the base system---need to be updated to r253970 or later. 20130802: find -delete can now delete the pathnames given as arguments, instead of only files found below them or if the pathname did not contain any slashes. Formerly, the following error message would result: find: -delete: : relative path potentially not safe Deleting the pathnames given as arguments can be prevented without error messages using -mindepth 1 or by changing directory and passing "." as argument to find. This works in the old as well as the new version of find. 20130726: Behavior of devfs rules path matching has been changed. Pattern is now always matched against fully qualified devfs path and slash characters must be explicitly matched by slashes in pattern (FNM_PATHNAME). Rulesets involving devfs subdirectories must be reviewed. 20130716: The default ARM ABI has changed to the ARM EABI. The old ABI is incompatible with the ARM EABI and all programs and modules will need to be rebuilt to work with a new kernel. To keep using the old ABI ensure the WITHOUT_ARM_EABI knob is set. NOTE: Support for the old ABI will be removed in the future and users are advised to upgrade. 20130709: pkg_install has been disconnected from the build if you really need it you should add WITH_PKGTOOLS in your src.conf(5). 20130709: Most of network statistics structures were changed to be able keep 64-bits counters. Thus all tools, that work with networking statistics, must be rebuilt (netstat(1), bsnmpd(1), etc.) 20130629: Fix targets that run multiple make's to use && rather than ; so that subsequent steps depend on success of previous. NOTE: if building 'universe' with -j* on stable/8 or stable/9 it would be better to start the build using bmake, to avoid overloading the machine. 20130618: Fix a bug that allowed a tracing process (e.g. gdb) to write to a memory-mapped file in the traced process's address space even if neither the traced process nor the tracing process had write access to that file. 20130615: CVS has been removed from the base system. An exact copy of the code is available from the devel/cvs port. 20130613: Some people report the following error after the switch to bmake: make: illegal option -- J usage: make [-BPSXeiknpqrstv] [-C directory] [-D variable] ... *** [buildworld] Error code 2 this likely due to an old instance of make in ${MAKEPATH} (${MAKEOBJDIRPREFIX}${.CURDIR}/make.${MACHINE}) which src/Makefile will use that blindly, if it exists, so if you see the above error: rm -rf `make -V MAKEPATH` should resolve it. 20130516: Use bmake by default. Whereas before one could choose to build with bmake via -DWITH_BMAKE one must now use -DWITHOUT_BMAKE to use the old make. The goal is to remove these knobs for 10-RELEASE. It is worth noting that bmake (like gmake) treats the command line as the unit of failure, rather than statements within the command line. Thus '(cd some/where && dosomething)' is safer than 'cd some/where; dosomething'. The '()' allows consistent behavior in parallel build. 20130429: Fix a bug that allows NFS clients to issue READDIR on files. 20130426: The WITHOUT_IDEA option has been removed because the IDEA patent expired. 20130426: The sysctl which controls TRIM support under ZFS has been renamed from vfs.zfs.trim_disable -> vfs.zfs.trim.enabled and has been enabled by default. 20130425: The mergemaster command now uses the default MAKEOBJDIRPREFIX rather than creating it's own in the temporary directory in order allow access to bootstrapped versions of tools such as install and mtree. When upgrading from version of FreeBSD where the install command does not support -l, you will need to install a new mergemaster command if mergemaster -p is required. This can be accomplished with the command (cd src/usr.sbin/mergemaster && make install). 20130404: Legacy ATA stack, disabled and replaced by new CAM-based one since FreeBSD 9.0, completely removed from the sources. Kernel modules atadisk and atapi*, user-level tools atacontrol and burncd are removed. Kernel option `options ATA_CAM` is now permanently enabled and removed. 20130319: SOCK_CLOEXEC and SOCK_NONBLOCK flags have been added to socket(2) and socketpair(2). Software, in particular Kerberos, may automatically detect and use these during building. The resulting binaries will not work on older kernels. 20130308: CTL_DISABLE has also been added to the sparc64 GENERIC (for further information, see the respective 20130304 entry). 20130304: Recent commits to callout(9) changed the size of struct callout, so the KBI is probably heavily disturbed. Also, some functions in callout(9)/sleep(9)/sleepqueue(9)/condvar(9) KPIs were replaced by macros. Every kernel module using it won't load, so rebuild is requested. The ctl device has been re-enabled in GENERIC for i386 and amd64, but does not initialize by default (because of the new CTL_DISABLE option) to save memory. To re-enable it, remove the CTL_DISABLE option from the kernel config file or set kern.cam.ctl.disable=0 in /boot/loader.conf. 20130301: The ctl device has been disabled in GENERIC for i386 and amd64. This was done due to the extra memory being allocated at system initialisation time by the ctl driver which was only used if a CAM target device was created. This makes a FreeBSD system unusable on 128MB or less of RAM. 20130208: A new compression method (lz4) has been merged to -HEAD. Please refer to zpool-features(7) for more information. Please refer to the "ZFS notes" section of this file for information on upgrading boot ZFS pools. 20130129: A BSD-licensed patch(1) variant has been added and is installed as bsdpatch, being the GNU version the default patch. To inverse the logic and use the BSD-licensed one as default, while having the GNU version installed as gnupatch, rebuild and install world with the WITH_BSD_PATCH knob set. 20130121: Due to the use of the new -l option to install(1) during build and install, you must take care not to directly set the INSTALL make variable in your /etc/make.conf, /etc/src.conf, or on the command line. If you wish to use the -C flag for all installs you may be able to add INSTALL+=-C to /etc/make.conf or /etc/src.conf. 20130118: The install(1) option -M has changed meaning and now takes an argument that is a file or path to append logs to. In the unlikely event that -M was the last option on the command line and the command line contained at least two files and a target directory the first file will have logs appended to it. The -M option served little practical purpose in the last decade so its use is expected to be extremely rare. 20121223: After switching to Clang as the default compiler some users of ZFS on i386 systems started to experience stack overflow kernel panics. Please consider using 'options KSTACK_PAGES=4' in such configurations. 20121222: GEOM_LABEL now mangles label names read from file system metadata. Mangling affect labels containing spaces, non-printable characters, '%' or '"'. Device names in /etc/fstab and other places may need to be updated. 20121217: By default, only the 10 most recent kernel dumps will be saved. To restore the previous behaviour (no limit on the number of kernel dumps stored in the dump directory) add the following line to /etc/rc.conf: savecore_flags="" 20121201: With the addition of auditdistd(8), a new auditdistd user is now required during installworld. "mergemaster -p" can be used to add the user prior to installworld, as documented in the handbook. 20121117: The sin6_scope_id member variable in struct sockaddr_in6 is now filled by the kernel before passing the structure to the userland via sysctl or routing socket. This means the KAME-specific embedded scope id in sin6_addr.s6_addr[2] is always cleared in userland application. This behavior can be controlled by net.inet6.ip6.deembed_scopeid. __FreeBSD_version is bumped to 1000025. 20121105: On i386 and amd64 systems WITH_CLANG_IS_CC is now the default. This means that the world and kernel will be compiled with clang and that clang will be installed as /usr/bin/cc, /usr/bin/c++, and /usr/bin/cpp. To disable this behavior and revert to building with gcc, compile with WITHOUT_CLANG_IS_CC. Really old versions of current may need to bootstrap WITHOUT_CLANG first if the clang build fails (its compatibility window doesn't extend to the 9 stable branch point). 20121102: The IPFIREWALL_FORWARD kernel option has been removed. Its functionality now turned on by default. 20121023: The ZERO_COPY_SOCKET kernel option has been removed and split into SOCKET_SEND_COW and SOCKET_RECV_PFLIP. NB: SOCKET_SEND_COW uses the VM page based copy-on-write mechanism which is not safe and may result in kernel crashes. NB: The SOCKET_RECV_PFLIP mechanism is useless as no current driver supports disposeable external page sized mbuf storage. Proper replacements for both zero-copy mechanisms are under consideration and will eventually lead to complete removal of the two kernel options. 20121023: The IPv4 network stack has been converted to network byte order. The following modules need to be recompiled together with kernel: carp(4), divert(4), gif(4), siftr(4), gre(4), pf(4), ipfw(4), ng_ipfw(4), stf(4). 20121022: Support for non-MPSAFE filesystems was removed from VFS. The VFS_VERSION was bumped, all filesystem modules shall be recompiled. 20121018: All the non-MPSAFE filesystems have been disconnected from the build. The full list includes: codafs, hpfs, ntfs, nwfs, portalfs, smbfs, xfs. 20121016: The interface cloning API and ABI has changed. The following modules need to be recompiled together with kernel: ipfw(4), pfsync(4), pflog(4), usb(4), wlan(4), stf(4), vlan(4), disc(4), edsc(4), if_bridge(4), gif(4), tap(4), faith(4), epair(4), enc(4), tun(4), if_lagg(4), gre(4). 20121015: The sdhci driver was split in two parts: sdhci (generic SD Host Controller logic) and sdhci_pci (actual hardware driver). No kernel config modifications are required, but if you load sdhc as a module you must switch to sdhci_pci instead. 20121014: Import the FUSE kernel and userland support into base system. 20121013: The GNU sort(1) program has been removed since the BSD-licensed sort(1) has been the default for quite some time and no serious problems have been reported. The corresponding WITH_GNU_SORT knob has also gone. 20121006: The pfil(9) API/ABI for AF_INET family has been changed. Packet filtering modules: pf(4), ipfw(4), ipfilter(4) need to be recompiled with new kernel. 20121001: The net80211(4) ABI has been changed to allow for improved driver PS-POLL and power-save support. All wireless drivers need to be recompiled to work with the new kernel. 20120913: The random(4) support for the VIA hardware random number generator (`PADLOCK') is no longer enabled unconditionally. Add the padlock_rng device in the custom kernel config if needed. The GENERIC kernels on i386 and amd64 do include the device, so the change only affects the custom kernel configurations. 20120908: The pf(4) packet filter ABI has been changed. pfctl(8) and snmp_pf module need to be recompiled to work with new kernel. 20120828: A new ZFS feature flag "com.delphix:empty_bpobj" has been merged to -HEAD. Pools that have empty_bpobj in active state can not be imported read-write with ZFS implementations that do not support this feature. For more information read the zpool-features(5) manual page. 20120727: The sparc64 ZFS loader has been changed to no longer try to auto- detect ZFS providers based on diskN aliases but now requires these to be explicitly listed in the OFW boot-device environment variable. 20120712: The OpenSSL has been upgraded to 1.0.1c. Any binaries requiring libcrypto.so.6 or libssl.so.6 must be recompiled. Also, there are configuration changes. Make sure to merge /etc/ssl/openssl.cnf. 20120712: The following sysctls and tunables have been renamed for consistency with other variables: kern.cam.da.da_send_ordered -> kern.cam.da.send_ordered kern.cam.ada.ada_send_ordered -> kern.cam.ada.send_ordered 20120628: The sort utility has been replaced with BSD sort. For now, GNU sort is also available as "gnusort" or the default can be set back to GNU sort by setting WITH_GNU_SORT. In this case, BSD sort will be installed as "bsdsort". 20120611: A new version of ZFS (pool version 5000) has been merged to -HEAD. Starting with this version the old system of ZFS pool versioning is superseded by "feature flags". This concept enables forward compatibility against certain future changes in functionality of ZFS pools. The first read-only compatible "feature flag" for ZFS pools is named "com.delphix:async_destroy". For more information read the new zpool-features(5) manual page. Please refer to the "ZFS notes" section of this file for information on upgrading boot ZFS pools. 20120417: The malloc(3) implementation embedded in libc now uses sources imported as contrib/jemalloc. The most disruptive API change is to /etc/malloc.conf. If your system has an old-style /etc/malloc.conf, delete it prior to installworld, and optionally re-create it using the new format after rebooting. See malloc.conf(5) for details (specifically the TUNING section and the "opt.*" entries in the MALLCTL NAMESPACE section). 20120328: Big-endian MIPS TARGET_ARCH values no longer end in "eb". mips64eb is now spelled mips64. mipsn32eb is now spelled mipsn32. mipseb is now spelled mips. This is to aid compatibility with third-party software that expects this naming scheme in uname(3). Little-endian settings are unchanged. If you are updating a big-endian mips64 machine from before this change, you may need to set MACHINE_ARCH=mips64 in your environment before the new build system will recognize your machine. 20120306: Disable by default the option VFS_ALLOW_NONMPSAFE for all supported platforms. 20120229: Now unix domain sockets behave "as expected" on nullfs(5). Previously nullfs(5) did not pass through all behaviours to the underlying layer, as a result if we bound to a socket on the lower layer we could connect only to the lower path; if we bound to the upper layer we could connect only to the upper path. The new behavior is one can connect to both the lower and the upper paths regardless what layer path one binds to. 20120211: The getifaddrs upgrade path broken with 20111215 has been restored. If you have upgraded in between 20111215 and 20120209 you need to recompile libc again with your kernel. You still need to recompile world to be able to configure CARP but this restriction already comes from 20111215. 20120114: The set_rcvar() function has been removed from /etc/rc.subr. All base and ports rc.d scripts have been updated, so if you have a port installed with a script in /usr/local/etc/rc.d you can either hand-edit the rcvar= line, or reinstall the port. An easy way to handle the mass-update of /etc/rc.d: rm /etc/rc.d/* && mergemaster -i 20120109: panic(9) now stops other CPUs in the SMP systems, disables interrupts on the current CPU and prevents other threads from running. This behavior can be reverted using the kern.stop_scheduler_on_panic tunable/sysctl. The new behavior can be incompatible with kern.sync_on_panic. 20111215: The carp(4) facility has been changed significantly. Configuration of the CARP protocol via ifconfig(8) has changed, as well as format of CARP events submitted to devd(8) has changed. See manual pages for more information. The arpbalance feature of carp(4) is currently not supported anymore. Size of struct in_aliasreq, struct in6_aliasreq has changed. User utilities using SIOCAIFADDR, SIOCAIFADDR_IN6, e.g. ifconfig(8), need to be recompiled. 20111122: The acpi_wmi(4) status device /dev/wmistat has been renamed to /dev/wmistat0. 20111108: The option VFS_ALLOW_NONMPSAFE option has been added in order to explicitely support non-MPSAFE filesystems. It is on by default for all supported platform at this present time. 20111101: The broken amd(4) driver has been replaced with esp(4) in the amd64, i386 and pc98 GENERIC kernel configuration files. 20110930: sysinstall has been removed 20110923: The stable/9 branch created in subversion. This corresponds to the RELENG_9 branch in CVS. 20110913: This commit modifies vfs_register() so that it uses a hash calculation to set vfc_typenum, which is enabled by default. The first time a system is booted after this change, the vfc_typenum values will change for all file systems. The main effect of this is a change to the NFS server file handles for file systems that use vfc_typenum in their fsid, such as ZFS. It will, however, prevent vfc_typenum from changing when file systems are loaded in a different order for subsequent reboots. To disable this, you can set vfs.typenumhash=0 in /boot/loader.conf until you are ready to remount all NFS clients after a reboot. 20110828: Bump the shared library version numbers for libraries that do not use symbol versioning, have changed the ABI compared to stable/8 and which shared library version was not bumped. Done as part of 9.0-RELEASE cycle. 20110815: During the merge of Capsicum features, the fget(9) KPI was modified. This may require the rebuilding of out-of-tree device drivers -- issues have been reported specifically with the nVidia device driver. __FreeBSD_version is bumped to 900041. Also, there is a period between 20110811 and 20110814 where the special devices /dev/{stdin,stdout,stderr} did not work correctly. Building world from a kernel during that window may not work. 20110628: The packet filter (pf) code has been updated to OpenBSD 4.5. You need to update userland tools to be in sync with kernel. This update breaks backward compatibility with earlier pfsync(4) versions. Care must be taken when updating redundant firewall setups. 20110608: The following sysctls and tunables are retired on x86 platforms: machdep.hlt_cpus machdep.hlt_logical_cpus The following sysctl is retired: machdep.hyperthreading_allowed The sysctls were supposed to provide a way to dynamically offline and online selected CPUs on x86 platforms, but the implementation has not been reliable especially with SCHED_ULE scheduler. machdep.hyperthreading_allowed tunable is still available to ignore hyperthreading CPUs at OS level. Individual CPUs can be disabled using hint.lapic.X.disabled tunable, where X is an APIC ID of a CPU. Be advised, though, that disabling CPUs in non-uniform fashion will result in non-uniform topology and may lead to sub-optimal system performance with SCHED_ULE, which is a default scheduler. 20110607: cpumask_t type is retired and cpuset_t is used in order to describe a mask of CPUs. 20110531: Changes to ifconfig(8) for dynamic address family detection mandate that you are running a kernel of 20110525 or later. Make sure to follow the update procedure to boot a new kernel before installing world. 20110513: Support for sun4v architecture is officially dropped 20110503: Several KPI breaking changes have been committed to the mii(4) layer, the PHY drivers and consequently some Ethernet drivers using mii(4). This means that miibus.ko and the modules of the affected Ethernet drivers need to be recompiled. Note to kernel developers: Given that the OUI bit reversion problem was fixed as part of these changes all mii(4) commits related to OUIs, i.e. to sys/dev/mii/miidevs, PHY driver probing and vendor specific handling, no longer can be merged verbatim to stable/8 and previous branches. 20110430: Users of the Atheros AR71xx SoC code now need to add 'device ar71xx_pci' into their kernel configurations along with 'device pci'. 20110427: The default NFS client is now the new NFS client, so fstype "newnfs" is now "nfs" and the regular/old NFS client is now fstype "oldnfs". Although mounts via fstype "nfs" will usually work without userland changes, it is recommended that the mount(8) and mount_nfs(8) commands be rebuilt from sources and that a link to mount_nfs called mount_oldnfs be created. The new client is compiled into the kernel with "options NFSCL" and this is needed for diskless root file systems. The GENERIC kernel configs have been changed to use NFSCL and NFSD (the new server) instead of NFSCLIENT and NFSSERVER. To use the regular/old client, you can "mount -t oldnfs ...". For a diskless root file system, you must also include a line like: vfs.root.mountfrom="oldnfs:" in the boot/loader.conf on the root fs on the NFS server to make a diskless root fs use the old client. 20110424: The GENERIC kernels for all architectures now default to the new CAM-based ATA stack. It means that all legacy ATA drivers were removed and replaced by respective CAM drivers. If you are using ATA device names in /etc/fstab or other places, make sure to update them respectively (adX -> adaY, acdX -> cdY, afdX -> daY, astX -> saY, where 'Y's are the sequential numbers starting from zero for each type in order of detection, unless configured otherwise with tunables, see cam(4)). There will be symbolic links created in /dev/ to map old adX devices to the respective adaY. They should provide basic compatibility for file systems mounting in most cases, but they do not support old user-level APIs and do not have respective providers in GEOM. Consider using updated management tools with new device names. It is possible to load devices ahci, ata, siis and mvs as modules, but option ATA_CAM should remain in kernel configuration to make ata module work as CAM driver supporting legacy ATA controllers. Device ata still can be used in modular fashion (atacore + ...). Modules atadisk and atapi* are not used and won't affect operation in ATA_CAM mode. Note that to use CAM-based ATA kernel should include CAM devices scbus, pass, da (or explicitly ada), cd and optionally others. All of them are parts of the cam module. ataraid(4) functionality is now supported by the RAID GEOM class. To use it you can load geom_raid kernel module and use graid(8) tool for management. Instead of /dev/arX device names, use /dev/raid/rX. No kernel config options or code have been removed, so if a problem arises, please report it and optionally revert to the old ATA stack. In order to do it you can remove from the kernel config: options ATA_CAM device ahci device mvs device siis , and instead add back: device atadisk # ATA disk drives device ataraid # ATA RAID drives device atapicd # ATAPI CDROM drives device atapifd # ATAPI floppy drives device atapist # ATAPI tape drives 20110423: The default NFS server has been changed to the new server, which was referred to as the experimental server. If you need to switch back to the old NFS server, you must now put the "-o" option on both the mountd and nfsd commands. This can be done using the mountd_flags and nfs_server_flags rc.conf variables until an update to the rc scripts is committed, which is coming soon. 20110418: The GNU Objective-C runtime library (libobjc), and other Objective-C related components have been removed from the base system. If you require an Objective-C library, please use one of the available ports. 20110331: ath(4) has been split into bus- and device- modules. if_ath contains the HAL, the TX rate control and the network device code. if_ath_pci contains the PCI bus glue. For Atheros MIPS embedded systems, if_ath_ahb contains the AHB glue. Users need to load both if_ath_pci and if_ath in order to use ath on everything else. TO REPEAT: if_ath_ahb is not needed for normal users. Normal users only need to load if_ath and if_ath_pci for ath(4) operation. 20110314: As part of the replacement of sysinstall, the process of building release media has changed significantly. For details, please re-read release(7), which has been updated to reflect the new build process. 20110218: GNU binutils 2.17.50 (as of 2007-07-03) has been merged to -HEAD. This is the last available version under GPLv2. It brings a number of new features, such as support for newer x86 CPU's (with SSE-3, SSSE-3, SSE 4.1 and SSE 4.2), better support for powerpc64, a number of new directives, and lots of other small improvements. See the ChangeLog file in contrib/binutils for the full details. 20110218: IPsec's HMAC_SHA256-512 support has been fixed to be RFC4868 compliant, and will now use half of hash for authentication. This will break interoperability with all stacks (including all actual FreeBSD versions) who implement draft-ietf-ipsec-ciph-sha-256-00 (they use 96 bits of hash for authentication). The only workaround with such peers is to use another HMAC algorithm for IPsec ("phase 2") authentication. 20110207: Remove the uio_yield prototype and symbol. This function has been misnamed since it was introduced and should not be globally exposed with this name. The equivalent functionality is now available using kern_yield(curthread->td_user_pri). The function remains undocumented. 20110112: A SYSCTL_[ADD_]UQUAD was added for unsigned uint64_t pointers, symmetric with the existing SYSCTL_[ADD_]QUAD. Type checking for scalar sysctls is defined but disabled. Code that needs UQUAD to pass the type checking that must compile on older systems where the define is not present can check against __FreeBSD_version >= 900030. The system dialog(1) has been replaced with a new version previously in ports as devel/cdialog. dialog(1) is mostly command-line compatible with the previous version, but the libdialog associated with it has a largely incompatible API. As such, the original version of libdialog will be kept temporarily as libodialog, until its base system consumers are replaced or updated. Bump __FreeBSD_version to 900030. 20110103: If you are trying to run make universe on a -stable system, and you get the following warning: "Makefile", line 356: "Target architecture for i386/conf/GENERIC unknown. config(8) likely too old." or something similar to it, then you must upgrade your -stable system to 8.2-Release or newer (really, any time after r210146 7/15/2010 in stable/8) or build the config from the latest stable/8 branch and install it on your system. Prior to this date, building a current universe on 8-stable system from between 7/15/2010 and 1/2/2011 would result in a weird shell parsing error in the first kernel build phase. A new config on those old systems will fix that problem for older versions of -current. 20101228: The TCP stack has been modified to allow Khelp modules to interact with it via helper hook points and store per-connection data in the TCP control block. Bump __FreeBSD_version to 900029. User space tools that rely on the size of struct tcpcb in tcp_var.h (e.g. sockstat) need to be recompiled. 20101114: Generic IEEE 802.3 annex 31B full duplex flow control support has been added to mii(4) and bge(4), bce(4), msk(4), nfe(4) and stge(4) along with brgphy(4), e1000phy(4) as well as ip1000phy() have been converted to take advantage of it instead of using custom implementations. This means that these drivers now no longer unconditionally advertise support for flow control but only do so if flow control is a selected media option. This was implemented in the generic support that way in order to allow flow control to be switched on and off via ifconfig(8) with the PHY specific default to typically off in order to protect from unwanted effects. Consequently, if you used flow control with one of the above mentioned drivers you now need to explicitly enable it, for example via: ifconfig bge0 media auto mediaopt flowcontrol Along with the above mentioned changes generic support for setting 1000baseT master mode also has been added and brgphy(4), ciphy(4), e1000phy(4) as well as ip1000phy(4) have been converted to take advantage of it. This means that these drivers now no longer take the link0 parameter for selecting master mode but the master media option has to be used instead, for example like in the following: ifconfig bge0 media 1000baseT mediaopt full-duplex,master Selection of master mode now is also available with all other PHY drivers supporting 1000baseT. 20101111: The TCP stack has received a significant update to add support for modularised congestion control and generally improve the clarity of congestion control decisions. Bump __FreeBSD_version to 900025. User space tools that rely on the size of struct tcpcb in tcp_var.h (e.g. sockstat) need to be recompiled. 20101002: The man(1) utility has been replaced by a new version that no longer uses /etc/manpath.config. Please consult man.conf(5) for how to migrate local entries to the new format. 20100928: The copyright strings printed by login(1) and sshd(8) at the time of a new connection have been removed to follow other operating systems and upstream sshd. 20100915: A workaround for a fixed ld bug has been removed in kernel code, so make sure that your system ld is built from sources after revision 210245 from 2010-07-19 (r211583 if building head kernel on stable/8, r211584 for stable/7; both from 2010-08-21). A symptom of incorrect ld version is different addresses for set_pcpu section and __start_set_pcpu symbol in kernel and/or modules. 20100913: The $ipv6_prefer variable in rc.conf(5) has been split into $ip6addrctl_policy and $ipv6_activate_all_interfaces. The $ip6addrctl_policy is a variable to choose a pre-defined address selection policy set by ip6addrctl(8). A value "ipv4_prefer", "ipv6_prefer" or "AUTO" can be specified. The default is "AUTO". The $ipv6_activate_all_interfaces specifies whether IFDISABLED flag (see an entry of 20090926) is set on an interface with no corresponding $ifconfig_IF_ipv6 line. The default is "NO" for security reason. If you want IPv6 link-local address on all interfaces by default, set this to "YES". The old ipv6_prefer="YES" is equivalent to ipv6_activate_all_interfaces="YES" and ip6addrctl_policy="ipv6_prefer". 20100913: DTrace has grown support for userland tracing. Due to this, DTrace is now i386 and amd64 only. dtruss(1) is now installed by default on those systems and a new kernel module is needed for userland tracing: fasttrap. No changes to your kernel config file are necessary to enable userland tracing, but you might consider adding 'STRIP=' and 'CFLAGS+=-fno-omit-frame-pointer' to your make.conf if you want to have informative userland stack traces in DTrace (ustack). 20100725: The acpi_aiboost(4) driver has been removed in favor of the new aibs(4) driver. You should update your kernel configuration file. 20100722: BSD grep has been imported to the base system and it is built by default. It is completely BSD licensed, highly GNU-compatible, uses less memory than its GNU counterpart and has a small codebase. However, it is slower than its GNU counterpart, which is mostly noticeable for larger searches, for smaller ones it is measurable but not significant. The reason is complex, the most important factor is that we lack a modern and efficient regex library and GNU overcomes this by optimizing the searches internally. Future work on improving the regex performance is planned, for the meantime, users that need better performance, can build GNU grep instead by setting the WITH_GNU_GREP knob. 20100713: Due to the import of powerpc64 support, all existing powerpc kernel configuration files must be updated with a machine directive like this: machine powerpc powerpc In addition, an updated config(8) is required to build powerpc kernels after this change. 20100713: A new version of ZFS (version 15) has been merged to -HEAD. This version uses a python library for the following subcommands: zfs allow, zfs unallow, zfs groupspace, zfs userspace. For full functionality of these commands the following port must be installed: sysutils/py-zfs 20100429: 'vm_page's are now hashed by physical address to an array of mutexes. Currently this is only used to serialize access to hold_count. Over time the page queue mutex will be peeled away. This changes the size of pmap on every architecture. And requires all callers of vm_page_hold and vm_page_unhold to be updated. 20100402: WITH_CTF can now be specified in src.conf (not recommended, there are some problems with static executables), make.conf (would also affect ports which do not use GNU make and do not override the compile targets) or in the kernel config (via "makeoptions WITH_CTF=yes"). When WITH_CTF was specified there before this was silently ignored, so make sure that WITH_CTF is not used in places which could lead to unwanted behavior. 20100311: The kernel option COMPAT_IA32 has been replaced with COMPAT_FREEBSD32 to allow 32-bit compatibility on non-x86 platforms. All kernel configurations on amd64 and ia64 platforms using these options must be modified accordingly. 20100113: The utmp user accounting database has been replaced with utmpx, the user accounting interface standardized by POSIX. Unfortunately the semantics of utmp and utmpx don't match, making it practically impossible to support both interfaces. The user accounting database is used by tools like finger(1), last(1), talk(1), w(1) and ac(8). All applications in the base system use utmpx. This means only local binaries (e.g. from the ports tree) may still use these utmp database files. These applications must be rebuilt to make use of utmpx. After the system has been upgraded, it is safe to remove the old log files (/var/run/utmp, /var/log/lastlog and /var/log/wtmp*), assuming their contents is of no importance anymore. Old wtmp databases can only be used by last(1) and ac(8) after they have been converted to the new format using wtmpcvt(1). 20100108: Introduce the kernel thread "deadlock resolver" (which can be enabled via the DEADLKRES option, see NOTES for more details) and the sleepq_type() function for sleepqueues. 20091202: The rc.firewall and rc.firewall6 were unified, and rc.firewall6 and rc.d/ip6fw were removed. According to the removal of rc.d/ip6fw, ipv6_firewall_* rc variables are obsoleted. Instead, the following new rc variables are added to rc.d/ipfw: firewall_client_net_ipv6, firewall_simple_iif_ipv6, firewall_simple_inet_ipv6, firewall_simple_oif_ipv6, firewall_simple_onet_ipv6, firewall_trusted_ipv6 The meanings correspond to the relevant IPv4 variables. 20091125: 8.0-RELEASE. 20091113: The default terminal emulation for syscons(4) has been changed from cons25 to xterm on all platforms except pc98. This means that the /etc/ttys file needs to be updated to ensure correct operation of applications on the console. The terminal emulation style can be toggled per window by using vidcontrol(1)'s -T flag. The TEKEN_CONS25 kernel configuration options can be used to change the compile-time default back to cons25. To prevent graphical artifacts, make sure the TERM environment variable is set to match the terminal emulation that is being performed by syscons(4). 20091109: The layout of the structure ieee80211req_scan_result has changed. Applications that require wireless scan results (e.g. ifconfig(8)) from net80211 need to be recompiled. Applications such as wpa_supplicant(8) may require a full world build without using NO_CLEAN in order to get synchronized with the new structure. 20091025: The iwn(4) driver has been updated to support the 5000 and 5150 series. There's one kernel module for each firmware. Adding "device iwnfw" to the kernel configuration file means including all three firmware images inside the kernel. If you want to include just the one for your wireless card, use the devices iwn4965fw, iwn5000fw or iwn5150fw. 20090926: The rc.d/network_ipv6, IPv6 configuration script has been integrated into rc.d/netif. The changes are the following: 1. To use IPv6, simply define $ifconfig_IF_ipv6 like $ifconfig_IF for IPv4. For aliases, $ifconfig_IF_aliasN should be used. Note that both variables need the "inet6" keyword at the head. Do not set $ipv6_network_interfaces manually if you do not understand what you are doing. It is not needed in most cases. $ipv6_ifconfig_IF and $ipv6_ifconfig_IF_aliasN still work, but they are obsolete. 2. $ipv6_enable is obsolete. Use $ipv6_prefer and "inet6 accept_rtadv" keyword in ifconfig(8) instead. If you define $ipv6_enable=YES, it means $ipv6_prefer=YES and all configured interfaces have "inet6 accept_rtadv" in the $ifconfig_IF_ipv6. These are for backward compatibility. 3. A new variable $ipv6_prefer has been added. If NO, IPv6 functionality of interfaces with no corresponding $ifconfig_IF_ipv6 is disabled by using "inet6 ifdisabled" flag, and the default address selection policy of ip6addrctl(8) is the IPv4-preferred one (see rc.d/ip6addrctl for more details). Note that if you want to configure IPv6 functionality on the disabled interfaces after boot, first you need to clear the flag by using ifconfig(8) like: ifconfig em0 inet6 -ifdisabled If YES, the default address selection policy is set as IPv6-preferred. The default value of $ipv6_prefer is NO. 4. If your system need to receive Router Advertisement messages, define "inet6 accept_rtadv" in $ifconfig_IF_ipv6. The rc(8) scripts automatically invoke rtsol(8) when the interface becomes UP. The Router Advertisement messages are used for SLAAC (State-Less Address AutoConfiguration). 20090922: 802.11s D3.03 support was committed. This is incompatible with the previous code, which was based on D3.0. 20090912: A sysctl variable net.inet6.ip6.accept_rtadv now sets the default value of a per-interface flag ND6_IFF_ACCEPT_RTADV, not a global knob to control whether accepting Router Advertisement messages or not. Also, a per-interface flag ND6_IFF_AUTO_LINKLOCAL has been added and a sysctl variable net.inet6.ip6.auto_linklocal is its default value. The ifconfig(8) utility now supports these flags. 20090910: ZFS snapshots are now mounted with MNT_IGNORE flag. Use -v option for mount(8) and -a option for df(1) to see them. 20090825: The old tunable hw.bus.devctl_disable has been superseded by hw.bus.devctl_queue. hw.bus.devctl_disable=1 in loader.conf should be replaced by hw.bus.devctl_queue=0. The default for this new tunable is 1000. 20090813: Remove the option STOP_NMI. The default action is now to use NMI only for KDB via the newly introduced function stop_cpus_hard() and maintain stop_cpus() to just use a normal IPI_STOP on ia32 and amd64. 20090803: The stable/8 branch created in subversion. This corresponds to the RELENG_8 branch in CVS. 20090719: Bump the shared library version numbers for all libraries that do not use symbol versioning as part of the 8.0-RELEASE cycle. Bump __FreeBSD_version to 800105. 20090714: Due to changes in the implementation of virtual network stack support, all network-related kernel modules must be recompiled. As this change breaks the ABI, bump __FreeBSD_version to 800104. 20090713: The TOE interface to the TCP syncache has been modified to remove struct tcpopt () from the ABI of the network stack. The cxgb driver is the only TOE consumer affected by this change, and needs to be recompiled along with the kernel. As this change breaks the ABI, bump __FreeBSD_version to 800103. 20090712: Padding has been added to struct tcpcb, sackhint and tcpstat in to facilitate future MFCs and bug fixes whilst maintaining the ABI. However, this change breaks the ABI, so bump __FreeBSD_version to 800102. User space tools that rely on the size of any of these structs (e.g. sockstat) need to be recompiled. 20090630: The NFS_LEGACYRPC option has been removed along with the old kernel RPC implementation that this option selected. Kernel configurations may need to be adjusted. 20090629: The network interface device nodes at /dev/net/ have been removed. All ioctl operations can be performed the normal way using routing sockets. The kqueue functionality can generally be replaced with routing sockets. 20090628: The documentation from the FreeBSD Documentation Project (Handbook, FAQ, etc.) is now installed via packages by sysinstall(8) and under the /usr/local/share/doc/freebsd directory instead of /usr/share/doc. 20090624: The ABI of various structures related to the SYSV IPC API have been changed. As a result, the COMPAT_FREEBSD[456] and COMPAT_43 kernel options now all require COMPAT_FREEBSD7. Bump __FreeBSD_version to 800100. 20090622: Layout of struct vnet has changed as routing related variables were moved to their own Vimage module. Modules need to be recompiled. Bump __FreeBSD_version to 800099. 20090619: NGROUPS_MAX and NGROUPS have been increased from 16 to 1023 and 1024 respectively. As long as no more than 16 groups per process are used, no changes should be visible. When more than 16 groups are used, old binaries may fail if they call getgroups() or getgrouplist() with statically sized storage. Recompiling will work around this, but applications should be modified to use dynamically allocated storage for group arrays as POSIX.1-2008 does not cap an implementation's number of supported groups at NGROUPS_MAX+1 as previous versions did. NFS and portalfs mounts may also be affected as the list of groups is truncated to 16. Users of NFS who use more than 16 groups, should take care that negative group permissions are not used on the exported file systems as they will not be reliable unless a GSSAPI based authentication method is used. 20090616: The compiling option ADAPTIVE_LOCKMGRS has been introduced. This option compiles in the support for adaptive spinning for lockmgrs which want to enable it. The lockinit() function now accepts the flag LK_ADAPTIVE in order to make the lock object subject to adaptive spinning when both held in write and read mode. 20090613: The layout of the structure returned by IEEE80211_IOC_STA_INFO has changed. User applications that use this ioctl need to be rebuilt. 20090611: The layout of struct thread has changed. Kernel and modules need to be rebuilt. 20090608: The layout of structs ifnet, domain, protosw and vnet_net has changed. Kernel modules need to be rebuilt. Bump __FreeBSD_version to 800097. 20090602: window(1) has been removed from the base system. It can now be installed from ports. The port is called misc/window. 20090601: The way we are storing and accessing `routing table' entries has changed. Programs reading the FIB, like netstat, need to be re-compiled. 20090601: A new netisr implementation has been added for FreeBSD 8. Network file system modules, such as igmp, ipdivert, and others, should be rebuilt. Bump __FreeBSD_version to 800096. 20090530: Remove the tunable/sysctl debug.mpsafevfs as its initial purpose is no more valid. 20090530: Add VOP_ACCESSX(9). File system modules need to be rebuilt. Bump __FreeBSD_version to 800094. 20090529: Add mnt_xflag field to 'struct mount'. File system modules need to be rebuilt. Bump __FreeBSD_version to 800093. 20090528: The compiling option ADAPTIVE_SX has been retired while it has been introduced the option NO_ADAPTIVE_SX which handles the reversed logic. The KPI for sx_init_flags() changes as accepting flags: SX_ADAPTIVESPIN flag has been retired while the SX_NOADAPTIVE flag has been introduced in order to handle the reversed logic. Bump __FreeBSD_version to 800092. 20090527: Add support for hierarchical jails. Remove global securelevel. Bump __FreeBSD_version to 800091. 20090523: The layout of struct vnet_net has changed, therefore modules need to be rebuilt. Bump __FreeBSD_version to 800090. 20090523: The newly imported zic(8) produces a new format in the output. Please run tzsetup(8) to install the newly created data to /etc/localtime. 20090520: The sysctl tree for the usb stack has renamed from hw.usb2.* to hw.usb.* and is now consistent again with previous releases. 20090520: 802.11 monitor mode support was revised and driver api's were changed. Drivers dependent on net80211 now support DLT_IEEE802_11_RADIO instead of DLT_IEEE802_11. No user-visible data structures were changed but applications that use DLT_IEEE802_11 may require changes. Bump __FreeBSD_version to 800088. 20090430: The layout of the following structs has changed: sysctl_oid, socket, ifnet, inpcbinfo, tcpcb, syncache_head, vnet_inet, vnet_inet6 and vnet_ipfw. Most modules need to be rebuild or panics may be experienced. World rebuild is required for correctly checking networking state from userland. Bump __FreeBSD_version to 800085. 20090429: MLDv2 and Source-Specific Multicast (SSM) have been merged to the IPv6 stack. VIMAGE hooks are in but not yet used. The implementation of SSM within FreeBSD's IPv6 stack closely follows the IPv4 implementation. For kernel developers: * The most important changes are that the ip6_output() and ip6_input() paths no longer take the IN6_MULTI_LOCK, and this lock has been downgraded to a non-recursive mutex. * As with the changes to the IPv4 stack to support SSM, filtering of inbound multicast traffic must now be performed by transport protocols within the IPv6 stack. This does not apply to TCP and SCTP, however, it does apply to UDP in IPv6 and raw IPv6. * The KPIs used by IPv6 multicast are similar to those used by the IPv4 stack, with the following differences: * im6o_mc_filter() is analogous to imo_multicast_filter(). * The legacy KAME entry points in6_joingroup and in6_leavegroup() are shimmed to in6_mc_join() and in6_mc_leave() respectively. * IN6_LOOKUP_MULTI() has been deprecated and removed. * IPv6 relies on MLD for the DAD mechanism. KAME's internal KPIs for MLDv1 have an additional 'timer' argument which is used to jitter the initial membership report for the solicited-node multicast membership on-link. * This is not strictly needed for MLDv2, which already jitters its report transmissions. However, the 'timer' argument is preserved in case MLDv1 is active on the interface. * The KAME linked-list based IPv6 membership implementation has been refactored to use a vector similar to that used by the IPv4 stack. Code which maintains a list of its own multicast memberships internally, e.g. carp, has been updated to reflect the new semantics. * There is a known Lock Order Reversal (LOR) due to in6_setscope() acquiring the IF_AFDATA_LOCK and being called within ip6_output(). Whilst MLDv2 tries to avoid this otherwise benign LOR, it is an implementation constraint which needs to be addressed in HEAD. For application developers: * The changes are broadly similar to those made for the IPv4 stack. * The use of IPv4 and IPv6 multicast socket options on the same socket, using mapped addresses, HAS NOT been tested or supported. * There are a number of issues with the implementation of various IPv6 multicast APIs which need to be resolved in the API surface before the implementation is fully compatible with KAME userland use, and these are mostly to do with interface index treatment. * The literature available discusses the use of either the delta / ASM API with setsockopt(2)/getsockopt(2), or the full-state / ASM API using setsourcefilter(3)/getsourcefilter(3). For more information please refer to RFC 3768, 'Socket Interface Extensions for Multicast Source Filters'. * Applications which use the published RFC 3678 APIs should be fine. For systems administrators: * The mtest(8) utility has been refactored to support IPv6, in addition to IPv4. Interface addresses are no longer accepted as arguments, their names must be used instead. The utility will map the interface name to its first IPv4 address as returned by getifaddrs(3). * The ifmcstat(8) utility has also been updated to print the MLDv2 endpoint state and source filter lists via sysctl(3). * The net.inet6.ip6.mcast.loop sysctl may be tuned to 0 to disable loopback of IPv6 multicast datagrams by default; it defaults to 1 to preserve the existing behaviour. Disabling multicast loopback is recommended for optimal system performance. * The IPv6 MROUTING code has been changed to examine this sysctl instead of attempting to perform a group lookup before looping back forwarded datagrams. Bump __FreeBSD_version to 800084. 20090422: Implement low-level Bluetooth HCI API. Bump __FreeBSD_version to 800083. 20090419: The layout of struct malloc_type, used by modules to register new memory allocation types, has changed. Most modules will need to be rebuilt or panics may be experienced. Bump __FreeBSD_version to 800081. 20090415: Anticipate overflowing inp_flags - add inp_flags2. This changes most offsets in inpcb, so checking v4 connection state will require a world rebuild. Bump __FreeBSD_version to 800080. 20090415: Add an llentry to struct route and struct route_in6. Modules embedding a struct route will need to be recompiled. Bump __FreeBSD_version to 800079. 20090414: The size of rt_metrics_lite and by extension rtentry has changed. Networking administration apps will need to be recompiled. The route command now supports show as an alias for get, weighting of routes, sticky and nostick flags to alter the behavior of stateful load balancing. Bump __FreeBSD_version to 800078. 20090408: Do not use Giant for kbdmux(4) locking. This is wrong and apparently causing more problems than it solves. This will re-open the issue where interrupt handlers may race with kbdmux(4) in polling mode. Typical symptoms include (but not limited to) duplicated and/or missing characters when low level console functions (such as gets) are used while interrupts are enabled (for example geli password prompt, mountroot prompt etc.). Disabling kbdmux(4) may help. 20090407: The size of structs vnet_net, vnet_inet and vnet_ipfw has changed; kernel modules referencing any of the above need to be recompiled. Bump __FreeBSD_version to 800075. 20090320: GEOM_PART has become the default partition slicer for storage devices, replacing GEOM_MBR, GEOM_BSD, GEOM_PC98 and GEOM_GPT slicers. It introduces some changes: MSDOS/EBR: the devices created from MSDOS extended partition entries (EBR) can be named differently than with GEOM_MBR and are now symlinks to devices with offset-based names. fstabs may need to be modified. BSD: the "geometry does not match label" warning is harmless in most cases but it points to problems in file system misalignment with disk geometry. The "c" partition is now implicit, covers the whole top-level drive and cannot be (mis)used by users. General: Kernel dumps are now not allowed to be written to devices whose partition types indicate they are meant to be used for file systems (or, in case of MSDOS partitions, as something else than the "386BSD" type). Most of these changes date approximately from 200812. 20090319: The uscanner(4) driver has been removed from the kernel. This follows Linux removing theirs in 2.6 and making libusb the default interface (supported by sane). 20090319: The multicast forwarding code has been cleaned up. netstat(1) only relies on KVM now for printing bandwidth upcall meters. The IPv4 and IPv6 modules are split into ip_mroute_mod and ip6_mroute_mod respectively. The config(5) options for statically compiling this code remain the same, i.e. 'options MROUTING'. 20090315: Support for the IFF_NEEDSGIANT network interface flag has been removed, which means that non-MPSAFE network device drivers are no longer supported. In particular, if_ar, if_sr, and network device drivers from the old (legacy) USB stack can no longer be built or used. 20090313: POSIX.1 Native Language Support (NLS) has been enabled in libc and a bunch of new language catalog files have also been added. This means that some common libc messages are now localized and they depend on the LC_MESSAGES environmental variable. 20090313: The k8temp(4) driver has been renamed to amdtemp(4) since support for Family 10 and Family 11 CPU families was added. 20090309: IGMPv3 and Source-Specific Multicast (SSM) have been merged to the IPv4 stack. VIMAGE hooks are in but not yet used. For kernel developers, the most important changes are that the ip_output() and ip_input() paths no longer take the IN_MULTI_LOCK(), and this lock has been downgraded to a non-recursive mutex. Transport protocols (UDP, Raw IP) are now responsible for filtering inbound multicast traffic according to group membership and source filters. The imo_multicast_filter() KPI exists for this purpose. Transports which do not use multicast (SCTP, TCP) already reject multicast by default. Forwarding and receive performance may improve as a mutex acquisition is no longer needed in the ip_input() low-level input path. in_addmulti() and in_delmulti() are shimmed to new KPIs which exist to support SSM in-kernel. For application developers, it is recommended that loopback of multicast datagrams be disabled for best performance, as this will still cause the lock to be taken for each looped-back datagram transmission. The net.inet.ip.mcast.loop sysctl may be tuned to 0 to disable loopback by default; it defaults to 1 to preserve the existing behaviour. For systems administrators, to obtain best performance with multicast reception and multiple groups, it is always recommended that a card with a suitably precise hash filter is used. Hash collisions will still result in the lock being taken within the transport protocol input path to check group membership. If deploying FreeBSD in an environment with IGMP snooping switches, it is recommended that the net.inet.igmp.sendlocal sysctl remain enabled; this forces 224.0.0.0/24 group membership to be announced via IGMP. The size of 'struct igmpstat' has changed; netstat needs to be recompiled to reflect this. Bump __FreeBSD_version to 800070. 20090309: libusb20.so.1 is now installed as libusb.so.1 and the ports system updated to use it. This requires a buildworld/installworld in order to update the library and dependencies (usbconfig, etc). Its advisable to rebuild all ports which uses libusb. More specific directions are given in the ports collection UPDATING file. Any /etc/libmap.conf entries for libusb are no longer required and can be removed. 20090302: A workaround is committed to allow the creation of System V shared memory segment of size > 2 GB on the 64-bit architectures. Due to a limitation of the existing ABI, the shm_segsz member of the struct shmid_ds, returned by shmctl(IPC_STAT) call is wrong for large segments. Note that limits must be explicitly raised to allow such segments to be created. 20090301: The layout of struct ifnet has changed, requiring a rebuild of all network device driver modules. 20090227: The /dev handling for the new USB stack has changed, a buildworld/installworld is required for libusb20. 20090223: The new USB2 stack has now been permanently moved in and all kernel and module names reverted to their previous values (eg, usb, ehci, ohci, ums, ...). The old usb stack can be compiled in by prefixing the name with the letter 'o', the old usb modules have been removed. Updating entry 20090216 for xorg and 20090215 for libmap may still apply. 20090217: The rc.conf(5) option if_up_delay has been renamed to defaultroute_delay to better reflect its purpose. If you have customized this setting in /etc/rc.conf you need to update it to use the new name. 20090216: xorg 7.4 wants to configure its input devices via hald which does not yet work with USB2. If the keyboard/mouse does not work in xorg then add Option "AllowEmptyInput" "off" to your ServerLayout section. This will cause X to use the configured kbd and mouse sections from your xorg.conf. 20090215: The GENERIC kernels for all architectures now default to the new USB2 stack. No kernel config options or code have been removed so if a problem arises please report it and optionally revert to the old USB stack. If you are loading USB kernel modules or have a custom kernel that includes GENERIC then ensure that usb names are also changed over, eg uftdi -> usb2_serial_ftdi. Older programs linked against the ports libusb 0.1 need to be redirected to the new stack's libusb20. /etc/libmap.conf can be used for this: # Map old usb library to new one for usb2 stack libusb-0.1.so.8 libusb20.so.1 20090209: All USB ethernet devices now attach as interfaces under the name ueN (eg. ue0). This is to provide a predictable name as vendors often change usb chipsets in a product without notice. 20090203: The ichsmb(4) driver has been changed to require SMBus slave addresses be left-justified (xxxxxxx0b) rather than right-justified. All of the other SMBus controller drivers require left-justified slave addresses, so this change makes all the drivers provide the same interface. 20090201: INET6 statistics (struct ip6stat) was updated. netstat(1) needs to be recompiled. 20090119: NTFS has been removed from GENERIC kernel on amd64 to match GENERIC on i386. Should not cause any issues since mount_ntfs(8) will load ntfs.ko module automatically when NTFS support is actually needed, unless ntfs.ko is not installed or security level prohibits loading kernel modules. If either is the case, "options NTFS" has to be added into kernel config. 20090115: TCP Appropriate Byte Counting (RFC 3465) support added to kernel. New field in struct tcpcb breaks ABI, so bump __FreeBSD_version to 800061. User space tools that rely on the size of struct tcpcb in tcp_var.h (e.g. sockstat) need to be recompiled. 20081225: ng_tty(4) module updated to match the new TTY subsystem. Due to API change, user-level applications must be updated. New API support added to mpd5 CVS and expected to be present in next mpd5.3 release. 20081219: With __FreeBSD_version 800060 the makefs tool is part of the base system (it was a port). 20081216: The afdata and ifnet locks have been changed from mutexes to rwlocks, network modules will need to be re-compiled. 20081214: __FreeBSD_version 800059 incorporates the new arp-v2 rewrite. RTF_CLONING, RTF_LLINFO and RTF_WASCLONED flags are eliminated. The new code reduced struct rtentry{} by 16 bytes on 32-bit architecture and 40 bytes on 64-bit architecture. The userland applications "arp" and "ndp" have been updated accordingly. The output from "netstat -r" shows only routing entries and none of the L2 information. 20081130: __FreeBSD_version 800057 marks the switchover from the binary ath hal to source code. Users must add the line: options AH_SUPPORT_AR5416 to their kernel config files when specifying: device ath_hal The ath_hal module no longer exists; the code is now compiled together with the driver in the ath module. It is now possible to tailor chip support (i.e. reduce the set of chips and thereby the code size); consult ath_hal(4) for details. 20081121: __FreeBSD_version 800054 adds memory barriers to , new interfaces to ifnet to facilitate multiple hardware transmit queues for cards that support them, and a lock-less ring-buffer implementation to enable drivers to more efficiently manage queueing of packets. 20081117: A new version of ZFS (version 13) has been merged to -HEAD. This version has zpool attribute "listsnapshots" off by default, which means "zfs list" does not show snapshots, and is the same as Solaris behavior. 20081028: dummynet(4) ABI has changed. ipfw(8) needs to be recompiled. 20081009: The uhci, ohci, ehci and slhci USB Host controller drivers have been put into separate modules. If you load the usb module separately through loader.conf you will need to load the appropriate *hci module as well. E.g. for a UHCI-based USB 2.0 controller add the following to loader.conf: uhci_load="YES" ehci_load="YES" 20081009: The ABI used by the PMC toolset has changed. Please keep userland (libpmc(3)) and the kernel module (hwpmc(4)) in sync. 20081009: atapci kernel module now includes only generic PCI ATA driver. AHCI driver moved to ataahci kernel module. All vendor-specific code moved into separate kernel modules: ataacard, ataacerlabs, ataadaptec, ataamd, ataati, atacenatek, atacypress, atacyrix, atahighpoint, ataintel, ataite, atajmicron, atamarvell, atamicron, atanational, atanetcell, atanvidia, atapromise, ataserverworks, atasiliconimage, atasis, atavia 20080820: The TTY subsystem of the kernel has been replaced by a new implementation, which provides better scalability and an improved driver model. Most common drivers have been migrated to the new TTY subsystem, while others have not. The following drivers have not yet been ported to the new TTY layer: PCI/ISA: cy, digi, rc, rp, sio USB: ubser, ucycom Line disciplines: ng_h4, ng_tty, ppp, sl, snp Adding these drivers to your kernel configuration file shall cause compilation to fail. 20080818: ntpd has been upgraded to 4.2.4p5. 20080801: OpenSSH has been upgraded to 5.1p1. For many years, FreeBSD's version of OpenSSH preferred DSA over RSA for host and user authentication keys. With this upgrade, we've switched to the vendor's default of RSA over DSA. This may cause upgraded clients to warn about unknown host keys even for previously known hosts. Users should follow the usual procedure for verifying host keys before accepting the RSA key. This can be circumvented by setting the "HostKeyAlgorithms" option to "ssh-dss,ssh-rsa" in ~/.ssh/config or on the ssh command line. Please note that the sequence of keys offered for authentication has been changed as well. You may want to specify IdentityFile in a different order to revert this behavior. 20080713: The sio(4) driver has been removed from the i386 and amd64 kernel configuration files. This means uart(4) is now the default serial port driver on those platforms as well. To prevent collisions with the sio(4) driver, the uart(4) driver uses different names for its device nodes. This means the onboard serial port will now most likely be called "ttyu0" instead of "ttyd0". You may need to reconfigure applications to use the new device names. When using the serial port as a boot console, be sure to update /boot/device.hints and /etc/ttys before booting the new kernel. If you forget to do so, you can still manually specify the hints at the loader prompt: set hint.uart.0.at="isa" set hint.uart.0.port="0x3F8" set hint.uart.0.flags="0x10" set hint.uart.0.irq="4" boot -s 20080609: The gpt(8) utility has been removed. Use gpart(8) to partition disks instead. 20080603: The version that Linuxulator emulates was changed from 2.4.2 to 2.6.16. If you experience any problems with Linux binaries please try to set sysctl compat.linux.osrelease to 2.4.2 and if it fixes the problem contact emulation mailing list. 20080525: ISDN4BSD (I4B) was removed from the src tree. You may need to update a your kernel configuration and remove relevant entries. 20080509: I have checked in code to support multiple routing tables. See the man pages setfib(1) and setfib(2). This is a hopefully backwards compatible version, but to make use of it you need to compile your kernel with options ROUTETABLES=2 (or more up to 16). 20080420: The 802.11 wireless support was redone to enable multi-bss operation on devices that are capable. The underlying device is no longer used directly but instead wlanX devices are cloned with ifconfig. This requires changes to rc.conf files. For example, change: ifconfig_ath0="WPA DHCP" to wlans_ath0=wlan0 ifconfig_wlan0="WPA DHCP" see rc.conf(5) for more details. In addition, mergemaster of /etc/rc.d is highly recommended. Simultaneous update of userland and kernel wouldn't hurt either. As part of the multi-bss changes the wlan_scan_ap and wlan_scan_sta modules were merged into the base wlan module. All references to these modules (e.g. in kernel config files) must be removed. 20080408: psm(4) has gained write(2) support in native operation level. Arbitrary commands can be written to /dev/psm%d and status can be read back from it. Therefore, an application is responsible for status validation and error recovery. It is a no-op in other operation levels. 20080312: Support for KSE threading has been removed from the kernel. To run legacy applications linked against KSE libmap.conf may be used. The following libmap.conf may be used to ensure compatibility with any prior release: libpthread.so.1 libthr.so.1 libpthread.so.2 libthr.so.2 libkse.so.3 libthr.so.3 20080301: The layout of struct vmspace has changed. This affects libkvm and any executables that link against libkvm and use the kvm_getprocs() function. In particular, but not exclusively, it affects ps(1), fstat(1), pkill(1), systat(1), top(1) and w(1). The effects are minimal, but it's advisable to upgrade world nonetheless. 20080229: The latest em driver no longer has support in it for the 82575 adapter, this is now moved to the igb driver. The split was done to make new features that are incompatible with older hardware easier to do. 20080220: The new geom_lvm(4) geom class has been renamed to geom_linux_lvm(4), likewise the kernel option is now GEOM_LINUX_LVM. 20080211: The default NFS mount mode has changed from UDP to TCP for increased reliability. If you rely on (insecurely) NFS mounting across a firewall you may need to update your firewall rules. 20080208: Belatedly note the addition of m_collapse for compacting mbuf chains. 20080126: The fts(3) structures have been changed to use adequate integer types for their members and so to be able to cope with huge file trees. The old fts(3) ABI is preserved through symbol versioning in libc, so third-party binaries using fts(3) should still work, although they will not take advantage of the extended types. At the same time, some third-party software might fail to build after this change due to unportable assumptions made in its source code about fts(3) structure members. Such software should be fixed by its vendor or, in the worst case, in the ports tree. FreeBSD_version 800015 marks this change for the unlikely case that a portable fix is impossible. 20080123: To upgrade to -current after this date, you must be running FreeBSD not older than 6.0-RELEASE. Upgrading to -current from 5.x now requires a stop over at RELENG_6 or RELENG_7 systems. 20071128: The ADAPTIVE_GIANT kernel option has been retired because its functionality is the default now. 20071118: The AT keyboard emulation of sunkbd(4) has been turned on by default. In order to make the special symbols of the Sun keyboards driven by sunkbd(4) work under X these now have to be configured the same way as Sun USB keyboards driven by ukbd(4) (which also does AT keyboard emulation), f.e.: Option "XkbLayout" "us" Option "XkbRules" "xorg" Option "XkbSymbols" "pc(pc105)+sun_vndr/usb(sun_usb)+us" 20071024: It has been decided that it is desirable to provide ABI backwards compatibility to the FreeBSD 4/5/6 versions of the PCIOCGETCONF, PCIOCREAD and PCIOCWRITE IOCTLs, which was broken with the introduction of PCI domain support (see the 20070930 entry). Unfortunately, this required the ABI of PCIOCGETCONF to be broken again in order to be able to provide backwards compatibility to the old version of that IOCTL. Thus consumers of PCIOCGETCONF have to be recompiled again. As for prominent ports this affects neither pciutils nor xorg-server this time, the hal port needs to be rebuilt however. 20071020: The misnamed kthread_create() and friends have been renamed to kproc_create() etc. Many of the callers already used kproc_start().. I will return kthread_create() and friends in a while with implementations that actually create threads, not procs. Renaming corresponds with version 800002. 20071010: RELENG_7 branched. COMMON ITEMS: General Notes ------------- Avoid using make -j when upgrading. While generally safe, there are sometimes problems using -j to upgrade. If your upgrade fails with -j, please try again without -j. From time to time in the past there have been problems using -j with buildworld and/or installworld. This is especially true when upgrading between "distant" versions (eg one that cross a major release boundary or several minor releases, or when several months have passed on the -current branch). Sometimes, obscure build problems are the result of environment poisoning. This can happen because the make utility reads its environment when searching for values for global variables. To run your build attempts in an "environmental clean room", prefix all make commands with 'env -i '. See the env(1) manual page for more details. When upgrading from one major version to another it is generally best to upgrade to the latest code in the currently installed branch first, then do an upgrade to the new branch. This is the best-tested upgrade path, and has the highest probability of being successful. Please try this approach before reporting problems with a major version upgrade. When upgrading a live system, having a root shell around before installing anything can help undo problems. Not having a root shell around can lead to problems if pam has changed too much from your starting point to allow continued authentication after the upgrade. ZFS notes --------- When upgrading the boot ZFS pool to a new version, always follow these two steps: 1.) recompile and reinstall the ZFS boot loader and boot block (this is part of "make buildworld" and "make installworld") 2.) update the ZFS boot block on your boot drive The following example updates the ZFS boot block on the first partition (freebsd-boot) of a GPT partitioned drive ad0: "gpart bootcode -p /boot/gptzfsboot -i 1 ad0" Non-boot pools do not need these updates. To build a kernel ----------------- If you are updating from a prior version of FreeBSD (even one just a few days old), you should follow this procedure. It is the most failsafe as it uses a /usr/obj tree with a fresh mini-buildworld, make kernel-toolchain make -DALWAYS_CHECK_MAKE buildkernel KERNCONF=YOUR_KERNEL_HERE make -DALWAYS_CHECK_MAKE installkernel KERNCONF=YOUR_KERNEL_HERE To test a kernel once --------------------- If you just want to boot a kernel once (because you are not sure if it works, or if you want to boot a known bad kernel to provide debugging information) run make installkernel KERNCONF=YOUR_KERNEL_HERE KODIR=/boot/testkernel nextboot -k testkernel To just build a kernel when you know that it won't mess you up -------------------------------------------------------------- This assumes you are already running a CURRENT system. Replace ${arch} with the architecture of your machine (e.g. "i386", "arm", "amd64", "ia64", "pc98", "sparc64", "powerpc", "mips", etc). cd src/sys/${arch}/conf config KERNEL_NAME_HERE cd ../compile/KERNEL_NAME_HERE make depend make make install If this fails, go to the "To build a kernel" section. To rebuild everything and install it on the current system. ----------------------------------------------------------- # Note: sometimes if you are running current you gotta do more than # is listed here if you are upgrading from a really old current. make buildworld make kernel KERNCONF=YOUR_KERNEL_HERE [1] [3] mergemaster -p [5] make installworld mergemaster -i [4] make delete-old [6] To cross-install current onto a separate partition -------------------------------------------------- # In this approach we use a separate partition to hold # current's root, 'usr', and 'var' directories. A partition # holding "/", "/usr" and "/var" should be about 2GB in # size. make buildworld make buildkernel KERNCONF=YOUR_KERNEL_HERE make installworld DESTDIR=${CURRENT_ROOT} make distribution DESTDIR=${CURRENT_ROOT} # if newfs'd make installkernel KERNCONF=YOUR_KERNEL_HERE DESTDIR=${CURRENT_ROOT} cp /etc/fstab ${CURRENT_ROOT}/etc/fstab # if newfs'd To upgrade in-place from stable to current ---------------------------------------------- make buildworld [9] make kernel KERNCONF=YOUR_KERNEL_HERE [8] [1] [3] mergemaster -p [5] make installworld mergemaster -i [4] make delete-old [6] Make sure that you've read the UPDATING file to understand the tweaks to various things you need. At this point in the life cycle of current, things change often and you are on your own to cope. The defaults can also change, so please read ALL of the UPDATING entries. Also, if you are tracking -current, you must be subscribed to freebsd-current@freebsd.org. Make sure that before you update your sources that you have read and understood all the recent messages there. If in doubt, please track -stable which has much fewer pitfalls. [1] If you have third party modules, such as vmware, you should disable them at this point so they don't crash your system on reboot. [3] From the bootblocks, boot -s, and then do fsck -p mount -u / mount -a cd src adjkerntz -i # if CMOS is wall time Also, when doing a major release upgrade, it is required that you boot into single user mode to do the installworld. [4] Note: This step is non-optional. Failure to do this step can result in a significant reduction in the functionality of the system. Attempting to do it by hand is not recommended and those that pursue this avenue should read this file carefully, as well as the archives of freebsd-current and freebsd-hackers mailing lists for potential gotchas. The -U option is also useful to consider. See mergemaster(8) for more information. [5] Usually this step is a noop. However, from time to time you may need to do this if you get unknown user in the following step. It never hurts to do it all the time. You may need to install a new mergemaster (cd src/usr.sbin/mergemaster && make install) after the buildworld before this step if you last updated from current before 20130425 or from -stable before 20130430. [6] This only deletes old files and directories. Old libraries can be deleted by "make delete-old-libs", but you have to make sure that no program is using those libraries anymore. [8] In order to have a kernel that can run the 4.x binaries needed to do an installworld, you must include the COMPAT_FREEBSD4 option in your kernel. Failure to do so may leave you with a system that is hard to boot to recover. A similar kernel option COMPAT_FREEBSD5 is required to run the 5.x binaries on more recent kernels. And so on for COMPAT_FREEBSD6 and COMPAT_FREEBSD7. Make sure that you merge any new devices from GENERIC since the last time you updated your kernel config file. [9] When checking out sources, you must include the -P flag to have cvs prune empty directories. If CPUTYPE is defined in your /etc/make.conf, make sure to use the "?=" instead of the "=" assignment operator, so that buildworld can override the CPUTYPE if it needs to. MAKEOBJDIRPREFIX must be defined in an environment variable, and not on the command line, or in /etc/make.conf. buildworld will warn if it is improperly defined. FORMAT: This file contains a list, in reverse chronological order, of major breakages in tracking -current. It is not guaranteed to be a complete list of such breakages, and only contains entries since October 10, 2007. If you need to see UPDATING entries from before that date, you will need to fetch an UPDATING file from an older FreeBSD release. Copyright information: Copyright 1998-2009 M. Warner Losh. All Rights Reserved. Redistribution, publication, translation and use, with or without modification, in full or in part, in any form or format of this document are permitted without further permission from the author. THIS DOCUMENT IS PROVIDED BY WARNER LOSH ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WARNER LOSH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Contact Warner Losh if you have any questions about your use of this document. $FreeBSD$ Index: releng/10.1/contrib/ntp/ntpd/ntp_config.c =================================================================== --- releng/10.1/contrib/ntp/ntpd/ntp_config.c (revision 276158) +++ releng/10.1/contrib/ntp/ntpd/ntp_config.c (revision 276159) @@ -1,2582 +1,2582 @@ /* * ntp_config.c - read and apply configuration information */ #ifdef HAVE_CONFIG_H # include #endif #ifdef HAVE_NETINFO # include #endif #include "ntpd.h" #include "ntp_io.h" #include "ntp_unixtime.h" #include "ntp_refclock.h" #include "ntp_filegen.h" #include "ntp_stdlib.h" #include #include #include #include #include #ifdef HAVE_SYS_PARAM_H #include #endif #include #ifndef SIGCHLD # define SIGCHLD SIGCLD #endif #if !defined(VMS) # ifdef HAVE_SYS_WAIT_H # include # endif #endif /* VMS */ #ifdef SYS_WINNT # include static HANDLE ResolverThreadHandle = NULL; HANDLE ResolverEventHandle; #else int resolver_pipe_fd[2]; /* used to let the resolver process alert the parent process */ #endif /* SYS_WINNT */ /* * [Bug 467]: Some linux headers collide with CONFIG_PHONE and CONFIG_KEYS * so #include these later. */ #include "ntp_config.h" #include "ntp_cmdargs.h" extern int priority_done; /* * These routines are used to read the configuration file at * startup time. An entry in the file must fit on a single line. * Entries are processed as multiple tokens separated by white space * Lines are considered terminated when a '#' is encountered. Blank * lines are ignored. */ /* * Translation table - keywords to function index */ struct keyword { const char *text; int keytype; }; /* * Command keywords */ static struct keyword keywords[] = { { "automax", CONFIG_AUTOMAX }, { "broadcast", CONFIG_BROADCAST }, { "broadcastclient", CONFIG_BROADCASTCLIENT }, { "broadcastdelay", CONFIG_BDELAY }, { "calldelay", CONFIG_CDELAY}, #ifdef OPENSSL { "crypto", CONFIG_CRYPTO }, #endif /* OPENSSL */ { "controlkey", CONFIG_CONTROLKEY }, { "disable", CONFIG_DISABLE }, { "driftfile", CONFIG_DRIFTFILE }, { "enable", CONFIG_ENABLE }, { "end", CONFIG_END }, { "filegen", CONFIG_FILEGEN }, { "fudge", CONFIG_FUDGE }, { "includefile", CONFIG_INCLUDEFILE }, { "keys", CONFIG_KEYS }, { "keysdir", CONFIG_KEYSDIR }, { "logconfig", CONFIG_LOGCONFIG }, { "logfile", CONFIG_LOGFILE }, { "manycastclient", CONFIG_MANYCASTCLIENT }, { "manycastserver", CONFIG_MANYCASTSERVER }, { "multicastclient", CONFIG_MULTICASTCLIENT }, { "peer", CONFIG_PEER }, { "phone", CONFIG_PHONE }, { "pidfile", CONFIG_PIDFILE }, { "discard", CONFIG_DISCARD }, { "requestkey", CONFIG_REQUESTKEY }, { "restrict", CONFIG_RESTRICT }, { "revoke", CONFIG_REVOKE }, { "server", CONFIG_SERVER }, { "setvar", CONFIG_SETVAR }, { "statistics", CONFIG_STATISTICS }, { "statsdir", CONFIG_STATSDIR }, { "tick", CONFIG_ADJ }, { "tinker", CONFIG_TINKER }, { "tos", CONFIG_TOS }, { "trap", CONFIG_TRAP }, { "trustedkey", CONFIG_TRUSTEDKEY }, { "ttl", CONFIG_TTL }, { "", CONFIG_UNKNOWN } }; /* * "peer", "server", "broadcast" modifier keywords */ static struct keyword mod_keywords[] = { { "autokey", CONF_MOD_SKEY }, { "burst", CONF_MOD_BURST }, { "iburst", CONF_MOD_IBURST }, { "key", CONF_MOD_KEY }, { "maxpoll", CONF_MOD_MAXPOLL }, { "minpoll", CONF_MOD_MINPOLL }, { "mode", CONF_MOD_MODE }, /* refclocks */ { "noselect", CONF_MOD_NOSELECT }, { "preempt", CONF_MOD_PREEMPT }, { "true", CONF_MOD_TRUE }, { "prefer", CONF_MOD_PREFER }, { "ttl", CONF_MOD_TTL }, /* NTP peers */ { "version", CONF_MOD_VERSION }, { "dynamic", CONF_MOD_DYNAMIC }, { "", CONFIG_UNKNOWN } }; /* * "restrict" modifier keywords */ static struct keyword res_keywords[] = { { "ignore", CONF_RES_IGNORE }, { "limited", CONF_RES_LIMITED }, { "kod", CONF_RES_DEMOBILIZE }, { "lowpriotrap", CONF_RES_LPTRAP }, { "mask", CONF_RES_MASK }, { "nomodify", CONF_RES_NOMODIFY }, { "nopeer", CONF_RES_NOPEER }, { "noquery", CONF_RES_NOQUERY }, { "noserve", CONF_RES_NOSERVE }, { "notrap", CONF_RES_NOTRAP }, { "notrust", CONF_RES_NOTRUST }, { "ntpport", CONF_RES_NTPPORT }, { "version", CONF_RES_VERSION }, { "", CONFIG_UNKNOWN } }; /* * "trap" modifier keywords */ static struct keyword trap_keywords[] = { { "port", CONF_TRAP_PORT }, { "interface", CONF_TRAP_INTERFACE }, { "", CONFIG_UNKNOWN } }; /* * "fudge" modifier keywords */ static struct keyword fudge_keywords[] = { { "flag1", CONF_FDG_FLAG1 }, { "flag2", CONF_FDG_FLAG2 }, { "flag3", CONF_FDG_FLAG3 }, { "flag4", CONF_FDG_FLAG4 }, { "refid", CONF_FDG_REFID }, /* this mapping should be cleaned up (endianness, \0) - kd 20041031 */ { "stratum", CONF_FDG_STRATUM }, { "time1", CONF_FDG_TIME1 }, { "time2", CONF_FDG_TIME2 }, { "", CONFIG_UNKNOWN } }; /* * "filegen" modifier keywords */ static struct keyword filegen_keywords[] = { { "disable", CONF_FGEN_FLAG_DISABLE }, { "enable", CONF_FGEN_FLAG_ENABLE }, { "file", CONF_FGEN_FILE }, { "link", CONF_FGEN_FLAG_LINK }, { "nolink", CONF_FGEN_FLAG_NOLINK }, { "type", CONF_FGEN_TYPE }, { "", CONFIG_UNKNOWN } }; /* * "type" modifier keywords */ static struct keyword fgen_types[] = { { "age", FILEGEN_AGE }, { "day", FILEGEN_DAY }, { "month", FILEGEN_MONTH }, { "none", FILEGEN_NONE }, { "pid", FILEGEN_PID }, { "week", FILEGEN_WEEK }, { "year", FILEGEN_YEAR }, { "", CONFIG_UNKNOWN} }; /* * "enable", "disable" modifier keywords */ static struct keyword flags_keywords[] = { { "auth", PROTO_AUTHENTICATE }, { "bclient", PROTO_BROADCLIENT }, { "calibrate", PROTO_CAL }, { "kernel", PROTO_KERNEL }, { "monitor", PROTO_MONITOR }, { "ntp", PROTO_NTP }, { "stats", PROTO_FILEGEN }, { "", CONFIG_UNKNOWN } }; /* * "discard" modifier keywords */ static struct keyword discard_keywords[] = { { "average", CONF_DISCARD_AVERAGE }, { "minimum", CONF_DISCARD_MINIMUM }, { "monitor", CONF_DISCARD_MONITOR }, { "", CONFIG_UNKNOWN } }; /* * "tinker" modifier keywords */ static struct keyword tinker_keywords[] = { { "step", CONF_CLOCK_MAX }, { "panic", CONF_CLOCK_PANIC }, { "dispersion", CONF_CLOCK_PHI }, { "stepout", CONF_CLOCK_MINSTEP }, { "allan", CONF_CLOCK_ALLAN }, { "huffpuff", CONF_CLOCK_HUFFPUFF }, { "freq", CONF_CLOCK_FREQ }, { "", CONFIG_UNKNOWN } }; /* * "tos" modifier keywords */ static struct keyword tos_keywords[] = { { "minclock", CONF_TOS_MINCLOCK }, { "maxclock", CONF_TOS_MAXCLOCK }, { "minsane", CONF_TOS_MINSANE }, { "floor", CONF_TOS_FLOOR }, { "ceiling", CONF_TOS_CEILING }, { "cohort", CONF_TOS_COHORT }, { "mindist", CONF_TOS_MINDISP }, { "maxdist", CONF_TOS_MAXDIST }, { "maxhop", CONF_TOS_MAXHOP }, { "beacon", CONF_TOS_BEACON }, { "orphan", CONF_TOS_ORPHAN }, { "", CONFIG_UNKNOWN } }; #ifdef OPENSSL /* * "crypto" modifier keywords */ static struct keyword crypto_keywords[] = { { "cert", CONF_CRYPTO_CERT }, { "gqpar", CONF_CRYPTO_GQPAR }, { "host", CONF_CRYPTO_RSA }, { "ident", CONF_CRYPTO_IDENT }, { "iffpar", CONF_CRYPTO_IFFPAR }, { "leap", CONF_CRYPTO_LEAP }, { "mvpar", CONF_CRYPTO_MVPAR }, { "pw", CONF_CRYPTO_PW }, { "randfile", CONF_CRYPTO_RAND }, { "sign", CONF_CRYPTO_SIGN }, { "", CONFIG_UNKNOWN } }; #endif /* OPENSSL */ /* * Address type selection, IPv4 or IPv4. * Used on various lines. */ static struct keyword addr_type[] = { { "-4", CONF_ADDR_IPV4 }, { "-6", CONF_ADDR_IPV6 }, { "", CONFIG_UNKNOWN } }; /* * "logconfig" building blocks */ struct masks { const char *name; unsigned long mask; }; static struct masks logcfg_class[] = { { "clock", NLOG_OCLOCK }, { "peer", NLOG_OPEER }, { "sync", NLOG_OSYNC }, { "sys", NLOG_OSYS }, { (char *)0, 0 } }; static struct masks logcfg_item[] = { { "info", NLOG_INFO }, { "allinfo", NLOG_SYSINFO|NLOG_PEERINFO|NLOG_CLOCKINFO|NLOG_SYNCINFO }, { "events", NLOG_EVENT }, { "allevents", NLOG_SYSEVENT|NLOG_PEEREVENT|NLOG_CLOCKEVENT|NLOG_SYNCEVENT }, { "status", NLOG_STATUS }, { "allstatus", NLOG_SYSSTATUS|NLOG_PEERSTATUS|NLOG_CLOCKSTATUS|NLOG_SYNCSTATUS }, { "statistics", NLOG_STATIST }, { "allstatistics", NLOG_SYSSTATIST|NLOG_PEERSTATIST|NLOG_CLOCKSTATIST|NLOG_SYNCSTATIST }, { "allclock", (NLOG_INFO|NLOG_STATIST|NLOG_EVENT|NLOG_STATUS)<name) { if (strncmp(*s, m->name, strlen(m->name)) == 0) { *s += strlen(m->name); return m->mask; } else { m++; } } return 0; } /* * get_match - find logmask value */ static unsigned long get_match( char *s, struct masks *m ) { while (m->name) { if (strcmp(s, m->name) == 0) { return m->mask; } else { m++; } } return 0; } /* * get_logmask - build bitmask for ntp_syslogmask */ static unsigned long get_logmask( char *s ) { char *t; unsigned long offset; unsigned long mask; t = s; offset = get_pfxmatch(&t, logcfg_class); mask = get_match(t, logcfg_item); if (mask) return mask << offset; else msyslog(LOG_ERR, "logconfig: illegal argument %s - ignored", s); return 0; } /* * getconfig - get command line options and read the configuration file */ void getconfig( int argc, char *argv[] ) { register int i; int c; int errflg; int status; int istart; int peerversion; int minpoll; int maxpoll; int ttl; long stratum; unsigned long ul; keyid_t peerkey; u_char *peerkeystr; u_long fudgeflag; u_int peerflags; int hmode; struct sockaddr_storage peeraddr; struct sockaddr_storage maskaddr; FILE *fp[MAXINCLUDELEVEL+1]; FILE *includefile; int includelevel = 0; char line[MAXLINE]; char *(tokens[MAXTOKENS]); int ntokens = 0; int tok = CONFIG_UNKNOWN; struct interface *localaddr; struct refclockstat clock_stat; FILEGEN *filegen; /* * Initialize, initialize */ errflg = 0; #ifndef SYS_WINNT config_file = CONFIG_FILE; #else temp = CONFIG_FILE; if (!ExpandEnvironmentStrings((LPCTSTR)temp, (LPTSTR)config_file_storage, (DWORD)sizeof(config_file_storage))) { msyslog(LOG_ERR, "ExpandEnvironmentStrings CONFIG_FILE failed: %m\n"); exit(1); } config_file = config_file_storage; temp = ALT_CONFIG_FILE; if (!ExpandEnvironmentStrings((LPCTSTR)temp, (LPTSTR)alt_config_file_storage, (DWORD)sizeof(alt_config_file_storage))) { msyslog(LOG_ERR, "ExpandEnvironmentStrings ALT_CONFIG_FILE failed: %m\n"); exit(1); } alt_config_file = alt_config_file_storage; #endif /* SYS_WINNT */ res_fp = NULL; ntp_syslogmask = NLOG_SYNCMASK; /* set more via logconfig */ /* * install a non default variable with this daemon version */ (void) sprintf(line, "daemon_version=\"%s\"", Version); set_sys_var(line, strlen(line)+1, RO); /* * Say how we're setting the time of day */ (void) sprintf(line, "settimeofday=\"%s\"", set_tod_using); set_sys_var(line, strlen(line)+1, RO); /* * Initialize the loop. */ loop_config(LOOP_DRIFTINIT, 0.); getCmdOpts(argc, argv); if ( (fp[0] = fopen(FindConfig(config_file), "r")) == NULL #ifdef HAVE_NETINFO /* If there is no config_file, try NetInfo. */ && check_netinfo && !(config_netinfo = get_netinfo_config()) #endif /* HAVE_NETINFO */ ) { fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig(config_file)); msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(config_file)); #ifdef SYS_WINNT /* Under WinNT try alternate_config_file name, first NTP.CONF, then NTP.INI */ if ((fp[0] = fopen(FindConfig(alt_config_file), "r")) == NULL) { /* * Broadcast clients can sometimes run without * a configuration file. */ fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig(alt_config_file)); msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(alt_config_file)); return; } #else /* not SYS_WINNT */ return; #endif /* not SYS_WINNT */ } proto_config(PROTO_MONITOR, 0, 0., NULL); for (;;) { if (tok == CONFIG_END) break; if (fp[includelevel]) tok = gettokens(fp[includelevel], line, tokens, &ntokens); #ifdef HAVE_NETINFO else tok = gettokens_netinfo(config_netinfo, tokens, &ntokens); #endif /* HAVE_NETINFO */ if (tok == CONFIG_UNKNOWN) { if (includelevel > 0) { fclose(fp[includelevel--]); continue; } else { break; } } switch(tok) { case CONFIG_PEER: case CONFIG_SERVER: case CONFIG_MANYCASTCLIENT: case CONFIG_BROADCAST: if (tok == CONFIG_PEER) hmode = MODE_ACTIVE; else if (tok == CONFIG_SERVER) hmode = MODE_CLIENT; else if (tok == CONFIG_MANYCASTCLIENT) hmode = MODE_CLIENT; else hmode = MODE_BROADCAST; if (ntokens < 2) { msyslog(LOG_ERR, "No address for %s, line ignored", tokens[0]); break; } istart = 1; memset((char *)&peeraddr, 0, sizeof(peeraddr)); peeraddr.ss_family = default_ai_family; switch (matchkey(tokens[istart], addr_type, 0)) { case CONF_ADDR_IPV4: peeraddr.ss_family = AF_INET; istart++; break; case CONF_ADDR_IPV6: peeraddr.ss_family = AF_INET6; istart++; break; } status = getnetnum(tokens[istart], &peeraddr, 0, t_UNK); if (status == -1) break; /* Found IPv6 address */ if(status != 1) { errflg = -1; } else { errflg = 0; if ( #ifdef REFCLOCK !ISREFCLOCKADR(&peeraddr) && #endif ISBADADR(&peeraddr)) { msyslog(LOG_ERR, "attempt to configure invalid address %s", stoa(&peeraddr)); break; } /* * Shouldn't be able to specify multicast * address for server/peer! * and unicast address for manycastclient! */ if (peeraddr.ss_family == AF_INET) { if (((tok == CONFIG_SERVER) || (tok == CONFIG_PEER)) && #ifdef REFCLOCK !ISREFCLOCKADR(&peeraddr) && #endif IN_CLASSD(ntohl(((struct sockaddr_in*)&peeraddr)->sin_addr.s_addr))) { msyslog(LOG_ERR, "attempt to configure invalid address %s", stoa(&peeraddr)); break; } if ((tok == CONFIG_MANYCASTCLIENT) && !IN_CLASSD(ntohl(((struct sockaddr_in*)&peeraddr)->sin_addr.s_addr))) { msyslog(LOG_ERR, "attempt to configure invalid address %s", stoa(&peeraddr)); break; } } else if(peeraddr.ss_family == AF_INET6) { if (((tok == CONFIG_SERVER) || (tok == CONFIG_PEER)) && #ifdef REFCLOCK !ISREFCLOCKADR(&peeraddr) && #endif IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)&peeraddr)->sin6_addr)) { msyslog(LOG_ERR, "attempt to configure in valid address %s", stoa(&peeraddr)); break; } if ((tok == CONFIG_MANYCASTCLIENT) && !IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6*)&peeraddr)->sin6_addr)) { msyslog(LOG_ERR, "attempt to configure in valid address %s", stoa(&peeraddr)); break; } } } if (peeraddr.ss_family == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS) break; peerversion = NTP_VERSION; minpoll = NTP_MINDPOLL; maxpoll = NTP_MAXDPOLL; peerkey = 0; peerkeystr = (u_char *)"*"; peerflags = 0; ttl = 0; istart++; for (i = istart; i < ntokens; i++) switch (matchkey(tokens[i], mod_keywords, 1)) { case CONF_MOD_VERSION: if (i >= ntokens-1) { msyslog(LOG_ERR, "peer/server version requires an argument"); errflg = 1; break; } peerversion = atoi(tokens[++i]); if ((u_char)peerversion > NTP_VERSION || (u_char)peerversion < NTP_OLDVERSION) { msyslog(LOG_ERR, "inappropriate version number %s, line ignored", tokens[i]); errflg = 1; } break; case CONF_MOD_KEY: if (i >= ntokens-1) { msyslog(LOG_ERR, "key: argument required"); errflg = 1; break; } peerkey = (int)atol(tokens[++i]); peerflags |= FLAG_AUTHENABLE; break; case CONF_MOD_MINPOLL: if (i >= ntokens-1) { msyslog(LOG_ERR, "minpoll: argument required"); errflg = 1; break; } minpoll = atoi(tokens[++i]); if (minpoll < NTP_MINPOLL) { msyslog(LOG_INFO, "minpoll: provided value (%d) is below minimum (%d)", minpoll, NTP_MINPOLL); minpoll = NTP_MINPOLL; } break; case CONF_MOD_MAXPOLL: if (i >= ntokens-1) { msyslog(LOG_ERR, "maxpoll: argument required" ); errflg = 1; break; } maxpoll = atoi(tokens[++i]); if (maxpoll > NTP_MAXPOLL) { msyslog(LOG_INFO, "maxpoll: provided value (%d) is above maximum (%d)", maxpoll, NTP_MAXPOLL); maxpoll = NTP_MAXPOLL; } break; case CONF_MOD_PREFER: peerflags |= FLAG_PREFER; break; case CONF_MOD_PREEMPT: peerflags |= FLAG_PREEMPT; break; case CONF_MOD_NOSELECT: peerflags |= FLAG_NOSELECT; break; case CONF_MOD_TRUE: peerflags |= FLAG_TRUE; case CONF_MOD_BURST: peerflags |= FLAG_BURST; break; case CONF_MOD_IBURST: peerflags |= FLAG_IBURST; break; case CONF_MOD_DYNAMIC: msyslog(LOG_WARNING, "Warning: the \"dynamic\" keyword has been obsoleted" " and will be removed in the next release\n"); break; #ifdef OPENSSL case CONF_MOD_SKEY: peerflags |= FLAG_SKEY | FLAG_AUTHENABLE; break; #endif /* OPENSSL */ case CONF_MOD_TTL: if (i >= ntokens-1) { msyslog(LOG_ERR, "ttl: argument required"); errflg = 1; break; } ttl = atoi(tokens[++i]); if (ttl >= MAX_TTL) { msyslog(LOG_ERR, "ttl: invalid argument"); errflg = 1; } break; case CONF_MOD_MODE: if (i >= ntokens-1) { msyslog(LOG_ERR, "mode: argument required"); errflg = 1; break; } ttl = atoi(tokens[++i]); break; case CONFIG_UNKNOWN: errflg = 1; break; } if (minpoll > maxpoll) { msyslog(LOG_ERR, "config error: minpoll > maxpoll"); errflg = 1; } if (errflg == 0) { if (peer_config(&peeraddr, ANY_INTERFACE_CHOOSE(&peeraddr), hmode, peerversion, minpoll, maxpoll, peerflags, ttl, peerkey, peerkeystr) == 0) { msyslog(LOG_ERR, "configuration of %s failed", stoa(&peeraddr)); } } else if (errflg == -1) { save_resolve(tokens[istart - 1], hmode, peerversion, minpoll, maxpoll, peerflags, ttl, peerkey, peerkeystr, peeraddr.ss_family); } break; case CONFIG_DRIFTFILE: if (ntokens >= 2) stats_config(STATS_FREQ_FILE, tokens[1]); else stats_config(STATS_FREQ_FILE, (char *)0); stats_write_period = stats_write_tolerance = 0; if (ntokens >= 3) stats_write_period = 60 * atol(tokens[2]); if (stats_write_period <= 0) stats_write_period = 3600; if (ntokens >= 4) { double ftemp; sscanf(tokens[3], "%lf", &ftemp); stats_write_tolerance = ftemp / 100; } break; case CONFIG_PIDFILE: if (ntokens >= 2) stats_config(STATS_PID_FILE, tokens[1]); else stats_config(STATS_PID_FILE, (char *)0); break; case CONFIG_END: for ( i = 0; i <= includelevel; i++ ) { fclose(fp[i]); } break; case CONFIG_INCLUDEFILE: if (ntokens < 2) { msyslog(LOG_ERR, "includefile needs one argument"); break; } if (includelevel >= MAXINCLUDELEVEL) { fprintf(stderr, "getconfig: Maximum include file level exceeded.\n"); msyslog(LOG_INFO, "getconfig: Maximum include file level exceeded."); break; } includefile = fopen(FindConfig(tokens[1]), "r"); if (includefile == NULL) { fprintf(stderr, "getconfig: Couldn't open <%s>\n", FindConfig(tokens[1])); msyslog(LOG_INFO, "getconfig: Couldn't open <%s>", FindConfig(tokens[1])); break; } fp[++includelevel] = includefile; break; case CONFIG_LOGFILE: if (ntokens >= 2) { FILE *new_file; new_file = fopen(tokens[1], "a"); if (new_file != NULL) { NLOG(NLOG_SYSINFO) /* conditional if clause for conditional syslog */ msyslog(LOG_NOTICE, "logging to file %s", tokens[1]); if (syslog_file != NULL && fileno(syslog_file) != fileno(new_file)) (void)fclose(syslog_file); syslog_file = new_file; syslogit = 0; } else msyslog(LOG_ERR, "Cannot open log file %s", tokens[1]); } else msyslog(LOG_ERR, "logfile needs one argument"); break; case CONFIG_LOGCONFIG: for (i = 1; i < ntokens; i++) { int add = 1; int equals = 0; char * s = &tokens[i][0]; switch (*s) { case '+': case '-': case '=': add = *s == '+'; equals = *s == '='; s++; break; default: break; } if (equals) { ntp_syslogmask = get_logmask(s); } else { if (add) { ntp_syslogmask |= get_logmask(s); } else { ntp_syslogmask &= ~get_logmask(s); } } #ifdef DEBUG if (debug) printf("ntp_syslogmask = 0x%08lx (%s)\n", ntp_syslogmask, tokens[i]); #endif } break; case CONFIG_BROADCASTCLIENT: if (ntokens == 1) { proto_config(PROTO_BROADCLIENT, 1, 0., NULL); } else { proto_config(PROTO_BROADCLIENT, 2, 0., NULL); } break; case CONFIG_MULTICASTCLIENT: case CONFIG_MANYCASTSERVER: if (ntokens > 1) { istart = 1; memset((char *)&peeraddr, 0, sizeof(peeraddr)); peeraddr.ss_family = default_ai_family; switch (matchkey(tokens[istart], addr_type, 0)) { case CONF_ADDR_IPV4: peeraddr.ss_family = AF_INET; istart++; break; case CONF_ADDR_IPV6: peeraddr.ss_family = AF_INET6; istart++; break; } /* * Abuse maskaddr to store the prefered ip * version. */ memset((char *)&maskaddr, 0, sizeof(maskaddr)); maskaddr.ss_family = peeraddr.ss_family; for (i = istart; i < ntokens; i++) { memset((char *)&peeraddr, 0, sizeof(peeraddr)); peeraddr.ss_family = maskaddr.ss_family; if (getnetnum(tokens[i], &peeraddr, 1, t_UNK) == 1) proto_config(PROTO_MULTICAST_ADD, 0, 0., &peeraddr); } } else proto_config(PROTO_MULTICAST_ADD, 0, 0., NULL); if (tok == CONFIG_MULTICASTCLIENT) proto_config(PROTO_MULTICAST_ADD, 1, 0., NULL); else if (tok == CONFIG_MANYCASTSERVER) sys_manycastserver = 1; break; case CONFIG_KEYS: if (ntokens >= 2) { getauthkeys(tokens[1]); } break; case CONFIG_KEYSDIR: if (ntokens < 2) { msyslog(LOG_ERR, "Keys directory name required"); break; } keysdir = (char *)emalloc(strlen(tokens[1]) + 1); strcpy(keysdir, tokens[1]); break; case CONFIG_TINKER: for (i = 1; i < ntokens; i++) { int temp; double ftemp; temp = matchkey(tokens[i++], tinker_keywords, 1); if (i > ntokens - 1) { msyslog(LOG_ERR, "tinker: missing argument"); errflg++; break; } sscanf(tokens[i], "%lf", &ftemp); switch(temp) { case CONF_CLOCK_MAX: loop_config(LOOP_MAX, ftemp); break; case CONF_CLOCK_PANIC: loop_config(LOOP_PANIC, ftemp); break; case CONF_CLOCK_PHI: loop_config(LOOP_PHI, ftemp); break; case CONF_CLOCK_MINSTEP: loop_config(LOOP_MINSTEP, ftemp); break; case CONF_CLOCK_ALLAN: loop_config(LOOP_ALLAN, ftemp); break; case CONF_CLOCK_HUFFPUFF: loop_config(LOOP_HUFFPUFF, ftemp); break; case CONF_CLOCK_FREQ: loop_config(LOOP_FREQ, ftemp); break; } } break; case CONFIG_TOS: for (i = 1; i < ntokens; i++) { int temp; double ftemp; temp = matchkey(tokens[i++], tos_keywords, 1); if (i > ntokens - 1) { msyslog(LOG_ERR, "tos: missing argument"); errflg++; break; } sscanf(tokens[i], "%lf", &ftemp); switch(temp) { case CONF_TOS_MINCLOCK: proto_config(PROTO_MINCLOCK, 0, ftemp, NULL); break; case CONF_TOS_MAXCLOCK: proto_config(PROTO_MAXCLOCK, 0, ftemp, NULL); break; case CONF_TOS_MINSANE: proto_config(PROTO_MINSANE, 0, ftemp, NULL); break; case CONF_TOS_FLOOR: proto_config(PROTO_FLOOR, 0, ftemp, NULL); break; case CONF_TOS_CEILING: proto_config(PROTO_CEILING, 0, ftemp, NULL); break; case CONF_TOS_COHORT: proto_config(PROTO_COHORT, 0, ftemp, NULL); break; case CONF_TOS_MINDISP: proto_config(PROTO_MINDISP, 0, ftemp, NULL); break; case CONF_TOS_MAXDIST: proto_config(PROTO_MAXDIST, 0, ftemp, NULL); break; case CONF_TOS_MAXHOP: proto_config(PROTO_MAXHOP, 0, ftemp, NULL); break; case CONF_TOS_ORPHAN: proto_config(PROTO_ORPHAN, 0, ftemp, NULL); break; case CONF_TOS_BEACON: proto_config(PROTO_BEACON, 0, ftemp, NULL); break; } } break; case CONFIG_TTL: for (i = 1; i < ntokens && i < MAX_TTL; i++) { sys_ttl[i - 1] = (u_char) atoi(tokens[i]); sys_ttlmax = i - 1; } break; case CONFIG_DISCARD: for (i = 1; i < ntokens; i++) { int temp; temp = matchkey(tokens[i++], discard_keywords, 1); if (i > ntokens - 1) { msyslog(LOG_ERR, "discard: missing argument"); errflg++; break; } switch(temp) { case CONF_DISCARD_AVERAGE: res_avg_interval = atoi(tokens[i]); break; case CONF_DISCARD_MINIMUM: res_min_interval = atoi(tokens[i]); break; case CONF_DISCARD_MONITOR: mon_age = atoi(tokens[i]); break; default: msyslog(LOG_ERR, "discard: unknown keyword"); break; } } break; #ifdef OPENSSL case CONFIG_REVOKE: if (ntokens >= 2) sys_revoke = (u_char) max(atoi(tokens[1]), KEY_REVOKE); break; case CONFIG_AUTOMAX: if (ntokens >= 2) sys_automax = 1 << max(atoi(tokens[1]), 10); break; case CONFIG_CRYPTO: if (ntokens == 1) { crypto_config(CRYPTO_CONF_NONE, NULL); break; } for (i = 1; i < ntokens; i++) { int temp; temp = matchkey(tokens[i++], crypto_keywords, 1); if (i > ntokens - 1) { msyslog(LOG_ERR, "crypto: missing argument"); errflg++; break; } switch(temp) { case CONF_CRYPTO_CERT: crypto_config(CRYPTO_CONF_CERT, tokens[i]); break; case CONF_CRYPTO_RSA: crypto_config(CRYPTO_CONF_PRIV, tokens[i]); break; case CONF_CRYPTO_IDENT: crypto_config(CRYPTO_CONF_IDENT, tokens[i]); break; case CONF_CRYPTO_IFFPAR: crypto_config(CRYPTO_CONF_IFFPAR, tokens[i]); break; case CONF_CRYPTO_GQPAR: crypto_config(CRYPTO_CONF_GQPAR, tokens[i]); break; case CONF_CRYPTO_MVPAR: crypto_config(CRYPTO_CONF_MVPAR, tokens[i]); break; case CONF_CRYPTO_LEAP: crypto_config(CRYPTO_CONF_LEAP, tokens[i]); break; case CONF_CRYPTO_PW: crypto_config(CRYPTO_CONF_PW, tokens[i]); break; case CONF_CRYPTO_RAND: crypto_config(CRYPTO_CONF_RAND, tokens[i]); break; case CONF_CRYPTO_SIGN: crypto_config(CRYPTO_CONF_SIGN, tokens[i]); break; default: msyslog(LOG_ERR, "crypto: unknown keyword"); break; } } break; #endif /* OPENSSL */ case CONFIG_RESTRICT: if (ntokens < 2) { msyslog(LOG_ERR, "restrict requires an address"); break; } istart = 1; memset((char *)&peeraddr, 0, sizeof(peeraddr)); peeraddr.ss_family = default_ai_family; switch (matchkey(tokens[istart], addr_type, 0)) { case CONF_ADDR_IPV4: peeraddr.ss_family = AF_INET; istart++; break; case CONF_ADDR_IPV6: peeraddr.ss_family = AF_INET6; istart++; break; } /* * Assume default means an IPv4 address, except * if forced by a -4 or -6. */ if (STREQ(tokens[istart], "default")) { if (peeraddr.ss_family == 0) peeraddr.ss_family = AF_INET; } else if (getnetnum(tokens[istart], &peeraddr, 1, t_UNK) != 1) break; /* * Use peerversion as flags, peerkey as mflags. Ick. */ peerversion = 0; peerkey = 0; errflg = 0; SET_HOSTMASK(&maskaddr, peeraddr.ss_family); istart++; for (i = istart; i < ntokens; i++) { switch (matchkey(tokens[i], res_keywords, 1)) { case CONF_RES_MASK: if (i >= ntokens-1) { msyslog(LOG_ERR, "mask keyword needs argument"); errflg++; break; } i++; if (getnetnum(tokens[i], &maskaddr, 1, t_MSK) != 1) errflg++; break; case CONF_RES_IGNORE: peerversion |= RES_IGNORE; break; case CONF_RES_NOSERVE: peerversion |= RES_DONTSERVE; break; case CONF_RES_NOTRUST: peerversion |= RES_DONTTRUST; break; case CONF_RES_NOQUERY: peerversion |= RES_NOQUERY; break; case CONF_RES_NOMODIFY: peerversion |= RES_NOMODIFY; break; case CONF_RES_NOPEER: peerversion |= RES_NOPEER; break; case CONF_RES_NOTRAP: peerversion |= RES_NOTRAP; break; case CONF_RES_LPTRAP: peerversion |= RES_LPTRAP; break; case CONF_RES_NTPPORT: peerkey |= RESM_NTPONLY; break; case CONF_RES_VERSION: peerversion |= RES_VERSION; break; case CONF_RES_DEMOBILIZE: peerversion |= RES_DEMOBILIZE; break; case CONF_RES_LIMITED: peerversion |= RES_LIMITED; break; case CONFIG_UNKNOWN: errflg++; break; } } if (SOCKNUL(&peeraddr)) ANYSOCK(&maskaddr); if (!errflg) hack_restrict(RESTRICT_FLAGS, &peeraddr, &maskaddr, (int)peerkey, peerversion); break; case CONFIG_BDELAY: if (ntokens >= 2) { double tmp; if (sscanf(tokens[1], "%lf", &tmp) != 1) { msyslog(LOG_ERR, "broadcastdelay value %s undecodable", tokens[1]); } else { proto_config(PROTO_BROADDELAY, 0, tmp, NULL); } } break; case CONFIG_CDELAY: if (ntokens >= 2) { u_long ui; if (sscanf(tokens[1], "%ld", &ui) != 1) msyslog(LOG_ERR, "illegal value - line ignored"); else proto_config(PROTO_CALLDELAY, ui, 0, NULL); } break; case CONFIG_TRUSTEDKEY: for (i = 1; i < ntokens; i++) { keyid_t tkey; tkey = atol(tokens[i]); if (tkey == 0) { msyslog(LOG_ERR, "trusted key %s unlikely", tokens[i]); } else { authtrust(tkey, 1); } } break; case CONFIG_REQUESTKEY: if (ntokens >= 2) { if (!atouint(tokens[1], &ul)) { msyslog(LOG_ERR, "%s is undecodable as request key", tokens[1]); } else if (ul == 0) { msyslog(LOG_ERR, "%s makes a poor request keyid", tokens[1]); } else { #ifdef DEBUG if (debug > 3) printf( "set info_auth_key to %08lx\n", ul); #endif info_auth_keyid = (keyid_t)ul; } } break; case CONFIG_CONTROLKEY: if (ntokens >= 2) { keyid_t ckey; ckey = atol(tokens[1]); if (ckey == 0) { msyslog(LOG_ERR, "%s makes a poor control keyid", tokens[1]); } else { ctl_auth_keyid = ckey; } } break; case CONFIG_TRAP: if (ntokens < 2) { msyslog(LOG_ERR, "no address for trap command, line ignored"); break; } istart = 1; memset((char *)&peeraddr, 0, sizeof(peeraddr)); peeraddr.ss_family = default_ai_family; switch (matchkey(tokens[istart], addr_type, 0)) { case CONF_ADDR_IPV4: peeraddr.ss_family = AF_INET; istart++; break; case CONF_ADDR_IPV6: peeraddr.ss_family = AF_INET6; istart++; break; } if (getnetnum(tokens[istart], &peeraddr, 1, t_UNK) != 1) break; /* * Use peerversion for port number. Barf. */ errflg = 0; peerversion = 0; localaddr = 0; istart++; for (i = istart; i < ntokens-1; i++) switch (matchkey(tokens[i], trap_keywords, 1)) { case CONF_TRAP_PORT: if (i >= ntokens-1) { msyslog(LOG_ERR, "trap port requires an argument"); errflg = 1; break; } peerversion = atoi(tokens[++i]); if (peerversion <= 0 || peerversion > 32767) { msyslog(LOG_ERR, "invalid port number %s, trap ignored", tokens[i]); errflg = 1; } break; case CONF_TRAP_INTERFACE: if (i >= ntokens-1) { msyslog(LOG_ERR, "trap interface requires an argument"); errflg = 1; break; } memset((char *)&maskaddr, 0, sizeof(maskaddr)); maskaddr.ss_family = peeraddr.ss_family; if (getnetnum(tokens[++i], &maskaddr, 1, t_UNK) != 1) { errflg = 1; break; } localaddr = findinterface(&maskaddr); if (localaddr == NULL) { msyslog(LOG_ERR, "can't find interface with address %s", stoa(&maskaddr)); errflg = 1; } break; case CONFIG_UNKNOWN: errflg++; break; } if (!errflg) { if (peerversion != 0) ((struct sockaddr_in6*)&peeraddr)->sin6_port = htons( (u_short) peerversion); else ((struct sockaddr_in6*)&peeraddr)->sin6_port = htons(TRAPPORT); if (localaddr == NULL) localaddr = ANY_INTERFACE_CHOOSE(&peeraddr); if (!ctlsettrap(&peeraddr, localaddr, 0, NTP_VERSION)) msyslog(LOG_ERR, "can't set trap for %s, no resources", stoa(&peeraddr)); } break; case CONFIG_FUDGE: if (ntokens < 2) { msyslog(LOG_ERR, "no address for fudge command, line ignored"); break; } memset((char *)&peeraddr, 0, sizeof(peeraddr)); if (getnetnum(tokens[1], &peeraddr, 1, t_REF) != 1) break; if (!ISREFCLOCKADR(&peeraddr)) { msyslog(LOG_ERR, "%s is inappropriate address for the fudge command, line ignored", stoa(&peeraddr)); break; } memset((void *)&clock_stat, 0, sizeof clock_stat); fudgeflag = 0; errflg = 0; for (i = 2; i < ntokens-1; i++) { switch (c = matchkey(tokens[i], fudge_keywords, 1)) { case CONF_FDG_TIME1: if (sscanf(tokens[++i], "%lf", &clock_stat.fudgetime1) != 1) { msyslog(LOG_ERR, "fudge %s time1 value in error", stoa(&peeraddr)); errflg = i; break; } clock_stat.haveflags |= CLK_HAVETIME1; break; case CONF_FDG_TIME2: if (sscanf(tokens[++i], "%lf", &clock_stat.fudgetime2) != 1) { msyslog(LOG_ERR, "fudge %s time2 value in error", stoa(&peeraddr)); errflg = i; break; } clock_stat.haveflags |= CLK_HAVETIME2; break; case CONF_FDG_STRATUM: if (!atoint(tokens[++i], &stratum)) { msyslog(LOG_ERR, "fudge %s stratum value in error", stoa(&peeraddr)); errflg = i; break; } clock_stat.fudgeval1 = stratum; clock_stat.haveflags |= CLK_HAVEVAL1; break; case CONF_FDG_REFID: i++; memcpy(&clock_stat.fudgeval2, tokens[i], min(strlen(tokens[i]), 4)); clock_stat.haveflags |= CLK_HAVEVAL2; break; case CONF_FDG_FLAG1: case CONF_FDG_FLAG2: case CONF_FDG_FLAG3: case CONF_FDG_FLAG4: if (!atouint(tokens[++i], &fudgeflag) || fudgeflag > 1) { msyslog(LOG_ERR, "fudge %s flag value in error", stoa(&peeraddr)); errflg = i; break; } switch(c) { case CONF_FDG_FLAG1: c = CLK_FLAG1; clock_stat.haveflags|=CLK_HAVEFLAG1; break; case CONF_FDG_FLAG2: c = CLK_FLAG2; clock_stat.haveflags|=CLK_HAVEFLAG2; break; case CONF_FDG_FLAG3: c = CLK_FLAG3; clock_stat.haveflags|=CLK_HAVEFLAG3; break; case CONF_FDG_FLAG4: c = CLK_FLAG4; clock_stat.haveflags|=CLK_HAVEFLAG4; break; } if (fudgeflag == 0) clock_stat.flags &= ~c; else clock_stat.flags |= c; break; case CONFIG_UNKNOWN: errflg = -1; break; } } #ifdef REFCLOCK /* * If reference clock support isn't defined the * fudge line will still be accepted and syntax * checked, but will essentially do nothing. */ if (!errflg) { refclock_control(&peeraddr, &clock_stat, (struct refclockstat *)0); } #endif break; case CONFIG_STATSDIR: if (ntokens >= 2) stats_config(STATS_STATSDIR,tokens[1]); break; case CONFIG_STATISTICS: for (i = 1; i < ntokens; i++) { filegen = filegen_get(tokens[i]); if (filegen == NULL) { msyslog(LOG_ERR, "no statistics named %s available", tokens[i]); continue; } #ifdef DEBUG if (debug > 3) printf("enabling filegen for %s statistics \"%s%s\"\n", tokens[i], filegen->prefix, filegen->basename); #endif filegen->flag |= FGEN_FLAG_ENABLED; } break; case CONFIG_FILEGEN: if (ntokens < 2) { msyslog(LOG_ERR, "no id for filegen command, line ignored"); break; } filegen = filegen_get(tokens[1]); if (filegen == NULL) { msyslog(LOG_ERR, "unknown filegen \"%s\" ignored", tokens[1]); break; } /* * peerversion is (ab)used for filegen file (index) * peerkey is (ab)used for filegen type * peerflags is (ab)used for filegen flags */ peerversion = 0; peerkey = filegen->type; peerflags = filegen->flag; errflg = 0; for (i = 2; i < ntokens; i++) { switch (matchkey(tokens[i], filegen_keywords, 1)) { case CONF_FGEN_FILE: if (i >= ntokens - 1) { msyslog(LOG_ERR, "filegen %s file requires argument", tokens[1]); errflg = i; break; } peerversion = ++i; break; case CONF_FGEN_TYPE: if (i >= ntokens -1) { msyslog(LOG_ERR, "filegen %s type requires argument", tokens[1]); errflg = i; break; } peerkey = matchkey(tokens[++i], fgen_types, 1); if (peerkey == CONFIG_UNKNOWN) { msyslog(LOG_ERR, "filegen %s unknown type \"%s\"", tokens[1], tokens[i]); errflg = i; break; } break; case CONF_FGEN_FLAG_LINK: peerflags |= FGEN_FLAG_LINK; break; case CONF_FGEN_FLAG_NOLINK: peerflags &= ~FGEN_FLAG_LINK; break; case CONF_FGEN_FLAG_ENABLE: peerflags |= FGEN_FLAG_ENABLED; break; case CONF_FGEN_FLAG_DISABLE: peerflags &= ~FGEN_FLAG_ENABLED; break; } } if (!errflg) filegen_config(filegen, tokens[peerversion], (u_char)peerkey, (u_char)peerflags); break; case CONFIG_SETVAR: if (ntokens < 2) { msyslog(LOG_ERR, "no value for setvar command - line ignored"); } else { set_sys_var(tokens[1], strlen(tokens[1])+1, (u_short) (RW | ((((ntokens > 2) && !strcmp(tokens[2], "default"))) ? DEF : 0))); } break; case CONFIG_ENABLE: for (i = 1; i < ntokens; i++) { int flag; flag = matchkey(tokens[i], flags_keywords, 1); if (flag == CONFIG_UNKNOWN) { msyslog(LOG_ERR, "enable unknown flag %s", tokens[i]); errflg = 1; break; } proto_config(flag, 1, 0., NULL); } break; case CONFIG_DISABLE: for (i = 1; i < ntokens; i++) { int flag; flag = matchkey(tokens[i], flags_keywords, 1); if (flag == CONFIG_UNKNOWN) { msyslog(LOG_ERR, "disable unknown flag %s", tokens[i]); errflg = 1; break; } proto_config(flag, 0, 0., NULL); } break; case CONFIG_PHONE: for (i = 1; i < ntokens && i < MAXPHONE - 1; i++) { sys_phone[i - 1] = emalloc(strlen(tokens[i]) + 1); strcpy(sys_phone[i - 1], tokens[i]); } sys_phone[i] = NULL; break; case CONFIG_ADJ: { double ftemp; sscanf(tokens[1], "%lf", &ftemp); proto_config(PROTO_ADJ, 0, ftemp, NULL); } break; } } if (fp[0]) (void)fclose(fp[0]); #ifdef HAVE_NETINFO if (config_netinfo) free_netinfo_config(config_netinfo); #endif /* HAVE_NETINFO */ #if !defined(VMS) && !defined(SYS_VXWORKS) /* find a keyid */ if (info_auth_keyid == 0) req_keyid = 65535; else req_keyid = info_auth_keyid; /* if doesn't exist, make up one at random */ if (!authhavekey(req_keyid)) { char rankey[9]; int j; for (i = 0; i < 8; i++) for (j = 1; j < 100; ++j) { - rankey[i] = (char) (ntp_random() & 0xff); + rankey[i] = (char) (arc4random() & 0xff); if (rankey[i] != 0) break; } rankey[8] = 0; authusekey(req_keyid, KEY_TYPE_MD5, (u_char *)rankey); authtrust(req_keyid, 1); if (!authhavekey(req_keyid)) { msyslog(LOG_ERR, "getconfig: Couldn't generate a valid random key!"); /* HMS: Should this be fatal? */ } } /* save keyid so we will accept config requests with it */ info_auth_keyid = req_keyid; #endif /* !defined(VMS) && !defined(SYS_VXWORKS) */ if (res_fp != NULL) { if (call_resolver) { /* * Need name resolution */ do_resolve_internal(); } } } #ifdef HAVE_NETINFO /* * get_netinfo_config - find the nearest NetInfo domain with an ntp * configuration and initialize the configuration state. */ static struct netinfo_config_state * get_netinfo_config() { ni_status status; void *domain; ni_id config_dir; struct netinfo_config_state *config; if (ni_open(NULL, ".", &domain) != NI_OK) return NULL; while ((status = ni_pathsearch(domain, &config_dir, NETINFO_CONFIG_DIR)) == NI_NODIR) { void *next_domain; if (ni_open(domain, "..", &next_domain) != NI_OK) { ni_free(next_domain); break; } ni_free(domain); domain = next_domain; } if (status != NI_OK) { ni_free(domain); return NULL; } config = (struct netinfo_config_state *)malloc(sizeof(struct netinfo_config_state)); config->domain = domain; config->config_dir = config_dir; config->prop_index = 0; config->val_index = 0; config->val_list = NULL; return config; } /* * free_netinfo_config - release NetInfo configuration state */ static void free_netinfo_config(struct netinfo_config_state *config) { ni_free(config->domain); free(config); } /* * gettokens_netinfo - return tokens from NetInfo */ static int gettokens_netinfo ( struct netinfo_config_state *config, char **tokenlist, int *ntokens ) { int prop_index = config->prop_index; int val_index = config->val_index; char **val_list = config->val_list; /* * Iterate through each keyword and look for a property that matches it. */ again: if (!val_list) { for (; prop_index < (sizeof(keywords)/sizeof(keywords[0])); prop_index++) { ni_namelist namelist; struct keyword current_prop = keywords[prop_index]; /* * For each value associated in the property, we're going to return * a separate line. We squirrel away the values in the config state * so the next time through, we don't need to do this lookup. */ NI_INIT(&namelist); if (ni_lookupprop(config->domain, &config->config_dir, current_prop.text, &namelist) == NI_OK) { ni_index index; /* Found the property, but it has no values */ if (namelist.ni_namelist_len == 0) continue; if (! (val_list = config->val_list = (char**)malloc(sizeof(char*) * (namelist.ni_namelist_len + 1)))) { msyslog(LOG_ERR, "out of memory while configuring"); break; } for (index = 0; index < namelist.ni_namelist_len; index++) { char *value = namelist.ni_namelist_val[index]; if (! (val_list[index] = (char*)malloc(strlen(value)+1))) { msyslog(LOG_ERR, "out of memory while configuring"); break; } strcpy(val_list[index], value); } val_list[index] = NULL; break; } ni_namelist_free(&namelist); } config->prop_index = prop_index; } /* No list; we're done here. */ if (!val_list) return CONFIG_UNKNOWN; /* * We have a list of values for the current property. * Iterate through them and return each in order. */ if (val_list[val_index]) { int ntok = 1; int quoted = 0; char *tokens = val_list[val_index]; msyslog(LOG_INFO, "%s %s", keywords[prop_index].text, val_list[val_index]); (const char*)tokenlist[0] = keywords[prop_index].text; for (ntok = 1; ntok < MAXTOKENS; ntok++) { tokenlist[ntok] = tokens; while (!ISEOL(*tokens) && (!ISSPACE(*tokens) || quoted)) quoted ^= (*tokens++ == '"'); if (ISEOL(*tokens)) { *tokens = '\0'; break; } else { /* must be space */ *tokens++ = '\0'; while (ISSPACE(*tokens)) tokens++; if (ISEOL(*tokens)) break; } } if (ntok == MAXTOKENS) { /* HMS: chomp it to lose the EOL? */ msyslog(LOG_ERR, "gettokens_netinfo: too many tokens. Ignoring: %s", tokens); } else { *ntokens = ntok + 1; } config->val_index++; /* HMS: Should this be in the 'else'? */ return keywords[prop_index].keytype; } /* We're done with the current property. */ prop_index = ++config->prop_index; /* Free val_list and reset counters. */ for (val_index = 0; val_list[val_index]; val_index++) free(val_list[val_index]); free(val_list); val_list = config->val_list = NULL; val_index = config->val_index = 0; goto again; } #endif /* HAVE_NETINFO */ /* * gettokens - read a line and return tokens */ static int gettokens ( FILE *fp, char *line, char **tokenlist, int *ntokens ) { register char *cp; register int ntok; register int quoted = 0; /* * Find start of first token */ again: while ((cp = fgets(line, MAXLINE, fp)) != NULL) { cp = line; while (ISSPACE(*cp)) cp++; if (!ISEOL(*cp)) break; } if (cp == NULL) { *ntokens = 0; return CONFIG_UNKNOWN; /* hack. Is recognized as EOF */ } /* * Now separate out the tokens */ for (ntok = 0; ntok < MAXTOKENS; ntok++) { tokenlist[ntok] = cp; while (!ISEOL(*cp) && (!ISSPACE(*cp) || quoted)) quoted ^= (*cp++ == '"'); if (ISEOL(*cp)) { *cp = '\0'; break; } else { /* must be space */ *cp++ = '\0'; while (ISSPACE(*cp)) cp++; if (ISEOL(*cp)) break; } } /* Heiko: Remove leading and trailing quotes around tokens */ { int i,j = 0; for (i = 0; i < ntok; i++) { /* Now check if the first char is a quote and remove that */ if ( tokenlist[ntok][0] == '"' ) tokenlist[ntok]++; /* Now check the last char ... */ j = strlen(tokenlist[ntok])-1; if ( tokenlist[ntok][j] == '"' ) tokenlist[ntok][j] = '\0'; } } if (ntok == MAXTOKENS) { --ntok; /* HMS: chomp it to lose the EOL? */ msyslog(LOG_ERR, "gettokens: too many tokens on the line. Ignoring %s", cp); } else { /* * Return the match */ *ntokens = ntok + 1; ntok = matchkey(tokenlist[0], keywords, 1); if (ntok == CONFIG_UNKNOWN) goto again; } return ntok; } /* * matchkey - match a keyword to a list */ static int matchkey( register char *word, register struct keyword *keys, int complain ) { for (;;) { if (keys->keytype == CONFIG_UNKNOWN) { if (complain) msyslog(LOG_ERR, "configure: keyword \"%s\" unknown, line ignored", word); return CONFIG_UNKNOWN; } if (STRSAME(word, keys->text)) return keys->keytype; keys++; } } /* * getnetnum - return a net number (this is crude, but careful) */ static int getnetnum( const char *num, struct sockaddr_storage *addr, int complain, enum gnn_type a_type ) { struct addrinfo hints; struct addrinfo *ptr; int retval; #if 0 printf("getnetnum: <%s> is a %s (%d)\n", num, (a_type == t_UNK) ? "t_UNK" : (a_type == t_REF) ? "t_REF" : (a_type == t_MSK) ? "t_MSK" : "???", a_type); #endif /* Get host address. Looking for UDP datagram connection */ memset(&hints, 0, sizeof (hints)); if (addr->ss_family == AF_INET || addr->ss_family == AF_INET6) hints.ai_family = addr->ss_family; else hints.ai_family = AF_UNSPEC; /* * If we don't have an IPv6 stack, just look up IPv4 addresses */ if (isc_net_probeipv6() != ISC_R_SUCCESS) hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; if (a_type != t_UNK) { hints.ai_flags = AI_NUMERICHOST; } #ifdef DEBUG if (debug > 3) printf("getnetnum: calling getaddrinfo(%s,...)\n", num); #endif retval = getaddrinfo(num, "ntp", &hints, &ptr); if (retval != 0 || (ptr->ai_family == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS)) { if (complain) msyslog(LOG_ERR, "getaddrinfo: \"%s\" invalid host address, ignored", num); #ifdef DEBUG if (debug > 0) printf( "getaddrinfo: \"%s\" invalid host address%s.\n", num, (complain) ? ", ignored" : ""); #endif if (retval == 0 && ptr->ai_family == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS) { return -1; } else { return 0; } } memcpy(addr, ptr->ai_addr, ptr->ai_addrlen); #ifdef DEBUG if (debug > 1) printf("getnetnum given %s, got %s (%s/%d)\n", num, stoa(addr), (a_type == t_UNK) ? "t_UNK" : (a_type == t_REF) ? "t_REF" : (a_type == t_MSK) ? "t_MSK" : "???", a_type); #endif freeaddrinfo(ptr); return 1; } #if !defined(VMS) && !defined(SYS_WINNT) /* * catchchild - receive the resolver's exit status */ static RETSIGTYPE catchchild( int sig ) { /* * We only start up one child, and if we're here * it should have already exited. Hence the following * shouldn't hang. If it does, please tell me. */ #if !defined (SYS_WINNT) && !defined(SYS_VXWORKS) (void) wait(0); #endif /* SYS_WINNT && VXWORKS*/ } #endif /* VMS */ /* * save_resolve - save configuration info into a file for later name resolution */ static void save_resolve( char *name, int mode, int version, int minpoll, int maxpoll, u_int flags, int ttl, keyid_t keyid, u_char *keystr, u_char peeraf ) { #ifndef SYS_VXWORKS if (res_fp == NULL) { #ifndef SYS_WINNT (void) strcpy(res_file, RES_TEMPFILE); #else /* no /tmp directory under NT */ { if(!(GetTempPath((DWORD)MAX_PATH, (LPTSTR)res_file))) { msyslog(LOG_ERR, "cannot get pathname for temporary directory: %m"); return; } (void) strcat(res_file, "ntpdXXXXXX"); } #endif /* SYS_WINNT */ #ifdef HAVE_MKSTEMP { int fd; res_fp = NULL; if ((fd = mkstemp(res_file)) != -1) res_fp = fdopen(fd, "r+"); } #else (void) mktemp(res_file); res_fp = fopen(res_file, "w"); #endif if (res_fp == NULL) { msyslog(LOG_ERR, "open failed for %s: %m", res_file); return; } } #ifdef DEBUG if (debug) { printf("resolving %s\n", name); } #endif (void)fprintf(res_fp, "%s %u %d %d %d %d %d %d %u %s\n", name, peeraf, mode, version, minpoll, maxpoll, flags, ttl, keyid, keystr); #ifdef DEBUG if (debug > 1) printf("config: %s %u %d %d %d %d %x %d %u %s\n", name, peeraf, mode, version, minpoll, maxpoll, flags, ttl, keyid, keystr); #endif #else /* SYS_VXWORKS */ /* save resolve info to a struct */ #endif /* SYS_VXWORKS */ } /* * abort_resolve - terminate the resolver stuff and delete the file */ static void abort_resolve(void) { /* * In an ideal world we would might reread the file and * log the hosts which aren't getting configured. Since * this is too much work, however, just close and delete * the temp file. */ if (res_fp != NULL) (void) fclose(res_fp); res_fp = NULL; #ifndef SYS_VXWORKS /* we don't open the file to begin with */ #if !defined(VMS) (void) unlink(res_file); #else (void) delete(res_file); #endif /* VMS */ #endif /* SYS_VXWORKS */ } /* * do_resolve_internal - start up the resolver function (not program) */ /* * On VMS, this routine will simply refuse to resolve anything. * * Possible implementation: keep `res_file' in memory, do async * name resolution via QIO, update from within completion AST. * I'm unlikely to find the time for doing this, though. -wjm */ static void do_resolve_internal(void) { int i; if (res_fp == NULL) { /* belch */ msyslog(LOG_ERR, "do_resolve_internal: Fatal: res_fp == NULL"); exit(1); } /* we are done with this now */ (void) fclose(res_fp); res_fp = NULL; #if !defined(VMS) && !defined (SYS_VXWORKS) req_file = res_file; /* set up pointer to res file */ #ifndef SYS_WINNT (void) signal_no_reset(SIGCHLD, catchchild); #ifndef SYS_VXWORKS /* the parent process will write to the pipe * in order to wake up to child process * which may be waiting in a select() call * on the read fd */ if (pipe(resolver_pipe_fd) < 0) { msyslog(LOG_ERR, "unable to open resolver pipe"); exit(1); } i = fork(); /* Shouldn't the code below be re-ordered? * I.e. first check if the fork() returned an error, then * check whether we're parent or child. * Martin Burnicki */ if (i == 0) { /* * this used to close everything * I don't think this is necessary */ /* * To the unknown commenter above: * Well, I think it's better to clean up * after oneself. I have had problems with * refclock-io when intres was running - things * where fine again when ntpintres was gone. * So some systems react erratic at least. * * Frank Kardel * * 94-11-16: * Further debugging has proven that the above is * absolutely harmful. The internal resolver * is still in the SIGIO process group and the lingering * async io information causes it to process requests from * all file decriptor causing a race between the NTP daemon * and the resolver. which then eats data when it wins 8-(. * It is absolutly necessary to kill any IO associations * shared with the NTP daemon. * * We also block SIGIO (currently no ports means to * disable the signal handle for IO). * * Thanks to wgstuken@informatik.uni-erlangen.de to notice * that it is the ntp-resolver child running into trouble. * * THUS: */ /* This is the child process who will read the pipe, * so we close the write fd */ close(resolver_pipe_fd[1]); closelog(); kill_asyncio(0); (void) signal_no_reset(SIGCHLD, SIG_DFL); #ifdef DEBUG if (0) debug = 2; #endif # ifndef LOG_DAEMON openlog("ntpd_initres", LOG_PID); # else /* LOG_DAEMON */ # ifndef LOG_NTP # define LOG_NTP LOG_DAEMON # endif openlog("ntpd_initres", LOG_PID | LOG_NDELAY, LOG_NTP); #ifndef SYS_CYGWIN32 # ifdef DEBUG if (debug) setlogmask(LOG_UPTO(LOG_DEBUG)); else # endif /* DEBUG */ setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */ # endif /* LOG_DAEMON */ #endif ntp_intres(); /* * If we got here, the intres code screwed up. * Print something so we don't die without complaint */ msyslog(LOG_ERR, "call to ntp_intres lost"); abort_resolve(); exit(1); } #else /* vxWorks spawns a thread... -casey */ i = sp (ntp_intres); /*i = taskSpawn("ntp_intres",100,VX_FP_TASK,20000,ntp_intres);*/ #endif if (i == -1) { msyslog(LOG_ERR, "fork() failed, can't start ntp_intres: %m"); (void) signal_no_reset(SIGCHLD, SIG_DFL); abort_resolve(); } else { /* This is the parent process who will write to the pipe, * so we close the read fd */ close(resolver_pipe_fd[0]); } #else /* SYS_WINNT */ { /* NT's equivalent of fork() is _spawn(), but the start point * of the new process is an executable filename rather than * a function name as desired here. */ DWORD dwThreadId; fflush(stdout); ResolverEventHandle = CreateEvent(NULL, FALSE, FALSE, NULL); if (ResolverEventHandle == NULL) { msyslog(LOG_ERR, "Unable to create resolver event object, can't start ntp_intres"); abort_resolve(); } ResolverThreadHandle = CreateThread( NULL, /* no security attributes */ 0, /* use default stack size */ (LPTHREAD_START_ROUTINE) ntp_intres, /* thread function */ NULL, /* argument to thread function */ 0, /* use default creation flags */ &dwThreadId); /* returns the thread identifier */ if (ResolverThreadHandle == NULL) { msyslog(LOG_ERR, "CreateThread() failed, can't start ntp_intres"); CloseHandle(ResolverEventHandle); ResolverEventHandle = NULL; abort_resolve(); } } #endif /* SYS_WINNT */ #else /* VMS VX_WORKS */ msyslog(LOG_ERR, "Name resolution not implemented for VMS - use numeric addresses"); abort_resolve(); #endif /* VMS VX_WORKS */ } Index: releng/10.1/contrib/ntp/ntpd/ntp_control.c =================================================================== --- releng/10.1/contrib/ntp/ntpd/ntp_control.c (revision 276158) +++ releng/10.1/contrib/ntp/ntpd/ntp_control.c (revision 276159) @@ -1,3001 +1,3016 @@ /* * ntp_control.c - respond to control messages and send async traps */ /* * $FreeBSD$ */ #ifdef HAVE_CONFIG_H #include #endif #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_control.h" #include "ntp_unixtime.h" #include "ntp_stdlib.h" #include #include #include #include #include +#ifndef MIN +#define MIN(a, b) (((a) <= (b)) ? (a) : (b)) +#endif + /* * Structure to hold request procedure information */ #define NOAUTH 0 #define AUTH 1 #define NO_REQUEST (-1) struct ctl_proc { short control_code; /* defined request code */ u_short flags; /* flags word */ void (*handler) P((struct recvbuf *, int)); /* handle request */ }; /* * Only one flag. Authentication required or not. */ #define NOAUTH 0 #define AUTH 1 /* * Request processing routines */ static void ctl_error P((int)); #ifdef REFCLOCK static u_short ctlclkstatus P((struct refclockstat *)); #endif static void ctl_flushpkt P((int)); static void ctl_putdata P((const char *, unsigned int, int)); static void ctl_putstr P((const char *, const char *, unsigned int)); static void ctl_putdbl P((const char *, double)); static void ctl_putuint P((const char *, u_long)); static void ctl_puthex P((const char *, u_long)); static void ctl_putint P((const char *, long)); static void ctl_putts P((const char *, l_fp *)); static void ctl_putadr P((const char *, u_int32, struct sockaddr_storage*)); static void ctl_putid P((const char *, char *)); static void ctl_putarray P((const char *, double *, int)); static void ctl_putsys P((int)); static void ctl_putpeer P((int, struct peer *)); #ifdef OPENSSL static void ctl_putfs P((const char *, tstamp_t)); #endif #ifdef REFCLOCK static void ctl_putclock P((int, struct refclockstat *, int)); #endif /* REFCLOCK */ static struct ctl_var *ctl_getitem P((struct ctl_var *, char **)); static u_long count_var P((struct ctl_var *)); static void control_unspec P((struct recvbuf *, int)); static void read_status P((struct recvbuf *, int)); static void read_variables P((struct recvbuf *, int)); static void write_variables P((struct recvbuf *, int)); static void read_clock_status P((struct recvbuf *, int)); static void write_clock_status P((struct recvbuf *, int)); static void set_trap P((struct recvbuf *, int)); static void unset_trap P((struct recvbuf *, int)); static struct ctl_trap *ctlfindtrap P((struct sockaddr_storage *, struct interface *)); static struct ctl_proc control_codes[] = { { CTL_OP_UNSPEC, NOAUTH, control_unspec }, { CTL_OP_READSTAT, NOAUTH, read_status }, { CTL_OP_READVAR, NOAUTH, read_variables }, { CTL_OP_WRITEVAR, AUTH, write_variables }, { CTL_OP_READCLOCK, NOAUTH, read_clock_status }, { CTL_OP_WRITECLOCK, NOAUTH, write_clock_status }, { CTL_OP_SETTRAP, NOAUTH, set_trap }, { CTL_OP_UNSETTRAP, NOAUTH, unset_trap }, { NO_REQUEST, 0 } }; /* * System variable values. The array can be indexed by the variable * index to find the textual name. */ static struct ctl_var sys_var[] = { { 0, PADDING, "" }, /* 0 */ { CS_LEAP, RW, "leap" }, /* 1 */ { CS_STRATUM, RO, "stratum" }, /* 2 */ { CS_PRECISION, RO, "precision" }, /* 3 */ { CS_ROOTDELAY, RO, "rootdelay" }, /* 4 */ { CS_ROOTDISPERSION, RO, "rootdispersion" }, /* 5 */ { CS_REFID, RO, "refid" }, /* 6 */ { CS_REFTIME, RO, "reftime" }, /* 7 */ { CS_POLL, RO, "poll" }, /* 8 */ { CS_PEERID, RO, "peer" }, /* 9 */ { CS_STATE, RO, "state" }, /* 10 */ { CS_OFFSET, RO, "offset" }, /* 11 */ { CS_DRIFT, RO, "frequency" }, /* 12 */ { CS_JITTER, RO, "jitter" }, /* 13 */ { CS_ERROR, RO, "noise" }, /* 14 */ { CS_CLOCK, RO, "clock" }, /* 15 */ { CS_PROCESSOR, RO, "processor" }, /* 16 */ { CS_SYSTEM, RO, "system" }, /* 17 */ { CS_VERSION, RO, "version" }, /* 18 */ { CS_STABIL, RO, "stability" }, /* 19 */ { CS_VARLIST, RO, "sys_var_list" }, /* 20 */ #ifdef OPENSSL { CS_FLAGS, RO, "flags" }, /* 21 */ { CS_HOST, RO, "hostname" }, /* 22 */ { CS_PUBLIC, RO, "update" }, /* 23 */ { CS_CERTIF, RO, "cert" }, /* 24 */ { CS_REVTIME, RO, "expire" }, /* 25 */ { CS_LEAPTAB, RO, "leapsec" }, /* 26 */ { CS_TAI, RO, "tai" }, /* 27 */ { CS_DIGEST, RO, "signature" }, /* 28 */ { CS_IDENT, RO, "ident" }, /* 29 */ { CS_REVOKE, RO, "expire" }, /* 30 */ #endif /* OPENSSL */ { 0, EOV, "" } /* 21/31 */ }; static struct ctl_var *ext_sys_var = (struct ctl_var *)0; /* * System variables we print by default (in fuzzball order, * more-or-less) */ static u_char def_sys_var[] = { CS_VERSION, CS_PROCESSOR, CS_SYSTEM, CS_LEAP, CS_STRATUM, CS_PRECISION, CS_ROOTDELAY, CS_ROOTDISPERSION, CS_PEERID, CS_REFID, CS_REFTIME, CS_POLL, CS_CLOCK, CS_STATE, CS_OFFSET, CS_DRIFT, CS_JITTER, CS_ERROR, CS_STABIL, #ifdef OPENSSL CS_HOST, CS_DIGEST, CS_FLAGS, CS_PUBLIC, CS_IDENT, CS_LEAPTAB, CS_TAI, CS_CERTIF, #endif /* OPENSSL */ 0 }; /* * Peer variable list */ static struct ctl_var peer_var[] = { { 0, PADDING, "" }, /* 0 */ { CP_CONFIG, RO, "config" }, /* 1 */ { CP_AUTHENABLE, RO, "authenable" }, /* 2 */ { CP_AUTHENTIC, RO, "authentic" }, /* 3 */ { CP_SRCADR, RO, "srcadr" }, /* 4 */ { CP_SRCPORT, RO, "srcport" }, /* 5 */ { CP_DSTADR, RO, "dstadr" }, /* 6 */ { CP_DSTPORT, RO, "dstport" }, /* 7 */ { CP_LEAP, RO, "leap" }, /* 8 */ { CP_HMODE, RO, "hmode" }, /* 9 */ { CP_STRATUM, RO, "stratum" }, /* 10 */ { CP_PPOLL, RO, "ppoll" }, /* 11 */ { CP_HPOLL, RO, "hpoll" }, /* 12 */ { CP_PRECISION, RO, "precision" }, /* 13 */ { CP_ROOTDELAY, RO, "rootdelay" }, /* 14 */ { CP_ROOTDISPERSION, RO, "rootdispersion" }, /* 15 */ { CP_REFID, RO, "refid" }, /* 16 */ { CP_REFTIME, RO, "reftime" }, /* 17 */ { CP_ORG, RO, "org" }, /* 18 */ { CP_REC, RO, "rec" }, /* 19 */ { CP_XMT, RO, "xmt" }, /* 20 */ { CP_REACH, RO, "reach" }, /* 21 */ { CP_UNREACH, RO, "unreach" }, /* 22 */ { CP_TIMER, RO, "timer" }, /* 23 */ { CP_DELAY, RO, "delay" }, /* 24 */ { CP_OFFSET, RO, "offset" }, /* 25 */ { CP_JITTER, RO, "jitter" }, /* 26 */ { CP_DISPERSION, RO, "dispersion" }, /* 27 */ { CP_KEYID, RO, "keyid" }, /* 28 */ { CP_FILTDELAY, RO, "filtdelay=" }, /* 29 */ { CP_FILTOFFSET, RO, "filtoffset=" }, /* 30 */ { CP_PMODE, RO, "pmode" }, /* 31 */ { CP_RECEIVED, RO, "received"}, /* 32 */ { CP_SENT, RO, "sent" }, /* 33 */ { CP_FILTERROR, RO, "filtdisp=" }, /* 34 */ { CP_FLASH, RO, "flash" }, /* 35 */ { CP_TTL, RO, "ttl" }, /* 36 */ { CP_VARLIST, RO, "peer_var_list" }, /* 37 */ #ifdef OPENSSL { CP_FLAGS, RO, "flags" }, /* 38 */ { CP_HOST, RO, "hostname" }, /* 39 */ { CP_VALID, RO, "valid" }, /* 40 */ { CP_INITSEQ, RO, "initsequence" }, /* 41 */ { CP_INITKEY, RO, "initkey" }, /* 42 */ { CP_INITTSP, RO, "timestamp" }, /* 43 */ { CP_DIGEST, RO, "signature" }, /* 44 */ { CP_IDENT, RO, "trust" }, /* 45 */ #endif /* OPENSSL */ { 0, EOV, "" } /* 38/46 */ }; /* * Peer variables we print by default */ static u_char def_peer_var[] = { CP_SRCADR, CP_SRCPORT, CP_DSTADR, CP_DSTPORT, CP_LEAP, CP_STRATUM, CP_PRECISION, CP_ROOTDELAY, CP_ROOTDISPERSION, CP_REFID, CP_REACH, CP_UNREACH, CP_HMODE, CP_PMODE, CP_HPOLL, CP_PPOLL, CP_FLASH, CP_KEYID, CP_TTL, CP_OFFSET, CP_DELAY, CP_DISPERSION, CP_JITTER, CP_REFTIME, CP_ORG, CP_REC, CP_XMT, CP_FILTDELAY, CP_FILTOFFSET, CP_FILTERROR, #ifdef OPENSSL CP_HOST, CP_DIGEST, CP_VALID, CP_FLAGS, CP_IDENT, CP_INITSEQ, #endif /* OPENSSL */ 0 }; #ifdef REFCLOCK /* * Clock variable list */ static struct ctl_var clock_var[] = { { 0, PADDING, "" }, /* 0 */ { CC_TYPE, RO, "type" }, /* 1 */ { CC_TIMECODE, RO, "timecode" }, /* 2 */ { CC_POLL, RO, "poll" }, /* 3 */ { CC_NOREPLY, RO, "noreply" }, /* 4 */ { CC_BADFORMAT, RO, "badformat" }, /* 5 */ { CC_BADDATA, RO, "baddata" }, /* 6 */ { CC_FUDGETIME1, RO, "fudgetime1" }, /* 7 */ { CC_FUDGETIME2, RO, "fudgetime2" }, /* 8 */ { CC_FUDGEVAL1, RO, "stratum" }, /* 9 */ { CC_FUDGEVAL2, RO, "refid" }, /* 10 */ { CC_FLAGS, RO, "flags" }, /* 11 */ { CC_DEVICE, RO, "device" }, /* 12 */ { CC_VARLIST, RO, "clock_var_list" }, /* 13 */ { 0, EOV, "" } /* 14 */ }; /* * Clock variables printed by default */ static u_char def_clock_var[] = { CC_DEVICE, CC_TYPE, /* won't be output if device = known */ CC_TIMECODE, CC_POLL, CC_NOREPLY, CC_BADFORMAT, CC_BADDATA, CC_FUDGETIME1, CC_FUDGETIME2, CC_FUDGEVAL1, CC_FUDGEVAL2, CC_FLAGS, 0 }; #endif /* * System and processor definitions. */ #ifndef HAVE_UNAME # ifndef STR_SYSTEM # define STR_SYSTEM "UNIX" # endif # ifndef STR_PROCESSOR # define STR_PROCESSOR "unknown" # endif static char str_system[] = STR_SYSTEM; static char str_processor[] = STR_PROCESSOR; #else # include static struct utsname utsnamebuf; #endif /* HAVE_UNAME */ /* * Trap structures. We only allow a few of these, and send a copy of * each async message to each live one. Traps time out after an hour, it * is up to the trap receipient to keep resetting it to avoid being * timed out. */ /* ntp_request.c */ struct ctl_trap ctl_trap[CTL_MAXTRAPS]; int num_ctl_traps; /* * Type bits, for ctlsettrap() call. */ #define TRAP_TYPE_CONFIG 0 /* used by configuration code */ #define TRAP_TYPE_PRIO 1 /* priority trap */ #define TRAP_TYPE_NONPRIO 2 /* nonpriority trap */ /* * List relating reference clock types to control message time sources. * Index by the reference clock type. This list will only be used iff * the reference clock driver doesn't set peer->sstclktype to something * different than CTL_SST_TS_UNSPEC. */ static u_char clocktypes[] = { CTL_SST_TS_NTP, /* REFCLK_NONE (0) */ CTL_SST_TS_LOCAL, /* REFCLK_LOCALCLOCK (1) */ CTL_SST_TS_UHF, /* deprecated REFCLK_GPS_TRAK (2) */ CTL_SST_TS_HF, /* REFCLK_WWV_PST (3) */ CTL_SST_TS_LF, /* REFCLK_WWVB_SPECTRACOM (4) */ CTL_SST_TS_UHF, /* REFCLK_TRUETIME (5) */ CTL_SST_TS_UHF, /* REFCLK_GOES_TRAK (6) IRIG_AUDIO? */ CTL_SST_TS_HF, /* REFCLK_CHU (7) */ CTL_SST_TS_LF, /* REFCLOCK_PARSE (default) (8) */ CTL_SST_TS_LF, /* REFCLK_GPS_MX4200 (9) */ CTL_SST_TS_UHF, /* REFCLK_GPS_AS2201 (10) */ CTL_SST_TS_UHF, /* REFCLK_GPS_ARBITER (11) */ CTL_SST_TS_UHF, /* REFCLK_IRIG_TPRO (12) */ CTL_SST_TS_ATOM, /* REFCLK_ATOM_LEITCH (13) */ CTL_SST_TS_LF, /* deprecated REFCLK_MSF_EES (14) */ CTL_SST_TS_NTP, /* not used (15) */ CTL_SST_TS_UHF, /* REFCLK_IRIG_BANCOMM (16) */ CTL_SST_TS_UHF, /* REFCLK_GPS_DATU (17) */ CTL_SST_TS_TELEPHONE, /* REFCLK_NIST_ACTS (18) */ CTL_SST_TS_HF, /* REFCLK_WWV_HEATH (19) */ CTL_SST_TS_UHF, /* REFCLK_GPS_NMEA (20) */ CTL_SST_TS_UHF, /* REFCLK_GPS_VME (21) */ CTL_SST_TS_ATOM, /* REFCLK_ATOM_PPS (22) */ CTL_SST_TS_NTP, /* not used (23) */ CTL_SST_TS_NTP, /* not used (24) */ CTL_SST_TS_NTP, /* not used (25) */ CTL_SST_TS_UHF, /* REFCLK_GPS_HP (26) */ CTL_SST_TS_TELEPHONE, /* REFCLK_ARCRON_MSF (27) */ CTL_SST_TS_TELEPHONE, /* REFCLK_SHM (28) */ CTL_SST_TS_UHF, /* REFCLK_PALISADE (29) */ CTL_SST_TS_UHF, /* REFCLK_ONCORE (30) */ CTL_SST_TS_UHF, /* REFCLK_JUPITER (31) */ CTL_SST_TS_LF, /* REFCLK_CHRONOLOG (32) */ CTL_SST_TS_LF, /* REFCLK_DUMBCLOCK (33) */ CTL_SST_TS_LF, /* REFCLK_ULINK (34) */ CTL_SST_TS_LF, /* REFCLK_PCF (35) */ CTL_SST_TS_LF, /* REFCLK_WWV (36) */ CTL_SST_TS_LF, /* REFCLK_FG (37) */ CTL_SST_TS_UHF, /* REFCLK_HOPF_SERIAL (38) */ CTL_SST_TS_UHF, /* REFCLK_HOPF_PCI (39) */ CTL_SST_TS_LF, /* REFCLK_JJY (40) */ CTL_SST_TS_UHF, /* REFCLK_TT560 (41) */ CTL_SST_TS_UHF, /* REFCLK_ZYFER (42) */ CTL_SST_TS_UHF, /* REFCLK_RIPENCC (43) */ CTL_SST_TS_UHF, /* REFCLK_NEOCLOCK4X (44) */ }; /* * Keyid used for authenticating write requests. */ keyid_t ctl_auth_keyid; /* * We keep track of the last error reported by the system internally */ static u_char ctl_sys_last_event; static u_char ctl_sys_num_events; /* * Statistic counters to keep track of requests and responses. */ u_long ctltimereset; /* time stats reset */ u_long numctlreq; /* number of requests we've received */ u_long numctlbadpkts; /* number of bad control packets */ u_long numctlresponses; /* number of resp packets sent with data */ u_long numctlfrags; /* number of fragments sent */ u_long numctlerrors; /* number of error responses sent */ u_long numctltooshort; /* number of too short input packets */ u_long numctlinputresp; /* number of responses on input */ u_long numctlinputfrag; /* number of fragments on input */ u_long numctlinputerr; /* number of input pkts with err bit set */ u_long numctlbadoffset; /* number of input pkts with nonzero offset */ u_long numctlbadversion; /* number of input pkts with unknown version */ u_long numctldatatooshort; /* data too short for count */ u_long numctlbadop; /* bad op code found in packet */ u_long numasyncmsgs; /* number of async messages we've sent */ /* * Response packet used by these routines. Also some state information * so that we can handle packet formatting within a common set of * subroutines. Note we try to enter data in place whenever possible, * but the need to set the more bit correctly means we occasionally * use the extra buffer and copy. */ static struct ntp_control rpkt; static u_char res_version; static u_char res_opcode; static associd_t res_associd; static int res_offset; static u_char * datapt; static u_char * dataend; static int datalinelen; static int datanotbinflag; static struct sockaddr_storage *rmt_addr; static struct interface *lcl_inter; static u_char res_authenticate; static u_char res_authokay; static keyid_t res_keyid; #define MAXDATALINELEN (72) static u_char res_async; /* set to 1 if this is async trap response */ /* * Pointers for saving state when decoding request packets */ static char *reqpt; static char *reqend; /* * init_control - initialize request data */ void init_control(void) { int i; #ifdef HAVE_UNAME uname(&utsnamebuf); #endif /* HAVE_UNAME */ ctl_clr_stats(); ctl_auth_keyid = 0; ctl_sys_last_event = EVNT_UNSPEC; ctl_sys_num_events = 0; num_ctl_traps = 0; for (i = 0; i < CTL_MAXTRAPS; i++) ctl_trap[i].tr_flags = 0; } /* * ctl_error - send an error response for the current request */ static void ctl_error( int errcode ) { #ifdef DEBUG if (debug >= 4) printf("sending control error %d\n", errcode); #endif /* * Fill in the fields. We assume rpkt.sequence and rpkt.associd * have already been filled in. */ rpkt.r_m_e_op = (u_char) (CTL_RESPONSE|CTL_ERROR|(res_opcode & CTL_OP_MASK)); rpkt.status = htons((u_short) ((errcode<<8) & 0xff00)); rpkt.count = 0; /* * send packet and bump counters */ if (res_authenticate && sys_authenticate) { int maclen; *(u_int32 *)((u_char *)&rpkt + CTL_HEADER_LEN) = htonl(res_keyid); maclen = authencrypt(res_keyid, (u_int32 *)&rpkt, CTL_HEADER_LEN); sendpkt(rmt_addr, lcl_inter, -2, (struct pkt *)&rpkt, CTL_HEADER_LEN + maclen); } else { sendpkt(rmt_addr, lcl_inter, -3, (struct pkt *)&rpkt, CTL_HEADER_LEN); } numctlerrors++; } /* * process_control - process an incoming control message */ void process_control( struct recvbuf *rbufp, int restrict_mask ) { register struct ntp_control *pkt; register int req_count; register int req_data; register struct ctl_proc *cc; int properlen; int maclen; #ifdef DEBUG if (debug > 2) printf("in process_control()\n"); #endif /* * Save the addresses for error responses */ numctlreq++; rmt_addr = &rbufp->recv_srcadr; lcl_inter = rbufp->dstadr; pkt = (struct ntp_control *)&rbufp->recv_pkt; /* * If the length is less than required for the header, or * it is a response or a fragment, ignore this. */ if (rbufp->recv_length < CTL_HEADER_LEN || pkt->r_m_e_op & (CTL_RESPONSE|CTL_MORE|CTL_ERROR) || pkt->offset != 0) { #ifdef DEBUG if (debug) printf("invalid format in control packet\n"); #endif if (rbufp->recv_length < CTL_HEADER_LEN) numctltooshort++; if (pkt->r_m_e_op & CTL_RESPONSE) numctlinputresp++; if (pkt->r_m_e_op & CTL_MORE) numctlinputfrag++; if (pkt->r_m_e_op & CTL_ERROR) numctlinputerr++; if (pkt->offset != 0) numctlbadoffset++; return; } res_version = PKT_VERSION(pkt->li_vn_mode); if (res_version > NTP_VERSION || res_version < NTP_OLDVERSION) { #ifdef DEBUG if (debug) printf("unknown version %d in control packet\n", res_version); #endif numctlbadversion++; return; } /* * Pull enough data from the packet to make intelligent * responses */ rpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, res_version, MODE_CONTROL); res_opcode = pkt->r_m_e_op; rpkt.sequence = pkt->sequence; rpkt.associd = pkt->associd; rpkt.status = 0; res_offset = 0; res_associd = htons(pkt->associd); res_async = 0; res_authenticate = 0; res_keyid = 0; res_authokay = 0; req_count = (int)htons(pkt->count); datanotbinflag = 0; datalinelen = 0; datapt = rpkt.data; dataend = &(rpkt.data[CTL_MAX_DATA_LEN]); /* * We're set up now. Make sure we've got at least enough * incoming data space to match the count. */ req_data = rbufp->recv_length - CTL_HEADER_LEN; if (req_data < req_count || rbufp->recv_length & 0x3) { ctl_error(CERR_BADFMT); numctldatatooshort++; return; } properlen = req_count + CTL_HEADER_LEN; #ifdef DEBUG if (debug > 2 && (rbufp->recv_length & 0x3) != 0) printf("Packet length %d unrounded\n", rbufp->recv_length); #endif /* round up proper len to a 8 octet boundary */ properlen = (properlen + 7) & ~7; maclen = rbufp->recv_length - properlen; if ((rbufp->recv_length & (sizeof(u_long) - 1)) == 0 && maclen >= MIN_MAC_LEN && maclen <= MAX_MAC_LEN && sys_authenticate) { res_authenticate = 1; res_keyid = ntohl(*(u_int32 *)((u_char *)pkt + properlen)); #ifdef DEBUG if (debug > 2) printf( "recv_len %d, properlen %d, wants auth with keyid %08x, MAC length=%d\n", rbufp->recv_length, properlen, res_keyid, maclen); #endif if (!authistrusted(res_keyid)) { #ifdef DEBUG if (debug > 2) printf("invalid keyid %08x\n", res_keyid); #endif } else if (authdecrypt(res_keyid, (u_int32 *)pkt, rbufp->recv_length - maclen, maclen)) { #ifdef DEBUG if (debug > 2) printf("authenticated okay\n"); #endif res_authokay = 1; } else { #ifdef DEBUG if (debug > 2) printf("authentication failed\n"); #endif res_keyid = 0; } } /* * Set up translate pointers */ reqpt = (char *)pkt->data; reqend = reqpt + req_count; /* * Look for the opcode processor */ for (cc = control_codes; cc->control_code != NO_REQUEST; cc++) { if (cc->control_code == res_opcode) { #ifdef DEBUG if (debug > 2) printf("opcode %d, found command handler\n", res_opcode); #endif if (cc->flags == AUTH && (!res_authokay || res_keyid != ctl_auth_keyid)) { ctl_error(CERR_PERMISSION); return; } (cc->handler)(rbufp, restrict_mask); return; } } /* * Can't find this one, return an error. */ numctlbadop++; ctl_error(CERR_BADOP); return; } /* * ctlpeerstatus - return a status word for this peer */ u_short ctlpeerstatus( register struct peer *peer ) { register u_short status; status = peer->status; if (peer->flags & FLAG_CONFIG) status |= CTL_PST_CONFIG; if (peer->flags & FLAG_AUTHENABLE) status |= CTL_PST_AUTHENABLE; if (peer->flags & FLAG_AUTHENTIC) status |= CTL_PST_AUTHENTIC; if (peer->reach != 0) status |= CTL_PST_REACH; return (u_short)CTL_PEER_STATUS(status, peer->num_events, peer->last_event); } /* * ctlclkstatus - return a status word for this clock */ #ifdef REFCLOCK static u_short ctlclkstatus( struct refclockstat *this_clock ) { return ((u_short)(((this_clock->currentstatus) << 8) | (this_clock->lastevent))); } #endif /* * ctlsysstatus - return the system status word */ u_short ctlsysstatus(void) { register u_char this_clock; this_clock = CTL_SST_TS_UNSPEC; #ifdef REFCLOCK if (sys_peer != 0) { if (sys_peer->sstclktype != CTL_SST_TS_UNSPEC) { this_clock = sys_peer->sstclktype; if (pps_control) this_clock |= CTL_SST_TS_PPS; } else { if (sys_peer->refclktype < sizeof(clocktypes)) this_clock = clocktypes[sys_peer->refclktype]; if (pps_control) this_clock |= CTL_SST_TS_PPS; } } #endif /* REFCLOCK */ return (u_short)CTL_SYS_STATUS(sys_leap, this_clock, ctl_sys_num_events, ctl_sys_last_event); } /* * ctl_flushpkt - write out the current packet and prepare * another if necessary. */ static void ctl_flushpkt( int more ) { int dlen; int sendlen; if (!more && datanotbinflag) { /* * Big hack, output a trailing \r\n */ *datapt++ = '\r'; *datapt++ = '\n'; } dlen = datapt - (u_char *)rpkt.data; sendlen = dlen + CTL_HEADER_LEN; /* * Pad to a multiple of 32 bits */ while (sendlen & 0x3) { *datapt++ = '\0'; sendlen++; } /* * Fill in the packet with the current info */ rpkt.r_m_e_op = (u_char)(CTL_RESPONSE|more|(res_opcode & CTL_OP_MASK)); rpkt.count = htons((u_short) dlen); rpkt.offset = htons( (u_short) res_offset); if (res_async) { register int i; for (i = 0; i < CTL_MAXTRAPS; i++) { if (ctl_trap[i].tr_flags & TRAP_INUSE) { rpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, ctl_trap[i].tr_version, MODE_CONTROL); rpkt.sequence = htons(ctl_trap[i].tr_sequence); sendpkt(&ctl_trap[i].tr_addr, ctl_trap[i].tr_localaddr, -4, (struct pkt *)&rpkt, sendlen); if (!more) ctl_trap[i].tr_sequence++; numasyncmsgs++; } } } else { if (res_authenticate && sys_authenticate) { int maclen; int totlen = sendlen; keyid_t keyid = htonl(res_keyid); /* * If we are going to authenticate, then there * is an additional requirement that the MAC * begin on a 64 bit boundary. */ while (totlen & 7) { *datapt++ = '\0'; totlen++; } memcpy(datapt, &keyid, sizeof keyid); maclen = authencrypt(res_keyid, (u_int32 *)&rpkt, totlen); sendpkt(rmt_addr, lcl_inter, -5, (struct pkt *)&rpkt, totlen + maclen); } else { sendpkt(rmt_addr, lcl_inter, -6, (struct pkt *)&rpkt, sendlen); } if (more) numctlfrags++; else numctlresponses++; } /* * Set us up for another go around. */ res_offset += dlen; datapt = (u_char *)rpkt.data; } /* * ctl_putdata - write data into the packet, fragmenting and starting * another if this one is full. */ static void ctl_putdata( const char *dp, unsigned int dlen, int bin /* set to 1 when data is binary */ ) { int overhead; + unsigned int currentlen; overhead = 0; if (!bin) { datanotbinflag = 1; overhead = 3; if (datapt != rpkt.data) { *datapt++ = ','; datalinelen++; if ((dlen + datalinelen + 1) >= MAXDATALINELEN) { *datapt++ = '\r'; *datapt++ = '\n'; datalinelen = 0; } else { *datapt++ = ' '; datalinelen++; } } } /* * Save room for trailing junk */ - if (dlen + overhead + datapt > dataend) { + while (dlen + overhead + datapt > dataend) { /* * Not enough room in this one, flush it out. */ + currentlen = MIN(dlen, dataend - datapt); + + memcpy(datapt, dp, currentlen); + + datapt += currentlen; + dp += currentlen; + dlen -= currentlen; + datalinelen += currentlen; + ctl_flushpkt(CTL_MORE); } + memmove((char *)datapt, dp, (unsigned)dlen); datapt += dlen; datalinelen += dlen; } /* * ctl_putstr - write a tagged string into the response packet */ static void ctl_putstr( const char *tag, const char *data, unsigned int len ) { register char *cp; register const char *cq; char buffer[400]; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; if (len > 0) { *cp++ = '='; *cp++ = '"'; if (len > (int) (sizeof(buffer) - (cp - buffer) - 1)) len = sizeof(buffer) - (cp - buffer) - 1; memmove(cp, data, (unsigned)len); cp += len; *cp++ = '"'; } ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); } /* * ctl_putdbl - write a tagged, signed double into the response packet */ static void ctl_putdbl( const char *tag, double ts ) { register char *cp; register const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; *cp++ = '='; (void)sprintf(cp, "%.3f", ts); while (*cp != '\0') cp++; ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); } /* * ctl_putuint - write a tagged unsigned integer into the response */ static void ctl_putuint( const char *tag, u_long uval ) { register char *cp; register const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; *cp++ = '='; (void) sprintf(cp, "%lu", uval); while (*cp != '\0') cp++; ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); } /* * ctl_putfs - write a decoded filestamp into the response */ #ifdef OPENSSL static void ctl_putfs( const char *tag, tstamp_t uval ) { register char *cp; register const char *cq; char buffer[200]; struct tm *tm = NULL; time_t fstamp; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; *cp++ = '='; fstamp = uval - JAN_1970; tm = gmtime(&fstamp); if (tm == NULL) return; sprintf(cp, "%04d%02d%02d%02d%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min); while (*cp != '\0') cp++; ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); } #endif /* * ctl_puthex - write a tagged unsigned integer, in hex, into the response */ static void ctl_puthex( const char *tag, u_long uval ) { register char *cp; register const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; *cp++ = '='; (void) sprintf(cp, "0x%lx", uval); while (*cp != '\0') cp++; ctl_putdata(buffer,(unsigned)( cp - buffer ), 0); } /* * ctl_putint - write a tagged signed integer into the response */ static void ctl_putint( const char *tag, long ival ) { register char *cp; register const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; *cp++ = '='; (void) sprintf(cp, "%ld", ival); while (*cp != '\0') cp++; ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); } /* * ctl_putts - write a tagged timestamp, in hex, into the response */ static void ctl_putts( const char *tag, l_fp *ts ) { register char *cp; register const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; *cp++ = '='; (void) sprintf(cp, "0x%08lx.%08lx", ts->l_ui & ULONG_CONST(0xffffffff), ts->l_uf & ULONG_CONST(0xffffffff)); while (*cp != '\0') cp++; ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); } /* * ctl_putadr - write an IP address into the response */ static void ctl_putadr( const char *tag, u_int32 addr32, struct sockaddr_storage* addr ) { register char *cp; register const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; *cp++ = '='; if (addr == NULL) cq = numtoa(addr32); else cq = stoa(addr); while (*cq != '\0') *cp++ = *cq++; ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); } /* * ctl_putid - write a tagged clock ID into the response */ static void ctl_putid( const char *tag, char *id ) { register char *cp; register const char *cq; char buffer[200]; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; *cp++ = '='; cq = id; while (*cq != '\0' && (cq - id) < 4) *cp++ = *cq++; ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); } /* * ctl_putarray - write a tagged eight element double array into the response */ static void ctl_putarray( const char *tag, double *arr, int start ) { register char *cp; register const char *cq; char buffer[200]; int i; cp = buffer; cq = tag; while (*cq != '\0') *cp++ = *cq++; i = start; do { if (i == 0) i = NTP_SHIFT; i--; (void)sprintf(cp, " %.2f", arr[i] * 1e3); while (*cp != '\0') cp++; } while(i != start); ctl_putdata(buffer, (unsigned)(cp - buffer), 0); } /* * ctl_putsys - output a system variable */ static void ctl_putsys( int varid ) { l_fp tmp; char str[256]; #ifdef OPENSSL struct cert_info *cp; char cbuf[256]; #endif /* OPENSSL */ switch (varid) { case CS_LEAP: ctl_putuint(sys_var[CS_LEAP].text, sys_leap); break; case CS_STRATUM: ctl_putuint(sys_var[CS_STRATUM].text, sys_stratum); break; case CS_PRECISION: ctl_putint(sys_var[CS_PRECISION].text, sys_precision); break; case CS_ROOTDELAY: ctl_putdbl(sys_var[CS_ROOTDELAY].text, sys_rootdelay * 1e3); break; case CS_ROOTDISPERSION: ctl_putdbl(sys_var[CS_ROOTDISPERSION].text, sys_rootdispersion * 1e3); break; case CS_REFID: if (sys_stratum > 1 && sys_stratum < STRATUM_UNSPEC) ctl_putadr(sys_var[CS_REFID].text, sys_refid, NULL); else ctl_putid(sys_var[CS_REFID].text, (char *)&sys_refid); break; case CS_REFTIME: ctl_putts(sys_var[CS_REFTIME].text, &sys_reftime); break; case CS_POLL: ctl_putuint(sys_var[CS_POLL].text, sys_poll); break; case CS_PEERID: if (sys_peer == NULL) ctl_putuint(sys_var[CS_PEERID].text, 0); else ctl_putuint(sys_var[CS_PEERID].text, sys_peer->associd); break; case CS_STATE: ctl_putuint(sys_var[CS_STATE].text, (unsigned)state); break; case CS_OFFSET: ctl_putdbl(sys_var[CS_OFFSET].text, last_offset * 1e3); break; case CS_DRIFT: ctl_putdbl(sys_var[CS_DRIFT].text, drift_comp * 1e6); break; case CS_JITTER: ctl_putdbl(sys_var[CS_JITTER].text, sys_jitter * 1e3); break; case CS_ERROR: ctl_putdbl(sys_var[CS_ERROR].text, clock_jitter * 1e3); break; case CS_CLOCK: get_systime(&tmp); ctl_putts(sys_var[CS_CLOCK].text, &tmp); break; case CS_PROCESSOR: #ifndef HAVE_UNAME ctl_putstr(sys_var[CS_PROCESSOR].text, str_processor, sizeof(str_processor) - 1); #else ctl_putstr(sys_var[CS_PROCESSOR].text, utsnamebuf.machine, strlen(utsnamebuf.machine)); #endif /* HAVE_UNAME */ break; case CS_SYSTEM: #ifndef HAVE_UNAME ctl_putstr(sys_var[CS_SYSTEM].text, str_system, sizeof(str_system) - 1); #else sprintf(str, "%s/%s", utsnamebuf.sysname, utsnamebuf.release); ctl_putstr(sys_var[CS_SYSTEM].text, str, strlen(str)); #endif /* HAVE_UNAME */ break; case CS_VERSION: ctl_putstr(sys_var[CS_VERSION].text, Version, strlen(Version)); break; case CS_STABIL: ctl_putdbl(sys_var[CS_STABIL].text, clock_stability * 1e6); break; case CS_VARLIST: { char buf[CTL_MAX_DATA_LEN]; register char *s, *t, *be; register const char *ss; register int i; register struct ctl_var *k; s = buf; be = buf + sizeof(buf) - strlen(sys_var[CS_VARLIST].text) - 4; if (s > be) break; /* really long var name */ strcpy(s, sys_var[CS_VARLIST].text); strcat(s, "=\""); s += strlen(s); t = s; for (k = sys_var; !(k->flags &EOV); k++) { if (k->flags & PADDING) continue; i = strlen(k->text); if (s+i+1 >= be) break; if (s != t) *s++ = ','; strcpy(s, k->text); s += i; } for (k = ext_sys_var; k && !(k->flags &EOV); k++) { if (k->flags & PADDING) continue; ss = k->text; if (!ss) continue; while (*ss && *ss != '=') ss++; i = ss - k->text; if (s + i + 1 >= be) break; if (s != t) *s++ = ','; strncpy(s, k->text, (unsigned)i); s += i; } if (s+2 >= be) break; *s++ = '"'; *s = '\0'; ctl_putdata(buf, (unsigned)( s - buf ), 0); } break; #ifdef OPENSSL case CS_FLAGS: if (crypto_flags) { ctl_puthex(sys_var[CS_FLAGS].text, crypto_flags); } break; case CS_DIGEST: if (crypto_flags) { const EVP_MD *dp; dp = EVP_get_digestbynid(crypto_flags >> 16); strcpy(str, OBJ_nid2ln(EVP_MD_pkey_type(dp))); ctl_putstr(sys_var[CS_DIGEST].text, str, strlen(str)); } break; case CS_HOST: if (sys_hostname != NULL) ctl_putstr(sys_var[CS_HOST].text, sys_hostname, strlen(sys_hostname)); break; case CS_CERTIF: for (cp = cinfo; cp != NULL; cp = cp->link) { sprintf(cbuf, "%s %s 0x%x", cp->subject, cp->issuer, cp->flags); ctl_putstr(sys_var[CS_CERTIF].text, cbuf, strlen(cbuf)); ctl_putfs(sys_var[CS_REVOKE].text, cp->last); } break; case CS_PUBLIC: if (hostval.fstamp != 0) ctl_putfs(sys_var[CS_PUBLIC].text, ntohl(hostval.tstamp)); break; case CS_REVTIME: if (hostval.tstamp != 0) ctl_putfs(sys_var[CS_REVTIME].text, ntohl(hostval.tstamp)); break; case CS_IDENT: if (iffpar_pkey != NULL) ctl_putstr(sys_var[CS_IDENT].text, iffpar_file, strlen(iffpar_file)); if (gqpar_pkey != NULL) ctl_putstr(sys_var[CS_IDENT].text, gqpar_file, strlen(gqpar_file)); if (mvpar_pkey != NULL) ctl_putstr(sys_var[CS_IDENT].text, mvpar_file, strlen(mvpar_file)); break; case CS_LEAPTAB: if (tai_leap.fstamp != 0) ctl_putfs(sys_var[CS_LEAPTAB].text, ntohl(tai_leap.fstamp)); break; case CS_TAI: ctl_putuint(sys_var[CS_TAI].text, sys_tai); break; #endif /* OPENSSL */ } } /* * ctl_putpeer - output a peer variable */ static void ctl_putpeer( int varid, struct peer *peer ) { int temp; #ifdef OPENSSL char str[256]; struct autokey *ap; #endif /* OPENSSL */ switch (varid) { case CP_CONFIG: ctl_putuint(peer_var[CP_CONFIG].text, (unsigned)((peer->flags & FLAG_CONFIG) != 0)); break; case CP_AUTHENABLE: ctl_putuint(peer_var[CP_AUTHENABLE].text, (unsigned)((peer->flags & FLAG_AUTHENABLE) != 0)); break; case CP_AUTHENTIC: ctl_putuint(peer_var[CP_AUTHENTIC].text, (unsigned)((peer->flags & FLAG_AUTHENTIC) != 0)); break; case CP_SRCADR: ctl_putadr(peer_var[CP_SRCADR].text, 0, &peer->srcadr); break; case CP_SRCPORT: ctl_putuint(peer_var[CP_SRCPORT].text, ntohs(((struct sockaddr_in*)&peer->srcadr)->sin_port)); break; case CP_DSTADR: if (peer->dstadr) { ctl_putadr(peer_var[CP_DSTADR].text, 0, &(peer->dstadr->sin)); } else { ctl_putadr(peer_var[CP_DSTADR].text, 0, NULL); } break; case CP_DSTPORT: ctl_putuint(peer_var[CP_DSTPORT].text, (u_long)(peer->dstadr ? ntohs(((struct sockaddr_in*)&peer->dstadr->sin)->sin_port) : 0)); break; case CP_LEAP: ctl_putuint(peer_var[CP_LEAP].text, peer->leap); break; case CP_HMODE: ctl_putuint(peer_var[CP_HMODE].text, peer->hmode); break; case CP_STRATUM: ctl_putuint(peer_var[CP_STRATUM].text, peer->stratum); break; case CP_PPOLL: ctl_putuint(peer_var[CP_PPOLL].text, peer->ppoll); break; case CP_HPOLL: ctl_putuint(peer_var[CP_HPOLL].text, peer->hpoll); break; case CP_PRECISION: ctl_putint(peer_var[CP_PRECISION].text, peer->precision); break; case CP_ROOTDELAY: ctl_putdbl(peer_var[CP_ROOTDELAY].text, peer->rootdelay * 1e3); break; case CP_ROOTDISPERSION: ctl_putdbl(peer_var[CP_ROOTDISPERSION].text, peer->rootdispersion * 1e3); break; case CP_REFID: if (peer->flags & FLAG_REFCLOCK) { ctl_putid(peer_var[CP_REFID].text, (char *)&peer->refid); } else { if (peer->stratum > 1 && peer->stratum < STRATUM_UNSPEC) ctl_putadr(peer_var[CP_REFID].text, peer->refid, NULL); else ctl_putid(peer_var[CP_REFID].text, (char *)&peer->refid); } break; case CP_REFTIME: ctl_putts(peer_var[CP_REFTIME].text, &peer->reftime); break; case CP_ORG: ctl_putts(peer_var[CP_ORG].text, &peer->org); break; case CP_REC: ctl_putts(peer_var[CP_REC].text, &peer->rec); break; case CP_XMT: ctl_putts(peer_var[CP_XMT].text, &peer->xmt); break; case CP_REACH: ctl_puthex(peer_var[CP_REACH].text, peer->reach); break; case CP_FLASH: temp = peer->flash; ctl_puthex(peer_var[CP_FLASH].text, temp); break; case CP_TTL: ctl_putint(peer_var[CP_TTL].text, sys_ttl[peer->ttl]); break; case CP_UNREACH: ctl_putuint(peer_var[CP_UNREACH].text, peer->unreach); break; case CP_TIMER: ctl_putuint(peer_var[CP_TIMER].text, peer->nextdate - current_time); break; case CP_DELAY: ctl_putdbl(peer_var[CP_DELAY].text, peer->delay * 1e3); break; case CP_OFFSET: ctl_putdbl(peer_var[CP_OFFSET].text, peer->offset * 1e3); break; case CP_JITTER: ctl_putdbl(peer_var[CP_JITTER].text, peer->jitter * 1e3); break; case CP_DISPERSION: ctl_putdbl(peer_var[CP_DISPERSION].text, peer->disp * 1e3); break; case CP_KEYID: ctl_putuint(peer_var[CP_KEYID].text, peer->keyid); break; case CP_FILTDELAY: ctl_putarray(peer_var[CP_FILTDELAY].text, peer->filter_delay, (int)peer->filter_nextpt); break; case CP_FILTOFFSET: ctl_putarray(peer_var[CP_FILTOFFSET].text, peer->filter_offset, (int)peer->filter_nextpt); break; case CP_FILTERROR: ctl_putarray(peer_var[CP_FILTERROR].text, peer->filter_disp, (int)peer->filter_nextpt); break; case CP_PMODE: ctl_putuint(peer_var[CP_PMODE].text, peer->pmode); break; case CP_RECEIVED: ctl_putuint(peer_var[CP_RECEIVED].text, peer->received); break; case CP_SENT: ctl_putuint(peer_var[CP_SENT].text, peer->sent); break; case CP_VARLIST: { char buf[CTL_MAX_DATA_LEN]; register char *s, *t, *be; register int i; register struct ctl_var *k; s = buf; be = buf + sizeof(buf) - strlen(peer_var[CP_VARLIST].text) - 4; if (s > be) break; /* really long var name */ strcpy(s, peer_var[CP_VARLIST].text); strcat(s, "=\""); s += strlen(s); t = s; for (k = peer_var; !(k->flags &EOV); k++) { if (k->flags & PADDING) continue; i = strlen(k->text); if (s + i + 1 >= be) break; if (s != t) *s++ = ','; strcpy(s, k->text); s += i; } if (s+2 >= be) break; *s++ = '"'; *s = '\0'; ctl_putdata(buf, (unsigned)(s - buf), 0); } break; #ifdef OPENSSL case CP_FLAGS: if (peer->crypto) ctl_puthex(peer_var[CP_FLAGS].text, peer->crypto); break; case CP_DIGEST: if (peer->crypto) { const EVP_MD *dp; dp = EVP_get_digestbynid(peer->crypto >> 16); strcpy(str, OBJ_nid2ln(EVP_MD_pkey_type(dp))); ctl_putstr(peer_var[CP_DIGEST].text, str, strlen(str)); } break; case CP_HOST: if (peer->subject != NULL) ctl_putstr(peer_var[CP_HOST].text, peer->subject, strlen(peer->subject)); break; case CP_VALID: /* not used */ break; case CP_IDENT: if (peer->issuer != NULL) ctl_putstr(peer_var[CP_IDENT].text, peer->issuer, strlen(peer->issuer)); break; case CP_INITSEQ: if ((ap = (struct autokey *)peer->recval.ptr) == NULL) break; ctl_putint(peer_var[CP_INITSEQ].text, ap->seq); ctl_puthex(peer_var[CP_INITKEY].text, ap->key); ctl_putfs(peer_var[CP_INITTSP].text, ntohl(peer->recval.tstamp)); break; #endif /* OPENSSL */ } } #ifdef REFCLOCK /* * ctl_putclock - output clock variables */ static void ctl_putclock( int varid, struct refclockstat *clock_stat, int mustput ) { switch(varid) { case CC_TYPE: if (mustput || clock_stat->clockdesc == NULL || *(clock_stat->clockdesc) == '\0') { ctl_putuint(clock_var[CC_TYPE].text, clock_stat->type); } break; case CC_TIMECODE: ctl_putstr(clock_var[CC_TIMECODE].text, clock_stat->p_lastcode, (unsigned)clock_stat->lencode); break; case CC_POLL: ctl_putuint(clock_var[CC_POLL].text, clock_stat->polls); break; case CC_NOREPLY: ctl_putuint(clock_var[CC_NOREPLY].text, clock_stat->noresponse); break; case CC_BADFORMAT: ctl_putuint(clock_var[CC_BADFORMAT].text, clock_stat->badformat); break; case CC_BADDATA: ctl_putuint(clock_var[CC_BADDATA].text, clock_stat->baddata); break; case CC_FUDGETIME1: if (mustput || (clock_stat->haveflags & CLK_HAVETIME1)) ctl_putdbl(clock_var[CC_FUDGETIME1].text, clock_stat->fudgetime1 * 1e3); break; case CC_FUDGETIME2: if (mustput || (clock_stat->haveflags & CLK_HAVETIME2)) ctl_putdbl(clock_var[CC_FUDGETIME2].text, clock_stat->fudgetime2 * 1e3); break; case CC_FUDGEVAL1: if (mustput || (clock_stat->haveflags & CLK_HAVEVAL1)) ctl_putint(clock_var[CC_FUDGEVAL1].text, clock_stat->fudgeval1); break; case CC_FUDGEVAL2: if (mustput || (clock_stat->haveflags & CLK_HAVEVAL2)) { if (clock_stat->fudgeval1 > 1) ctl_putadr(clock_var[CC_FUDGEVAL2].text, (u_int32)clock_stat->fudgeval2, NULL); else ctl_putid(clock_var[CC_FUDGEVAL2].text, (char *)&clock_stat->fudgeval2); } break; case CC_FLAGS: if (mustput || (clock_stat->haveflags & (CLK_HAVEFLAG1 | CLK_HAVEFLAG2 | CLK_HAVEFLAG3 | CLK_HAVEFLAG4))) ctl_putuint(clock_var[CC_FLAGS].text, clock_stat->flags); break; case CC_DEVICE: if (clock_stat->clockdesc == NULL || *(clock_stat->clockdesc) == '\0') { if (mustput) ctl_putstr(clock_var[CC_DEVICE].text, "", 0); } else { ctl_putstr(clock_var[CC_DEVICE].text, clock_stat->clockdesc, strlen(clock_stat->clockdesc)); } break; case CC_VARLIST: { char buf[CTL_MAX_DATA_LEN]; register char *s, *t, *be; register const char *ss; register int i; register struct ctl_var *k; s = buf; be = buf + sizeof(buf); if (s + strlen(clock_var[CC_VARLIST].text) + 4 > be) break; /* really long var name */ strcpy(s, clock_var[CC_VARLIST].text); strcat(s, "=\""); s += strlen(s); t = s; for (k = clock_var; !(k->flags &EOV); k++) { if (k->flags & PADDING) continue; i = strlen(k->text); if (s + i + 1 >= be) break; if (s != t) *s++ = ','; strcpy(s, k->text); s += i; } for (k = clock_stat->kv_list; k && !(k->flags & EOV); k++) { if (k->flags & PADDING) continue; ss = k->text; if (!ss) continue; while (*ss && *ss != '=') ss++; i = ss - k->text; if (s+i+1 >= be) break; if (s != t) *s++ = ','; strncpy(s, k->text, (unsigned)i); s += i; *s = '\0'; } if (s+2 >= be) break; *s++ = '"'; *s = '\0'; ctl_putdata(buf, (unsigned)( s - buf ), 0); } break; } } #endif /* * ctl_getitem - get the next data item from the incoming packet */ static struct ctl_var * ctl_getitem( struct ctl_var *var_list, char **data ) { register struct ctl_var *v; register char *cp; register char *tp; static struct ctl_var eol = { 0, EOV, }; static char buf[128]; /* * Delete leading commas and white space */ while (reqpt < reqend && (*reqpt == ',' || isspace((unsigned char)*reqpt))) reqpt++; if (reqpt >= reqend) return (0); if (var_list == (struct ctl_var *)0) return (&eol); /* * Look for a first character match on the tag. If we find * one, see if it is a full match. */ v = var_list; cp = reqpt; while (!(v->flags & EOV)) { if (!(v->flags & PADDING) && *cp == *(v->text)) { tp = v->text; while (*tp != '\0' && *tp != '=' && cp < reqend && *cp == *tp) { cp++; tp++; } if ((*tp == '\0') || (*tp == '=')) { while (cp < reqend && isspace((unsigned char)*cp)) cp++; if (cp == reqend || *cp == ',') { buf[0] = '\0'; *data = buf; if (cp < reqend) cp++; reqpt = cp; return v; } if (*cp == '=') { cp++; tp = buf; while (cp < reqend && isspace((unsigned char)*cp)) cp++; while (cp < reqend && *cp != ',') { *tp++ = *cp++; if (tp >= buf + sizeof(buf)) { ctl_error(CERR_BADFMT); numctlbadpkts++; #if 0 /* Avoid possible DOS attack */ /* If we get a smarter msyslog we can re-enable this */ msyslog(LOG_WARNING, "Possible 'ntpdx' exploit from %s:%d (possibly spoofed)\n", stoa(rmt_addr), SRCPORT(rmt_addr) ); #endif return (0); } } if (cp < reqend) cp++; *tp-- = '\0'; while (tp >= buf) { if (!isspace((unsigned int)(*tp))) break; *tp-- = '\0'; } reqpt = cp; *data = buf; return (v); } } cp = reqpt; } v++; } return v; } /* * control_unspec - response to an unspecified op-code */ /*ARGSUSED*/ static void control_unspec( struct recvbuf *rbufp, int restrict_mask ) { struct peer *peer; /* * What is an appropriate response to an unspecified op-code? * I return no errors and no data, unless a specified assocation * doesn't exist. */ if (res_associd != 0) { if ((peer = findpeerbyassoc(res_associd)) == 0) { ctl_error(CERR_BADASSOC); return; } rpkt.status = htons(ctlpeerstatus(peer)); } else { rpkt.status = htons(ctlsysstatus()); } ctl_flushpkt(0); } /* * read_status - return either a list of associd's, or a particular * peer's status. */ /*ARGSUSED*/ static void read_status( struct recvbuf *rbufp, int restrict_mask ) { register int i; register struct peer *peer; u_short ass_stat[CTL_MAX_DATA_LEN / sizeof(u_short)]; #ifdef DEBUG if (debug > 2) printf("read_status: ID %d\n", res_associd); #endif /* * Two choices here. If the specified association ID is * zero we return all known assocation ID's. Otherwise * we return a bunch of stuff about the particular peer. */ if (res_associd == 0) { register int n; n = 0; rpkt.status = htons(ctlsysstatus()); for (i = 0; i < NTP_HASH_SIZE; i++) { for (peer = assoc_hash[i]; peer != 0; peer = peer->ass_next) { ass_stat[n++] = htons(peer->associd); ass_stat[n++] = htons(ctlpeerstatus(peer)); if (n == CTL_MAX_DATA_LEN/sizeof(u_short)) { ctl_putdata((char *)ass_stat, n * sizeof(u_short), 1); n = 0; } } } if (n != 0) ctl_putdata((char *)ass_stat, n * sizeof(u_short), 1); ctl_flushpkt(0); } else { peer = findpeerbyassoc(res_associd); if (peer == 0) { ctl_error(CERR_BADASSOC); } else { register u_char *cp; rpkt.status = htons(ctlpeerstatus(peer)); if (res_authokay) peer->num_events = 0; /* * For now, output everything we know about the * peer. May be more selective later. */ for (cp = def_peer_var; *cp != 0; cp++) ctl_putpeer((int)*cp, peer); ctl_flushpkt(0); } } } /* * read_variables - return the variables the caller asks for */ /*ARGSUSED*/ static void read_variables( struct recvbuf *rbufp, int restrict_mask ) { register struct ctl_var *v; register int i; char *valuep; u_char *wants; unsigned int gotvar = (CS_MAXCODE > CP_MAXCODE) ? (CS_MAXCODE + 1) : (CP_MAXCODE + 1); if (res_associd == 0) { /* * Wants system variables. Figure out which he wants * and give them to him. */ rpkt.status = htons(ctlsysstatus()); if (res_authokay) ctl_sys_num_events = 0; gotvar += count_var(ext_sys_var); wants = (u_char *)emalloc(gotvar); memset((char *)wants, 0, gotvar); gotvar = 0; while ((v = ctl_getitem(sys_var, &valuep)) != 0) { if (v->flags & EOV) { if ((v = ctl_getitem(ext_sys_var, &valuep)) != 0) { if (v->flags & EOV) { ctl_error(CERR_UNKNOWNVAR); free((char *)wants); return; } wants[CS_MAXCODE + 1 + v->code] = 1; gotvar = 1; continue; } else { break; /* shouldn't happen ! */ } } wants[v->code] = 1; gotvar = 1; } if (gotvar) { for (i = 1; i <= CS_MAXCODE; i++) if (wants[i]) ctl_putsys(i); for (i = 0; ext_sys_var && !(ext_sys_var[i].flags & EOV); i++) if (wants[i + CS_MAXCODE + 1]) ctl_putdata(ext_sys_var[i].text, strlen(ext_sys_var[i].text), 0); } else { register u_char *cs; register struct ctl_var *kv; for (cs = def_sys_var; *cs != 0; cs++) ctl_putsys((int)*cs); for (kv = ext_sys_var; kv && !(kv->flags & EOV); kv++) if (kv->flags & DEF) ctl_putdata(kv->text, strlen(kv->text), 0); } free((char *)wants); } else { register struct peer *peer; /* * Wants info for a particular peer. See if we know * the guy. */ peer = findpeerbyassoc(res_associd); if (peer == 0) { ctl_error(CERR_BADASSOC); return; } rpkt.status = htons(ctlpeerstatus(peer)); if (res_authokay) peer->num_events = 0; wants = (u_char *)emalloc(gotvar); memset((char*)wants, 0, gotvar); gotvar = 0; while ((v = ctl_getitem(peer_var, &valuep)) != 0) { if (v->flags & EOV) { ctl_error(CERR_UNKNOWNVAR); free((char *)wants); return; } wants[v->code] = 1; gotvar = 1; } if (gotvar) { for (i = 1; i <= CP_MAXCODE; i++) if (wants[i]) ctl_putpeer(i, peer); } else { register u_char *cp; for (cp = def_peer_var; *cp != 0; cp++) ctl_putpeer((int)*cp, peer); } free((char *)wants); } ctl_flushpkt(0); } /* * write_variables - write into variables. We only allow leap bit * writing this way. */ /*ARGSUSED*/ static void write_variables( struct recvbuf *rbufp, int restrict_mask ) { register struct ctl_var *v; register int ext_var; char *valuep; long val = 0; /* * If he's trying to write into a peer tell him no way */ if (res_associd != 0) { ctl_error(CERR_PERMISSION); return; } /* * Set status */ rpkt.status = htons(ctlsysstatus()); /* * Look through the variables. Dump out at the first sign of * trouble. */ while ((v = ctl_getitem(sys_var, &valuep)) != 0) { ext_var = 0; if (v->flags & EOV) { if ((v = ctl_getitem(ext_sys_var, &valuep)) != 0) { if (v->flags & EOV) { ctl_error(CERR_UNKNOWNVAR); return; } ext_var = 1; } else { break; } } if (!(v->flags & CAN_WRITE)) { ctl_error(CERR_PERMISSION); return; } if (!ext_var && (*valuep == '\0' || !atoint(valuep, &val))) { ctl_error(CERR_BADFMT); return; } if (!ext_var && (val & ~LEAP_NOTINSYNC) != 0) { ctl_error(CERR_BADVALUE); return; } if (ext_var) { char *s = (char *)emalloc(strlen(v->text) + strlen(valuep) + 2); const char *t; char *tt = s; t = v->text; while (*t && *t != '=') *tt++ = *t++; *tt++ = '='; strcat(tt, valuep); set_sys_var(s, strlen(s)+1, v->flags); free(s); } else { /* * This one seems sane. Save it. */ switch(v->code) { case CS_LEAP: default: ctl_error(CERR_UNSPEC); /* really */ return; } } } /* * If we got anything, do it. xxx nothing to do *** */ /* if (leapind != ~0 || leapwarn != ~0) { if (!leap_setleap((int)leapind, (int)leapwarn)) { ctl_error(CERR_PERMISSION); return; } } */ ctl_flushpkt(0); } /* * read_clock_status - return clock radio status */ /*ARGSUSED*/ static void read_clock_status( struct recvbuf *rbufp, int restrict_mask ) { #ifndef REFCLOCK /* * If no refclock support, no data to return */ ctl_error(CERR_BADASSOC); #else register struct ctl_var *v; register int i; register struct peer *peer; char *valuep; u_char *wants; unsigned int gotvar; struct refclockstat clock_stat; if (res_associd == 0) { /* * Find a clock for this jerk. If the system peer * is a clock use it, else search the hash tables * for one. */ if (sys_peer != 0 && (sys_peer->flags & FLAG_REFCLOCK)) { peer = sys_peer; } else { peer = 0; for (i = 0; peer == 0 && i < NTP_HASH_SIZE; i++) { for (peer = assoc_hash[i]; peer != 0; peer = peer->ass_next) { if (peer->flags & FLAG_REFCLOCK) break; } } if (peer == 0) { ctl_error(CERR_BADASSOC); return; } } } else { peer = findpeerbyassoc(res_associd); if (peer == 0 || !(peer->flags & FLAG_REFCLOCK)) { ctl_error(CERR_BADASSOC); return; } } /* * If we got here we have a peer which is a clock. Get his * status. */ clock_stat.kv_list = (struct ctl_var *)0; refclock_control(&peer->srcadr, (struct refclockstat *)0, &clock_stat); /* * Look for variables in the packet. */ rpkt.status = htons(ctlclkstatus(&clock_stat)); gotvar = CC_MAXCODE + 1 + count_var(clock_stat.kv_list); wants = (u_char *)emalloc(gotvar); memset((char*)wants, 0, gotvar); gotvar = 0; while ((v = ctl_getitem(clock_var, &valuep)) != 0) { if (v->flags & EOV) { if ((v = ctl_getitem(clock_stat.kv_list, &valuep)) != 0) { if (v->flags & EOV) { ctl_error(CERR_UNKNOWNVAR); free((char*)wants); free_varlist(clock_stat.kv_list); return; } wants[CC_MAXCODE + 1 + v->code] = 1; gotvar = 1; continue; } else { break; /* shouldn't happen ! */ } } wants[v->code] = 1; gotvar = 1; } if (gotvar) { for (i = 1; i <= CC_MAXCODE; i++) if (wants[i]) ctl_putclock(i, &clock_stat, 1); for (i = 0; clock_stat.kv_list && !(clock_stat.kv_list[i].flags & EOV); i++) if (wants[i + CC_MAXCODE + 1]) ctl_putdata(clock_stat.kv_list[i].text, strlen(clock_stat.kv_list[i].text), 0); } else { register u_char *cc; register struct ctl_var *kv; for (cc = def_clock_var; *cc != 0; cc++) ctl_putclock((int)*cc, &clock_stat, 0); for (kv = clock_stat.kv_list; kv && !(kv->flags & EOV); kv++) if (kv->flags & DEF) ctl_putdata(kv->text, strlen(kv->text), 0); } free((char*)wants); free_varlist(clock_stat.kv_list); ctl_flushpkt(0); #endif } /* * write_clock_status - we don't do this */ /*ARGSUSED*/ static void write_clock_status( struct recvbuf *rbufp, int restrict_mask ) { ctl_error(CERR_PERMISSION); } /* * Trap support from here on down. We send async trap messages when the * upper levels report trouble. Traps can by set either by control * messages or by configuration. */ /* * set_trap - set a trap in response to a control message */ static void set_trap( struct recvbuf *rbufp, int restrict_mask ) { int traptype; /* * See if this guy is allowed */ if (restrict_mask & RES_NOTRAP) { ctl_error(CERR_PERMISSION); return; } /* * Determine his allowed trap type. */ traptype = TRAP_TYPE_PRIO; if (restrict_mask & RES_LPTRAP) traptype = TRAP_TYPE_NONPRIO; /* * Call ctlsettrap() to do the work. Return * an error if it can't assign the trap. */ if (!ctlsettrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype, (int)res_version)) ctl_error(CERR_NORESOURCE); ctl_flushpkt(0); } /* * unset_trap - unset a trap in response to a control message */ static void unset_trap( struct recvbuf *rbufp, int restrict_mask ) { int traptype; /* * We don't prevent anyone from removing his own trap unless the * trap is configured. Note we also must be aware of the * possibility that restriction flags were changed since this * guy last set his trap. Set the trap type based on this. */ traptype = TRAP_TYPE_PRIO; if (restrict_mask & RES_LPTRAP) traptype = TRAP_TYPE_NONPRIO; /* * Call ctlclrtrap() to clear this out. */ if (!ctlclrtrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype)) ctl_error(CERR_BADASSOC); ctl_flushpkt(0); } /* * ctlsettrap - called to set a trap */ int ctlsettrap( struct sockaddr_storage *raddr, struct interface *linter, int traptype, int version ) { register struct ctl_trap *tp; register struct ctl_trap *tptouse; /* * See if we can find this trap. If so, we only need update * the flags and the time. */ if ((tp = ctlfindtrap(raddr, linter)) != NULL) { switch (traptype) { case TRAP_TYPE_CONFIG: tp->tr_flags = TRAP_INUSE|TRAP_CONFIGURED; break; case TRAP_TYPE_PRIO: if (tp->tr_flags & TRAP_CONFIGURED) return (1); /* don't change anything */ tp->tr_flags = TRAP_INUSE; break; case TRAP_TYPE_NONPRIO: if (tp->tr_flags & TRAP_CONFIGURED) return (1); /* don't change anything */ tp->tr_flags = TRAP_INUSE|TRAP_NONPRIO; break; } tp->tr_settime = current_time; tp->tr_resets++; return (1); } /* * First we heard of this guy. Try to find a trap structure * for him to use, clearing out lesser priority guys if we * have to. Clear out anyone who's expired while we're at it. */ tptouse = NULL; for (tp = ctl_trap; tp < &ctl_trap[CTL_MAXTRAPS]; tp++) { if ((tp->tr_flags & TRAP_INUSE) && !(tp->tr_flags & TRAP_CONFIGURED) && ((tp->tr_settime + CTL_TRAPTIME) > current_time)) { tp->tr_flags = 0; num_ctl_traps--; } if (!(tp->tr_flags & TRAP_INUSE)) { tptouse = tp; } else if (!(tp->tr_flags & TRAP_CONFIGURED)) { switch (traptype) { case TRAP_TYPE_CONFIG: if (tptouse == NULL) { tptouse = tp; break; } if (tptouse->tr_flags & TRAP_NONPRIO && !(tp->tr_flags & TRAP_NONPRIO)) break; if (!(tptouse->tr_flags & TRAP_NONPRIO) && tp->tr_flags & TRAP_NONPRIO) { tptouse = tp; break; } if (tptouse->tr_origtime < tp->tr_origtime) tptouse = tp; break; case TRAP_TYPE_PRIO: if (tp->tr_flags & TRAP_NONPRIO) { if (tptouse == NULL || (tptouse->tr_flags & TRAP_INUSE && tptouse->tr_origtime < tp->tr_origtime)) tptouse = tp; } break; case TRAP_TYPE_NONPRIO: break; } } } /* * If we don't have room for him return an error. */ if (tptouse == NULL) return (0); /* * Set up this structure for him. */ tptouse->tr_settime = tptouse->tr_origtime = current_time; tptouse->tr_count = tptouse->tr_resets = 0; tptouse->tr_sequence = 1; tptouse->tr_addr = *raddr; tptouse->tr_localaddr = linter; tptouse->tr_version = (u_char) version; tptouse->tr_flags = TRAP_INUSE; if (traptype == TRAP_TYPE_CONFIG) tptouse->tr_flags |= TRAP_CONFIGURED; else if (traptype == TRAP_TYPE_NONPRIO) tptouse->tr_flags |= TRAP_NONPRIO; num_ctl_traps++; return (1); } /* * ctlclrtrap - called to clear a trap */ int ctlclrtrap( struct sockaddr_storage *raddr, struct interface *linter, int traptype ) { register struct ctl_trap *tp; if ((tp = ctlfindtrap(raddr, linter)) == NULL) return (0); if (tp->tr_flags & TRAP_CONFIGURED && traptype != TRAP_TYPE_CONFIG) return (0); tp->tr_flags = 0; num_ctl_traps--; return (1); } /* * ctlfindtrap - find a trap given the remote and local addresses */ static struct ctl_trap * ctlfindtrap( struct sockaddr_storage *raddr, struct interface *linter ) { register struct ctl_trap *tp; for (tp = ctl_trap; tp < &ctl_trap[CTL_MAXTRAPS]; tp++) { if ((tp->tr_flags & TRAP_INUSE) && (NSRCPORT(raddr) == NSRCPORT(&tp->tr_addr)) && SOCKCMP(raddr, &tp->tr_addr) && (linter == tp->tr_localaddr) ) return (tp); } return (struct ctl_trap *)NULL; } /* * report_event - report an event to the trappers */ void report_event( int err, struct peer *peer ) { register int i; /* * Record error code in proper spots, but have mercy on the * log file. */ if (!(err & (PEER_EVENT | CRPT_EVENT))) { if (ctl_sys_num_events < CTL_SYS_MAXEVENTS) ctl_sys_num_events++; if (ctl_sys_last_event != (u_char)err) { NLOG(NLOG_SYSEVENT) msyslog(LOG_INFO, "system event '%s' (0x%02x) status '%s' (0x%02x)", eventstr(err), err, sysstatstr(ctlsysstatus()), ctlsysstatus()); #ifdef DEBUG if (debug) printf("report_event: system event '%s' (0x%02x) status '%s' (0x%02x)\n", eventstr(err), err, sysstatstr(ctlsysstatus()), ctlsysstatus()); #endif ctl_sys_last_event = (u_char)err; } } else if (peer != 0) { char *src; #ifdef REFCLOCK if (ISREFCLOCKADR(&peer->srcadr)) src = refnumtoa(&peer->srcadr); else #endif src = stoa(&peer->srcadr); peer->last_event = (u_char)(err & ~PEER_EVENT); if (peer->num_events < CTL_PEER_MAXEVENTS) peer->num_events++; NLOG(NLOG_PEEREVENT) msyslog(LOG_INFO, "peer %s event '%s' (0x%02x) status '%s' (0x%02x)", src, eventstr(err), err, peerstatstr(ctlpeerstatus(peer)), ctlpeerstatus(peer)); #ifdef DEBUG if (debug) printf( "peer %s event '%s' (0x%02x) status '%s' (0x%02x)\n", src, eventstr(err), err, peerstatstr(ctlpeerstatus(peer)), ctlpeerstatus(peer)); #endif } else { msyslog(LOG_ERR, "report_event: err '%s' (0x%02x), no peer", eventstr(err), err); #ifdef DEBUG printf( "report_event: peer event '%s' (0x%02x), no peer\n", eventstr(err), err); #endif return; } /* * If no trappers, return. */ if (num_ctl_traps <= 0) return; /* * Set up the outgoing packet variables */ res_opcode = CTL_OP_ASYNCMSG; res_offset = 0; res_async = 1; res_authenticate = 0; datapt = rpkt.data; dataend = &(rpkt.data[CTL_MAX_DATA_LEN]); if (!(err & PEER_EVENT)) { rpkt.associd = 0; rpkt.status = htons(ctlsysstatus()); /* * For now, put everything we know about system * variables. Don't send crypto strings. */ for (i = 1; i <= CS_MAXCODE; i++) { #ifdef OPENSSL if (i > CS_VARLIST) continue; #endif /* OPENSSL */ ctl_putsys(i); } #ifdef REFCLOCK /* * for clock exception events: add clock variables to * reflect info on exception */ if (err == EVNT_CLOCKEXCPT) { struct refclockstat clock_stat; struct ctl_var *kv; clock_stat.kv_list = (struct ctl_var *)0; refclock_control(&peer->srcadr, (struct refclockstat *)0, &clock_stat); ctl_puthex("refclockstatus", ctlclkstatus(&clock_stat)); for (i = 1; i <= CC_MAXCODE; i++) ctl_putclock(i, &clock_stat, 0); for (kv = clock_stat.kv_list; kv && !(kv->flags & EOV); kv++) if (kv->flags & DEF) ctl_putdata(kv->text, strlen(kv->text), 0); free_varlist(clock_stat.kv_list); } #endif /* REFCLOCK */ } else { rpkt.associd = htons(peer->associd); rpkt.status = htons(ctlpeerstatus(peer)); /* * Dump it all. Later, maybe less. */ for (i = 1; i <= CP_MAXCODE; i++) { #ifdef OPENSSL if (i > CP_VARLIST) continue; #endif /* OPENSSL */ ctl_putpeer(i, peer); } #ifdef REFCLOCK /* * for clock exception events: add clock variables to * reflect info on exception */ if (err == EVNT_PEERCLOCK) { struct refclockstat clock_stat; struct ctl_var *kv; clock_stat.kv_list = (struct ctl_var *)0; refclock_control(&peer->srcadr, (struct refclockstat *)0, &clock_stat); ctl_puthex("refclockstatus", ctlclkstatus(&clock_stat)); for (i = 1; i <= CC_MAXCODE; i++) ctl_putclock(i, &clock_stat, 0); for (kv = clock_stat.kv_list; kv && !(kv->flags & EOV); kv++) if (kv->flags & DEF) ctl_putdata(kv->text, strlen(kv->text), 0); free_varlist(clock_stat.kv_list); } #endif /* REFCLOCK */ } /* * We're done, return. */ ctl_flushpkt(0); } /* * ctl_clr_stats - clear stat counters */ void ctl_clr_stats(void) { ctltimereset = current_time; numctlreq = 0; numctlbadpkts = 0; numctlresponses = 0; numctlfrags = 0; numctlerrors = 0; numctlfrags = 0; numctltooshort = 0; numctlinputresp = 0; numctlinputfrag = 0; numctlinputerr = 0; numctlbadoffset = 0; numctlbadversion = 0; numctldatatooshort = 0; numctlbadop = 0; numasyncmsgs = 0; } static u_long count_var( struct ctl_var *k ) { register u_long c; if (!k) return (0); c = 0; while (!(k++->flags & EOV)) c++; return (c); } char * add_var( struct ctl_var **kv, u_long size, u_short def ) { register u_long c; register struct ctl_var *k; c = count_var(*kv); k = *kv; *kv = (struct ctl_var *)emalloc((c+2)*sizeof(struct ctl_var)); if (k) { memmove((char *)*kv, (char *)k, sizeof(struct ctl_var)*c); free((char *)k); } (*kv)[c].code = (u_short) c; (*kv)[c].text = (char *)emalloc(size); (*kv)[c].flags = def; (*kv)[c+1].code = 0; (*kv)[c+1].text = (char *)0; (*kv)[c+1].flags = EOV; return (char *)(*kv)[c].text; } void set_var( struct ctl_var **kv, const char *data, u_long size, u_short def ) { register struct ctl_var *k; register const char *s; register const char *t; char *td; if (!data || !size) return; k = *kv; if (k != NULL) { while (!(k->flags & EOV)) { s = data; t = k->text; if (t) { while (*t != '=' && *s - *t == 0) { s++; t++; } if (*s == *t && ((*t == '=') || !*t)) { free((void *)k->text); td = (char *)emalloc(size); memmove(td, data, size); k->text =td; k->flags = def; return; } } else { td = (char *)emalloc(size); memmove(td, data, size); k->text = td; k->flags = def; return; } k++; } } td = add_var(kv, size, def); memmove(td, data, size); } void set_sys_var( const char *data, u_long size, u_short def ) { set_var(&ext_sys_var, data, size, def); } void free_varlist( struct ctl_var *kv ) { struct ctl_var *k; if (kv) { for (k = kv; !(k->flags & EOV); k++) free((void *)k->text); free((void *)kv); } } Index: releng/10.1/contrib/ntp/ntpd/ntp_crypto.c =================================================================== --- releng/10.1/contrib/ntp/ntpd/ntp_crypto.c (revision 276158) +++ releng/10.1/contrib/ntp/ntpd/ntp_crypto.c (revision 276159) @@ -1,4189 +1,4201 @@ /* * ntp_crypto.c - NTP version 4 public key routines */ #ifdef HAVE_CONFIG_H #include #endif #ifdef OPENSSL #include #include #include #include #include #include "ntpd.h" #include "ntp_stdlib.h" #include "ntp_unixtime.h" #include "ntp_string.h" #include #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" #ifdef KERNEL_PLL #include "ntp_syscall.h" #endif /* KERNEL_PLL */ /* * Extension field message format * * These are always signed and saved before sending in network byte * order. They must be converted to and from host byte order for * processing. * * +-------+-------+ * | op | len | <- extension pointer * +-------+-------+ * | assocID | * +---------------+ * | timestamp | <- value pointer * +---------------+ * | filestamp | * +---------------+ * | value len | * +---------------+ * | | * = value = * | | * +---------------+ * | signature len | * +---------------+ * | | * = signature = * | | * +---------------+ * * The CRYPTO_RESP bit is set to 0 for requests, 1 for responses. * Requests carry the association ID of the receiver; responses carry * the association ID of the sender. Some messages include only the * operation/length and association ID words and so have length 8 * octets. Ohers include the value structure and associated value and * signature fields. These messages include the timestamp, filestamp, * value and signature words and so have length at least 24 octets. The * signature and/or value fields can be empty, in which case the * respective length words are zero. An empty value with nonempty * signature is syntactically valid, but semantically questionable. * * The filestamp represents the time when a cryptographic data file such * as a public/private key pair is created. It follows every reference * depending on that file and serves as a means to obsolete earlier data * of the same type. The timestamp represents the time when the * cryptographic data of the message were last signed. Creation of a * cryptographic data file or signing a message can occur only when the * creator or signor is synchronized to an authoritative source and * proventicated to a trusted authority. * * Note there are four conditions required for server trust. First, the * public key on the certificate must be verified, which involves a * number of format, content and consistency checks. Next, the server * identity must be confirmed by one of four schemes: private * certificate, IFF scheme, GQ scheme or certificate trail hike to a * self signed trusted certificate. Finally, the server signature must * be verified. */ /* * Cryptodefines */ #define TAI_1972 10 /* initial TAI offset (s) */ #define MAX_LEAP 100 /* max UTC leapseconds (s) */ #define VALUE_LEN (6 * 4) /* min response field length */ #define YEAR (60 * 60 * 24 * 365) /* seconds in year */ /* * Global cryptodata in host byte order */ u_int32 crypto_flags = 0x0; /* status word */ /* * Global cryptodata in network byte order */ struct cert_info *cinfo = NULL; /* certificate info/value */ struct value hostval; /* host value */ struct value pubkey; /* public key */ struct value tai_leap; /* leapseconds table */ EVP_PKEY *iffpar_pkey = NULL; /* IFF parameters */ EVP_PKEY *gqpar_pkey = NULL; /* GQ parameters */ EVP_PKEY *mvpar_pkey = NULL; /* MV parameters */ char *iffpar_file = NULL; /* IFF parameters file */ char *gqpar_file = NULL; /* GQ parameters file */ char *mvpar_file = NULL; /* MV parameters file */ /* * Private cryptodata in host byte order */ static char *passwd = NULL; /* private key password */ static EVP_PKEY *host_pkey = NULL; /* host key */ static EVP_PKEY *sign_pkey = NULL; /* sign key */ static const EVP_MD *sign_digest = NULL; /* sign digest */ static u_int sign_siglen; /* sign key length */ static char *rand_file = NULL; /* random seed file */ static char *host_file = NULL; /* host key file */ static char *sign_file = NULL; /* sign key file */ static char *cert_file = NULL; /* certificate file */ static char *leap_file = NULL; /* leapseconds file */ static tstamp_t if_fstamp = 0; /* IFF filestamp */ static tstamp_t gq_fstamp = 0; /* GQ file stamp */ static tstamp_t mv_fstamp = 0; /* MV filestamp */ static u_int ident_scheme = 0; /* server identity scheme */ /* * Cryptotypes */ static int crypto_verify P((struct exten *, struct value *, struct peer *)); static int crypto_encrypt P((struct exten *, struct value *, keyid_t *)); static int crypto_alice P((struct peer *, struct value *)); static int crypto_alice2 P((struct peer *, struct value *)); static int crypto_alice3 P((struct peer *, struct value *)); static int crypto_bob P((struct exten *, struct value *)); static int crypto_bob2 P((struct exten *, struct value *)); static int crypto_bob3 P((struct exten *, struct value *)); static int crypto_iff P((struct exten *, struct peer *)); static int crypto_gq P((struct exten *, struct peer *)); static int crypto_mv P((struct exten *, struct peer *)); static u_int crypto_send P((struct exten *, struct value *)); static tstamp_t crypto_time P((void)); static u_long asn2ntp P((ASN1_TIME *)); static struct cert_info *cert_parse P((u_char *, u_int, tstamp_t)); static int cert_sign P((struct exten *, struct value *)); static int cert_valid P((struct cert_info *, EVP_PKEY *)); static int cert_install P((struct exten *, struct peer *)); static void cert_free P((struct cert_info *)); static EVP_PKEY *crypto_key P((char *, tstamp_t *)); static int bighash P((BIGNUM *, BIGNUM *)); static struct cert_info *crypto_cert P((char *)); static void crypto_tai P((char *)); #ifdef SYS_WINNT int readlink(char * link, char * file, int len) { return (-1); } #endif /* * session_key - generate session key * * This routine generates a session key from the source address, * destination address, key ID and private value. The value of the * session key is the MD5 hash of these values, while the next key ID is * the first four octets of the hash. * * Returns the next key ID */ keyid_t session_key( struct sockaddr_storage *srcadr, /* source address */ struct sockaddr_storage *dstadr, /* destination address */ keyid_t keyno, /* key ID */ keyid_t private, /* private value */ u_long lifetime /* key lifetime */ ) { EVP_MD_CTX ctx; /* message digest context */ u_char dgst[EVP_MAX_MD_SIZE]; /* message digest */ keyid_t keyid; /* key identifer */ u_int32 header[10]; /* data in network byte order */ u_int hdlen, len; if (!dstadr) return 0; /* * Generate the session key and key ID. If the lifetime is * greater than zero, install the key and call it trusted. */ hdlen = 0; switch(srcadr->ss_family) { case AF_INET: header[0] = ((struct sockaddr_in *)srcadr)->sin_addr.s_addr; header[1] = ((struct sockaddr_in *)dstadr)->sin_addr.s_addr; header[2] = htonl(keyno); header[3] = htonl(private); hdlen = 4 * sizeof(u_int32); break; case AF_INET6: memcpy(&header[0], &GET_INADDR6(*srcadr), sizeof(struct in6_addr)); memcpy(&header[4], &GET_INADDR6(*dstadr), sizeof(struct in6_addr)); header[8] = htonl(keyno); header[9] = htonl(private); hdlen = 10 * sizeof(u_int32); break; } EVP_DigestInit(&ctx, EVP_md5()); EVP_DigestUpdate(&ctx, (u_char *)header, hdlen); EVP_DigestFinal(&ctx, dgst, &len); memcpy(&keyid, dgst, 4); keyid = ntohl(keyid); if (lifetime != 0) { MD5auth_setkey(keyno, dgst, len); authtrust(keyno, lifetime); } #ifdef DEBUG if (debug > 1) printf( "session_key: %s > %s %08x %08x hash %08x life %lu\n", stoa(srcadr), stoa(dstadr), keyno, private, keyid, lifetime); #endif return (keyid); } /* * make_keylist - generate key list * * Returns * XEVNT_OK success * XEVNT_PER host certificate expired * * This routine constructs a pseudo-random sequence by repeatedly * hashing the session key starting from a given source address, * destination address, private value and the next key ID of the * preceeding session key. The last entry on the list is saved along * with its sequence number and public signature. */ int make_keylist( struct peer *peer, /* peer structure pointer */ struct interface *dstadr /* interface */ ) { EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; /* NTP timestamp */ struct autokey *ap; /* autokey pointer */ struct value *vp; /* value pointer */ keyid_t keyid = 0; /* next key ID */ keyid_t cookie; /* private value */ u_long lifetime; u_int len, mpoll; int i; if (!dstadr) return XEVNT_OK; /* * Allocate the key list if necessary. */ tstamp = crypto_time(); if (peer->keylist == NULL) peer->keylist = emalloc(sizeof(keyid_t) * NTP_MAXSESSION); /* * Generate an initial key ID which is unique and greater than * NTP_MAXKEY. */ while (1) { keyid = (ntp_random() + NTP_MAXKEY + 1) & ((1 << sizeof(keyid_t)) - 1); if (authhavekey(keyid)) continue; break; } /* * Generate up to NTP_MAXSESSION session keys. Stop if the * next one would not be unique or not a session key ID or if * it would expire before the next poll. The private value * included in the hash is zero if broadcast mode, the peer * cookie if client mode or the host cookie if symmetric modes. */ mpoll = 1 << min(peer->ppoll, peer->hpoll); lifetime = min(sys_automax, NTP_MAXSESSION * mpoll); if (peer->hmode == MODE_BROADCAST) cookie = 0; else cookie = peer->pcookie; for (i = 0; i < NTP_MAXSESSION; i++) { peer->keylist[i] = keyid; peer->keynumber = i; keyid = session_key(&dstadr->sin, &peer->srcadr, keyid, cookie, lifetime); lifetime -= mpoll; if (auth_havekey(keyid) || keyid <= NTP_MAXKEY || lifetime <= mpoll) break; } /* * Save the last session key ID, sequence number and timestamp, * then sign these values for later retrieval by the clients. Be * careful not to use invalid key media. Use the public values * timestamp as filestamp. */ vp = &peer->sndval; if (vp->ptr == NULL) vp->ptr = emalloc(sizeof(struct autokey)); ap = (struct autokey *)vp->ptr; ap->seq = htonl(peer->keynumber); ap->key = htonl(keyid); vp->tstamp = htonl(tstamp); vp->fstamp = hostval.tstamp; vp->vallen = htonl(sizeof(struct autokey)); vp->siglen = 0; if (tstamp != 0) { if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); if (vp->sig == NULL) vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)vp, 12); EVP_SignUpdate(&ctx, vp->ptr, sizeof(struct autokey)); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); else msyslog(LOG_ERR, "make_keys %s\n", ERR_error_string(ERR_get_error(), NULL)); peer->flags |= FLAG_ASSOC; } #ifdef DEBUG if (debug) printf("make_keys: %d %08x %08x ts %u fs %u poll %d\n", ntohl(ap->seq), ntohl(ap->key), cookie, ntohl(vp->tstamp), ntohl(vp->fstamp), peer->hpoll); #endif return (XEVNT_OK); } /* * crypto_recv - parse extension fields * * This routine is called when the packet has been matched to an * association and passed sanity, format and MAC checks. We believe the * extension field values only if the field has proper format and * length, the timestamp and filestamp are valid and the signature has * valid length and is verified. There are a few cases where some values * are believed even if the signature fails, but only if the proventic * bit is not set. */ int crypto_recv( struct peer *peer, /* peer structure pointer */ struct recvbuf *rbufp /* packet buffer pointer */ ) { const EVP_MD *dp; /* message digest algorithm */ u_int32 *pkt; /* receive packet pointer */ struct autokey *ap, *bp; /* autokey pointer */ struct exten *ep, *fp; /* extension pointers */ int has_mac; /* length of MAC field */ int authlen; /* offset of MAC field */ associd_t associd; /* association ID */ tstamp_t tstamp = 0; /* timestamp */ tstamp_t fstamp = 0; /* filestamp */ u_int len; /* extension field length */ u_int code; /* extension field opcode */ u_int vallen = 0; /* value length */ X509 *cert; /* X509 certificate */ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */ keyid_t cookie; /* crumbles */ int hismode; /* packet mode */ int rval = XEVNT_OK; u_char *ptr; u_int32 temp32; /* * Initialize. Note that the packet has already been checked for * valid format and extension field lengths. First extract the * field length, command code and association ID in host byte * order. These are used with all commands and modes. Then check * the version number, which must be 2, and length, which must * be at least 8 for requests and VALUE_LEN (24) for responses. * Packets that fail either test sink without a trace. The * association ID is saved only if nonzero. */ authlen = LEN_PKT_NOMAC; hismode = (int)PKT_MODE((&rbufp->recv_pkt)->li_vn_mode); while ((has_mac = rbufp->recv_length - authlen) > MAX_MAC_LEN) { pkt = (u_int32 *)&rbufp->recv_pkt + authlen / 4; ep = (struct exten *)pkt; code = ntohl(ep->opcode) & 0xffff0000; len = ntohl(ep->opcode) & 0x0000ffff; associd = (associd_t) ntohl(pkt[1]); rval = XEVNT_OK; #ifdef DEBUG if (debug) printf( "crypto_recv: flags 0x%x ext offset %d len %u code 0x%x assocID %d\n", peer->crypto, authlen, len, code >> 16, associd); #endif /* * Check version number and field length. If bad, * quietly ignore the packet. */ if (((code >> 24) & 0x3f) != CRYPTO_VN || len < 8) { sys_unknownversion++; code |= CRYPTO_ERROR; } /* * Little vulnerability bandage here. If a perp tosses a * fake association ID over the fence, we better toss it * out. Only the first one counts. */ if (code & CRYPTO_RESP) { if (peer->assoc == 0) peer->assoc = associd; else if (peer->assoc != associd) code |= CRYPTO_ERROR; } if (len >= VALUE_LEN) { tstamp = ntohl(ep->tstamp); fstamp = ntohl(ep->fstamp); vallen = ntohl(ep->vallen); } switch (code) { /* * Install status word, host name, signature scheme and * association ID. In OpenSSL the signature algorithm is * bound to the digest algorithm, so the NID completely * defines the signature scheme. Note the request and * response are identical, but neither is validated by * signature. The request is processed here only in * symmetric modes. The server name field might be * useful to implement access controls in future. */ case CRYPTO_ASSOC: /* * If the machine is running when this message * arrives, the other fellow has reset and so * must we. Otherwise, pass the extension field * to the transmit side. */ if (peer->crypto) { rval = XEVNT_ERR; break; } fp = emalloc(len); memcpy(fp, ep, len); temp32 = CRYPTO_RESP; fp->opcode |= htonl(temp32); peer->cmmd = fp; /* fall through */ case CRYPTO_ASSOC | CRYPTO_RESP: /* * Discard the message if it has already been * stored or the message has been amputated. */ if (peer->crypto) break; if (vallen == 0 || vallen > MAXHOSTNAME || len < VALUE_LEN + vallen) { rval = XEVNT_LEN; break; } /* * Check the identity schemes are compatible. If * the client has PC, the server must have PC, * in which case the server public key and * identity are presumed valid, so we skip the * certificate and identity exchanges and move * immediately to the cookie exchange which * confirms the server signature. */ #ifdef DEBUG if (debug) printf( "crypto_recv: ident host 0x%x server 0x%x\n", crypto_flags, fstamp); #endif temp32 = (crypto_flags | ident_scheme) & fstamp & CRYPTO_FLAG_MASK; if (crypto_flags & CRYPTO_FLAG_PRIV) { if (!(fstamp & CRYPTO_FLAG_PRIV)) { rval = XEVNT_KEY; break; } else { fstamp |= CRYPTO_FLAG_VALID | CRYPTO_FLAG_VRFY | CRYPTO_FLAG_SIGN; } /* * In symmetric modes it is an error if either * peer requests identity and the other peer * does not support it. */ } else if ((hismode == MODE_ACTIVE || hismode == MODE_PASSIVE) && ((crypto_flags | fstamp) & CRYPTO_FLAG_MASK) && !temp32) { rval = XEVNT_KEY; break; /* * It is an error if the client requests * identity and the server does not support it. */ } else if (hismode == MODE_CLIENT && (fstamp & CRYPTO_FLAG_MASK) && !temp32) { rval = XEVNT_KEY; break; } /* * Otherwise, the identity scheme(s) are those * that both client and server support. */ fstamp = temp32 | (fstamp & ~CRYPTO_FLAG_MASK); /* * Discard the message if the signature digest * NID is not supported. */ temp32 = (fstamp >> 16) & 0xffff; dp = (const EVP_MD *)EVP_get_digestbynid(temp32); if (dp == NULL) { rval = XEVNT_MD; break; } /* * Save status word, host name and message * digest/signature type. */ peer->crypto = fstamp; peer->digest = dp; peer->subject = emalloc(vallen + 1); memcpy(peer->subject, ep->pkt, vallen); peer->subject[vallen] = '\0'; peer->issuer = emalloc(vallen + 1); strcpy(peer->issuer, peer->subject); temp32 = (fstamp >> 16) & 0xffff; snprintf(statstr, NTP_MAXSTRLEN, "flags 0x%x host %s signature %s", fstamp, peer->subject, OBJ_nid2ln(temp32)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * Decode X509 certificate in ASN.1 format and extract * the data containing, among other things, subject * name and public key. In the default identification * scheme, the certificate trail is followed to a self * signed trusted certificate. */ case CRYPTO_CERT | CRYPTO_RESP: /* * Discard the message if invalid. */ if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; /* * Scan the certificate list to delete old * versions and link the newest version first on * the list. */ if ((rval = cert_install(ep, peer)) != XEVNT_OK) break; /* * If we snatch the certificate before the * server certificate has been signed by its * server, it will be self signed. When it is, * we chase the certificate issuer, which the * server has, and keep going until a self * signed trusted certificate is found. Be sure * to update the issuer field, since it may * change. */ if (peer->issuer != NULL) free(peer->issuer); peer->issuer = emalloc(strlen(cinfo->issuer) + 1); strcpy(peer->issuer, cinfo->issuer); /* * We plug in the public key and lifetime from * the first certificate received. However, note * that this certificate might not be signed by * the server, so we can't check the * signature/digest NID. */ if (peer->pkey == NULL) { ptr = (u_char *)cinfo->cert.ptr; cert = d2i_X509(NULL, &ptr, ntohl(cinfo->cert.vallen)); peer->pkey = X509_get_pubkey(cert); X509_free(cert); } peer->flash &= ~TEST8; temp32 = cinfo->nid; snprintf(statstr, NTP_MAXSTRLEN, "cert %s 0x%x %s (%u) fs %u", cinfo->subject, cinfo->flags, OBJ_nid2ln(temp32), temp32, ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * Schnorr (IFF)identity scheme. This scheme is designed * for use with shared secret group keys and where the * certificate may be generated by a third party. The * client sends a challenge to the server, which * performs a calculation and returns the result. A * positive result is possible only if both client and * server contain the same secret group key. */ case CRYPTO_IFF | CRYPTO_RESP: /* * Discard the message if invalid or certificate * trail not trusted. */ if (!(peer->crypto & CRYPTO_FLAG_VALID)) { rval = XEVNT_ERR; break; } if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; /* * If the the challenge matches the response, * the certificate public key, as well as the * server public key, signatyre and identity are * all verified at the same time. The server is * declared trusted, so we skip further * certificate stages and move immediately to * the cookie stage. */ if ((rval = crypto_iff(ep, peer)) != XEVNT_OK) break; peer->crypto |= CRYPTO_FLAG_VRFY | CRYPTO_FLAG_PROV; peer->flash &= ~TEST8; snprintf(statstr, NTP_MAXSTRLEN, "iff fs %u", ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * Guillou-Quisquater (GQ) identity scheme. This scheme * is designed for use with public certificates carrying * the GQ public key in an extension field. The client * sends a challenge to the server, which performs a * calculation and returns the result. A positive result * is possible only if both client and server contain * the same group key and the server has the matching GQ * private key. */ case CRYPTO_GQ | CRYPTO_RESP: /* * Discard the message if invalid or certificate * trail not trusted. */ if (!(peer->crypto & CRYPTO_FLAG_VALID)) { rval = XEVNT_ERR; break; } if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; /* * If the the challenge matches the response, * the certificate public key, as well as the * server public key, signatyre and identity are * all verified at the same time. The server is * declared trusted, so we skip further * certificate stages and move immediately to * the cookie stage. */ if ((rval = crypto_gq(ep, peer)) != XEVNT_OK) break; peer->crypto |= CRYPTO_FLAG_VRFY | CRYPTO_FLAG_PROV; peer->flash &= ~TEST8; snprintf(statstr, NTP_MAXSTRLEN, "gq fs %u", ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * MV */ case CRYPTO_MV | CRYPTO_RESP: /* * Discard the message if invalid or certificate * trail not trusted. */ if (!(peer->crypto & CRYPTO_FLAG_VALID)) { rval = XEVNT_ERR; break; } if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; /* * If the the challenge matches the response, * the certificate public key, as well as the * server public key, signatyre and identity are * all verified at the same time. The server is * declared trusted, so we skip further * certificate stages and move immediately to * the cookie stage. */ if ((rval = crypto_mv(ep, peer)) != XEVNT_OK) break; peer->crypto |= CRYPTO_FLAG_VRFY | CRYPTO_FLAG_PROV; peer->flash &= ~TEST8; snprintf(statstr, NTP_MAXSTRLEN, "mv fs %u", ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * Cookie request in symmetric modes. Roll a random * cookie and install in symmetric mode. Encrypt for the * response, which is transmitted later. */ case CRYPTO_COOK: /* * Discard the message if invalid or certificate * trail not trusted. */ if (!(peer->crypto & CRYPTO_FLAG_VALID)) { rval = XEVNT_ERR; break; } if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; /* * Pass the extension field to the transmit * side. If already agreed, walk away. */ fp = emalloc(len); memcpy(fp, ep, len); temp32 = CRYPTO_RESP; fp->opcode |= htonl(temp32); peer->cmmd = fp; if (peer->crypto & CRYPTO_FLAG_AGREE) { peer->flash &= ~TEST8; break; } /* * Install cookie values and light the cookie * bit. The transmit side will pick up and * encrypt it for the response. */ key_expire(peer); peer->cookval.tstamp = ep->tstamp; peer->cookval.fstamp = ep->fstamp; RAND_bytes((u_char *)&peer->pcookie, 4); peer->crypto &= ~CRYPTO_FLAG_AUTO; peer->crypto |= CRYPTO_FLAG_AGREE; peer->flash &= ~TEST8; snprintf(statstr, NTP_MAXSTRLEN, "cook %x ts %u fs %u", peer->pcookie, ntohl(ep->tstamp), ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * Cookie response in client and symmetric modes. If the * cookie bit is set, the working cookie is the EXOR of * the current and new values. */ case CRYPTO_COOK | CRYPTO_RESP: /* * Discard the message if invalid or identity * not confirmed or signature not verified with * respect to the cookie values. */ if (!(peer->crypto & CRYPTO_FLAG_VRFY)) { rval = XEVNT_ERR; break; } if ((rval = crypto_verify(ep, &peer->cookval, peer)) != XEVNT_OK) break; /* * Decrypt the cookie, hunting all the time for * errors. */ if (vallen == (u_int) EVP_PKEY_size(host_pkey)) { - RSA_private_decrypt(vallen, + u_int32 *cookiebuf = malloc( + RSA_size(host_pkey->pkey.rsa)); + if (cookiebuf == NULL) { + rval = XEVNT_CKY; + break; + } + if (RSA_private_decrypt(vallen, (u_char *)ep->pkt, - (u_char *)&temp32, + (u_char *)cookiebuf, host_pkey->pkey.rsa, - RSA_PKCS1_OAEP_PADDING); - cookie = ntohl(temp32); + RSA_PKCS1_OAEP_PADDING) != 4) { + rval = XEVNT_CKY; + free(cookiebuf); + break; + } else { + cookie = ntohl(*cookiebuf); + free(cookiebuf); + } } else { rval = XEVNT_CKY; break; } /* * Install cookie values and light the cookie * bit. If this is not broadcast client mode, we * are done here. */ key_expire(peer); peer->cookval.tstamp = ep->tstamp; peer->cookval.fstamp = ep->fstamp; if (peer->crypto & CRYPTO_FLAG_AGREE) peer->pcookie ^= cookie; else peer->pcookie = cookie; if (peer->hmode == MODE_CLIENT && !(peer->cast_flags & MDF_BCLNT)) peer->crypto |= CRYPTO_FLAG_AUTO; else peer->crypto &= ~CRYPTO_FLAG_AUTO; peer->crypto |= CRYPTO_FLAG_AGREE; peer->flash &= ~TEST8; snprintf(statstr, NTP_MAXSTRLEN, "cook %x ts %u fs %u", peer->pcookie, ntohl(ep->tstamp), ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * Install autokey values in broadcast client and * symmetric modes. We have to do this every time the * sever/peer cookie changes or a new keylist is * rolled. Ordinarily, this is automatic as this message * is piggybacked on the first NTP packet sent upon * either of these events. Note that a broadcast client * or symmetric peer can receive this response without a * matching request. */ case CRYPTO_AUTO | CRYPTO_RESP: /* * Discard the message if invalid or identity * not confirmed or signature not verified with * respect to the receive autokey values. */ if (!(peer->crypto & CRYPTO_FLAG_VRFY)) { rval = XEVNT_ERR; break; } if ((rval = crypto_verify(ep, &peer->recval, peer)) != XEVNT_OK) break; /* * Install autokey values and light the * autokey bit. This is not hard. */ if (peer->recval.ptr == NULL) peer->recval.ptr = emalloc(sizeof(struct autokey)); bp = (struct autokey *)peer->recval.ptr; peer->recval.tstamp = ep->tstamp; peer->recval.fstamp = ep->fstamp; ap = (struct autokey *)ep->pkt; bp->seq = ntohl(ap->seq); bp->key = ntohl(ap->key); peer->pkeyid = bp->key; peer->crypto |= CRYPTO_FLAG_AUTO; peer->flash &= ~TEST8; snprintf(statstr, NTP_MAXSTRLEN, "auto seq %d key %x ts %u fs %u", bp->seq, bp->key, ntohl(ep->tstamp), ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * X509 certificate sign response. Validate the * certificate signed by the server and install. Later * this can be provided to clients of this server in * lieu of the self signed certificate in order to * validate the public key. */ case CRYPTO_SIGN | CRYPTO_RESP: /* * Discard the message if invalid or not * proventic. */ if (!(peer->crypto & CRYPTO_FLAG_PROV)) { rval = XEVNT_ERR; break; } if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; /* * Scan the certificate list to delete old * versions and link the newest version first on * the list. */ if ((rval = cert_install(ep, peer)) != XEVNT_OK) break; peer->crypto |= CRYPTO_FLAG_SIGN; peer->flash &= ~TEST8; temp32 = cinfo->nid; snprintf(statstr, NTP_MAXSTRLEN, "sign %s 0x%x %s (%u) fs %u", cinfo->issuer, cinfo->flags, OBJ_nid2ln(temp32), temp32, ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * Install leapseconds table in symmetric modes. This * table is proventicated to the NIST primary servers, * either by copying the file containing the table from * a NIST server to a trusted server or directly using * this protocol. While the entire table is installed at * the server, presently only the current TAI offset is * provided via the kernel to other applications. */ case CRYPTO_TAI: /* * Discard the message if invalid. */ if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; /* * Pass the extension field to the transmit * side. Continue below if a leapseconds table * accompanies the message. */ fp = emalloc(len); memcpy(fp, ep, len); temp32 = CRYPTO_RESP; fp->opcode |= htonl(temp32); peer->cmmd = fp; if (len <= VALUE_LEN) { peer->flash &= ~TEST8; break; } /* fall through */ case CRYPTO_TAI | CRYPTO_RESP: /* * If this is a response, discard the message if * signature not verified with respect to the * leapsecond table values. */ if (peer->cmmd == NULL) { if ((rval = crypto_verify(ep, &peer->tai_leap, peer)) != XEVNT_OK) break; } /* * Initialize peer variables with latest update. */ peer->tai_leap.tstamp = ep->tstamp; peer->tai_leap.fstamp = ep->fstamp; peer->tai_leap.vallen = ep->vallen; /* * Install the new table if there is no stored * table or the new table is more recent than * the stored table. Since a filestamp may have * changed, recompute the signatures. */ if (ntohl(peer->tai_leap.fstamp) > ntohl(tai_leap.fstamp)) { tai_leap.fstamp = ep->fstamp; tai_leap.vallen = ep->vallen; if (tai_leap.ptr != NULL) free(tai_leap.ptr); tai_leap.ptr = emalloc(vallen); memcpy(tai_leap.ptr, ep->pkt, vallen); crypto_update(); } crypto_flags |= CRYPTO_FLAG_TAI; peer->crypto |= CRYPTO_FLAG_LEAP; peer->flash &= ~TEST8; snprintf(statstr, NTP_MAXSTRLEN, "leap %u ts %u fs %u", vallen, ntohl(ep->tstamp), ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; /* * We come here in symmetric modes for miscellaneous * commands that have value fields but are processed on * the transmit side. All we need do here is check for * valid field length. Remaining checks are below and on * the transmit side. */ case CRYPTO_CERT: case CRYPTO_IFF: case CRYPTO_GQ: case CRYPTO_MV: case CRYPTO_SIGN: if (len < VALUE_LEN) { rval = XEVNT_LEN; break; } /* fall through */ /* * We come here for miscellaneous requests and unknown * requests and responses. If an unknown response or * error, forget it. If a request, save the extension * field for later. Unknown requests will be caught on * the transmit side. */ default: if (code & (CRYPTO_RESP | CRYPTO_ERROR)) { rval = XEVNT_ERR; } else if ((rval = crypto_verify(ep, NULL, peer)) == XEVNT_OK) { fp = emalloc(len); memcpy(fp, ep, len); temp32 = CRYPTO_RESP; fp->opcode |= htonl(temp32); peer->cmmd = fp; } } /* * We don't log length/format/timestamp errors and * duplicates, which are log clogging vulnerabilities. * The first error found terminates the extension field * scan and we return the laundry to the caller. A * length/format/timestamp error on transmit is * cheerfully ignored, as the message is not sent. */ if (rval > XEVNT_TSP) { snprintf(statstr, NTP_MAXSTRLEN, "error %x opcode %x ts %u fs %u", rval, code, tstamp, fstamp); record_crypto_stats(&peer->srcadr, statstr); report_event(rval, peer); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; } else if (rval > XEVNT_OK && (code & CRYPTO_RESP)) { rval = XEVNT_OK; } authlen += len; } return (rval); } /* * crypto_xmit - construct extension fields * * This routine is called both when an association is configured and * when one is not. The only case where this matters is to retrieve the * autokey information, in which case the caller has to provide the * association ID to match the association. * * Returns length of extension field. */ int crypto_xmit( struct pkt *xpkt, /* transmit packet pointer */ struct sockaddr_storage *srcadr_sin, /* active runway */ int start, /* offset to extension field */ struct exten *ep, /* extension pointer */ keyid_t cookie /* session cookie */ ) { u_int32 *pkt; /* packet pointer */ struct peer *peer; /* peer structure pointer */ u_int opcode; /* extension field opcode */ struct exten *fp; /* extension pointers */ struct cert_info *cp, *xp; /* certificate info/value pointer */ char certname[MAXHOSTNAME + 1]; /* subject name buffer */ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */ tstamp_t tstamp; u_int vallen; u_int len; struct value vtemp; associd_t associd; int rval; keyid_t tcookie; /* * Generate the requested extension field request code, length * and association ID. If this is a response and the host is not * synchronized, light the error bit and go home. */ pkt = (u_int32 *)xpkt + start / 4; fp = (struct exten *)pkt; opcode = ntohl(ep->opcode); associd = (associd_t) ntohl(ep->associd); fp->associd = htonl(associd); len = 8; rval = XEVNT_OK; tstamp = crypto_time(); switch (opcode & 0xffff0000) { /* * Send association request and response with status word and * host name. Note, this message is not signed and the filestamp * contains only the status word. */ case CRYPTO_ASSOC | CRYPTO_RESP: len += crypto_send(fp, &hostval); fp->fstamp = htonl(crypto_flags); break; case CRYPTO_ASSOC: len += crypto_send(fp, &hostval); fp->fstamp = htonl(crypto_flags | ident_scheme); break; /* * Send certificate request. Use the values from the extension * field. */ case CRYPTO_CERT: memset(&vtemp, 0, sizeof(vtemp)); vtemp.tstamp = ep->tstamp; vtemp.fstamp = ep->fstamp; vtemp.vallen = ep->vallen; vtemp.ptr = (u_char *)ep->pkt; len += crypto_send(fp, &vtemp); break; /* * Send certificate response or sign request. Use the values * from the certificate cache. If the request contains no * subject name, assume the name of this host. This is for * backwards compatibility. Private certificates are never sent. */ case CRYPTO_SIGN: case CRYPTO_CERT | CRYPTO_RESP: vallen = ntohl(ep->vallen); if (vallen == 8) { strcpy(certname, sys_hostname); } else if (vallen == 0 || vallen > MAXHOSTNAME) { rval = XEVNT_LEN; break; } else { memcpy(certname, ep->pkt, vallen); certname[vallen] = '\0'; } /* * Find all certificates with matching subject. If a * self-signed, trusted certificate is found, use that. * If not, use the first one with matching subject. A * private certificate is never divulged or signed. */ xp = NULL; for (cp = cinfo; cp != NULL; cp = cp->link) { if (cp->flags & CERT_PRIV) continue; if (strcmp(certname, cp->subject) == 0) { if (xp == NULL) xp = cp; if (strcmp(certname, cp->issuer) == 0 && cp->flags & CERT_TRUST) { xp = cp; break; } } } /* * Be careful who you trust. If not yet synchronized, * give back an empty response. If certificate not found * or beyond the lifetime, return an error. This is to * avoid a bad dude trying to get an expired certificate * re-signed. Otherwise, send it. * * Note the timestamp and filestamp are taken from the * certificate value structure. For all certificates the * timestamp is the latest signature update time. For * host and imported certificates the filestamp is the * creation epoch. For signed certificates the filestamp * is the creation epoch of the trusted certificate at * the base of the certificate trail. In principle, this * allows strong checking for signature masquerade. */ if (tstamp == 0) break; if (xp == NULL) rval = XEVNT_CRT; else if (tstamp < xp->first || tstamp > xp->last) rval = XEVNT_SRV; else len += crypto_send(fp, &xp->cert); break; /* * Send challenge in Schnorr (IFF) identity scheme. */ case CRYPTO_IFF: if ((peer = findpeerbyassoc(ep->pkt[0])) == NULL) { rval = XEVNT_ERR; break; } if ((rval = crypto_alice(peer, &vtemp)) == XEVNT_OK) { len += crypto_send(fp, &vtemp); value_free(&vtemp); } break; /* * Send response in Schnorr (IFF) identity scheme. */ case CRYPTO_IFF | CRYPTO_RESP: if ((rval = crypto_bob(ep, &vtemp)) == XEVNT_OK) { len += crypto_send(fp, &vtemp); value_free(&vtemp); } break; /* * Send challenge in Guillou-Quisquater (GQ) identity scheme. */ case CRYPTO_GQ: if ((peer = findpeerbyassoc(ep->pkt[0])) == NULL) { rval = XEVNT_ERR; break; } if ((rval = crypto_alice2(peer, &vtemp)) == XEVNT_OK) { len += crypto_send(fp, &vtemp); value_free(&vtemp); } break; /* * Send response in Guillou-Quisquater (GQ) identity scheme. */ case CRYPTO_GQ | CRYPTO_RESP: if ((rval = crypto_bob2(ep, &vtemp)) == XEVNT_OK) { len += crypto_send(fp, &vtemp); value_free(&vtemp); } break; /* * Send challenge in MV identity scheme. */ case CRYPTO_MV: if ((peer = findpeerbyassoc(ep->pkt[0])) == NULL) { rval = XEVNT_ERR; break; } if ((rval = crypto_alice3(peer, &vtemp)) == XEVNT_OK) { len += crypto_send(fp, &vtemp); value_free(&vtemp); } break; /* * Send response in MV identity scheme. */ case CRYPTO_MV | CRYPTO_RESP: if ((rval = crypto_bob3(ep, &vtemp)) == XEVNT_OK) { len += crypto_send(fp, &vtemp); value_free(&vtemp); } break; /* * Send certificate sign response. The integrity of the request * certificate has already been verified on the receive side. * Sign the response using the local server key. Use the * filestamp from the request and use the timestamp as the * current time. Light the error bit if the certificate is * invalid or contains an unverified signature. */ case CRYPTO_SIGN | CRYPTO_RESP: if ((rval = cert_sign(ep, &vtemp)) == XEVNT_OK) len += crypto_send(fp, &vtemp); value_free(&vtemp); break; /* * Send public key and signature. Use the values from the public * key. */ case CRYPTO_COOK: len += crypto_send(fp, &pubkey); break; /* * Encrypt and send cookie and signature. Light the error bit if * anything goes wrong. */ case CRYPTO_COOK | CRYPTO_RESP: if ((opcode & 0xffff) < VALUE_LEN) { rval = XEVNT_LEN; break; } if (PKT_MODE(xpkt->li_vn_mode) == MODE_SERVER) { tcookie = cookie; } else { if ((peer = findpeerbyassoc(associd)) == NULL) { rval = XEVNT_ERR; break; } tcookie = peer->pcookie; } if ((rval = crypto_encrypt(ep, &vtemp, &tcookie)) == XEVNT_OK) len += crypto_send(fp, &vtemp); value_free(&vtemp); break; /* * Find peer and send autokey data and signature in broadcast * server and symmetric modes. Use the values in the autokey * structure. If no association is found, either the server has * restarted with new associations or some perp has replayed an * old message, in which case light the error bit. */ case CRYPTO_AUTO | CRYPTO_RESP: if ((peer = findpeerbyassoc(associd)) == NULL) { rval = XEVNT_ERR; break; } peer->flags &= ~FLAG_ASSOC; len += crypto_send(fp, &peer->sndval); break; /* * Send leapseconds table and signature. Use the values from the * tai structure. If no table has been loaded, just send an * empty request. */ case CRYPTO_TAI: case CRYPTO_TAI | CRYPTO_RESP: if (crypto_flags & CRYPTO_FLAG_TAI) len += crypto_send(fp, &tai_leap); break; /* * Default - Fall through for requests; for unknown responses, * flag as error. */ default: if (opcode & CRYPTO_RESP) rval = XEVNT_ERR; } /* * In case of error, flame the log. If a request, toss the * puppy; if a response, return so the sender can flame, too. */ if (rval != XEVNT_OK) { opcode |= CRYPTO_ERROR; snprintf(statstr, NTP_MAXSTRLEN, "error %x opcode %x", rval, opcode); record_crypto_stats(srcadr_sin, statstr); report_event(rval, NULL); #ifdef DEBUG if (debug) printf("crypto_xmit: %s\n", statstr); #endif if (!(opcode & CRYPTO_RESP)) return (0); } /* * Round up the field length to a multiple of 8 bytes and save * the request code and length. */ len = ((len + 7) / 8) * 8; fp->opcode = htonl((opcode & 0xffff0000) | len); #ifdef DEBUG if (debug) printf( "crypto_xmit: flags 0x%x ext offset %d len %u code 0x%x assocID %d\n", crypto_flags, start, len, opcode >> 16, associd); #endif return (len); } /* * crypto_verify - parse and verify the extension field and value * * Returns * XEVNT_OK success * XEVNT_LEN bad field format or length * XEVNT_TSP bad timestamp * XEVNT_FSP bad filestamp * XEVNT_PUB bad or missing public key * XEVNT_SGL bad signature length * XEVNT_SIG signature not verified * XEVNT_ERR protocol error */ static int crypto_verify( struct exten *ep, /* extension pointer */ struct value *vp, /* value pointer */ struct peer *peer /* peer structure pointer */ ) { EVP_PKEY *pkey; /* server public key */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp, tstamp1 = 0; /* timestamp */ tstamp_t fstamp, fstamp1 = 0; /* filestamp */ u_int vallen; /* value length */ u_int siglen; /* signature length */ u_int opcode, len; int i; /* * We require valid opcode and field lengths, timestamp, * filestamp, public key, digest, signature length and * signature, where relevant. Note that preliminary length * checks are done in the main loop. */ len = ntohl(ep->opcode) & 0x0000ffff; opcode = ntohl(ep->opcode) & 0xffff0000; /* * Check for valid operation code and protocol. The opcode must * not have the error bit set. If a response, it must have a * value header. If a request and does not contain a value * header, no need for further checking. */ if (opcode & CRYPTO_ERROR) return (XEVNT_ERR); if (opcode & CRYPTO_RESP) { if (len < VALUE_LEN) return (XEVNT_LEN); } else { if (len < VALUE_LEN) return (XEVNT_OK); } /* * We have a value header. Check for valid field lengths. The * field length must be long enough to contain the value header, * value and signature. Note both the value and signature fields * are rounded up to the next word. */ vallen = ntohl(ep->vallen); i = (vallen + 3) / 4; siglen = ntohl(ep->pkt[i++]); if (len < VALUE_LEN + ((vallen + 3) / 4) * 4 + ((siglen + 3) / 4) * 4) return (XEVNT_LEN); /* * Punt if this is a response with no data. Punt if this is a * request and a previous response is pending. */ if (opcode & CRYPTO_RESP) { if (vallen == 0) return (XEVNT_LEN); } else { if (peer->cmmd != NULL) return (XEVNT_LEN); } /* * Check for valid timestamp and filestamp. If the timestamp is * zero, the sender is not synchronized and signatures are * disregarded. If not, the timestamp must not precede the * filestamp. The timestamp and filestamp must not precede the * corresponding values in the value structure, if present. Once * the autokey values have been installed, the timestamp must * always be later than the corresponding value in the value * structure. Duplicate timestamps are illegal once the cookie * has been validated. */ tstamp = ntohl(ep->tstamp); fstamp = ntohl(ep->fstamp); if (tstamp == 0) return (XEVNT_OK); if (tstamp < fstamp) return (XEVNT_TSP); if (vp != NULL) { tstamp1 = ntohl(vp->tstamp); fstamp1 = ntohl(vp->fstamp); if ((tstamp < tstamp1 || (tstamp == tstamp1 && (peer->crypto & CRYPTO_FLAG_AUTO)))) return (XEVNT_TSP); if ((tstamp < fstamp1 || fstamp < fstamp1)) return (XEVNT_FSP); } /* * Check for valid signature length, public key and digest * algorithm. */ if (crypto_flags & peer->crypto & CRYPTO_FLAG_PRIV) pkey = sign_pkey; else pkey = peer->pkey; if (siglen == 0 || pkey == NULL || peer->digest == NULL) return (XEVNT_OK); if (siglen != (u_int)EVP_PKEY_size(pkey)) return (XEVNT_SGL); /* * Darn, I thought we would never get here. Verify the * signature. If the identity exchange is verified, light the * proventic bit. If no client identity scheme is specified, * avoid doing the sign exchange. */ EVP_VerifyInit(&ctx, peer->digest); EVP_VerifyUpdate(&ctx, (u_char *)&ep->tstamp, vallen + 12); if (EVP_VerifyFinal(&ctx, (u_char *)&ep->pkt[i], siglen, pkey) <= 0) return (XEVNT_SIG); if (peer->crypto & CRYPTO_FLAG_VRFY) { peer->crypto |= CRYPTO_FLAG_PROV; if (!(crypto_flags & CRYPTO_FLAG_MASK)) peer->crypto |= CRYPTO_FLAG_SIGN; } return (XEVNT_OK); } /* * crypto_encrypt - construct encrypted cookie and signature from * extension field and cookie * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_CKY bad or missing cookie * XEVNT_PER host certificate expired */ static int crypto_encrypt( struct exten *ep, /* extension pointer */ struct value *vp, /* value pointer */ keyid_t *cookie /* server cookie */ ) { EVP_PKEY *pkey; /* public key */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; /* NTP timestamp */ u_int32 temp32; u_int len; u_char *ptr; /* * Extract the public key from the request. */ len = ntohl(ep->vallen); ptr = (u_char *)ep->pkt; pkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, &ptr, len); if (pkey == NULL) { msyslog(LOG_ERR, "crypto_encrypt %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_PUB); } /* * Encrypt the cookie, encode in ASN.1 and sign. */ tstamp = crypto_time(); memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = hostval.tstamp; len = EVP_PKEY_size(pkey); vp->vallen = htonl(len); vp->ptr = emalloc(len); temp32 = htonl(*cookie); if (!RSA_public_encrypt(4, (u_char *)&temp32, vp->ptr, pkey->pkey.rsa, RSA_PKCS1_OAEP_PADDING)) { msyslog(LOG_ERR, "crypto_encrypt %s\n", ERR_error_string(ERR_get_error(), NULL)); EVP_PKEY_free(pkey); return (XEVNT_CKY); } EVP_PKEY_free(pkey); vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); return (XEVNT_OK); } /* * crypto_ident - construct extension field for identity scheme * * This routine determines which identity scheme is in use and * constructs an extension field for that scheme. */ u_int crypto_ident( struct peer *peer /* peer structure pointer */ ) { char filename[MAXFILENAME + 1]; /* * If the server identity has already been verified, no further * action is necessary. Otherwise, try to load the identity file * of the certificate issuer. If the issuer file is not found, * try the host file. If nothing found, declare a cryptobust. * Note we can't get here unless the trusted certificate has * been found and the CRYPTO_FLAG_VALID bit is set, so the * certificate issuer is valid. */ if (peer->ident_pkey != NULL) EVP_PKEY_free(peer->ident_pkey); if (peer->crypto & CRYPTO_FLAG_GQ) { snprintf(filename, MAXFILENAME, "ntpkey_gq_%s", peer->issuer); peer->ident_pkey = crypto_key(filename, &peer->fstamp); if (peer->ident_pkey != NULL) return (CRYPTO_GQ); snprintf(filename, MAXFILENAME, "ntpkey_gq_%s", sys_hostname); peer->ident_pkey = crypto_key(filename, &peer->fstamp); if (peer->ident_pkey != NULL) return (CRYPTO_GQ); } if (peer->crypto & CRYPTO_FLAG_IFF) { snprintf(filename, MAXFILENAME, "ntpkey_iff_%s", peer->issuer); peer->ident_pkey = crypto_key(filename, &peer->fstamp); if (peer->ident_pkey != NULL) return (CRYPTO_IFF); snprintf(filename, MAXFILENAME, "ntpkey_iff_%s", sys_hostname); peer->ident_pkey = crypto_key(filename, &peer->fstamp); if (peer->ident_pkey != NULL) return (CRYPTO_IFF); } if (peer->crypto & CRYPTO_FLAG_MV) { snprintf(filename, MAXFILENAME, "ntpkey_mv_%s", peer->issuer); peer->ident_pkey = crypto_key(filename, &peer->fstamp); if (peer->ident_pkey != NULL) return (CRYPTO_MV); snprintf(filename, MAXFILENAME, "ntpkey_mv_%s", sys_hostname); peer->ident_pkey = crypto_key(filename, &peer->fstamp); if (peer->ident_pkey != NULL) return (CRYPTO_MV); } /* * No compatible identity scheme is available. Life is hard. */ msyslog(LOG_INFO, "crypto_ident: no compatible identity scheme found"); return (0); } /* * crypto_args - construct extension field from arguments * * This routine creates an extension field with current timestamps and * specified opcode, association ID and optional string. Note that the * extension field is created here, but freed after the crypto_xmit() * call in the protocol module. * * Returns extension field pointer (no errors). */ struct exten * crypto_args( struct peer *peer, /* peer structure pointer */ u_int opcode, /* operation code */ char *str /* argument string */ ) { tstamp_t tstamp; /* NTP timestamp */ struct exten *ep; /* extension field pointer */ u_int len; /* extension field length */ tstamp = crypto_time(); len = sizeof(struct exten); if (str != NULL) len += strlen(str); ep = emalloc(len); memset(ep, 0, len); if (opcode == 0) return (ep); ep->opcode = htonl(opcode + len); /* * If a response, send our ID; if a request, send the * responder's ID. */ if (opcode & CRYPTO_RESP) ep->associd = htonl(peer->associd); else ep->associd = htonl(peer->assoc); ep->tstamp = htonl(tstamp); ep->fstamp = hostval.tstamp; ep->vallen = 0; if (str != NULL) { ep->vallen = htonl(strlen(str)); memcpy((char *)ep->pkt, str, strlen(str)); } else { ep->pkt[0] = peer->associd; } return (ep); } /* * crypto_send - construct extension field from value components * * Returns extension field length. Note: it is not polite to send a * nonempty signature with zero timestamp or a nonzero timestamp with * empty signature, but these rules are not enforced here. */ u_int crypto_send( struct exten *ep, /* extension field pointer */ struct value *vp /* value pointer */ ) { u_int len, temp32; int i; /* * Copy data. If the data field is empty or zero length, encode * an empty value with length zero. */ ep->tstamp = vp->tstamp; ep->fstamp = vp->fstamp; ep->vallen = vp->vallen; len = 12; temp32 = ntohl(vp->vallen); if (temp32 > 0 && vp->ptr != NULL) memcpy(ep->pkt, vp->ptr, temp32); /* * Copy signature. If the signature field is empty or zero * length, encode an empty signature with length zero. */ i = (temp32 + 3) / 4; len += i * 4 + 4; ep->pkt[i++] = vp->siglen; temp32 = ntohl(vp->siglen); if (temp32 > 0 && vp->sig != NULL) memcpy(&ep->pkt[i], vp->sig, temp32); len += temp32; return (len); } /* * crypto_update - compute new public value and sign extension fields * * This routine runs periodically, like once a day, and when something * changes. It updates the timestamps on three value structures and one * value structure list, then signs all the structures: * * hostval host name (not signed) * pubkey public key * cinfo certificate info/value list * tai_leap leapseconds file * * Filestamps are proventicated data, so this routine is run only when * the host has been synchronized to a proventicated source. Thus, the * timestamp is proventicated, too, and can be used to deflect * clogging attacks and even cook breakfast. * * Returns void (no errors) */ void crypto_update(void) { EVP_MD_CTX ctx; /* message digest context */ struct cert_info *cp, *cpn; /* certificate info/value */ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */ tstamp_t tstamp; /* NTP timestamp */ u_int len; if ((tstamp = crypto_time()) == 0) return; hostval.tstamp = htonl(tstamp); /* * Sign public key and timestamps. The filestamp is derived from * the host key file extension from wherever the file was * generated. */ if (pubkey.vallen != 0) { pubkey.tstamp = hostval.tstamp; pubkey.siglen = 0; if (pubkey.sig == NULL) pubkey.sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&pubkey, 12); EVP_SignUpdate(&ctx, pubkey.ptr, ntohl(pubkey.vallen)); if (EVP_SignFinal(&ctx, pubkey.sig, &len, sign_pkey)) pubkey.siglen = htonl(len); } /* * Sign certificates and timestamps. The filestamp is derived * from the certificate file extension from wherever the file * was generated. Note we do not throw expired certificates * away; they may have signed younger ones. */ for (cp = cinfo; cp != NULL; cp = cpn) { cpn = cp->link; cp->cert.tstamp = hostval.tstamp; cp->cert.siglen = 0; if (cp->cert.sig == NULL) cp->cert.sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&cp->cert, 12); EVP_SignUpdate(&ctx, cp->cert.ptr, ntohl(cp->cert.vallen)); if (EVP_SignFinal(&ctx, cp->cert.sig, &len, sign_pkey)) cp->cert.siglen = htonl(len); } /* * Sign leapseconds table and timestamps. The filestamp is * derived from the leapsecond file extension from wherever the * file was generated. */ if (tai_leap.vallen != 0) { tai_leap.tstamp = hostval.tstamp; tai_leap.siglen = 0; if (tai_leap.sig == NULL) tai_leap.sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&tai_leap, 12); EVP_SignUpdate(&ctx, tai_leap.ptr, ntohl(tai_leap.vallen)); if (EVP_SignFinal(&ctx, tai_leap.sig, &len, sign_pkey)) tai_leap.siglen = htonl(len); } snprintf(statstr, NTP_MAXSTRLEN, "update ts %u", ntohl(hostval.tstamp)); record_crypto_stats(NULL, statstr); #ifdef DEBUG if (debug) printf("crypto_update: %s\n", statstr); #endif } /* * value_free - free value structure components. * * Returns void (no errors) */ void value_free( struct value *vp /* value structure */ ) { if (vp->ptr != NULL) free(vp->ptr); if (vp->sig != NULL) free(vp->sig); memset(vp, 0, sizeof(struct value)); } /* * crypto_time - returns current NTP time in seconds. */ tstamp_t crypto_time() { l_fp tstamp; /* NTP time */ L_CLR(&tstamp); L_CLR(&tstamp); if (sys_leap != LEAP_NOTINSYNC) get_systime(&tstamp); return (tstamp.l_ui); } /* * asn2ntp - convert ASN1_TIME time structure to NTP time in seconds. */ u_long asn2ntp ( ASN1_TIME *asn1time /* pointer to ASN1_TIME structure */ ) { char *v; /* pointer to ASN1_TIME string */ struct tm tm; /* used to convert to NTP time */ /* * Extract time string YYMMDDHHMMSSZ from ASN1 time structure. * Note that the YY, MM, DD fields start with one, the HH, MM, * SS fiels start with zero and the Z character should be 'Z' * for UTC. Also note that years less than 50 map to years * greater than 100. Dontcha love ASN.1? Better than MIL-188. */ if (asn1time->length > 13) return ((u_long)(~0)); /* We can't use -1 here. It's invalid */ v = (char *)asn1time->data; tm.tm_year = (v[0] - '0') * 10 + v[1] - '0'; if (tm.tm_year < 50) tm.tm_year += 100; tm.tm_mon = (v[2] - '0') * 10 + v[3] - '0' - 1; tm.tm_mday = (v[4] - '0') * 10 + v[5] - '0'; tm.tm_hour = (v[6] - '0') * 10 + v[7] - '0'; tm.tm_min = (v[8] - '0') * 10 + v[9] - '0'; tm.tm_sec = (v[10] - '0') * 10 + v[11] - '0'; tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_isdst = 0; return (timegm(&tm) + JAN_1970); } /* * bigdig() - compute a BIGNUM MD5 hash of a BIGNUM number. */ static int bighash( BIGNUM *bn, /* BIGNUM * from */ BIGNUM *bk /* BIGNUM * to */ ) { EVP_MD_CTX ctx; /* message digest context */ u_char dgst[EVP_MAX_MD_SIZE]; /* message digest */ u_char *ptr; /* a BIGNUM as binary string */ u_int len; len = BN_num_bytes(bn); ptr = emalloc(len); BN_bn2bin(bn, ptr); EVP_DigestInit(&ctx, EVP_md5()); EVP_DigestUpdate(&ctx, ptr, len); EVP_DigestFinal(&ctx, dgst, &len); BN_bin2bn(dgst, len, bk); /* XXX MEMLEAK? free ptr? */ return (1); } /* *********************************************************************** * * * The following routines implement the Schnorr (IFF) identity scheme * * * *********************************************************************** * * The Schnorr (IFF) identity scheme is intended for use when * the ntp-genkeys program does not generate the certificates used in * the protocol and the group key cannot be conveyed in the certificate * itself. For this purpose, new generations of IFF values must be * securely transmitted to all members of the group before use. The * scheme is self contained and independent of new generations of host * keys, sign keys and certificates. * * The IFF identity scheme is based on DSA cryptography and algorithms * described in Stinson p. 285. The IFF values hide in a DSA cuckoo * structure, but only the primes and generator are used. The p is a * 512-bit prime, q a 160-bit prime that divides p - 1 and is a qth root * of 1 mod p; that is, g^q = 1 mod p. The TA rolls primvate random * group key b disguised as a DSA structure member, then computes public * key g^(q - b). These values are shared only among group members and * never revealed in messages. Alice challenges Bob to confirm identity * using the protocol described below. * * How it works * * The scheme goes like this. Both Alice and Bob have the public primes * p, q and generator g. The TA gives private key b to Bob and public * key v = g^(q - a) mod p to Alice. * * Alice rolls new random challenge r and sends to Bob in the IFF * request message. Bob rolls new random k, then computes y = k + b r * mod q and x = g^k mod p and sends (y, hash(x)) to Alice in the * response message. Besides making the response shorter, the hash makes * it effectivey impossible for an intruder to solve for b by observing * a number of these messages. * * Alice receives the response and computes g^y v^r mod p. After a bit * of algebra, this simplifies to g^k. If the hash of this result * matches hash(x), Alice knows that Bob has the group key b. The signed * response binds this knowledge to Bob's private key and the public key * previously received in his certificate. * * crypto_alice - construct Alice's challenge in IFF scheme * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_ID bad or missing group key */ static int crypto_alice( struct peer *peer, /* peer pointer */ struct value *vp /* value pointer */ ) { DSA *dsa; /* IFF parameters */ BN_CTX *bctx; /* BIGNUM context */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; u_int len; /* * The identity parameters must have correct format and content. */ if (peer->ident_pkey == NULL) return (XEVNT_ID); if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) { msyslog(LOG_INFO, "crypto_alice: defective key"); return (XEVNT_PUB); } /* * Roll new random r (0 < r < q). The OpenSSL library has a bug * omitting BN_rand_range, so we have to do it the hard way. */ bctx = BN_CTX_new(); len = BN_num_bytes(dsa->q); if (peer->iffval != NULL) BN_free(peer->iffval); peer->iffval = BN_new(); BN_rand(peer->iffval, len * 8, -1, 1); /* r */ BN_mod(peer->iffval, peer->iffval, dsa->q, bctx); BN_CTX_free(bctx); /* * Sign and send to Bob. The filestamp is from the local file. */ tstamp = crypto_time(); memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = htonl(peer->fstamp); vp->vallen = htonl(len); vp->ptr = emalloc(len); BN_bn2bin(peer->iffval, vp->ptr); vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); return (XEVNT_OK); } /* * crypto_bob - construct Bob's response to Alice's challenge * * Returns * XEVNT_OK success * XEVNT_ID bad or missing group key * XEVNT_ERR protocol error * XEVNT_PER host expired certificate */ static int crypto_bob( struct exten *ep, /* extension pointer */ struct value *vp /* value pointer */ ) { DSA *dsa; /* IFF parameters */ DSA_SIG *sdsa; /* DSA signature context fake */ BN_CTX *bctx; /* BIGNUM context */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; /* NTP timestamp */ BIGNUM *bn, *bk, *r; u_char *ptr; u_int len; /* * If the IFF parameters are not valid, something awful * happened or we are being tormented. */ if (iffpar_pkey == NULL) { msyslog(LOG_INFO, "crypto_bob: scheme unavailable"); return (XEVNT_ID); } dsa = iffpar_pkey->pkey.dsa; /* * Extract r from the challenge. */ len = ntohl(ep->vallen); if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) { msyslog(LOG_ERR, "crypto_bob %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_ERR); } /* * Bob rolls random k (0 < k < q), computes y = k + b r mod q * and x = g^k mod p, then sends (y, hash(x)) to Alice. */ bctx = BN_CTX_new(); bk = BN_new(); bn = BN_new(); sdsa = DSA_SIG_new(); BN_rand(bk, len * 8, -1, 1); /* k */ BN_mod_mul(bn, dsa->priv_key, r, dsa->q, bctx); /* b r mod q */ BN_add(bn, bn, bk); BN_mod(bn, bn, dsa->q, bctx); /* k + b r mod q */ sdsa->r = BN_dup(bn); BN_mod_exp(bk, dsa->g, bk, dsa->p, bctx); /* g^k mod p */ bighash(bk, bk); sdsa->s = BN_dup(bk); BN_CTX_free(bctx); BN_free(r); BN_free(bn); BN_free(bk); /* * Encode the values in ASN.1 and sign. */ tstamp = crypto_time(); memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = htonl(if_fstamp); len = i2d_DSA_SIG(sdsa, NULL); if (len <= 0) { msyslog(LOG_ERR, "crypto_bob %s\n", ERR_error_string(ERR_get_error(), NULL)); DSA_SIG_free(sdsa); return (XEVNT_ERR); } vp->vallen = htonl(len); ptr = emalloc(len); vp->ptr = ptr; i2d_DSA_SIG(sdsa, &ptr); DSA_SIG_free(sdsa); vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); return (XEVNT_OK); } /* * crypto_iff - verify Bob's response to Alice's challenge * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_ID bad or missing group key * XEVNT_FSP bad filestamp */ int crypto_iff( struct exten *ep, /* extension pointer */ struct peer *peer /* peer structure pointer */ ) { DSA *dsa; /* IFF parameters */ BN_CTX *bctx; /* BIGNUM context */ DSA_SIG *sdsa; /* DSA parameters */ BIGNUM *bn, *bk; u_int len; const u_char *ptr; int temp; /* * If the IFF parameters are not valid or no challenge was sent, * something awful happened or we are being tormented. */ if (peer->ident_pkey == NULL) { msyslog(LOG_INFO, "crypto_iff: scheme unavailable"); return (XEVNT_ID); } if (ntohl(ep->fstamp) != peer->fstamp) { msyslog(LOG_INFO, "crypto_iff: invalid filestamp %u", ntohl(ep->fstamp)); return (XEVNT_FSP); } if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) { msyslog(LOG_INFO, "crypto_iff: defective key"); return (XEVNT_PUB); } if (peer->iffval == NULL) { msyslog(LOG_INFO, "crypto_iff: missing challenge"); return (XEVNT_ID); } /* * Extract the k + b r and g^k values from the response. */ bctx = BN_CTX_new(); bk = BN_new(); bn = BN_new(); len = ntohl(ep->vallen); ptr = (const u_char *)ep->pkt; if ((sdsa = d2i_DSA_SIG(NULL, &ptr, len)) == NULL) { msyslog(LOG_ERR, "crypto_iff %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_ERR); } /* * Compute g^(k + b r) g^(q - b)r mod p. */ BN_mod_exp(bn, dsa->pub_key, peer->iffval, dsa->p, bctx); BN_mod_exp(bk, dsa->g, sdsa->r, dsa->p, bctx); BN_mod_mul(bn, bn, bk, dsa->p, bctx); /* * Verify the hash of the result matches hash(x). */ bighash(bn, bn); temp = BN_cmp(bn, sdsa->s); BN_free(bn); BN_free(bk); BN_CTX_free(bctx); BN_free(peer->iffval); peer->iffval = NULL; DSA_SIG_free(sdsa); if (temp == 0) return (XEVNT_OK); else return (XEVNT_ID); } /* *********************************************************************** * * * The following routines implement the Guillou-Quisquater (GQ) * * identity scheme * * * *********************************************************************** * * The Guillou-Quisquater (GQ) identity scheme is intended for use when * the ntp-genkeys program generates the certificates used in the * protocol and the group key can be conveyed in a certificate extension * field. The scheme is self contained and independent of new * generations of host keys, sign keys and certificates. * * The GQ identity scheme is based on RSA cryptography and algorithms * described in Stinson p. 300 (with errors). The GQ values hide in a * RSA cuckoo structure, but only the modulus is used. The 512-bit * public modulus is n = p q, where p and q are secret large primes. The * TA rolls random group key b disguised as a RSA structure member. * Except for the public key, these values are shared only among group * members and never revealed in messages. * * When rolling new certificates, Bob recomputes the private and * public keys. The private key u is a random roll, while the public key * is the inverse obscured by the group key v = (u^-1)^b. These values * replace the private and public keys normally generated by the RSA * scheme. Alice challenges Bob to confirm identity using the protocol * described below. * * How it works * * The scheme goes like this. Both Alice and Bob have the same modulus n * and some random b as the group key. These values are computed and * distributed in advance via secret means, although only the group key * b is truly secret. Each has a private random private key u and public * key (u^-1)^b, although not necessarily the same ones. Bob and Alice * can regenerate the key pair from time to time without affecting * operations. The public key is conveyed on the certificate in an * extension field; the private key is never revealed. * * Alice rolls new random challenge r and sends to Bob in the GQ * request message. Bob rolls new random k, then computes y = k u^r mod * n and x = k^b mod n and sends (y, hash(x)) to Alice in the response * message. Besides making the response shorter, the hash makes it * effectivey impossible for an intruder to solve for b by observing * a number of these messages. * * Alice receives the response and computes y^b v^r mod n. After a bit * of algebra, this simplifies to k^b. If the hash of this result * matches hash(x), Alice knows that Bob has the group key b. The signed * response binds this knowledge to Bob's private key and the public key * previously received in his certificate. * * crypto_alice2 - construct Alice's challenge in GQ scheme * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_ID bad or missing group key * XEVNT_PER host certificate expired */ static int crypto_alice2( struct peer *peer, /* peer pointer */ struct value *vp /* value pointer */ ) { RSA *rsa; /* GQ parameters */ BN_CTX *bctx; /* BIGNUM context */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; u_int len; /* * The identity parameters must have correct format and content. */ if (peer->ident_pkey == NULL) return (XEVNT_ID); if ((rsa = peer->ident_pkey->pkey.rsa) == NULL) { msyslog(LOG_INFO, "crypto_alice2: defective key"); return (XEVNT_PUB); } /* * Roll new random r (0 < r < n). The OpenSSL library has a bug * omitting BN_rand_range, so we have to do it the hard way. */ bctx = BN_CTX_new(); len = BN_num_bytes(rsa->n); if (peer->iffval != NULL) BN_free(peer->iffval); peer->iffval = BN_new(); BN_rand(peer->iffval, len * 8, -1, 1); /* r mod n */ BN_mod(peer->iffval, peer->iffval, rsa->n, bctx); BN_CTX_free(bctx); /* * Sign and send to Bob. The filestamp is from the local file. */ tstamp = crypto_time(); memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = htonl(peer->fstamp); vp->vallen = htonl(len); vp->ptr = emalloc(len); BN_bn2bin(peer->iffval, vp->ptr); vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); return (XEVNT_OK); } /* * crypto_bob2 - construct Bob's response to Alice's challenge * * Returns * XEVNT_OK success * XEVNT_ID bad or missing group key * XEVNT_ERR protocol error * XEVNT_PER host certificate expired */ static int crypto_bob2( struct exten *ep, /* extension pointer */ struct value *vp /* value pointer */ ) { RSA *rsa; /* GQ parameters */ DSA_SIG *sdsa; /* DSA parameters */ BN_CTX *bctx; /* BIGNUM context */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; /* NTP timestamp */ BIGNUM *r, *k, *g, *y; u_char *ptr; u_int len; /* * If the GQ parameters are not valid, something awful * happened or we are being tormented. */ if (gqpar_pkey == NULL) { msyslog(LOG_INFO, "crypto_bob2: scheme unavailable"); return (XEVNT_ID); } rsa = gqpar_pkey->pkey.rsa; /* * Extract r from the challenge. */ len = ntohl(ep->vallen); if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) { msyslog(LOG_ERR, "crypto_bob2 %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_ERR); } /* * Bob rolls random k (0 < k < n), computes y = k u^r mod n and * x = k^b mod n, then sends (y, hash(x)) to Alice. */ bctx = BN_CTX_new(); k = BN_new(); g = BN_new(); y = BN_new(); sdsa = DSA_SIG_new(); BN_rand(k, len * 8, -1, 1); /* k */ BN_mod(k, k, rsa->n, bctx); BN_mod_exp(y, rsa->p, r, rsa->n, bctx); /* u^r mod n */ BN_mod_mul(y, k, y, rsa->n, bctx); /* k u^r mod n */ sdsa->r = BN_dup(y); BN_mod_exp(g, k, rsa->e, rsa->n, bctx); /* k^b mod n */ bighash(g, g); sdsa->s = BN_dup(g); BN_CTX_free(bctx); BN_free(r); BN_free(k); BN_free(g); BN_free(y); /* * Encode the values in ASN.1 and sign. */ tstamp = crypto_time(); memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = htonl(gq_fstamp); len = i2d_DSA_SIG(sdsa, NULL); if (len <= 0) { msyslog(LOG_ERR, "crypto_bob2 %s\n", ERR_error_string(ERR_get_error(), NULL)); DSA_SIG_free(sdsa); return (XEVNT_ERR); } vp->vallen = htonl(len); ptr = emalloc(len); vp->ptr = ptr; i2d_DSA_SIG(sdsa, &ptr); DSA_SIG_free(sdsa); vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); return (XEVNT_OK); } /* * crypto_gq - verify Bob's response to Alice's challenge * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_ID bad or missing group keys * XEVNT_ERR protocol error * XEVNT_FSP bad filestamp */ int crypto_gq( struct exten *ep, /* extension pointer */ struct peer *peer /* peer structure pointer */ ) { RSA *rsa; /* GQ parameters */ BN_CTX *bctx; /* BIGNUM context */ DSA_SIG *sdsa; /* RSA signature context fake */ BIGNUM *y, *v; const u_char *ptr; u_int len; int temp; /* * If the GQ parameters are not valid or no challenge was sent, * something awful happened or we are being tormented. */ if (peer->ident_pkey == NULL) { msyslog(LOG_INFO, "crypto_gq: scheme unavailable"); return (XEVNT_ID); } if (ntohl(ep->fstamp) != peer->fstamp) { msyslog(LOG_INFO, "crypto_gq: invalid filestamp %u", ntohl(ep->fstamp)); return (XEVNT_FSP); } if ((rsa = peer->ident_pkey->pkey.rsa) == NULL) { msyslog(LOG_INFO, "crypto_gq: defective key"); return (XEVNT_PUB); } if (peer->iffval == NULL) { msyslog(LOG_INFO, "crypto_gq: missing challenge"); return (XEVNT_ID); } /* * Extract the y = k u^r and hash(x = k^b) values from the * response. */ bctx = BN_CTX_new(); y = BN_new(); v = BN_new(); len = ntohl(ep->vallen); ptr = (const u_char *)ep->pkt; if ((sdsa = d2i_DSA_SIG(NULL, &ptr, len)) == NULL) { msyslog(LOG_ERR, "crypto_gq %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_ERR); } /* * Compute v^r y^b mod n. */ BN_mod_exp(v, peer->grpkey, peer->iffval, rsa->n, bctx); /* v^r mod n */ BN_mod_exp(y, sdsa->r, rsa->e, rsa->n, bctx); /* y^b mod n */ BN_mod_mul(y, v, y, rsa->n, bctx); /* v^r y^b mod n */ /* * Verify the hash of the result matches hash(x). */ bighash(y, y); temp = BN_cmp(y, sdsa->s); BN_CTX_free(bctx); BN_free(y); BN_free(v); BN_free(peer->iffval); peer->iffval = NULL; DSA_SIG_free(sdsa); if (temp == 0) return (XEVNT_OK); else return (XEVNT_ID); } /* *********************************************************************** * * * The following routines implement the Mu-Varadharajan (MV) identity * * scheme * * * *********************************************************************** */ /* * The Mu-Varadharajan (MV) cryptosystem was originally intended when * servers broadcast messages to clients, but clients never send * messages to servers. There is one encryption key for the server and a * separate decryption key for each client. It operated something like a * pay-per-view satellite broadcasting system where the session key is * encrypted by the broadcaster and the decryption keys are held in a * tamperproof set-top box. * * The MV parameters and private encryption key hide in a DSA cuckoo * structure which uses the same parameters, but generated in a * different way. The values are used in an encryption scheme similar to * El Gamal cryptography and a polynomial formed from the expansion of * product terms (x - x[j]), as described in Mu, Y., and V. * Varadharajan: Robust and Secure Broadcasting, Proc. Indocrypt 2001, * 223-231. The paper has significant errors and serious omissions. * * Let q be the product of n distinct primes s'[j] (j = 1...n), where * each s'[j] has m significant bits. Let p be a prime p = 2 * q + 1, so * that q and each s'[j] divide p - 1 and p has M = n * m + 1 * significant bits. The elements x mod q of Zq with the elements 2 and * the primes removed form a field Zq* valid for polynomial arithetic. * Let g be a generator of Zp; that is, gcd(g, p - 1) = 1 and g^q = 1 * mod p. We expect M to be in the 500-bit range and n relatively small, * like 25, so the likelihood of a randomly generated element of x mod q * of Zq colliding with a factor of p - 1 is very small and can be * avoided. Associated with each s'[j] is an element s[j] such that s[j] * s'[j] = s'[j] mod q. We find s[j] as the quotient (q + s'[j]) / * s'[j]. These are the parameters of the scheme and they are expensive * to compute. * * We set up an instance of the scheme as follows. A set of random * values x[j] mod q (j = 1...n), are generated as the zeros of a * polynomial of order n. The product terms (x - x[j]) are expanded to * form coefficients a[i] mod q (i = 0...n) in powers of x. These are * used as exponents of the generator g mod p to generate the private * encryption key A. The pair (gbar, ghat) of public server keys and the * pairs (xbar[j], xhat[j]) (j = 1...n) of private client keys are used * to construct the decryption keys. The devil is in the details. * * The distinguishing characteristic of this scheme is the capability to * revoke keys. Included in the calculation of E, gbar and ghat is the * product s = prod(s'[j]) (j = 1...n) above. If the factor s'[j] is * subsequently removed from the product and E, gbar and ghat * recomputed, the jth client will no longer be able to compute E^-1 and * thus unable to decrypt the block. * * How it works * * The scheme goes like this. Bob has the server values (p, A, q, gbar, * ghat) and Alice the client values (p, xbar, xhat). * * Alice rolls new random challenge r (0 < r < p) and sends to Bob in * the MV request message. Bob rolls new random k (0 < k < q), encrypts * y = A^k mod p (a permutation) and sends (hash(y), gbar^k, ghat^k) to * Alice. * * Alice receives the response and computes the decryption key (the * inverse permutation) from previously obtained (xbar, xhat) and * (gbar^k, ghat^k) in the message. She computes the inverse, which is * unique by reasons explained in the ntp-keygen.c program sources. If * the hash of this result matches hash(y), Alice knows that Bob has the * group key b. The signed response binds this knowledge to Bob's * private key and the public key previously received in his * certificate. * * crypto_alice3 - construct Alice's challenge in MV scheme * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_ID bad or missing group key * XEVNT_PER host certificate expired */ static int crypto_alice3( struct peer *peer, /* peer pointer */ struct value *vp /* value pointer */ ) { DSA *dsa; /* MV parameters */ BN_CTX *bctx; /* BIGNUM context */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; u_int len; /* * The identity parameters must have correct format and content. */ if (peer->ident_pkey == NULL) return (XEVNT_ID); if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) { msyslog(LOG_INFO, "crypto_alice3: defective key"); return (XEVNT_PUB); } /* * Roll new random r (0 < r < q). The OpenSSL library has a bug * omitting BN_rand_range, so we have to do it the hard way. */ bctx = BN_CTX_new(); len = BN_num_bytes(dsa->p); if (peer->iffval != NULL) BN_free(peer->iffval); peer->iffval = BN_new(); BN_rand(peer->iffval, len * 8, -1, 1); /* r */ BN_mod(peer->iffval, peer->iffval, dsa->p, bctx); BN_CTX_free(bctx); /* * Sign and send to Bob. The filestamp is from the local file. */ tstamp = crypto_time(); memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = htonl(peer->fstamp); vp->vallen = htonl(len); vp->ptr = emalloc(len); BN_bn2bin(peer->iffval, vp->ptr); vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); return (XEVNT_OK); } /* * crypto_bob3 - construct Bob's response to Alice's challenge * * Returns * XEVNT_OK success * XEVNT_ERR protocol error * XEVNT_PER host certificate expired */ static int crypto_bob3( struct exten *ep, /* extension pointer */ struct value *vp /* value pointer */ ) { DSA *dsa; /* MV parameters */ DSA *sdsa; /* DSA signature context fake */ BN_CTX *bctx; /* BIGNUM context */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; /* NTP timestamp */ BIGNUM *r, *k, *u; u_char *ptr; u_int len; /* * If the MV parameters are not valid, something awful * happened or we are being tormented. */ if (mvpar_pkey == NULL) { msyslog(LOG_INFO, "crypto_bob3: scheme unavailable"); return (XEVNT_ID); } dsa = mvpar_pkey->pkey.dsa; /* * Extract r from the challenge. */ len = ntohl(ep->vallen); if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) { msyslog(LOG_ERR, "crypto_bob3 %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_ERR); } /* * Bob rolls random k (0 < k < q), making sure it is not a * factor of q. He then computes y = A^k r and sends (hash(y), * gbar^k, ghat^k) to Alice. */ bctx = BN_CTX_new(); k = BN_new(); u = BN_new(); sdsa = DSA_new(); sdsa->p = BN_new(); sdsa->q = BN_new(); sdsa->g = BN_new(); while (1) { BN_rand(k, BN_num_bits(dsa->q), 0, 0); BN_mod(k, k, dsa->q, bctx); BN_gcd(u, k, dsa->q, bctx); if (BN_is_one(u)) break; } BN_mod_exp(u, dsa->g, k, dsa->p, bctx); /* A r */ BN_mod_mul(u, u, r, dsa->p, bctx); bighash(u, sdsa->p); BN_mod_exp(sdsa->q, dsa->priv_key, k, dsa->p, bctx); /* gbar */ BN_mod_exp(sdsa->g, dsa->pub_key, k, dsa->p, bctx); /* ghat */ BN_CTX_free(bctx); BN_free(k); BN_free(r); BN_free(u); /* * Encode the values in ASN.1 and sign. */ tstamp = crypto_time(); memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = htonl(mv_fstamp); len = i2d_DSAparams(sdsa, NULL); if (len <= 0) { msyslog(LOG_ERR, "crypto_bob3 %s\n", ERR_error_string(ERR_get_error(), NULL)); DSA_free(sdsa); return (XEVNT_ERR); } vp->vallen = htonl(len); ptr = emalloc(len); vp->ptr = ptr; i2d_DSAparams(sdsa, &ptr); DSA_free(sdsa); vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); return (XEVNT_OK); } /* * crypto_mv - verify Bob's response to Alice's challenge * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_ID bad or missing group key * XEVNT_ERR protocol error * XEVNT_FSP bad filestamp */ int crypto_mv( struct exten *ep, /* extension pointer */ struct peer *peer /* peer structure pointer */ ) { DSA *dsa; /* MV parameters */ DSA *sdsa; /* DSA parameters */ BN_CTX *bctx; /* BIGNUM context */ BIGNUM *k, *u, *v; u_int len; const u_char *ptr; int temp; /* * If the MV parameters are not valid or no challenge was sent, * something awful happened or we are being tormented. */ if (peer->ident_pkey == NULL) { msyslog(LOG_INFO, "crypto_mv: scheme unavailable"); return (XEVNT_ID); } if (ntohl(ep->fstamp) != peer->fstamp) { msyslog(LOG_INFO, "crypto_mv: invalid filestamp %u", ntohl(ep->fstamp)); return (XEVNT_FSP); } if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) { msyslog(LOG_INFO, "crypto_mv: defective key"); return (XEVNT_PUB); } if (peer->iffval == NULL) { msyslog(LOG_INFO, "crypto_mv: missing challenge"); return (XEVNT_ID); } /* * Extract the (hash(y), gbar, ghat) values from the response. */ bctx = BN_CTX_new(); k = BN_new(); u = BN_new(); v = BN_new(); len = ntohl(ep->vallen); ptr = (const u_char *)ep->pkt; if ((sdsa = d2i_DSAparams(NULL, &ptr, len)) == NULL) { msyslog(LOG_ERR, "crypto_mv %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_ERR); } /* * Compute (gbar^xhat ghat^xbar)^-1 mod p. */ BN_mod_exp(u, sdsa->q, dsa->pub_key, dsa->p, bctx); BN_mod_exp(v, sdsa->g, dsa->priv_key, dsa->p, bctx); BN_mod_mul(u, u, v, dsa->p, bctx); BN_mod_inverse(u, u, dsa->p, bctx); BN_mod_mul(v, u, peer->iffval, dsa->p, bctx); /* * The result should match the hash of r mod p. */ bighash(v, v); temp = BN_cmp(v, sdsa->p); BN_CTX_free(bctx); BN_free(k); BN_free(u); BN_free(v); BN_free(peer->iffval); peer->iffval = NULL; DSA_free(sdsa); if (temp == 0) return (XEVNT_OK); else return (XEVNT_ID); } /* *********************************************************************** * * * The following routines are used to manipulate certificates * * * *********************************************************************** */ /* * cert_parse - parse x509 certificate and create info/value structures. * * The server certificate includes the version number, issuer name, * subject name, public key and valid date interval. If the issuer name * is the same as the subject name, the certificate is self signed and * valid only if the server is configured as trustable. If the names are * different, another issuer has signed the server certificate and * vouched for it. In this case the server certificate is valid if * verified by the issuer public key. * * Returns certificate info/value pointer if valid, NULL if not. */ struct cert_info * /* certificate information structure */ cert_parse( u_char *asn1cert, /* X509 certificate */ u_int len, /* certificate length */ tstamp_t fstamp /* filestamp */ ) { X509 *cert; /* X509 certificate */ X509_EXTENSION *ext; /* X509v3 extension */ struct cert_info *ret; /* certificate info/value */ BIO *bp; X509V3_EXT_METHOD *method; char pathbuf[MAXFILENAME]; u_char *uptr; char *ptr; int temp, cnt, i; /* * Decode ASN.1 objects and construct certificate structure. */ uptr = asn1cert; if ((cert = d2i_X509(NULL, &uptr, len)) == NULL) { msyslog(LOG_ERR, "cert_parse %s\n", ERR_error_string(ERR_get_error(), NULL)); return (NULL); } /* * Extract version, subject name and public key. */ ret = emalloc(sizeof(struct cert_info)); memset(ret, 0, sizeof(struct cert_info)); if ((ret->pkey = X509_get_pubkey(cert)) == NULL) { msyslog(LOG_ERR, "cert_parse %s\n", ERR_error_string(ERR_get_error(), NULL)); cert_free(ret); X509_free(cert); return (NULL); } ret->version = X509_get_version(cert); X509_NAME_oneline(X509_get_subject_name(cert), pathbuf, MAXFILENAME - 1); ptr = strstr(pathbuf, "CN="); if (ptr == NULL) { msyslog(LOG_INFO, "cert_parse: invalid subject %s", pathbuf); cert_free(ret); X509_free(cert); return (NULL); } ret->subject = emalloc(strlen(ptr) + 1); strcpy(ret->subject, ptr + 3); /* * Extract remaining objects. Note that the NTP serial number is * the NTP seconds at the time of signing, but this might not be * the case for other authority. We don't bother to check the * objects at this time, since the real crunch can happen only * when the time is valid but not yet certificated. */ ret->nid = OBJ_obj2nid(cert->cert_info->signature->algorithm); ret->digest = (const EVP_MD *)EVP_get_digestbynid(ret->nid); ret->serial = (u_long)ASN1_INTEGER_get(X509_get_serialNumber(cert)); X509_NAME_oneline(X509_get_issuer_name(cert), pathbuf, MAXFILENAME); if ((ptr = strstr(pathbuf, "CN=")) == NULL) { msyslog(LOG_INFO, "cert_parse: invalid issuer %s", pathbuf); cert_free(ret); X509_free(cert); return (NULL); } ret->issuer = emalloc(strlen(ptr) + 1); strcpy(ret->issuer, ptr + 3); ret->first = asn2ntp(X509_get_notBefore(cert)); ret->last = asn2ntp(X509_get_notAfter(cert)); /* * Extract extension fields. These are ad hoc ripoffs of * currently assigned functions and will certainly be changed * before prime time. */ cnt = X509_get_ext_count(cert); for (i = 0; i < cnt; i++) { ext = X509_get_ext(cert, i); method = X509V3_EXT_get(ext); temp = OBJ_obj2nid(ext->object); switch (temp) { /* * If a key_usage field is present, we decode whether * this is a trusted or private certificate. This is * dorky; all we want is to compare NIDs, but OpenSSL * insists on BIO text strings. */ case NID_ext_key_usage: bp = BIO_new(BIO_s_mem()); X509V3_EXT_print(bp, ext, 0, 0); BIO_gets(bp, pathbuf, MAXFILENAME); BIO_free(bp); #if DEBUG if (debug) printf("cert_parse: %s: %s\n", OBJ_nid2ln(temp), pathbuf); #endif if (strcmp(pathbuf, "Trust Root") == 0) ret->flags |= CERT_TRUST; else if (strcmp(pathbuf, "Private") == 0) ret->flags |= CERT_PRIV; break; /* * If a NID_subject_key_identifier field is present, it * contains the GQ public key. */ case NID_subject_key_identifier: ret->grplen = ext->value->length - 2; ret->grpkey = emalloc(ret->grplen); memcpy(ret->grpkey, &ext->value->data[2], ret->grplen); break; } } /* * If certificate is self signed, verify signature. */ if (strcmp(ret->subject, ret->issuer) == 0) { if (!X509_verify(cert, ret->pkey)) { msyslog(LOG_INFO, "cert_parse: signature not verified %s", pathbuf); cert_free(ret); X509_free(cert); return (NULL); } } /* * Verify certificate valid times. Note that certificates cannot * be retroactive. */ if (ret->first > ret->last || ret->first < fstamp) { msyslog(LOG_INFO, "cert_parse: invalid certificate %s first %u last %u fstamp %u", ret->subject, ret->first, ret->last, fstamp); cert_free(ret); X509_free(cert); return (NULL); } /* * Build the value structure to sign and send later. */ ret->cert.fstamp = htonl(fstamp); ret->cert.vallen = htonl(len); ret->cert.ptr = emalloc(len); memcpy(ret->cert.ptr, asn1cert, len); #ifdef DEBUG if (debug > 1) X509_print_fp(stdout, cert); #endif X509_free(cert); return (ret); } /* * cert_sign - sign x509 certificate equest and update value structure. * * The certificate request includes a copy of the host certificate, * which includes the version number, subject name and public key of the * host. The resulting certificate includes these values plus the * serial number, issuer name and valid interval of the server. The * valid interval extends from the current time to the same time one * year hence. This may extend the life of the signed certificate beyond * that of the signer certificate. * * It is convenient to use the NTP seconds of the current time as the * serial number. In the value structure the timestamp is the current * time and the filestamp is taken from the extension field. Note this * routine is called only when the client clock is synchronized to a * proventic source, so timestamp comparisons are valid. * * The host certificate is valid from the time it was generated for a * period of one year. A signed certificate is valid from the time of * signature for a period of one year, but only the host certificate (or * sign certificate if used) is actually used to encrypt and decrypt * signatures. The signature trail is built from the client via the * intermediate servers to the trusted server. Each signature on the * trail must be valid at the time of signature, but it could happen * that a signer certificate expire before the signed certificate, which * remains valid until its expiration. * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_CRT bad or missing certificate * XEVNT_VFY certificate not verified * XEVNT_PER host certificate expired */ static int cert_sign( struct exten *ep, /* extension field pointer */ struct value *vp /* value pointer */ ) { X509 *req; /* X509 certificate request */ X509 *cert; /* X509 certificate */ X509_EXTENSION *ext; /* certificate extension */ ASN1_INTEGER *serial; /* serial number */ X509_NAME *subj; /* distinguished (common) name */ EVP_PKEY *pkey; /* public key */ EVP_MD_CTX ctx; /* message digest context */ tstamp_t tstamp; /* NTP timestamp */ u_int len; u_char *ptr; int i, temp; /* * Decode ASN.1 objects and construct certificate structure. * Make sure the system clock is synchronized to a proventic * source. */ tstamp = crypto_time(); if (tstamp == 0) return (XEVNT_TSP); if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); ptr = (u_char *)ep->pkt; if ((req = d2i_X509(NULL, &ptr, ntohl(ep->vallen))) == NULL) { msyslog(LOG_ERR, "cert_sign %s\n", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_CRT); } /* * Extract public key and check for errors. */ if ((pkey = X509_get_pubkey(req)) == NULL) { msyslog(LOG_ERR, "cert_sign %s\n", ERR_error_string(ERR_get_error(), NULL)); X509_free(req); return (XEVNT_PUB); } /* * Generate X509 certificate signed by this server. For this * purpose the issuer name is the server name. Also copy any * extensions that might be present. */ cert = X509_new(); X509_set_version(cert, X509_get_version(req)); serial = ASN1_INTEGER_new(); ASN1_INTEGER_set(serial, tstamp); X509_set_serialNumber(cert, serial); X509_gmtime_adj(X509_get_notBefore(cert), 0L); X509_gmtime_adj(X509_get_notAfter(cert), YEAR); subj = X509_get_issuer_name(cert); X509_NAME_add_entry_by_txt(subj, "commonName", MBSTRING_ASC, (u_char *)sys_hostname, strlen(sys_hostname), -1, 0); subj = X509_get_subject_name(req); X509_set_subject_name(cert, subj); X509_set_pubkey(cert, pkey); ext = X509_get_ext(req, 0); temp = X509_get_ext_count(req); for (i = 0; i < temp; i++) { ext = X509_get_ext(req, i); X509_add_ext(cert, ext, -1); } X509_free(req); /* * Sign and verify the certificate. */ X509_sign(cert, sign_pkey, sign_digest); if (!X509_verify(cert, sign_pkey)) { printf("cert_sign\n%s\n", ERR_error_string(ERR_get_error(), NULL)); X509_free(cert); return (XEVNT_VFY); } len = i2d_X509(cert, NULL); /* * Build and sign the value structure. We have to sign it here, * since the response has to be returned right away. This is a * clogging hazard. */ memset(vp, 0, sizeof(struct value)); vp->tstamp = htonl(tstamp); vp->fstamp = ep->fstamp; vp->vallen = htonl(len); vp->ptr = emalloc(len); ptr = vp->ptr; i2d_X509(cert, &ptr); vp->siglen = 0; vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)vp, 12); EVP_SignUpdate(&ctx, vp->ptr, len); if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey)) vp->siglen = htonl(len); #ifdef DEBUG if (debug > 1) X509_print_fp(stdout, cert); #endif X509_free(cert); return (XEVNT_OK); } /* * cert_valid - verify certificate with given public key * * This is pretty ugly, as the certificate has to be verified in the * OpenSSL X509 structure, not in the DER format in the info/value * structure. * * Returns * XEVNT_OK success * XEVNT_VFY certificate not verified */ int cert_valid( struct cert_info *cinf, /* certificate information structure */ EVP_PKEY *pkey /* public key */ ) { X509 *cert; /* X509 certificate */ u_char *ptr; if (cinf->flags & CERT_SIGN) return (XEVNT_OK); ptr = (u_char *)cinf->cert.ptr; cert = d2i_X509(NULL, &ptr, ntohl(cinf->cert.vallen)); if (cert == NULL || !X509_verify(cert, pkey)) return (XEVNT_VFY); X509_free(cert); return (XEVNT_OK); } /* * cert - install certificate in certificate list * * This routine encodes an extension field into a certificate info/value * structure. It searches the certificate list for duplicates and * expunges whichever is older. It then searches the list for other * certificates that might be verified by this latest one. Finally, it * inserts this certificate first on the list. * * Returns * XEVNT_OK success * XEVNT_FSP bad or missing filestamp * XEVNT_CRT bad or missing certificate */ int cert_install( struct exten *ep, /* cert info/value */ struct peer *peer /* peer structure */ ) { struct cert_info *cp, *xp, *yp, **zp; /* * Parse and validate the signed certificate. If valid, * construct the info/value structure; otherwise, scamper home. */ if ((cp = cert_parse((u_char *)ep->pkt, ntohl(ep->vallen), ntohl(ep->fstamp))) == NULL) return (XEVNT_CRT); /* * Scan certificate list looking for another certificate with * the same subject and issuer. If another is found with the * same or older filestamp, unlink it and return the goodies to * the heap. If another is found with a later filestamp, discard * the new one and leave the building. * * Make a note to study this issue again. An earlier certificate * with a long lifetime might be overtaken by a later * certificate with a short lifetime, thus invalidating the * earlier signature. However, we gotta find a way to leak old * stuff from the cache, so we do it anyway. */ yp = cp; zp = &cinfo; for (xp = cinfo; xp != NULL; xp = xp->link) { if (strcmp(cp->subject, xp->subject) == 0 && strcmp(cp->issuer, xp->issuer) == 0) { if (ntohl(cp->cert.fstamp) <= ntohl(xp->cert.fstamp)) { *zp = xp->link;; cert_free(xp); } else { cert_free(cp); return (XEVNT_FSP); } break; } zp = &xp->link; } yp->link = cinfo; cinfo = yp; /* * Scan the certificate list to see if Y is signed by X. This is * independent of order. */ for (yp = cinfo; yp != NULL; yp = yp->link) { for (xp = cinfo; xp != NULL; xp = xp->link) { /* * If the issuer of certificate Y matches the * subject of certificate X, verify the * signature of Y using the public key of X. If * so, X signs Y. */ if (strcmp(yp->issuer, xp->subject) != 0 || xp->flags & CERT_ERROR) continue; if (cert_valid(yp, xp->pkey) != XEVNT_OK) { yp->flags |= CERT_ERROR; continue; } /* * The signature Y is valid only if it begins * during the lifetime of X; however, it is not * necessarily an error, since some other * certificate might sign Y. */ if (yp->first < xp->first || yp->first > xp->last) continue; yp->flags |= CERT_SIGN; /* * If X is trusted, then Y is trusted. Note that * we might stumble over a self-signed * certificate that is not trusted, at least * temporarily. This can happen when a dude * first comes up, but has not synchronized the * clock and had its certificate signed by its * server. In case of broken certificate trail, * this might result in a loop that could * persist until timeout. */ if (!(xp->flags & (CERT_TRUST | CERT_VALID))) continue; yp->flags |= CERT_VALID; /* * If subject Y matches the server subject name, * then Y has completed the certificate trail. * Save the group key and light the valid bit. */ if (strcmp(yp->subject, peer->subject) != 0) continue; if (yp->grpkey != NULL) { if (peer->grpkey != NULL) BN_free(peer->grpkey); peer->grpkey = BN_bin2bn(yp->grpkey, yp->grplen, NULL); } peer->crypto |= CRYPTO_FLAG_VALID; /* * If the server has an an identity scheme, * fetch the identity credentials. If not, the * identity is verified only by the trusted * certificate. The next signature will set the * server proventic. */ if (peer->crypto & (CRYPTO_FLAG_GQ | CRYPTO_FLAG_IFF | CRYPTO_FLAG_MV)) continue; peer->crypto |= CRYPTO_FLAG_VRFY; } } /* * That was awesome. Now update the timestamps and signatures. */ crypto_update(); return (XEVNT_OK); } /* * cert_free - free certificate information structure */ void cert_free( struct cert_info *cinf /* certificate info/value structure */ ) { if (cinf->pkey != NULL) EVP_PKEY_free(cinf->pkey); if (cinf->subject != NULL) free(cinf->subject); if (cinf->issuer != NULL) free(cinf->issuer); if (cinf->grpkey != NULL) free(cinf->grpkey); value_free(&cinf->cert); free(cinf); } /* *********************************************************************** * * * The following routines are used only at initialization time * * * *********************************************************************** */ /* * crypto_key - load cryptographic parameters and keys from files * * This routine loads a PEM-encoded public/private key pair and extracts * the filestamp from the file name. * * Returns public key pointer if valid, NULL if not. Side effect updates * the filestamp if valid. */ static EVP_PKEY * crypto_key( char *cp, /* file name */ tstamp_t *fstamp /* filestamp */ ) { FILE *str; /* file handle */ EVP_PKEY *pkey = NULL; /* public/private key */ char filename[MAXFILENAME]; /* name of key file */ char linkname[MAXFILENAME]; /* filestamp buffer) */ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */ char *ptr; /* * Open the key file. If the first character of the file name is * not '/', prepend the keys directory string. If something goes * wrong, abandon ship. */ if (*cp == '/') strcpy(filename, cp); else snprintf(filename, MAXFILENAME, "%s/%s", keysdir, cp); str = fopen(filename, "r"); if (str == NULL) return (NULL); /* * Read the filestamp, which is contained in the first line. */ if ((ptr = fgets(linkname, MAXFILENAME, str)) == NULL) { msyslog(LOG_ERR, "crypto_key: no data %s\n", filename); (void)fclose(str); return (NULL); } if ((ptr = strrchr(ptr, '.')) == NULL) { msyslog(LOG_ERR, "crypto_key: no filestamp %s\n", filename); (void)fclose(str); return (NULL); } if (sscanf(++ptr, "%u", fstamp) != 1) { msyslog(LOG_ERR, "crypto_key: invalid timestamp %s\n", filename); (void)fclose(str); return (NULL); } /* * Read and decrypt PEM-encoded private key. */ pkey = PEM_read_PrivateKey(str, NULL, NULL, passwd); fclose(str); if (pkey == NULL) { msyslog(LOG_ERR, "crypto_key %s\n", ERR_error_string(ERR_get_error(), NULL)); return (NULL); } /* * Leave tracks in the cryptostats. */ if ((ptr = strrchr(linkname, '\n')) != NULL) *ptr = '\0'; snprintf(statstr, NTP_MAXSTRLEN, "%s mod %d", &linkname[2], EVP_PKEY_size(pkey) * 8); record_crypto_stats(NULL, statstr); #ifdef DEBUG if (debug) printf("crypto_key: %s\n", statstr); if (debug > 1) { if (pkey->type == EVP_PKEY_DSA) DSA_print_fp(stdout, pkey->pkey.dsa, 0); else RSA_print_fp(stdout, pkey->pkey.rsa, 0); } #endif return (pkey); } /* * crypto_cert - load certificate from file * * This routine loads a X.509 RSA or DSA certificate from a file and * constructs a info/cert value structure for this machine. The * structure includes a filestamp extracted from the file name. Later * the certificate can be sent to another machine by request. * * Returns certificate info/value pointer if valid, NULL if not. */ static struct cert_info * /* certificate information */ crypto_cert( char *cp /* file name */ ) { struct cert_info *ret; /* certificate information */ FILE *str; /* file handle */ char filename[MAXFILENAME]; /* name of certificate file */ char linkname[MAXFILENAME]; /* filestamp buffer */ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */ tstamp_t fstamp; /* filestamp */ long len; char *ptr; char *name, *header; u_char *data; /* * Open the certificate file. If the first character of the file * name is not '/', prepend the keys directory string. If * something goes wrong, abandon ship. */ if (*cp == '/') strcpy(filename, cp); else snprintf(filename, MAXFILENAME, "%s/%s", keysdir, cp); str = fopen(filename, "r"); if (str == NULL) return (NULL); /* * Read the filestamp, which is contained in the first line. */ if ((ptr = fgets(linkname, MAXFILENAME, str)) == NULL) { msyslog(LOG_ERR, "crypto_cert: no data %s\n", filename); (void)fclose(str); return (NULL); } if ((ptr = strrchr(ptr, '.')) == NULL) { msyslog(LOG_ERR, "crypto_cert: no filestamp %s\n", filename); (void)fclose(str); return (NULL); } if (sscanf(++ptr, "%u", &fstamp) != 1) { msyslog(LOG_ERR, "crypto_cert: invalid filestamp %s\n", filename); (void)fclose(str); return (NULL); } /* * Read PEM-encoded certificate and install. */ if (!PEM_read(str, &name, &header, &data, &len)) { msyslog(LOG_ERR, "crypto_cert %s\n", ERR_error_string(ERR_get_error(), NULL)); (void)fclose(str); return (NULL); } free(header); if (strcmp(name, "CERTIFICATE") !=0) { msyslog(LOG_INFO, "crypto_cert: wrong PEM type %s", name); free(name); free(data); (void)fclose(str); return (NULL); } free(name); /* * Parse certificate and generate info/value structure. */ ret = cert_parse(data, len, fstamp); free(data); (void)fclose(str); if (ret == NULL) return (NULL); if ((ptr = strrchr(linkname, '\n')) != NULL) *ptr = '\0'; snprintf(statstr, NTP_MAXSTRLEN, "%s 0x%x len %lu", &linkname[2], ret->flags, len); record_crypto_stats(NULL, statstr); #ifdef DEBUG if (debug) printf("crypto_cert: %s\n", statstr); #endif return (ret); } /* * crypto_tai - load leapseconds table from file * * This routine loads the ERTS leapsecond file in NIST text format, * converts to a value structure and extracts a filestamp from the file * name. The data are used to establish the TAI offset from UTC, which * is provided to the kernel if supported. Later the data can be sent to * another machine on request. */ static void crypto_tai( char *cp /* file name */ ) { FILE *str; /* file handle */ char buf[NTP_MAXSTRLEN]; /* file line buffer */ u_int32 leapsec[MAX_LEAP]; /* NTP time at leaps */ int offset; /* offset at leap (s) */ char filename[MAXFILENAME]; /* name of leapseconds file */ char linkname[MAXFILENAME]; /* file link (for filestamp) */ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */ tstamp_t fstamp; /* filestamp */ u_int len; u_int32 *ptr; char *dp; int rval, i, j; /* * Open the file and discard comment lines. If the first * character of the file name is not '/', prepend the keys * directory string. If the file is not found, not to worry; it * can be retrieved over the net. But, if it is found with * errors, we crash and burn. */ if (*cp == '/') strcpy(filename, cp); else snprintf(filename, MAXFILENAME, "%s/%s", keysdir, cp); if ((str = fopen(filename, "r")) == NULL) return; /* * Extract filestamp if present. */ rval = readlink(filename, linkname, MAXFILENAME - 1); if (rval > 0) { linkname[rval] = '\0'; dp = strrchr(linkname, '.'); } else { dp = strrchr(filename, '.'); } if (dp != NULL) sscanf(++dp, "%u", &fstamp); else fstamp = 0; tai_leap.fstamp = htonl(fstamp); /* * We are rather paranoid here, since an intruder might cause a * coredump by infiltrating naughty values. Empty lines and * comments are ignored. Other lines must begin with two * integers followed by junk or comments. The first integer is * the NTP seconds of leap insertion, the second is the offset * of TAI relative to UTC after that insertion. The second word * must equal the initial insertion of ten seconds on 1 January * 1972 plus one second for each succeeding insertion. */ i = 0; while (i < MAX_LEAP) { dp = fgets(buf, NTP_MAXSTRLEN - 1, str); if (dp == NULL) break; if (strlen(buf) < 1) continue; if (*buf == '#') continue; if (sscanf(buf, "%u %d", &leapsec[i], &offset) != 2) continue; if (i != offset - TAI_1972) break; i++; } fclose(str); if (dp != NULL) { msyslog(LOG_INFO, "crypto_tai: leapseconds file %s error %d", cp, rval); exit (-1); } /* * The extension field table entries consists of the NTP seconds * of leap insertion in network byte order. */ len = i * sizeof(u_int32); tai_leap.vallen = htonl(len); ptr = emalloc(len); tai_leap.ptr = (u_char *)ptr; for (j = 0; j < i; j++) *ptr++ = htonl(leapsec[j]); crypto_flags |= CRYPTO_FLAG_TAI; snprintf(statstr, NTP_MAXSTRLEN, "%s fs %u leap %u len %u", cp, fstamp, leapsec[--j], len); record_crypto_stats(NULL, statstr); #ifdef DEBUG if (debug) printf("crypto_tai: %s\n", statstr); #endif } /* * crypto_setup - load keys, certificate and leapseconds table * * This routine loads the public/private host key and certificate. If * available, it loads the public/private sign key, which defaults to * the host key, and leapseconds table. The host key must be RSA, but * the sign key can be either RSA or DSA. In either case, the public key * on the certificate must agree with the sign key. */ void crypto_setup(void) { EVP_PKEY *pkey; /* private/public key pair */ char filename[MAXFILENAME]; /* file name buffer */ l_fp seed; /* crypto PRNG seed as NTP timestamp */ tstamp_t fstamp; /* filestamp */ tstamp_t sstamp; /* sign filestamp */ u_int len, bytes; u_char *ptr; /* * Initialize structures. */ if (!crypto_flags) return; gethostname(filename, MAXFILENAME); bytes = strlen(filename) + 1; sys_hostname = emalloc(bytes); memcpy(sys_hostname, filename, bytes); if (passwd == NULL) passwd = sys_hostname; memset(&hostval, 0, sizeof(hostval)); memset(&pubkey, 0, sizeof(pubkey)); memset(&tai_leap, 0, sizeof(tai_leap)); /* * Load required random seed file and seed the random number * generator. Be default, it is found in the user home * directory. The root home directory may be / or /root, * depending on the system. Wiggle the contents a bit and write * it back so the sequence does not repeat when we next restart. */ ERR_load_crypto_strings(); if (rand_file == NULL) { if ((RAND_file_name(filename, MAXFILENAME)) != NULL) { rand_file = emalloc(strlen(filename) + 1); strcpy(rand_file, filename); } } else if (*rand_file != '/') { snprintf(filename, MAXFILENAME, "%s/%s", keysdir, rand_file); free(rand_file); rand_file = emalloc(strlen(filename) + 1); strcpy(rand_file, filename); } if (rand_file == NULL) { msyslog(LOG_ERR, "crypto_setup: random seed file not specified"); exit (-1); } if ((bytes = RAND_load_file(rand_file, -1)) == 0) { msyslog(LOG_ERR, "crypto_setup: random seed file %s not found\n", rand_file); exit (-1); } - get_systime(&seed); + arc4random_buf(&seed, sizeof(l_fp)); RAND_seed(&seed, sizeof(l_fp)); RAND_write_file(rand_file); OpenSSL_add_all_algorithms(); #ifdef DEBUG if (debug) printf( "crypto_setup: OpenSSL version %lx random seed file %s bytes read %d\n", SSLeay(), rand_file, bytes); #endif /* * Load required host key from file "ntpkey_host_". It * also becomes the default sign key. */ if (host_file == NULL) { snprintf(filename, MAXFILENAME, "ntpkey_host_%s", sys_hostname); host_file = emalloc(strlen(filename) + 1); strcpy(host_file, filename); } pkey = crypto_key(host_file, &fstamp); if (pkey == NULL) { msyslog(LOG_ERR, "crypto_setup: host key file %s not found or corrupt", host_file); exit (-1); } host_pkey = pkey; sign_pkey = pkey; sstamp = fstamp; hostval.fstamp = htonl(fstamp); if (host_pkey->type != EVP_PKEY_RSA) { msyslog(LOG_ERR, "crypto_setup: host key is not RSA key type"); exit (-1); } hostval.vallen = htonl(strlen(sys_hostname)); hostval.ptr = (u_char *)sys_hostname; /* * Construct public key extension field for agreement scheme. */ len = i2d_PublicKey(host_pkey, NULL); ptr = emalloc(len); pubkey.ptr = ptr; i2d_PublicKey(host_pkey, &ptr); pubkey.vallen = htonl(len); pubkey.fstamp = hostval.fstamp; /* * Load optional sign key from file "ntpkey_sign_". If * loaded, it becomes the sign key. */ if (sign_file == NULL) { snprintf(filename, MAXFILENAME, "ntpkey_sign_%s", sys_hostname); sign_file = emalloc(strlen(filename) + 1); strcpy(sign_file, filename); } pkey = crypto_key(sign_file, &fstamp); if (pkey != NULL) { sign_pkey = pkey; sstamp = fstamp; } sign_siglen = EVP_PKEY_size(sign_pkey); /* * Load optional IFF parameters from file * "ntpkey_iff_". */ if (iffpar_file == NULL) { snprintf(filename, MAXFILENAME, "ntpkey_iff_%s", sys_hostname); iffpar_file = emalloc(strlen(filename) + 1); strcpy(iffpar_file, filename); } iffpar_pkey = crypto_key(iffpar_file, &if_fstamp); if (iffpar_pkey != NULL) crypto_flags |= CRYPTO_FLAG_IFF; /* * Load optional GQ parameters from file "ntpkey_gq_". */ if (gqpar_file == NULL) { snprintf(filename, MAXFILENAME, "ntpkey_gq_%s", sys_hostname); gqpar_file = emalloc(strlen(filename) + 1); strcpy(gqpar_file, filename); } gqpar_pkey = crypto_key(gqpar_file, &gq_fstamp); if (gqpar_pkey != NULL) crypto_flags |= CRYPTO_FLAG_GQ; /* * Load optional MV parameters from file "ntpkey_mv_". */ if (mvpar_file == NULL) { snprintf(filename, MAXFILENAME, "ntpkey_mv_%s", sys_hostname); mvpar_file = emalloc(strlen(filename) + 1); strcpy(mvpar_file, filename); } mvpar_pkey = crypto_key(mvpar_file, &mv_fstamp); if (mvpar_pkey != NULL) crypto_flags |= CRYPTO_FLAG_MV; /* * Load required certificate from file "ntpkey_cert_". */ if (cert_file == NULL) { snprintf(filename, MAXFILENAME, "ntpkey_cert_%s", sys_hostname); cert_file = emalloc(strlen(filename) + 1); strcpy(cert_file, filename); } if ((cinfo = crypto_cert(cert_file)) == NULL) { msyslog(LOG_ERR, "certificate file %s not found or corrupt", cert_file); exit (-1); } /* * The subject name must be the same as the host name, unless * the certificate is private, in which case it may have come * from another host. */ if (!(cinfo->flags & CERT_PRIV) && strcmp(cinfo->subject, sys_hostname) != 0) { msyslog(LOG_ERR, "crypto_setup: certificate %s not for this host", cert_file); cert_free(cinfo); exit (-1); } /* * It the certificate is trusted, the subject must be the same * as the issuer, in other words it must be self signed. */ if (cinfo->flags & CERT_TRUST && strcmp(cinfo->subject, cinfo->issuer) != 0) { if (cert_valid(cinfo, sign_pkey) != XEVNT_OK) { msyslog(LOG_ERR, "crypto_setup: certificate %s is trusted, but not self signed.", cert_file); cert_free(cinfo); exit (-1); } } sign_digest = cinfo->digest; if (cinfo->flags & CERT_PRIV) crypto_flags |= CRYPTO_FLAG_PRIV; crypto_flags |= cinfo->nid << 16; /* * Load optional leapseconds table from file "ntpkey_leap". If * the file is missing or defective, the values can later be * retrieved from a server. */ if (leap_file == NULL) leap_file = "ntpkey_leap"; crypto_tai(leap_file); #ifdef DEBUG if (debug) printf( "crypto_setup: flags 0x%x host %s signature %s\n", crypto_flags, sys_hostname, OBJ_nid2ln(cinfo->nid)); #endif } /* * crypto_config - configure data from crypto configuration command. */ void crypto_config( int item, /* configuration item */ char *cp /* file name */ ) { switch (item) { /* * Set random seed file name. */ case CRYPTO_CONF_RAND: rand_file = emalloc(strlen(cp) + 1); strcpy(rand_file, cp); break; /* * Set private key password. */ case CRYPTO_CONF_PW: passwd = emalloc(strlen(cp) + 1); strcpy(passwd, cp); break; /* * Set host file name. */ case CRYPTO_CONF_PRIV: host_file = emalloc(strlen(cp) + 1); strcpy(host_file, cp); break; /* * Set sign key file name. */ case CRYPTO_CONF_SIGN: sign_file = emalloc(strlen(cp) + 1); strcpy(sign_file, cp); break; /* * Set iff parameters file name. */ case CRYPTO_CONF_IFFPAR: iffpar_file = emalloc(strlen(cp) + 1); strcpy(iffpar_file, cp); break; /* * Set gq parameters file name. */ case CRYPTO_CONF_GQPAR: gqpar_file = emalloc(strlen(cp) + 1); strcpy(gqpar_file, cp); break; /* * Set mv parameters file name. */ case CRYPTO_CONF_MVPAR: mvpar_file = emalloc(strlen(cp) + 1); strcpy(mvpar_file, cp); break; /* * Set identity scheme. */ case CRYPTO_CONF_IDENT: if (!strcasecmp(cp, "iff")) ident_scheme |= CRYPTO_FLAG_IFF; else if (!strcasecmp(cp, "gq")) ident_scheme |= CRYPTO_FLAG_GQ; else if (!strcasecmp(cp, "mv")) ident_scheme |= CRYPTO_FLAG_MV; break; /* * Set certificate file name. */ case CRYPTO_CONF_CERT: cert_file = emalloc(strlen(cp) + 1); strcpy(cert_file, cp); break; /* * Set leapseconds file name. */ case CRYPTO_CONF_LEAP: leap_file = emalloc(strlen(cp) + 1); strcpy(leap_file, cp); break; } crypto_flags |= CRYPTO_FLAG_ENAB; } # else int ntp_crypto_bs_pubkey; # endif /* OPENSSL */ Index: releng/10.1/contrib/ntp/ntpd/ntp_proto.c =================================================================== --- releng/10.1/contrib/ntp/ntpd/ntp_proto.c (revision 276158) +++ releng/10.1/contrib/ntp/ntpd/ntp_proto.c (revision 276159) @@ -1,3450 +1,3451 @@ /* * ntp_proto.c - NTP version 4 protocol machinery * * ATTENTION: Get approval from Dave Mills on all changes to this file! * */ #ifdef HAVE_CONFIG_H #include #endif #include "ntpd.h" #include "ntp_stdlib.h" #include "ntp_unixtime.h" #include "ntp_control.h" #include "ntp_string.h" #include #if defined(VMS) && defined(VMS_LOCALUNIT) /*wjm*/ #include "ntp_refclock.h" #endif #if defined(__FreeBSD__) && __FreeBSD__ >= 3 #include #endif /* * This macro defines the authentication state. If x is 1 authentication * is required; othewise it is optional. */ #define AUTH(x, y) ((x) ? (y) == AUTH_OK : (y) == AUTH_OK || \ (y) == AUTH_NONE) /* * System variables are declared here. See Section 3.2 of the * specification. */ u_char sys_leap; /* system leap indicator */ u_char sys_stratum; /* stratum of system */ s_char sys_precision; /* local clock precision (log2 s) */ double sys_rootdelay; /* roundtrip delay to primary source */ double sys_rootdispersion; /* dispersion to primary source */ u_int32 sys_refid; /* source/loop in network byte order */ static double sys_offset; /* current local clock offset */ l_fp sys_reftime; /* time we were last updated */ struct peer *sys_peer; /* our current peer */ struct peer *sys_pps; /* our PPS peer */ struct peer *sys_prefer; /* our cherished peer */ int sys_kod; /* kod credit */ int sys_kod_rate = 2; /* max kod packets per second */ #ifdef OPENSSL u_long sys_automax; /* maximum session key lifetime */ #endif /* OPENSSL */ /* * Nonspecified system state variables. */ int sys_bclient; /* broadcast client enable */ double sys_bdelay; /* broadcast client default delay */ int sys_calldelay; /* modem callup delay (s) */ int sys_authenticate; /* requre authentication for config */ l_fp sys_authdelay; /* authentication delay */ static u_long sys_authdly[2]; /* authentication delay shift reg */ static double sys_mindisp = MINDISPERSE; /* min disp increment (s) */ static double sys_maxdist = MAXDISTANCE; /* selection threshold (s) */ double sys_jitter; /* system jitter (s) */ static int sys_hopper; /* anticlockhop counter */ static int sys_maxhop = MAXHOP; /* anticlockhop counter threshold */ int leap_next; /* leap consensus */ keyid_t sys_private; /* private value for session seed */ int sys_manycastserver; /* respond to manycast client pkts */ int peer_ntpdate; /* active peers in ntpdate mode */ int sys_survivors; /* truest of the truechimers */ #ifdef OPENSSL char *sys_hostname; /* gethostname() name */ #endif /* OPENSSL */ /* * TOS and multicast mapping stuff */ int sys_floor = 0; /* cluster stratum floor */ int sys_ceiling = STRATUM_UNSPEC; /* cluster stratum ceiling */ int sys_minsane = 1; /* minimum candidates */ int sys_minclock = NTP_MINCLOCK; /* minimum survivors */ int sys_maxclock = NTP_MAXCLOCK; /* maximum candidates */ int sys_cohort = 0; /* cohort switch */ int sys_orphan = STRATUM_UNSPEC + 1; /* orphan stratum */ double sys_orphandelay = 0; /* orphan root delay */ int sys_beacon = BEACON; /* manycast beacon interval */ int sys_ttlmax; /* max ttl mapping vector index */ u_char sys_ttl[MAX_TTL]; /* ttl mapping vector */ /* * Statistics counters */ u_long sys_stattime; /* time since reset */ u_long sys_received; /* packets received */ u_long sys_processed; /* packets processed */ u_long sys_newversionpkt; /* current version */ u_long sys_oldversionpkt; /* recent version */ u_long sys_unknownversion; /* invalid version */ u_long sys_restricted; /* access denied */ u_long sys_badlength; /* bad length or format */ u_long sys_badauth; /* bad authentication */ u_long sys_limitrejected; /* rate exceeded */ static double root_distance P((struct peer *)); static void clock_combine P((struct peer **, int)); static void peer_xmit P((struct peer *)); static void fast_xmit P((struct recvbuf *, int, keyid_t, int)); static void clock_update P((void)); static int default_get_precision P((void)); static int peer_unfit P((struct peer *)); /* * transmit - Transmit Procedure. See Section 3.4.2 of the * specification. */ void transmit( struct peer *peer /* peer structure pointer */ ) { int hpoll; /* * The polling state machine. There are two kinds of machines, * those that never expect a reply (broadcast and manycast * server modes) and those that do (all other modes). The dance * is intricate... */ /* * Orphan mode is active when enabled and when no servers less * than the orphan statum are available. In this mode packets * are sent at the orphan stratum. An orphan with no other * synchronization source is an orphan parent. It assumes root * delay zero and reference ID the loopback address. All others * are orphan children with root delay randomized over a 1-s * range. The root delay is used by the election algorithm to * select the order of synchronization. */ hpoll = peer->hpoll; if (sys_orphan < STRATUM_UNSPEC && sys_peer == NULL) { sys_leap = LEAP_NOWARNING; sys_stratum = sys_orphan; sys_refid = htonl(LOOPBACKADR); sys_rootdelay = 0; sys_rootdispersion = 0; } /* * In broadcast mode the poll interval is never changed from * minpoll. */ if (peer->cast_flags & (MDF_BCAST | MDF_MCAST)) { peer->outdate = current_time; peer_xmit(peer); poll_update(peer, hpoll); return; } /* * In manycast mode we start with unity ttl. The ttl is * increased by one for each poll until either sys_maxclock * servers have been found or the maximum ttl is reached. When * sys_maxclock servers are found we stop polling until one or * more servers have timed out or until less than minpoll * associations turn up. In this case additional better servers * are dragged in and preempt the existing ones. */ if (peer->cast_flags & MDF_ACAST) { peer->outdate = current_time; if (peer->unreach > sys_beacon) { peer->unreach = 0; peer->ttl = 0; peer_xmit(peer); } else if (sys_survivors < sys_minclock || peer_preempt < sys_maxclock) { if (peer->ttl < sys_ttlmax) peer->ttl++; peer_xmit(peer); } peer->unreach++; poll_update(peer, hpoll); return; } /* * In unicast modes the dance is much more intricate. It is * desigmed to back off whenever possible to minimize network * traffic. */ if (peer->burst == 0) { u_char oreach; /* * Update the reachability status. If not heard for * three consecutive polls, stuff infinity in the clock * filter. */ oreach = peer->reach; peer->outdate = current_time; if (peer == sys_peer) sys_hopper++; peer->reach <<= 1; if (!(peer->reach & 0x07)) clock_filter(peer, 0., 0., MAXDISPERSE); if (!peer->reach) { /* * Here the peer is unreachable. If it was * previously reachable, raise a trap. */ if (oreach) { report_event(EVNT_UNREACH, peer); peer->timereachable = current_time; } /* * Send a burst if enabled, but only once after * a peer becomes unreachable. If the prempt * flag is dim, bump the unreach counter by one; * otherwise, bump it by three. */ if (peer->flags & FLAG_IBURST && peer->unreach == 0) { peer->burst = NTP_BURST; } if (!(peer->flags & FLAG_PREEMPT)) peer->unreach++; else peer->unreach += 3; } else { /* * Here the peer is reachable. Set the poll * interval to the system poll interval. Send a * burst only if enabled and the peer is fit. * * Respond to the peer evaluation produced by * the selection algorithm. If less than the * outlyer level, up the unreach by three. If * there are excess associations, up the unreach * by two if not a candidate and by one if so. */ if (!(peer->flags & FLAG_PREEMPT)) { peer->unreach = 0; } else if (peer->status < CTL_PST_SEL_SELCAND) { peer->unreach += 3; } else if (peer_preempt > sys_maxclock) { if (peer->status < CTL_PST_SEL_SYNCCAND) peer->unreach += 2; else peer->unreach++; } else { peer->unreach = 0; } hpoll = sys_poll; if (peer->flags & FLAG_BURST && !peer_unfit(peer)) peer->burst = NTP_BURST; } /* * Watch for timeout. If ephemeral or preemptable, toss * the rascal; otherwise, bump the poll interval. */ if (peer->unreach >= NTP_UNREACH) { if (peer->flags & FLAG_PREEMPT || !(peer->flags & FLAG_CONFIG)) { peer_clear(peer, "TIME"); unpeer(peer); return; } else { hpoll++; } } } else { peer->burst--; /* * If a broadcast client at this point, the burst has * concluded, so we switch to client mode and purge the * keylist, since no further transmissions will be made. */ if (peer->burst == 0) { if (peer->cast_flags & MDF_BCLNT) { peer->hmode = MODE_BCLIENT; #ifdef OPENSSL key_expire(peer); #endif /* OPENSSL */ } /* * If ntpdate mode and the clock has not been * set and all peers have completed the burst, * we declare a successful failure. */ if (mode_ntpdate) { peer_ntpdate--; if (peer_ntpdate == 0) { msyslog(LOG_NOTICE, "no reply; clock not set"); exit (0); } } } } /* * Do not transmit if in broadcast client mode. */ if (peer->hmode != MODE_BCLIENT) peer_xmit(peer); poll_update(peer, hpoll); } /* * receive - Receive Procedure. See section 3.4.3 in the specification. */ void receive( struct recvbuf *rbufp ) { register struct peer *peer; /* peer structure pointer */ register struct pkt *pkt; /* receive packet pointer */ int hisversion; /* packet version */ int hisleap; /* packet leap indicator */ int hismode; /* packet mode */ int hisstratum; /* packet stratum */ int restrict_mask; /* restrict bits */ int has_mac; /* length of MAC field */ int authlen; /* offset of MAC field */ int is_authentic = 0; /* cryptosum ok */ keyid_t skeyid = 0; /* key ID */ struct sockaddr_storage *dstadr_sin; /* active runway */ struct peer *peer2; /* aux peer structure pointer */ l_fp p_org; /* origin timestamp */ l_fp p_rec; /* receive timestamp */ l_fp p_xmt; /* transmit timestamp */ #ifdef OPENSSL keyid_t tkeyid = 0; /* temporary key ID */ keyid_t pkeyid = 0; /* previous key ID */ struct autokey *ap; /* autokey structure pointer */ int rval; /* cookie snatcher */ #endif /* OPENSSL */ int retcode = AM_NOMATCH; int at_listhead; /* * Monitor the packet and get restrictions. Note that the packet * length for control and private mode packets must be checked * by the service routines. Note that no statistics counters are * recorded for restrict violations, since these counters are in * the restriction routine. Note the careful distinctions here * between a packet with a format error and a packet that is * simply discarded without prejudice. Some restrictions have to * be handled later in order to generate a kiss-of-death packet. */ /* * Bogus port check is before anything, since it probably * reveals a clogging attack. */ sys_received++; if (SRCPORT(&rbufp->recv_srcadr) == 0) { sys_badlength++; return; /* bogus port */ } at_listhead = ntp_monitor(rbufp); restrict_mask = restrictions(&rbufp->recv_srcadr, at_listhead); #ifdef DEBUG if (debug > 1) printf("receive: at %ld %s<-%s flags %x restrict %03x\n", current_time, stoa(&rbufp->dstadr->sin), stoa(&rbufp->recv_srcadr), rbufp->dstadr->flags, restrict_mask); #endif if (restrict_mask & RES_IGNORE) { sys_restricted++; return; /* ignore everything */ } pkt = &rbufp->recv_pkt; hisversion = PKT_VERSION(pkt->li_vn_mode); hisleap = PKT_LEAP(pkt->li_vn_mode); hismode = (int)PKT_MODE(pkt->li_vn_mode); hisstratum = PKT_TO_STRATUM(pkt->stratum); if (hismode == MODE_PRIVATE) { if (restrict_mask & RES_NOQUERY) { sys_restricted++; return; /* no query private */ } process_private(rbufp, ((restrict_mask & RES_NOMODIFY) == 0)); return; } if (hismode == MODE_CONTROL) { if (restrict_mask & RES_NOQUERY) { sys_restricted++; return; /* no query control */ } process_control(rbufp, restrict_mask); return; } if (restrict_mask & RES_DONTSERVE) { sys_restricted++; return; /* no time */ } if (rbufp->recv_length < LEN_PKT_NOMAC) { sys_badlength++; return; /* runt packet */ } /* * Version check must be after the query packets, since they * intentionally use early version. */ if (hisversion == NTP_VERSION) { sys_newversionpkt++; /* new version */ } else if (!(restrict_mask & RES_VERSION) && hisversion >= NTP_OLDVERSION) { sys_oldversionpkt++; /* previous version */ } else { sys_unknownversion++; return; /* old version */ } /* * Figure out his mode and validate the packet. This has some * legacy raunch that probably should be removed. In very early * NTP versions mode 0 was equivalent to what later versions * would interpret as client mode. */ if (hismode == MODE_UNSPEC) { if (hisversion == NTP_OLDVERSION) { hismode = MODE_CLIENT; } else { sys_badlength++; return; /* invalid mode */ } } /* * 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 2 or * 4, the packet is a runt and discarded forthwith. If greater * than 5, an extension field is present, so we subtract the * length of the field and go around again. */ authlen = LEN_PKT_NOMAC; has_mac = rbufp->recv_length - authlen; while (has_mac > 0) { int temp; if (has_mac % 4 != 0 || has_mac < 0) { sys_badlength++; return; /* bad MAC length */ } if (has_mac == 1 * 4 || has_mac == 3 * 4 || has_mac == MAX_MAC_LEN) { skeyid = ntohl(((u_int32 *)pkt)[authlen / 4]); break; } else if (has_mac > MAX_MAC_LEN) { temp = ntohl(((u_int32 *)pkt)[authlen / 4]) & 0xffff; if (temp < 4 || temp > NTP_MAXEXTEN || temp % 4 != 0) { sys_badlength++; return; /* bad MAC length */ } authlen += temp; has_mac -= temp; } else { sys_badlength++; return; /* bad MAC length */ } } #ifdef OPENSSL pkeyid = tkeyid = 0; #endif /* OPENSSL */ /* * We have tossed out as many buggy packets as possible early in * the game to reduce the exposure to a clogging attack. Now we * have to burn some cycles to find the association and * authenticate the packet if required. Note that we burn only * MD5 cycles, again to reduce exposure. There may be no * matching association and that's okay. * * More on the autokey mambo. Normally the local interface is * found when the association was mobilized with respect to a * designated remote address. We assume packets arriving from * the remote address arrive via this interface and the local * address used to construct the autokey is the unicast address * of the interface. However, if the sender is a broadcaster, * the interface broadcast address is used instead. & Notwithstanding this technobabble, if the sender is a * multicaster, the broadcast address is null, so we use the * unicast address anyway. Don't ask. */ peer = findpeer(&rbufp->recv_srcadr, rbufp->dstadr, hismode, &retcode); dstadr_sin = &rbufp->dstadr->sin; NTOHL_FP(&pkt->org, &p_org); NTOHL_FP(&pkt->rec, &p_rec); NTOHL_FP(&pkt->xmt, &p_xmt); /* * Authentication is conditioned by three switches: * * NOPEER (RES_NOPEER) do not mobilize an association unless * authenticated * NOTRUST (RES_DONTTRUST) do not allow access unless * authenticated (implies NOPEER) * enable (sys_authenticate) master NOPEER switch, by default * on * * The NOPEER and NOTRUST can be specified on a per-client basis * using the restrict command. The enable switch if on implies * NOPEER for all clients. There are four outcomes: * * NONE The packet has no MAC. * OK the packet has a MAC and authentication succeeds * ERROR the packet has a MAC and authentication fails * CRYPTO crypto-NAK. The MAC has four octets only. * * Note: The AUTH(x, y) macro is used to filter outcomes. If x * is zero, acceptable outcomes of y are NONE and OK. If x is * one, the only acceptable outcome of y is OK. */ if (has_mac == 0) { is_authentic = AUTH_NONE; /* not required */ #ifdef DEBUG if (debug) printf("receive: at %ld %s<-%s mode %d code %d auth %d\n", current_time, stoa(dstadr_sin), stoa(&rbufp->recv_srcadr), hismode, retcode, is_authentic); #endif } else if (has_mac == 4) { is_authentic = AUTH_CRYPTO; /* crypto-NAK */ #ifdef DEBUG if (debug) printf( "receive: at %ld %s<-%s mode %d code %d keyid %08x len %d mac %d auth %d\n", current_time, stoa(dstadr_sin), stoa(&rbufp->recv_srcadr), hismode, retcode, skeyid, authlen, has_mac, is_authentic); #endif } else { #ifdef OPENSSL /* * For autokey modes, generate the session key * and install in the key cache. Use the socket * broadcast or unicast address as appropriate. */ if (skeyid > NTP_MAXKEY) { /* * More on the autokey dance (AKD). A cookie is * constructed from public and private values. * For broadcast packets, the cookie is public * (zero). For packets that match no * association, the cookie is hashed from the * addresses and private value. For server * packets, the cookie was previously obtained * from the server. For symmetric modes, the * cookie was previously constructed using an * agreement protocol; however, should PKI be * unavailable, we construct a fake agreement as * the EXOR of the peer and host cookies. * * hismode ephemeral persistent * ======================================= * active 0 cookie# * passive 0% cookie# * client sys cookie 0% * server 0% sys cookie * broadcast 0 0 * * # if unsync, 0 * % can't happen */ if (hismode == MODE_BROADCAST) { /* * For broadcaster, use the interface * broadcast address when available; * otherwise, use the unicast address * found when the association was * mobilized. However, if this is from * the wildcard interface, game over. */ if (crypto_flags && rbufp->dstadr == any_interface) { sys_restricted++; return; /* no wildcard */ } pkeyid = 0; if (!SOCKNUL(&rbufp->dstadr->bcast)) dstadr_sin = &rbufp->dstadr->bcast; } else if (peer == NULL) { pkeyid = session_key( &rbufp->recv_srcadr, dstadr_sin, 0, sys_private, 0); } else { pkeyid = peer->pcookie; } /* * The session key includes both the public * values and cookie. In case of an extension * field, the cookie used for authentication * purposes is zero. Note the hash is saved for * use later in the autokey mambo. */ if (authlen > LEN_PKT_NOMAC && pkeyid != 0) { session_key(&rbufp->recv_srcadr, dstadr_sin, skeyid, 0, 2); tkeyid = session_key( &rbufp->recv_srcadr, dstadr_sin, skeyid, pkeyid, 0); } else { tkeyid = session_key( &rbufp->recv_srcadr, dstadr_sin, skeyid, pkeyid, 2); } } #endif /* OPENSSL */ /* * Compute the cryptosum. Note a clogging attack may * succeed in bloating the key cache. If an autokey, * purge it immediately, since we won't be needing it * again. If the packet is authentic, it can mobilize an * association. Note that there is no key zero. */ if (!authdecrypt(skeyid, (u_int32 *)pkt, authlen, has_mac)) { is_authentic = AUTH_ERROR; sys_badauth++; + return; } else { is_authentic = AUTH_OK; } #ifdef OPENSSL if (skeyid > NTP_MAXKEY) authtrust(skeyid, 0); #endif /* OPENSSL */ #ifdef DEBUG if (debug) printf( "receive: at %ld %s<-%s mode %d code %d keyid %08x len %d mac %d auth %d\n", current_time, stoa(dstadr_sin), stoa(&rbufp->recv_srcadr), hismode, retcode, skeyid, authlen, has_mac, is_authentic); #endif } /* * The association matching rules are implemented by a set of * routines and an association table. A packet matching an * association is processed by the peer process for that * association. If there are no errors, an ephemeral association * is mobilized: a broadcast packet mobilizes a broadcast client * aassociation; a manycast server packet mobilizes a manycast * client association; a symmetric active packet mobilizes a * symmetric passive association. */ switch (retcode) { /* * This is a client mode packet not matching any association. If * an ordinary client, simply toss a server mode packet back * over the fence. If a manycast client, we have to work a * little harder. */ case AM_FXMIT: /* * The vanilla case is when this is not a multicast * interface. If authentication succeeds, return a * server mode packet; if not and the key ID is nonzero, * return a crypto-NAK. */ if (!(rbufp->dstadr->flags & INT_MCASTOPEN)) { if (AUTH(restrict_mask & RES_DONTTRUST, is_authentic)) fast_xmit(rbufp, MODE_SERVER, skeyid, restrict_mask); else if (is_authentic == AUTH_ERROR) fast_xmit(rbufp, MODE_SERVER, 0, restrict_mask); return; /* hooray */ } /* * This must be manycast. Do not respond if not * configured as a manycast server. */ if (!sys_manycastserver) { sys_restricted++; return; /* not enabled */ } /* * Do not respond if unsynchronized or stratum is below * the floor or at or above the ceiling. */ if (sys_leap == LEAP_NOTINSYNC || sys_stratum < sys_floor || sys_stratum >= sys_ceiling) return; /* bad stratum */ /* * Do not respond if our stratum is greater than the * manycaster or it has already synchronized to us. */ if (sys_peer == NULL || hisstratum < sys_stratum || (sys_cohort && hisstratum == sys_stratum) || rbufp->dstadr->addr_refid == pkt->refid) return; /* no help */ /* * Respond only if authentication succeeds. Don't do a * crypto-NAK, as that would not be useful. */ if (AUTH(restrict_mask & RES_DONTTRUST, is_authentic)) fast_xmit(rbufp, MODE_SERVER, skeyid, restrict_mask); return; /* hooray */ /* * This is a server mode packet returned in response to a client * mode packet sent to a multicast group address. The origin * timestamp is a good nonce to reliably associate the reply * with what was sent. If there is no match, that's curious and * could be an intruder attempting to clog, so we just ignore * it. * * If the packet is authentic and the manycast association is * found, we mobilize a client association and copy pertinent * variables from the manycast association to the new client * association. If not, just ignore the packet. * * There is an implosion hazard at the manycast client, since * the manycast servers send the server packet immediately. If * the guy is already here, don't fire up a duplicate. */ case AM_MANYCAST: if (!AUTH(sys_authenticate | (restrict_mask & (RES_NOPEER | RES_DONTTRUST)), is_authentic)) return; /* bad auth */ if ((peer2 = findmanycastpeer(rbufp)) == NULL) { sys_restricted++; return; /* not enabled */ } if ((peer = newpeer(&rbufp->recv_srcadr, rbufp->dstadr, MODE_CLIENT, hisversion, NTP_MINDPOLL, NTP_MAXDPOLL, FLAG_IBURST | FLAG_PREEMPT, MDF_UCAST | MDF_ACLNT, 0, skeyid)) == NULL) return; /* system error */ /* * We don't need these, but it warms the billboards. */ peer->ttl = peer2->ttl; break; /* * This is the first packet received from a broadcast server. If * the packet is authentic and we are enabled as broadcast * client, mobilize a broadcast client association. We don't * kiss any frogs here. */ case AM_NEWBCL: if (!AUTH(sys_authenticate | (restrict_mask & (RES_NOPEER | RES_DONTTRUST)), is_authentic)) return; /* bad auth */ /* * Do not respond if unsynchronized or stratum is below * the floor or at or above the ceiling. */ if (hisleap == LEAP_NOTINSYNC || hisstratum < sys_floor || hisstratum >= sys_ceiling) return; /* bad stratum */ switch (sys_bclient) { /* * If not enabled, just skedaddle. */ case 0: sys_restricted++; return; /* not enabled */ /* * Execute the initial volley in order to calibrate the * propagation delay and run the Autokey protocol, if * enabled. */ case 1: if ((peer = newpeer(&rbufp->recv_srcadr, rbufp->dstadr, MODE_CLIENT, hisversion, NTP_MINDPOLL, NTP_MAXDPOLL, FLAG_MCAST | FLAG_IBURST, MDF_BCLNT, 0, skeyid)) == NULL) return; /* system error */ #ifdef OPENSSL if (skeyid > NTP_MAXKEY) crypto_recv(peer, rbufp); #endif /* OPENSSL */ return; /* hooray */ /* * Do not execute the initial volley. */ case 2: #ifdef OPENSSL /* * If a two-way exchange is not possible, * neither is Autokey. */ if (skeyid > NTP_MAXKEY) { msyslog(LOG_INFO, "receive: autokey requires two-way communication"); return; /* no autokey */ } #endif /* OPENSSL */ if ((peer = newpeer(&rbufp->recv_srcadr, rbufp->dstadr, MODE_BCLIENT, hisversion, NTP_MINDPOLL, NTP_MAXDPOLL, 0, MDF_BCLNT, 0, skeyid)) == NULL) return; /* system error */ } break; /* * This is the first packet received from a symmetric active * peer. If the packet is authentic and the first he sent, * mobilize a passive association. If not, kiss the frog. */ case AM_NEWPASS: /* * If the inbound packet is correctly authenticated and * enabled, a symmetric passive association is * mobilized. If not but correctly authenticated, a * symmetric active response is sent. If authentication * fails, send a crypto-NAK packet. */ if (!AUTH(restrict_mask & RES_DONTTRUST, is_authentic)) { if (is_authentic == AUTH_ERROR) fast_xmit(rbufp, MODE_ACTIVE, 0, restrict_mask); return; /* bad auth */ } if (!AUTH(sys_authenticate | (restrict_mask & RES_NOPEER), is_authentic)) { fast_xmit(rbufp, MODE_ACTIVE, skeyid, restrict_mask); return; /* hooray */ } /* * Do not respond if stratum is below the floor. */ if (hisstratum < sys_floor) return; /* bad stratum */ if ((peer = newpeer(&rbufp->recv_srcadr, rbufp->dstadr, MODE_PASSIVE, hisversion, NTP_MINDPOLL, NTP_MAXDPOLL, 0, MDF_UCAST, 0, skeyid)) == NULL) return; /* system error */ break; /* * Process regular packet. Nothing special. */ case AM_PROCPKT: break; /* * A passive packet matches a passive association. This is * usually the result of reconfiguring a client on the fly. As * this association might be legitamate and this packet an * attempt to deny service, just ignore it. */ case AM_ERR: return; /* * For everything else there is the bit bucket. */ default: return; } peer->flash &= ~PKT_TEST_MASK; /* * Next comes a rigorous schedule of timestamp checking. If the * transmit timestamp is zero, the server is horribly broken. */ if (L_ISZERO(&p_xmt)) { return; /* read rfc1305 */ /* * If the transmit timestamp duplicates a previous one, the * packet is a replay. This prevents the bad guys from replaying * the most recent packet, authenticated or not. */ } else if (L_ISEQU(&peer->org, &p_xmt)) { peer->flash |= TEST1; peer->oldpkt++; return; /* duplicate packet */ /* * If this is a broadcast mode packet, skip further checking. */ } else if (hismode != MODE_BROADCAST) { if (L_ISZERO(&p_org)) peer->flash |= TEST3; /* protocol unsynch */ else if (!L_ISEQU(&p_org, &peer->xmt)) peer->flash |= TEST2; /* bogus packet */ } /* * Update the origin and destination timestamps. If * unsynchronized or bogus abandon ship. If the crypto machine * breaks, light the crypto bit and plaint the log. */ peer->org = p_xmt; peer->rec = rbufp->recv_time; if (peer->flash & PKT_TEST_MASK) { #ifdef OPENSSL if (crypto_flags && (peer->flags & FLAG_SKEY)) { rval = crypto_recv(peer, rbufp); if (rval != XEVNT_OK) { peer_clear(peer, "CRYP"); peer->flash |= TEST9; /* crypto error */ } } #endif /* OPENSSL */ return; /* unsynch */ } /* * The timestamps are valid and the receive packet matches the * last one sent. If the packet is a crypto-NAK, the server * might have just changed keys. We reset the association * and restart the protocol. */ if (is_authentic == AUTH_CRYPTO) { peer_clear(peer, "AUTH"); return; /* crypto-NAK */ /* * If the association is authenticated, the key ID is nonzero * and received packets must be authenticated. This is designed * to avoid a bait-and-switch attack, which was possible in past * versions. If symmetric modes, return a crypto-NAK. The peer * should restart the protocol. */ } else if (!AUTH(peer->keyid || (restrict_mask & RES_DONTTRUST), is_authentic)) { peer->flash |= TEST5; if (hismode == MODE_ACTIVE || hismode == MODE_PASSIVE) fast_xmit(rbufp, MODE_ACTIVE, 0, restrict_mask); return; /* bad auth */ } /* * That was hard and I am sweaty, but the packet is squeaky * clean. Get on with real work. */ peer->received++; peer->timereceived = current_time; if (is_authentic == AUTH_OK) peer->flags |= FLAG_AUTHENTIC; else peer->flags &= ~FLAG_AUTHENTIC; #ifdef OPENSSL /* * More autokey dance. The rules of the cha-cha are as follows: * * 1. If there is no key or the key is not auto, do nothing. * * 2. If this packet is in response to the one just previously * sent or from a broadcast server, do the extension fields. * Otherwise, assume bogosity and bail out. * * 3. If an extension field contains a verified signature, it is * self-authenticated and we sit the dance. * * 4. If this is a server reply, check only to see that the * transmitted key ID matches the received key ID. * * 5. Check to see that one or more hashes of the current key ID * matches the previous key ID or ultimate original key ID * obtained from the broadcaster or symmetric peer. If no * match, sit the dance and wait for timeout. * * In case of crypto error, fire the orchestra and stop dancing. * This is considered a permanant error, so light the crypto bit * to suppress further requests. If preemptable or ephemeral, * scuttle the ship. */ if (crypto_flags && (peer->flags & FLAG_SKEY)) { peer->flash |= TEST8; rval = crypto_recv(peer, rbufp); if (rval != XEVNT_OK) { peer_clear(peer, "CRYP"); peer->flash |= TEST9; /* crypto error */ if (peer->flags & FLAG_PREEMPT || !(peer->flags & FLAG_CONFIG)) unpeer(peer); return; } else if (hismode == MODE_SERVER) { if (skeyid == peer->keyid) peer->flash &= ~TEST8; } else if (!(peer->flash & TEST8)) { peer->pkeyid = skeyid; } else if ((ap = (struct autokey *)peer->recval.ptr) != NULL) { int i; for (i = 0; ; i++) { if (tkeyid == peer->pkeyid || tkeyid == ap->key) { peer->flash &= ~TEST8; peer->pkeyid = skeyid; break; } if (i > ap->seq) break; tkeyid = session_key( &rbufp->recv_srcadr, dstadr_sin, tkeyid, pkeyid, 0); } } if (!(peer->crypto & CRYPTO_FLAG_PROV)) /* test 9 */ peer->flash |= TEST8; /* not proventic */ /* * If the transmit queue is nonempty, clamp the host * poll interval to the packet poll interval. */ if (peer->cmmd != 0) { peer->ppoll = pkt->ppoll; poll_update(peer, peer->hpoll); } } #endif /* OPENSSL */ /* * The dance is complete and the flash bits have been lit. Toss * the packet over the fence for processing, which may light up * more flashers. */ process_packet(peer, pkt); /* * Well, that was nice. If TEST4 is lit, either the crypto * machine jammed or a kiss-o'-death packet flew in, either of * which is fatal. */ if (peer->flash & TEST4) { msyslog(LOG_INFO, "receive: fatal error %04x for %s", peer->flash, stoa(&peer->srcadr)); return; } } /* * process_packet - Packet Procedure, a la Section 3.4.4 of the * specification. Or almost, at least. If we're in here we have a * reasonable expectation that we will be having a long term * relationship with this host. */ void process_packet( register struct peer *peer, register struct pkt *pkt ) { double t34, t21; double p_offset, p_del, p_disp; l_fp p_rec, p_xmt, p_org, p_reftime; l_fp ci; u_char pmode, pleap, pstratum; sys_processed++; peer->processed++; p_del = FPTOD(NTOHS_FP(pkt->rootdelay)); p_disp = FPTOD(NTOHS_FP(pkt->rootdispersion)); NTOHL_FP(&pkt->reftime, &p_reftime); NTOHL_FP(&pkt->rec, &p_rec); NTOHL_FP(&pkt->xmt, &p_xmt); pmode = PKT_MODE(pkt->li_vn_mode); pleap = PKT_LEAP(pkt->li_vn_mode); if (pmode != MODE_BROADCAST) NTOHL_FP(&pkt->org, &p_org); else p_org = peer->rec; pstratum = PKT_TO_STRATUM(pkt->stratum); /* * Test for kiss-o'death packet) */ if (pleap == LEAP_NOTINSYNC && pstratum == STRATUM_UNSPEC) { if (memcmp(&pkt->refid, "DENY", 4) == 0) { peer_clear(peer, "DENY"); peer->flash |= TEST4; /* access denied */ } } /* * Capture the header values. */ record_raw_stats(&peer->srcadr, peer->dstadr ? &peer->dstadr->sin : NULL, &p_org, &p_rec, &p_xmt, &peer->rec); peer->leap = pleap; peer->stratum = min(pstratum, STRATUM_UNSPEC); peer->pmode = pmode; peer->ppoll = pkt->ppoll; peer->precision = pkt->precision; peer->rootdelay = p_del; peer->rootdispersion = p_disp; peer->refid = pkt->refid; /* network byte order */ peer->reftime = p_reftime; /* * Verify the server is synchronized; that is, the leap bits and * stratum are valid, the root delay and root dispersion are * valid and the reference timestamp is not later than the * transmit timestamp. */ if (pleap == LEAP_NOTINSYNC || /* test 6 */ pstratum < sys_floor || pstratum >= sys_ceiling) peer->flash |= TEST6; /* peer not synch */ if (p_del < 0 || p_disp < 0 || p_del / /* test 7 */ 2 + p_disp >= MAXDISPERSE || !L_ISHIS(&p_xmt, &p_reftime)) peer->flash |= TEST7; /* bad header */ /* * If any tests fail at this point, the packet is discarded. * Note that some flashers may have already been set in the * receive() routine. */ if (peer->flash & PKT_TEST_MASK) { #ifdef DEBUG if (debug) printf("packet: flash header %04x\n", peer->flash); #endif return; } if (!(peer->reach)) { report_event(EVNT_REACH, peer); peer->timereachable = current_time; } poll_update(peer, peer->hpoll); peer->reach |= 1; /* * For a client/server association, calculate the clock offset, * roundtrip delay and dispersion. The equations are reordered * from the spec for more efficient use of temporaries. For a * broadcast association, offset the last measurement by the * computed delay during the client/server volley. Note that * org has been set to the time of last reception. Note the * computation of dispersion includes the system precision plus * that due to the frequency error since the origin time. * * It is very important to respect the hazards of overflow. The * only permitted operation on raw timestamps is subtraction, * where the result is a signed quantity spanning from 68 years * in the past to 68 years in the future. To avoid loss of * precision, these calculations are done using 64-bit integer * arithmetic. However, the offset and delay calculations are * sums and differences of these first-order differences, which * if done using 64-bit integer arithmetic, would be valid over * only half that span. Since the typical first-order * differences are usually very small, they are converted to 64- * bit doubles and all remaining calculations done in floating- * point arithmetic. This preserves the accuracy while retaining * the 68-year span. * * Let t1 = p_org, t2 = p_rec, t3 = p_xmt, t4 = peer->rec: */ ci = p_xmt; /* t3 - t4 */ L_SUB(&ci, &peer->rec); LFPTOD(&ci, t34); ci = p_rec; /* t2 - t1 */ L_SUB(&ci, &p_org); LFPTOD(&ci, t21); ci = peer->rec; /* t4 - t1 */ L_SUB(&ci, &p_org); /* * If running in a broadcast association, the clock offset is * (t1 - t0) corrected by the one-way delay, but we can't * measure that directly. Therefore, we start up in MODE_CLIENT * mode, set FLAG_MCAST and exchange eight messages to determine * the clock offset. When the last message is sent, we switch to * MODE_BCLIENT mode. The next broadcast message after that * computes the broadcast offset and clears FLAG_MCAST. */ if (pmode == MODE_BROADCAST) { p_offset = t34; if (peer->flags & FLAG_MCAST) { peer->estbdelay = peer->offset - p_offset; if (peer->hmode == MODE_CLIENT) return; peer->flags &= ~(FLAG_MCAST | FLAG_BURST); } p_offset += peer->estbdelay; p_del = peer->delay; p_disp = 0; } else { p_offset = (t21 + t34) / 2.; p_del = t21 - t34; LFPTOD(&ci, p_disp); p_disp = LOGTOD(sys_precision) + LOGTOD(peer->precision) + clock_phi * p_disp; } p_del = max(p_del, LOGTOD(sys_precision)); clock_filter(peer, p_offset, p_del, p_disp); record_peer_stats(&peer->srcadr, ctlpeerstatus(peer), peer->offset, peer->delay, peer->disp, peer->jitter); } /* * clock_update - Called at system process update intervals. */ static void clock_update(void) { u_char oleap; u_char ostratum; double dtemp; /* * There must be a system peer at this point. If we just changed * the system peer, but have a newer sample from the old one, * wait until newer data are available. */ if (sys_poll < sys_peer->minpoll) sys_poll = sys_peer->minpoll; if (sys_poll > sys_peer->maxpoll) sys_poll = sys_peer->maxpoll; poll_update(sys_peer, sys_poll); if (sys_peer->epoch <= sys_clocktime) return; #ifdef DEBUG if (debug) printf("clock_update: at %ld assoc %d \n", current_time, peer_associations); #endif oleap = sys_leap; ostratum = sys_stratum; switch (local_clock(sys_peer, sys_offset)) { /* * Clock exceeds panic threshold. Life as we know it ends. */ case -1: report_event(EVNT_SYSFAULT, NULL); exit (-1); /* not reached */ /* * Clock was stepped. Flush all time values of all peers. */ case 2: clear_all(); sys_leap = LEAP_NOTINSYNC; sys_stratum = STRATUM_UNSPEC; sys_peer = NULL; sys_rootdelay = 0; sys_rootdispersion = 0; memcpy(&sys_refid, "STEP", 4); report_event(EVNT_CLOCKRESET, NULL); break; /* * Clock was slewed. Update the system stratum, leap bits, root * delay, root dispersion, reference ID and reference time. If * the leap changes, we gotta reroll the keys. Except for * reference clocks, the minimum dispersion increment is not * less than sys_mindisp. */ case 1: sys_leap = leap_next; sys_stratum = min(sys_peer->stratum + 1, STRATUM_UNSPEC); sys_reftime = sys_peer->rec; /* * In orphan mode the stratum defaults to the orphan * stratum. The root delay is set to a random value * generated at startup. The root dispersion is set from * the peer dispersion; the peer root dispersion is * ignored. */ dtemp = sys_peer->disp + clock_phi * (current_time - sys_peer->update) + sys_jitter + fabs(sys_peer->offset); #ifdef REFCLOCK if (!(sys_peer->flags & FLAG_REFCLOCK) && dtemp < sys_mindisp) dtemp = sys_mindisp; #else if (dtemp < sys_mindisp) dtemp = sys_mindisp; #endif /* REFCLOCK */ if (sys_stratum >= sys_orphan) { sys_stratum = sys_orphan; sys_rootdelay = sys_peer->delay; sys_rootdispersion = dtemp; } else { sys_rootdelay = sys_peer->delay + sys_peer->rootdelay; sys_rootdispersion = dtemp + sys_peer->rootdispersion; } if (oleap == LEAP_NOTINSYNC) { report_event(EVNT_SYNCCHG, NULL); #ifdef OPENSSL expire_all(); crypto_update(); #endif /* OPENSSL */ } break; /* * Popcorn spike or step threshold exceeded. Pretend it never * happened. */ default: break; } if (ostratum != sys_stratum) report_event(EVNT_PEERSTCHG, NULL); } /* * poll_update - update peer poll interval */ void poll_update( struct peer *peer, int mpoll ) { int hpoll; /* * This routine figures out when the next poll should be sent. * That turns out to be wickedly complicated. The big problem is * that sometimes the time for the next poll is in the past. * Watch out for races here between the receive process and the * poll process. The key assertion is that, if nextdate equals * current_time, the call is from the poll process; otherwise, * it is from the receive process. * * First, bracket the poll interval according to the type of * association and options. If a fixed interval is configured, * use minpoll. This primarily is for reference clocks, but * works for any association. */ if (peer->flags & FLAG_FIXPOLL) { hpoll = peer->minpoll; /* * The ordinary case; clamp the poll interval between minpoll * and maxpoll. */ } else { hpoll = max(min(peer->maxpoll, mpoll), peer->minpoll); } #ifdef OPENSSL /* * Bit of crass arrogance at this point. If the poll interval * has changed and we have a keylist, the lifetimes in the * keylist are probably bogus. In this case purge the keylist * and regenerate it later. */ if (hpoll != peer->hpoll) key_expire(peer); #endif /* OPENSSL */ peer->hpoll = hpoll; /* * Now we figure out if there is an override. If during the * crypto protocol and a message is pending, make it wait not * more than two seconds. */ #ifdef OPENSSL if (peer->cmmd != NULL && (sys_leap != LEAP_NOTINSYNC || peer->crypto)) { peer->nextdate = current_time + RESP_DELAY; /* * If we get called from the receive routine while a burst is * pending, just slink away. If from the poll routine and a * reference clock or a pending crypto response, delay for one * second. If this is the first sent in a burst, wait for the * modem to come up. For others in the burst, delay two seconds. */ } else if (peer->burst > 0) { #else /* OPENSSL */ if (peer->burst > 0) { #endif /* OPENSSL */ if (peer->nextdate != current_time) return; #ifdef REFCLOCK else if (peer->flags & FLAG_REFCLOCK) peer->nextdate += RESP_DELAY; #endif /* REFCLOCK */ else if (peer->flags & (FLAG_IBURST | FLAG_BURST) && peer->burst == NTP_BURST) peer->nextdate += sys_calldelay; else peer->nextdate += BURST_DELAY; /* * The ordinary case; use the minimum of the host and peer * intervals, but not less than minpoll. In other words, * oversampling is okay but understampling is evil. */ } else { peer->nextdate = peer->outdate + RANDPOLL(max(min(peer->ppoll, hpoll), peer->minpoll)); } /* * If the time for the next poll has already happened, bring it * up to the next second after this one. This way the only way * to get nexdate == current time is from the poll routine. */ if (peer->nextdate <= current_time) peer->nextdate = current_time + 1; #ifdef DEBUG if (debug > 1) printf("poll_update: at %lu %s flags %04x poll %d burst %d last %lu next %lu\n", current_time, ntoa(&peer->srcadr), peer->flags, peer->hpoll, peer->burst, peer->outdate, peer->nextdate); #endif } /* * peer_crypto_clear - discard crypto information */ void peer_crypto_clear( struct peer *peer ) { /* * If cryptographic credentials have been acquired, toss them to * Valhalla. Note that autokeys are ephemeral, in that they are * tossed immediately upon use. Therefore, the keylist can be * purged anytime without needing to preserve random keys. Note * that, if the peer is purged, the cryptographic variables are * purged, too. This makes it much harder to sneak in some * unauthenticated data in the clock filter. */ DPRINTF(1, ("peer_crypto_clear: at %ld next %ld assoc ID %d\n", current_time, peer->nextdate, peer->associd)); #ifdef OPENSSL peer->assoc = 0; peer->crypto = 0; if (peer->pkey != NULL) EVP_PKEY_free(peer->pkey); peer->pkey = NULL; peer->digest = NULL; /* XXX MEMLEAK? check whether this needs to be freed in any way - never was freed */ if (peer->subject != NULL) free(peer->subject); peer->subject = NULL; if (peer->issuer != NULL) free(peer->issuer); peer->issuer = NULL; peer->pkeyid = 0; peer->pcookie = 0; if (peer->ident_pkey != NULL) EVP_PKEY_free(peer->ident_pkey); peer->ident_pkey = NULL; memset(&peer->fstamp, 0, sizeof(peer->fstamp)); if (peer->iffval != NULL) BN_free(peer->iffval); peer->iffval = NULL; if (peer->grpkey != NULL) BN_free(peer->grpkey); peer->grpkey = NULL; value_free(&peer->cookval); value_free(&peer->recval); if (peer->cmmd != NULL) { free(peer->cmmd); peer->cmmd = NULL; } key_expire(peer); value_free(&peer->encrypt); #endif /* OPENSSL */ } /* * peer_clear - clear peer filter registers. See Section 3.4.8 of the spec. */ void peer_clear( struct peer *peer, /* peer structure */ char *ident /* tally lights */ ) { int i; peer_crypto_clear(peer); if (peer == sys_peer) sys_peer = NULL; /* * Wipe the association clean and initialize the nonzero values. */ memset(CLEAR_TO_ZERO(peer), 0, LEN_CLEAR_TO_ZERO); peer->estbdelay = sys_bdelay; peer->ppoll = peer->maxpoll; peer->hpoll = peer->minpoll; peer->disp = MAXDISPERSE; peer->jitter = LOGTOD(sys_precision); for (i = 0; i < NTP_SHIFT; i++) { peer->filter_order[i] = i; peer->filter_disp[i] = MAXDISPERSE; } #ifdef REFCLOCK if (!(peer->flags & FLAG_REFCLOCK)) { peer->leap = LEAP_NOTINSYNC; peer->stratum = STRATUM_UNSPEC; memcpy(&peer->refid, ident, 4); } #else peer->leap = LEAP_NOTINSYNC; peer->stratum = STRATUM_UNSPEC; memcpy(&peer->refid, ident, 4); #endif /* REFCLOCK */ /* * During initialization use the association count to spread out * the polls at one-second intervals. Othersie, randomize over * the minimum poll interval in order to avoid broadcast * implosion. */ peer->nextdate = peer->update = peer->outdate = current_time; if (initializing) peer->nextdate += peer_associations; else if (peer->hmode == MODE_PASSIVE) peer->nextdate += RESP_DELAY; else peer->nextdate += (ntp_random() & ((1 << NTP_MINDPOLL) - 1)); DPRINTF(1, ("peer_clear: at %ld next %ld assoc ID %d refid %s\n", current_time, peer->nextdate, peer->associd, ident)); } /* * clock_filter - add incoming clock sample to filter register and run * the filter procedure to find the best sample. */ void clock_filter( struct peer *peer, /* peer structure pointer */ double sample_offset, /* clock offset */ double sample_delay, /* roundtrip delay */ double sample_disp /* dispersion */ ) { double dst[NTP_SHIFT]; /* distance vector */ int ord[NTP_SHIFT]; /* index vector */ int i, j, k, m; double dtemp, etemp; /* * Shift the new sample into the register and discard the oldest * one. The new offset and delay come directly from the * timestamp calculations. The dispersion grows from the last * outbound packet or reference clock update to the present time * and increased by the sum of the peer precision and the system * precision. The delay can sometimes swing negative due to * frequency skew, so it is clamped non-negative. */ j = peer->filter_nextpt; peer->filter_offset[j] = sample_offset; peer->filter_delay[j] = max(0, sample_delay); peer->filter_disp[j] = sample_disp; peer->filter_epoch[j] = current_time; j = (j + 1) % NTP_SHIFT; peer->filter_nextpt = j; /* * Update dispersions since the last update and at the same * time initialize the distance and index lists. The distance * list uses a compound metric. If the sample is valid and * younger than the minimum Allan intercept, use delay; * otherwise, use biased dispersion. */ dtemp = clock_phi * (current_time - peer->update); peer->update = current_time; for (i = NTP_SHIFT - 1; i >= 0; i--) { if (i != 0) peer->filter_disp[j] += dtemp; if (peer->filter_disp[j] >= MAXDISPERSE) peer->filter_disp[j] = MAXDISPERSE; if (peer->filter_disp[j] >= MAXDISPERSE) dst[i] = MAXDISPERSE; else if (peer->update - peer->filter_epoch[j] > allan_xpt) dst[i] = sys_maxdist + peer->filter_disp[j]; else dst[i] = peer->filter_delay[j]; ord[i] = j; j++; j %= NTP_SHIFT; } /* * If the clock discipline has stabilized, sort the samples in * both lists by distance. Note, we do not displace a higher * distance sample by a lower distance one unless lower by at * least the precision. */ if (state == 4) { for (i = 1; i < NTP_SHIFT; i++) { for (j = 0; j < i; j++) { if (dst[j] > dst[i] + LOGTOD(sys_precision)) { k = ord[j]; ord[j] = ord[i]; ord[i] = k; etemp = dst[j]; dst[j] = dst[i]; dst[i] = etemp; } } } } /* * Copy the index list to the association structure so ntpq * can see it later. Prune the distance list to samples less * than max distance, but keep at least two valid samples for * jitter calculation. */ m = 0; for (i = 0; i < NTP_SHIFT; i++) { peer->filter_order[i] = (u_char) ord[i]; if (dst[i] >= MAXDISPERSE || (m >= 2 && dst[i] >= sys_maxdist)) continue; m++; } /* * Compute the dispersion and jitter. The dispersion is weighted * exponentially by NTP_FWEIGHT (0.5) so it is normalized close * to 1.0. The jitter is the RMS differences relative to the * lowest delay sample. If no acceptable samples remain in the * shift register, quietly tiptoe home leaving only the * dispersion. */ peer->disp = peer->jitter = 0; k = ord[0]; for (i = NTP_SHIFT - 1; i >= 0; i--) { j = ord[i]; peer->disp = NTP_FWEIGHT * (peer->disp + peer->filter_disp[j]); if (i < m) peer->jitter += DIFF(peer->filter_offset[j], peer->filter_offset[k]); } /* * If no acceptable samples remain in the shift register, * quietly tiptoe home leaving only the dispersion. Otherwise, * save the offset, delay and jitter. Note the jitter must not * be less than the precision. */ if (m == 0) return; etemp = fabs(peer->offset - peer->filter_offset[k]); peer->offset = peer->filter_offset[k]; peer->delay = peer->filter_delay[k]; if (m > 1) peer->jitter /= m - 1; peer->jitter = max(SQRT(peer->jitter), LOGTOD(sys_precision)); /* * A new sample is useful only if it is younger than the last * one used. Note the order is FIFO if the clock discipline has * not stabilized. */ if (peer->filter_epoch[k] <= peer->epoch) { #ifdef DEBUG if (debug) printf("clock_filter: discard %lu\n", peer->epoch - peer->filter_epoch[k]); #endif return; } /* * If the difference between the last offset and the current one * exceeds the jitter by CLOCK_SGATE and the interval since the * last update is less than twice the system poll interval, * consider the update a popcorn spike and ignore it. */ if (etemp > CLOCK_SGATE * peer->jitter && m > 1 && peer->filter_epoch[k] - peer->epoch < 2. * ULOGTOD(sys_poll)) { #ifdef DEBUG if (debug) printf("clock_filter: popcorn %.6f %.6f\n", etemp, dtemp); #endif return; } /* * The mitigated sample statistics are saved for later * processing. If not in a burst, tickle the select. */ peer->epoch = peer->filter_epoch[k]; #ifdef DEBUG if (debug) printf( "clock_filter: n %d off %.6f del %.6f dsp %.6f jit %.6f, age %lu\n", m, peer->offset, peer->delay, peer->disp, peer->jitter, current_time - peer->epoch); #endif if (peer->burst == 0 || sys_leap == LEAP_NOTINSYNC) clock_select(); } /* * clock_select - find the pick-of-the-litter clock * * LOCKCLOCK: If the local clock is the prefer peer, it will always be * enabled, even if declared falseticker, (2) only the prefer peer can * be selected as the system peer, (3) if the external source is down, * the system leap bits are set to 11 and the stratum set to infinity. */ void clock_select(void) { struct peer *peer; int i, j, k, n; int nlist, nl3; int allow, osurv; double d, e, f, g; double high, low; double synch[NTP_MAXASSOC], error[NTP_MAXASSOC]; struct peer *osys_peer; struct peer *typeacts = NULL; struct peer *typelocal = NULL; struct peer *typesystem = NULL; static int list_alloc = 0; static struct endpoint *endpoint = NULL; static int *indx = NULL; static struct peer **peer_list = NULL; static u_int endpoint_size = 0; static u_int indx_size = 0; static u_int peer_list_size = 0; /* * Initialize and create endpoint, index and peer lists big * enough to handle all associations. */ osys_peer = sys_peer; sys_peer = NULL; sys_pps = NULL; sys_prefer = NULL; osurv = sys_survivors; sys_survivors = 0; #ifdef LOCKCLOCK sys_leap = LEAP_NOTINSYNC; sys_stratum = STRATUM_UNSPEC; memcpy(&sys_refid, "DOWN", 4); #endif /* LOCKCLOCK */ nlist = 0; for (n = 0; n < NTP_HASH_SIZE; n++) nlist += peer_hash_count[n]; if (nlist > list_alloc) { if (list_alloc > 0) { free(endpoint); free(indx); free(peer_list); } while (list_alloc < nlist) { list_alloc += 5; endpoint_size += 5 * 3 * sizeof(*endpoint); indx_size += 5 * 3 * sizeof(*indx); peer_list_size += 5 * sizeof(*peer_list); } endpoint = (struct endpoint *)emalloc(endpoint_size); indx = (int *)emalloc(indx_size); peer_list = (struct peer **)emalloc(peer_list_size); } /* * Initially, we populate the island with all the rifraff peers * that happen to be lying around. Those with seriously * defective clocks are immediately booted off the island. Then, * the falsetickers are culled and put to sea. The truechimers * remaining are subject to repeated rounds where the most * unpopular at each round is kicked off. When the population * has dwindled to sys_minclock, the survivors split a million * bucks and collectively crank the chimes. */ nlist = nl3 = 0; /* none yet */ for (n = 0; n < NTP_HASH_SIZE; n++) { for (peer = peer_hash[n]; peer != NULL; peer = peer->next) { peer->flags &= ~FLAG_SYSPEER; peer->status = CTL_PST_SEL_REJECT; /* * Leave the island immediately if the peer is * unfit to synchronize. */ if (peer_unfit(peer)) continue; /* * Don't allow the local clock or modem drivers * in the kitchen at this point, unless the * prefer peer. Do that later, but only if * nobody else is around. These guys are all * configured, so we never throw them away. */ #ifdef REFCLOCK if (peer->refclktype == REFCLK_LOCALCLOCK #if defined(VMS) && defined(VMS_LOCALUNIT) /* wjm: VMS_LOCALUNIT taken seriously */ && REFCLOCKUNIT(&peer->srcadr) != VMS_LOCALUNIT #endif /* VMS && VMS_LOCALUNIT */ ) { typelocal = peer; #ifndef LOCKCLOCK if (!(peer->flags & FLAG_PREFER)) continue; /* no local clock */ #endif /* LOCKCLOCK */ } if (peer->sstclktype == CTL_SST_TS_TELEPHONE) { typeacts = peer; if (!(peer->flags & FLAG_PREFER)) continue; /* no acts */ } #endif /* REFCLOCK */ /* * If we get this far, the peer can stay on the * island, but does not yet have the immunity * idol. */ peer->status = CTL_PST_SEL_SANE; peer_list[nlist++] = peer; /* * Insert each interval endpoint on the sorted * list. */ e = peer->offset; /* Upper end */ f = root_distance(peer); e = e + f; for (i = nl3 - 1; i >= 0; i--) { if (e >= endpoint[indx[i]].val) break; indx[i + 3] = indx[i]; } indx[i + 3] = nl3; endpoint[nl3].type = 1; endpoint[nl3++].val = e; e = e - f; /* Center point */ for (; i >= 0; i--) { if (e >= endpoint[indx[i]].val) break; indx[i + 2] = indx[i]; } indx[i + 2] = nl3; endpoint[nl3].type = 0; endpoint[nl3++].val = e; e = e - f; /* Lower end */ for (; i >= 0; i--) { if (e >= endpoint[indx[i]].val) break; indx[i + 1] = indx[i]; } indx[i + 1] = nl3; endpoint[nl3].type = -1; endpoint[nl3++].val = e; } } #ifdef DEBUG if (debug > 2) for (i = 0; i < nl3; i++) printf("select: endpoint %2d %.6f\n", endpoint[indx[i]].type, endpoint[indx[i]].val); #endif /* * This is the actual algorithm that cleaves the truechimers * from the falsetickers. The original algorithm was described * in Keith Marzullo's dissertation, but has been modified for * better accuracy. * * Briefly put, we first assume there are no falsetickers, then * scan the candidate list first from the low end upwards and * then from the high end downwards. The scans stop when the * number of intersections equals the number of candidates less * the number of falsetickers. If this doesn't happen for a * given number of falsetickers, we bump the number of * falsetickers and try again. If the number of falsetickers * becomes equal to or greater than half the number of * candidates, the Albanians have won the Byzantine wars and * correct synchronization is not possible. * * Here, nlist is the number of candidates and allow is the * number of falsetickers. Upon exit, the truechimers are the * susvivors with offsets not less than low and not greater than * high. There may be none of them. */ low = 1e9; high = -1e9; for (allow = 0; 2 * allow < nlist; allow++) { int found; /* * Bound the interval (low, high) as the largest * interval containing points from presumed truechimers. */ found = 0; n = 0; for (i = 0; i < nl3; i++) { low = endpoint[indx[i]].val; n -= endpoint[indx[i]].type; if (n >= nlist - allow) break; if (endpoint[indx[i]].type == 0) found++; } n = 0; for (j = nl3 - 1; j >= 0; j--) { high = endpoint[indx[j]].val; n += endpoint[indx[j]].type; if (n >= nlist - allow) break; if (endpoint[indx[j]].type == 0) found++; } /* * If the number of candidates found outside the * interval is greater than the number of falsetickers, * then at least one truechimer is outside the interval, * so go around again. This is what makes this algorithm * different than Marzullo's. */ if (found > allow) continue; /* * If an interval containing truechimers is found, stop. * If not, increase the number of falsetickers and go * around again. */ if (high > low) break; } /* * Clustering algorithm. Construct candidate list in order first * by stratum then by root distance, but keep only the best * NTP_MAXASSOC of them. Scan the list to find falsetickers, who * leave the island immediately. The TRUE peer is always a * truechimer. We must leave at least one peer to collect the * million bucks. If in orphan mode, rascals found with lower * stratum are guaranteed a seat on the bus. */ j = 0; for (i = 0; i < nlist; i++) { peer = peer_list[i]; if (nlist > 1 && (peer->offset <= low || peer->offset >= high) && !(peer->flags & FLAG_TRUE) && !(sys_stratum >= sys_orphan && peer->stratum < sys_orphan)) continue; peer->status = CTL_PST_SEL_DISTSYSPEER; /* * The order metric is formed from the stratum times * max distance (1.) plus the root distance. It strongly * favors the lowest stratum, but a higher stratum peer * can capture the clock if the low stratum dominant * hasn't been heard for awhile. */ d = root_distance(peer) + peer->stratum * sys_maxdist; if (j >= NTP_MAXASSOC) { if (d >= synch[j - 1]) continue; else j--; } for (k = j; k > 0; k--) { if (d >= synch[k - 1]) break; peer_list[k] = peer_list[k - 1]; error[k] = error[k - 1]; synch[k] = synch[k - 1]; } peer_list[k] = peer; error[k] = peer->jitter; synch[k] = d; j++; } nlist = j; /* * If no survivors remain at this point, check if the local * clock or modem drivers have been found. If so, nominate one * of them as the only survivor. Otherwise, give up and leave * the island to the rats. */ if (nlist == 0) { if (typeacts != 0) { typeacts->status = CTL_PST_SEL_DISTSYSPEER; peer_list[0] = typeacts; nlist = 1; } else if (typelocal != 0) { typelocal->status = CTL_PST_SEL_DISTSYSPEER; peer_list[0] = typelocal; nlist = 1; } else { if (osys_peer != NULL) { NLOG(NLOG_SYNCSTATUS) msyslog(LOG_INFO, "no servers reachable"); report_event(EVNT_PEERSTCHG, NULL); } } } /* * We can only trust the survivors if the number of candidates * sys_minsane is at least the number required to detect and * cast out one falsticker. For the Byzantine agreement * algorithm used here, that number is 4; however, the default * sys_minsane is 1 to speed initial synchronization. Careful * operators will tinker a higher value and use at least that * number of synchronization sources. */ if (nlist < sys_minsane) return; for (i = 0; i < nlist; i++) peer_list[i]->status = CTL_PST_SEL_SELCAND; /* * Now, vote outlyers off the island by select jitter weighted * by root distance. Continue voting as long as there are more * than sys_minclock survivors and the minimum select jitter is * greater than the maximum peer jitter. Stop if we are about to * discard a TRUE or PREFER peer, who of course has the * immunity idol. */ while (1) { d = 1e9; e = -1e9; f = g = 0; k = 0; for (i = 0; i < nlist; i++) { if (error[i] < d) d = error[i]; f = 0; if (nlist > 1) { for (j = 0; j < nlist; j++) f += DIFF(peer_list[j]->offset, peer_list[i]->offset); f = SQRT(f / (nlist - 1)); } if (f * synch[i] > e) { g = f; e = f * synch[i]; k = i; } } f = max(f, LOGTOD(sys_precision)); if (nlist <= sys_minclock || f <= d || peer_list[k]->flags & (FLAG_TRUE | FLAG_PREFER)) break; #ifdef DEBUG if (debug > 2) printf( "select: drop %s select %.6f jitter %.6f\n", ntoa(&peer_list[k]->srcadr), g, d); #endif for (j = k + 1; j < nlist; j++) { peer_list[j - 1] = peer_list[j]; error[j - 1] = error[j]; } nlist--; } /* * What remains is a list usually not greater than sys_minclock * peers. We want only a peer at the lowest stratum to become * the system peer, although all survivors are eligible for the * combining algorithm. Consider each peer in turn and OR the * leap bits on the assumption that, if some of them honk * nonzero bits, they must know what they are doing. Check for * prefer and pps peers at any stratum. Note that the head of * the list is at the lowest stratum and that unsynchronized * peers cannot survive this far. */ leap_next = 0; for (i = 0; i < nlist; i++) { peer = peer_list[i]; sys_survivors++; leap_next |= peer->leap; peer->status = CTL_PST_SEL_SYNCCAND; if (peer->flags & FLAG_PREFER) sys_prefer = peer; if (peer == osys_peer) typesystem = peer; #ifdef REFCLOCK if (peer->refclktype == REFCLK_ATOM_PPS) sys_pps = peer; #endif /* REFCLOCK */ #if DEBUG if (debug > 1) printf("cluster: survivor %s metric %.6f\n", ntoa(&peer_list[i]->srcadr), synch[i]); #endif } /* * Anticlockhop provision. Keep the current system peer if it is * a survivor but not first in the list. But do that only HOPPER * times. */ if (osys_peer == NULL || typesystem == NULL || typesystem == peer_list[0] || sys_hopper > sys_maxhop) { typesystem = peer_list[0]; sys_hopper = 0; } else { peer->selbroken++; } /* * Mitigation rules of the game. There are several types of * peers that can be selected here: (1) orphan, (2) prefer peer * (flag FLAG_PREFER) (3) pps peers (type REFCLK_ATOM_PPS), (4) * the existing system peer, if any, and (5) the head of the * survivor list. */ if (typesystem->stratum >= sys_orphan) { /* * If in orphan mode, choose the system peer. If the * lowest distance, we are the orphan parent and the * offset is zero. */ sys_peer = typesystem; sys_peer->status = CTL_PST_SEL_SYSPEER; if (sys_orphandelay < sys_peer->rootdelay) { sys_offset = 0; sys_refid = htonl(LOOPBACKADR); } else { sys_offset = sys_peer->offset; sys_refid = addr2refid(&sys_peer->srcadr); } sys_jitter = LOGTOD(sys_precision); #ifdef DEBUG if (debug > 1) printf("select: orphan offset %.6f\n", sys_offset); #endif } else if (sys_prefer) { /* * If a pps peer is present, choose it; otherwise, * choose the prefer peer. */ if (sys_pps) { sys_peer = sys_pps; sys_peer->status = CTL_PST_SEL_PPS; sys_offset = sys_peer->offset; if (!pps_control) NLOG(NLOG_SYSEVENT) msyslog(LOG_INFO, "pps sync enabled"); pps_control = current_time; #ifdef DEBUG if (debug > 1) printf("select: pps offset %.6f\n", sys_offset); #endif } else { sys_peer = sys_prefer; sys_peer->status = CTL_PST_SEL_SYSPEER; sys_offset = sys_peer->offset; #ifdef DEBUG if (debug > 1) printf("select: prefer offset %.6f\n", sys_offset); #endif } if (sys_peer->stratum == STRATUM_REFCLOCK || sys_peer->stratum == STRATUM_UNSPEC) sys_refid = sys_peer->refid; else sys_refid = addr2refid(&sys_peer->srcadr); sys_jitter = sys_peer->jitter; } else { /* * Otherwise, choose the anticlockhopper. */ sys_peer = typesystem; sys_peer->status = CTL_PST_SEL_SYSPEER; clock_combine(peer_list, nlist); if (sys_peer->stratum == STRATUM_REFCLOCK || sys_peer->stratum == STRATUM_UNSPEC) sys_refid = sys_peer->refid; else sys_refid = addr2refid(&sys_peer->srcadr); sys_jitter = SQRT(SQUARE(sys_peer->jitter) + SQUARE(sys_jitter)); #ifdef DEBUG if (debug > 1) printf("select: combine offset %.6f\n", sys_offset); #endif } /* * We have found the alpha male. */ sys_peer->flags |= FLAG_SYSPEER; if (osys_peer != sys_peer) { char *src; report_event(EVNT_PEERSTCHG, NULL); #ifdef REFCLOCK if (sys_peer->flags & FLAG_REFCLOCK) src = refnumtoa(&sys_peer->srcadr); else #endif /* REFCLOCK */ src = ntoa(&sys_peer->srcadr); NLOG(NLOG_SYNCSTATUS) msyslog(LOG_INFO, "synchronized to %s, stratum %d", src, sys_peer->stratum); } clock_update(); } /* * clock_combine - compute system offset and jitter from selected peers */ static void clock_combine( struct peer **peers, /* survivor list */ int npeers /* number of survivors */ ) { int i; double x, y, z, w; y = z = w = 0; for (i = 0; i < npeers; i++) { x = root_distance(peers[i]); y += 1. / x; z += peers[i]->offset / x; w += SQUARE(peers[i]->offset - peers[0]->offset) / x; } sys_offset = z / y; sys_jitter = SQRT(w / y); } /* * root_distance - compute synchronization distance from peer to root */ static double root_distance( struct peer *peer ) { double dist; /* * Careful squeak here. The value returned must be greater than * the minimum root dispersion in order to avoid clockhop with * highly precise reference clocks. In orphan mode lose the peer * root delay, as that is used by the election algorithm. */ if (peer->stratum >= sys_orphan) dist = 0; else dist = peer->rootdelay; dist += max(sys_mindisp, dist + peer->delay) / 2 + peer->rootdispersion + peer->disp + clock_phi * (current_time - peer->update) + peer->jitter; return (dist); } /* * peer_xmit - send packet for persistent association. */ static void peer_xmit( struct peer *peer /* peer structure pointer */ ) { struct pkt xpkt; /* transmit packet */ int sendlen, authlen; keyid_t xkeyid = 0; /* transmit key ID */ l_fp xmt_tx; if (!peer->dstadr) /* don't bother with peers without interface */ return; /* * This is deliciously complicated. There are three cases. * * case leap stratum refid delay dispersion * * normal system system system system system * orphan child 00 orphan system orphan system * orphan parent 00 orphan loopbk 0 0 */ /* * This is a normal packet. Use the system variables. */ if (sys_stratum < sys_orphan) { xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, peer->version, peer->hmode); xpkt.stratum = STRATUM_TO_PKT(sys_stratum); xpkt.refid = sys_refid; xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay)); xpkt.rootdispersion = HTONS_FP(DTOUFP(sys_rootdispersion)); /* * This is a orphan child packet. The host is synchronized to an * orphan parent. Show leap synchronized, orphan stratum, system * reference ID, orphan root delay and system root dispersion. */ } else if (sys_peer != NULL) { xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, peer->version, peer->hmode); xpkt.stratum = STRATUM_TO_PKT(sys_orphan); xpkt.refid = htonl(LOOPBACKADR); xpkt.rootdelay = HTONS_FP(DTOFP(sys_orphandelay)); xpkt.rootdispersion = HTONS_FP(DTOUFP(sys_rootdispersion)); /* * This is an orphan parent. Show leap synchronized, orphan * stratum, loopack reference ID and zero root delay and root * dispersion. */ } else { xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, peer->version, peer->hmode); xpkt.stratum = STRATUM_TO_PKT(sys_orphan); xpkt.refid = sys_refid; xpkt.rootdelay = 0; xpkt.rootdispersion = 0; } xpkt.ppoll = peer->hpoll; xpkt.precision = sys_precision; HTONL_FP(&sys_reftime, &xpkt.reftime); HTONL_FP(&peer->org, &xpkt.org); HTONL_FP(&peer->rec, &xpkt.rec); /* * If the received packet contains a MAC, the transmitted packet * is authenticated and contains a MAC. If not, the transmitted * packet is not authenticated. * * It is most important when autokey is in use that the local * interface IP address be known before the first packet is * sent. Otherwise, it is not possible to compute a correct MAC * the recipient will accept. Thus, the I/O semantics have to do * a little more work. In particular, the wildcard interface * might not be usable. */ sendlen = LEN_PKT_NOMAC; if (!(peer->flags & FLAG_AUTHENABLE)) { get_systime(&peer->xmt); HTONL_FP(&peer->xmt, &xpkt.xmt); sendpkt(&peer->srcadr, peer->dstadr, sys_ttl[peer->ttl], &xpkt, sendlen); peer->sent++; #ifdef DEBUG if (debug) printf("transmit: at %ld %s->%s mode %d\n", current_time, peer->dstadr ? stoa(&peer->dstadr->sin) : "-", stoa(&peer->srcadr), peer->hmode); #endif return; } /* * The received packet contains a MAC, so the transmitted packet * must be authenticated. If autokey is enabled, fuss with the * various modes; otherwise, symmetric key cryptography is used. */ #ifdef OPENSSL if (crypto_flags && (peer->flags & FLAG_SKEY)) { struct exten *exten; /* extension field */ /* * The Public Key Dance (PKD): Cryptographic credentials * are contained in extension fields, each including a * 4-octet length/code word followed by a 4-octet * association ID and optional additional data. Optional * data includes a 4-octet data length field followed by * the data itself. Request messages are sent from a * configured association; response messages can be sent * from a configured association or can take the fast * path without ever matching an association. Response * messages have the same code as the request, but have * a response bit and possibly an error bit set. In this * implementation, a message may contain no more than * one command and no more than one response. * * Cryptographic session keys include both a public and * a private componet. Request and response messages * using extension fields are always sent with the * private component set to zero. Packets without * extension fields indlude the private component when * the session key is generated. */ while (1) { /* * Allocate and initialize a keylist if not * already done. Then, use the list in inverse * order, discarding keys once used. Keep the * latest key around until the next one, so * clients can use client/server packets to * compute propagation delay. * * Note that once a key is used from the list, * it is retained in the key cache until the * next key is used. This is to allow a client * to retrieve the encrypted session key * identifier to verify authenticity. * * If for some reason a key is no longer in the * key cache, a birthday has happened and the * pseudo-random sequence is probably broken. In * that case, purge the keylist and regenerate * it. */ if (peer->keynumber == 0) make_keylist(peer, peer->dstadr); else peer->keynumber--; xkeyid = peer->keylist[peer->keynumber]; if (authistrusted(xkeyid)) break; else key_expire(peer); } peer->keyid = xkeyid; exten = NULL; switch (peer->hmode) { /* * In broadcast server mode the autokey values are * required by the broadcast clients. Push them when a * new keylist is generated; otherwise, push the * association message so the client can request them at * other times. */ case MODE_BROADCAST: if (peer->flags & FLAG_ASSOC) exten = crypto_args(peer, CRYPTO_AUTO | CRYPTO_RESP, NULL); else exten = crypto_args(peer, CRYPTO_ASSOC | CRYPTO_RESP, NULL); break; /* * In symmetric modes the digest, certificate, agreement * parameters, cookie and autokey values are required. * The leapsecond table is optional. But, a passive peer * will not believe the active peer until the latter has * synchronized, so the agreement must be postponed * until then. In any case, if a new keylist is * generated, the autokey values are pushed. * * If the crypto bit is lit, don't send requests. */ case MODE_ACTIVE: case MODE_PASSIVE: if (peer->flash & TEST9) break; /* * Parameter and certificate. */ if (!peer->crypto) exten = crypto_args(peer, CRYPTO_ASSOC, sys_hostname); else if (!(peer->crypto & CRYPTO_FLAG_VALID)) exten = crypto_args(peer, CRYPTO_CERT, peer->issuer); /* * Identity. Note we have to sign the * certificate before the cookie to avoid a * deadlock when the passive peer is walking the * certificate trail. Awesome. */ else if (!(peer->crypto & CRYPTO_FLAG_VRFY)) exten = crypto_args(peer, crypto_ident(peer), NULL); else if (sys_leap != LEAP_NOTINSYNC && !(peer->crypto & CRYPTO_FLAG_SIGN)) exten = crypto_args(peer, CRYPTO_SIGN, sys_hostname); /* * Autokey. We request the cookie only when the * server and client are synchronized and * signatures work both ways. On the other hand, * the active peer needs the autokey values * before then and when the passive peer is * waiting for the active peer to synchronize. * Any time we regenerate the key list, we offer * the autokey values without being asked. */ else if (sys_leap != LEAP_NOTINSYNC && peer->leap != LEAP_NOTINSYNC && !(peer->crypto & CRYPTO_FLAG_AGREE)) exten = crypto_args(peer, CRYPTO_COOK, NULL); else if (peer->flags & FLAG_ASSOC) exten = crypto_args(peer, CRYPTO_AUTO | CRYPTO_RESP, NULL); else if (!(peer->crypto & CRYPTO_FLAG_AUTO)) exten = crypto_args(peer, CRYPTO_AUTO, NULL); /* * Postamble. We trade leapseconds only when the * server and client are synchronized. */ else if (sys_leap != LEAP_NOTINSYNC && peer->leap != LEAP_NOTINSYNC && peer->crypto & CRYPTO_FLAG_TAI && !(peer->crypto & CRYPTO_FLAG_LEAP)) exten = crypto_args(peer, CRYPTO_TAI, NULL); break; /* * In client mode the digest, certificate, agreement * parameters and cookie are required. The leapsecond * table is optional. If broadcast client mode, the * autokey values are required as well. In broadcast * client mode, these values must be acquired during the * client/server exchange to avoid having to wait until * the next key list regeneration. Otherwise, the poor * dude may die a lingering death until becoming * unreachable and attempting rebirth. * * If neither the server or client have the agreement * parameters, the protocol transmits the cookie in the * clear. If the server has the parameters, the client * requests them and the protocol blinds it using the * agreed key. It is a protocol error if the client has * the parameters but the server does not. * * If the crypto bit is lit, don't send requests. */ case MODE_CLIENT: if (peer->flash & TEST9) break; /* * Parameter and certificate. */ if (!peer->crypto) exten = crypto_args(peer, CRYPTO_ASSOC, sys_hostname); else if (!(peer->crypto & CRYPTO_FLAG_VALID)) exten = crypto_args(peer, CRYPTO_CERT, peer->issuer); /* * Identity */ else if (!(peer->crypto & CRYPTO_FLAG_VRFY)) exten = crypto_args(peer, crypto_ident(peer), NULL); /* * Autokey */ else if (!(peer->crypto & CRYPTO_FLAG_AGREE)) exten = crypto_args(peer, CRYPTO_COOK, NULL); else if (!(peer->crypto & CRYPTO_FLAG_AUTO) && (peer->cast_flags & MDF_BCLNT)) exten = crypto_args(peer, CRYPTO_AUTO, NULL); /* * Postamble. We can sign the certificate here, * since there is no chance of deadlock. */ else if (sys_leap != LEAP_NOTINSYNC && !(peer->crypto & CRYPTO_FLAG_SIGN)) exten = crypto_args(peer, CRYPTO_SIGN, sys_hostname); else if (sys_leap != LEAP_NOTINSYNC && peer->crypto & CRYPTO_FLAG_TAI && !(peer->crypto & CRYPTO_FLAG_LEAP)) exten = crypto_args(peer, CRYPTO_TAI, NULL); break; } /* * Build the extension fields as directed. A response to * a request is always sent, even if an error. If an * error occurs when sending a request, the crypto * machinery broke or was misconfigured. In that case * light the crypto bit to suppress further requests. */ if (peer->cmmd != NULL) { peer->cmmd->associd = htonl(peer->associd); sendlen += crypto_xmit(&xpkt, &peer->srcadr, sendlen, peer->cmmd, 0); free(peer->cmmd); peer->cmmd = NULL; } if (exten != NULL) { int ltemp = 0; if (exten->opcode != 0) { ltemp = crypto_xmit(&xpkt, &peer->srcadr, sendlen, exten, 0); if (ltemp == 0) { peer->flash |= TEST9; /* crypto error */ free(exten); return; } } sendlen += ltemp; free(exten); } /* * If extension fields are present, we must use a * private cookie value of zero. Don't send if the * crypto bit is set and no extension field is present, * but in that case give back the key. Most intricate. */ if (sendlen > LEN_PKT_NOMAC) { session_key(&peer->dstadr->sin, &peer->srcadr, xkeyid, 0, 2); } else if (peer->flash & TEST9) { authtrust(xkeyid, 0); return; } } #endif /* OPENSSL */ /* * Stash the transmit timestamp corrected for the encryption * delay. If autokey, give back the key, as we use keys only * once. Check for errors such as missing keys, buffer overflow, * etc. */ xkeyid = peer->keyid; get_systime(&peer->xmt); L_ADD(&peer->xmt, &sys_authdelay); HTONL_FP(&peer->xmt, &xpkt.xmt); authlen = authencrypt(xkeyid, (u_int32 *)&xpkt, sendlen); if (authlen == 0) { msyslog(LOG_INFO, "transmit: %s key %u not found", stoa(&peer->srcadr), xkeyid); peer->flash |= TEST9; /* no key found */ return; } sendlen += authlen; #ifdef OPENSSL if (xkeyid > NTP_MAXKEY) authtrust(xkeyid, 0); #endif /* OPENSSL */ get_systime(&xmt_tx); if (sendlen > sizeof(xpkt)) { msyslog(LOG_ERR, "buffer overflow %u", sendlen); exit (-1); } sendpkt(&peer->srcadr, peer->dstadr, sys_ttl[peer->ttl], &xpkt, sendlen); /* * Calculate the encryption delay. Keep the minimum over * the latest two samples. */ L_SUB(&xmt_tx, &peer->xmt); L_ADD(&xmt_tx, &sys_authdelay); sys_authdly[1] = sys_authdly[0]; sys_authdly[0] = xmt_tx.l_uf; if (sys_authdly[0] < sys_authdly[1]) sys_authdelay.l_uf = sys_authdly[0]; else sys_authdelay.l_uf = sys_authdly[1]; peer->sent++; #ifdef OPENSSL #ifdef DEBUG if (debug) printf( "transmit: at %ld %s->%s mode %d keyid %08x len %d mac %d index %d\n", current_time, peer->dstadr ? ntoa(&peer->dstadr->sin) : "-", ntoa(&peer->srcadr), peer->hmode, xkeyid, sendlen - authlen, authlen, peer->keynumber); #endif #else #ifdef DEBUG if (debug) printf( "transmit: at %ld %s->%s mode %d keyid %08x len %d mac %d\n", current_time, peer->dstadr ? ntoa(&peer->dstadr->sin) : "-", ntoa(&peer->srcadr), peer->hmode, xkeyid, sendlen - authlen, authlen); #endif #endif /* OPENSSL */ } /* * fast_xmit - Send packet for nonpersistent association. Note that * neither the source or destination can be a broadcast address. */ static void fast_xmit( struct recvbuf *rbufp, /* receive packet pointer */ int xmode, /* transmit mode */ keyid_t xkeyid, /* transmit key ID */ int mask /* restrict mask */ ) { struct pkt xpkt; /* transmit packet structure */ struct pkt *rpkt; /* receive packet structure */ l_fp xmt_ts; /* timestamp */ l_fp xmt_tx; /* timestamp after authent */ int sendlen, authlen; #ifdef OPENSSL u_int32 temp32; #endif /* * Initialize transmit packet header fields from the receive * buffer provided. We leave some fields intact as received. If * the gazinta was from a multicast address, the gazoutta must * go out another way. * * The root delay field is special. If the system stratum is * less than the orphan stratum, send the real root delay. * Otherwise, if there is no system peer, send the orphan delay. * Otherwise, we must be an orphan parent, so send zero. */ rpkt = &rbufp->recv_pkt; if (rbufp->dstadr->flags & INT_MCASTOPEN) rbufp->dstadr = findinterface(&rbufp->recv_srcadr); /* * This is deliciously complicated. There are four cases. * * case leap stratum refid delay dispersion * * KoD 11 16 KISS system system * normal system system system system system * orphan child 00 orphan system orphan system * orphan parent 00 orphan loopbk 0 0 */ /* * This is a kiss-of-death (KoD) packet. Show leap * unsynchronized, stratum zero, reference ID the four-character * kiss code and system root delay. Note the rate limit on these * packets. Once a second initialize a bucket counter. Every * packet sent decrements the counter until reaching zero. If * the counter is zero, drop the kiss. */ if (mask & RES_LIMITED) { sys_limitrejected++; if (sys_kod == 0 || !(mask & RES_DEMOBILIZE)) return; sys_kod--; xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOTINSYNC, PKT_VERSION(rpkt->li_vn_mode), xmode); xpkt.stratum = STRATUM_UNSPEC; memcpy(&xpkt.refid, "RATE", 4); xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay)); xpkt.rootdispersion = HTONS_FP(DTOUFP(sys_rootdispersion)); /* * This is a normal packet. Use the system variables. */ } else if (sys_stratum < sys_orphan) { xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, PKT_VERSION(rpkt->li_vn_mode), xmode); xpkt.stratum = STRATUM_TO_PKT(sys_stratum); xpkt.refid = sys_refid; xpkt.rootdelay = HTONS_FP(DTOFP(sys_rootdelay)); xpkt.rootdispersion = HTONS_FP(DTOUFP(sys_rootdispersion)); /* * This is a orphan child packet. The host is synchronized to an * orphan parent. Show leap synchronized, orphan stratum, system * reference ID and orphan root delay. */ } else if (sys_peer != NULL) { xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, PKT_VERSION(rpkt->li_vn_mode), xmode); xpkt.stratum = STRATUM_TO_PKT(sys_orphan); xpkt.refid = sys_refid; xpkt.rootdelay = HTONS_FP(DTOFP(sys_orphandelay)); xpkt.rootdispersion = HTONS_FP(DTOUFP(sys_rootdispersion)); /* * This is an orphan parent. Show leap synchronized, orphan * stratum, loopack reference ID and zero root delay. */ } else { xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, PKT_VERSION(rpkt->li_vn_mode), xmode); xpkt.stratum = STRATUM_TO_PKT(sys_orphan); xpkt.refid = htonl(LOOPBACKADR); xpkt.rootdelay = HTONS_FP(DTOFP(0)); xpkt.rootdispersion = HTONS_FP(DTOFP(0)); } xpkt.ppoll = rpkt->ppoll; xpkt.precision = sys_precision; xpkt.rootdispersion = HTONS_FP(DTOUFP(sys_rootdispersion)); HTONL_FP(&sys_reftime, &xpkt.reftime); xpkt.org = rpkt->xmt; HTONL_FP(&rbufp->recv_time, &xpkt.rec); /* * If the received packet contains a MAC, the transmitted packet * is authenticated and contains a MAC. If not, the transmitted * packet is not authenticated. */ sendlen = LEN_PKT_NOMAC; if (rbufp->recv_length == sendlen) { get_systime(&xmt_ts); HTONL_FP(&xmt_ts, &xpkt.xmt); sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, 0, &xpkt, sendlen); #ifdef DEBUG if (debug) printf("transmit: at %ld %s->%s mode %d\n", current_time, stoa(&rbufp->dstadr->sin), stoa(&rbufp->recv_srcadr), xmode); #endif return; } /* * The received packet contains a MAC, so the transmitted packet * must be authenticated. For symmetric key cryptography, use * the predefined and trusted symmetric keys to generate the * cryptosum. For autokey cryptography, use the server private * value to generate the cookie, which is unique for every * source-destination-key ID combination. */ #ifdef OPENSSL if (xkeyid > NTP_MAXKEY) { keyid_t cookie; /* * The only way to get here is a reply to a legitimate * client request message, so the mode must be * MODE_SERVER. If an extension field is present, there * can be only one and that must be a command. Do what * needs, but with private value of zero so the poor * jerk can decode it. If no extension field is present, * use the cookie to generate the session key. */ cookie = session_key(&rbufp->recv_srcadr, &rbufp->dstadr->sin, 0, sys_private, 0); if (rbufp->recv_length >= (int)(sendlen + MAX_MAC_LEN + 2 * sizeof(u_int32))) { session_key(&rbufp->dstadr->sin, &rbufp->recv_srcadr, xkeyid, 0, 2); temp32 = CRYPTO_RESP; rpkt->exten[0] |= htonl(temp32); sendlen += crypto_xmit(&xpkt, &rbufp->recv_srcadr, sendlen, (struct exten *)rpkt->exten, cookie); } else { session_key(&rbufp->dstadr->sin, &rbufp->recv_srcadr, xkeyid, cookie, 2); } } #endif /* OPENSSL */ get_systime(&xmt_ts); L_ADD(&xmt_ts, &sys_authdelay); HTONL_FP(&xmt_ts, &xpkt.xmt); authlen = authencrypt(xkeyid, (u_int32 *)&xpkt, sendlen); sendlen += authlen; #ifdef OPENSSL if (xkeyid > NTP_MAXKEY) authtrust(xkeyid, 0); #endif /* OPENSSL */ get_systime(&xmt_tx); if (sendlen > sizeof(xpkt)) { msyslog(LOG_ERR, "buffer overflow %u", sendlen); exit (-1); } sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, 0, &xpkt, sendlen); /* * Calculate the encryption delay. Keep the minimum over the * latest two samples. */ L_SUB(&xmt_tx, &xmt_ts); L_ADD(&xmt_tx, &sys_authdelay); sys_authdly[1] = sys_authdly[0]; sys_authdly[0] = xmt_tx.l_uf; if (sys_authdly[0] < sys_authdly[1]) sys_authdelay.l_uf = sys_authdly[0]; else sys_authdelay.l_uf = sys_authdly[1]; #ifdef DEBUG if (debug) printf( "transmit: at %ld %s->%s mode %d keyid %08x len %d mac %d\n", current_time, ntoa(&rbufp->dstadr->sin), ntoa(&rbufp->recv_srcadr), xmode, xkeyid, sendlen - authlen, authlen); #endif } #ifdef OPENSSL /* * key_expire - purge the key list */ void key_expire( struct peer *peer /* peer structure pointer */ ) { int i; if (peer->keylist != NULL) { for (i = 0; i <= peer->keynumber; i++) authtrust(peer->keylist[i], 0); free(peer->keylist); peer->keylist = NULL; } value_free(&peer->sndval); peer->keynumber = 0; #ifdef DEBUG if (debug) printf("key_expire: at %lu\n", current_time); #endif } #endif /* OPENSSL */ /* * Determine if the peer is unfit for synchronization * * A peer is unfit for synchronization if * > TEST10 bad leap or stratum below floor or at or above ceiling * > TEST11 root distance exceeded * > TEST12 a direct or indirect synchronization loop would form * > TEST13 unreachable or noselect */ int /* FALSE if fit, TRUE if unfit */ peer_unfit( struct peer *peer /* peer structure pointer */ ) { int rval = 0; /* * A stratum error occurs if (1) the server has never been * synchronized, (2) the server stratum is below the floor or * greater than or equal to the ceiling, (3) the system stratum * is below the orphan stratum and the server stratum is greater * than or equal to the orphan stratum. */ if (peer->leap == LEAP_NOTINSYNC || peer->stratum < sys_floor || peer->stratum >= sys_ceiling || (sys_stratum < sys_orphan && peer->stratum >= sys_orphan)) rval |= TEST10; /* stratum out of bounds */ /* * A distance error occurs if the root distance is greater than * or equal to the distance threshold plus the increment due to * one poll interval. */ if (root_distance(peer) >= sys_maxdist + clock_phi * ULOGTOD(sys_poll)) rval |= TEST11; /* distance exceeded */ /* * A loop error occurs if the remote peer is synchronized to the * local peer of if the remote peer is synchronized to the same * server as the local peer, but only if the remote peer is not * the orphan parent. */ if (peer->stratum > 1 && peer->refid != htonl(LOOPBACKADR) && ((!peer->dstadr || peer->refid == peer->dstadr->addr_refid) || peer->refid == sys_refid)) rval |= TEST12; /* synch loop */ /* * An unreachable error occurs if the server is unreachable or * the noselect bit is set. */ if (!peer->reach || peer->flags & FLAG_NOSELECT) rval |= TEST13; /* unreachable */ peer->flash &= ~PEER_TEST_MASK; peer->flash |= rval; return (rval); } /* * Find the precision of this particular machine */ #define MINSTEP 100e-9 /* minimum clock increment (s) */ #define MAXSTEP 20e-3 /* maximum clock increment (s) */ #define MINLOOPS 5 /* minimum number of step samples */ /* * This routine calculates the system precision, defined as the minimum * of a sequence of differences between successive readings of the * system clock. However, if the system clock can be read more than once * during a tick interval, the difference can be zero or one LSB unit, * where the LSB corresponds to one nanosecond or one microsecond. * Conceivably, if some other process preempts this one and reads the * clock, the difference can be more than one LSB unit. * * For hardware clock frequencies of 10 MHz or less, we assume the * logical clock advances only at the hardware clock tick. For higher * frequencies, we assume the logical clock can advance no more than 100 * nanoseconds between ticks. */ int default_get_precision(void) { l_fp val; /* current seconds fraction */ l_fp last; /* last seconds fraction */ l_fp diff; /* difference */ double tick; /* computed tick value */ double dtemp; /* scratch */ int i; /* log2 precision */ /* * Loop to find tick value in nanoseconds. Toss out outlyer * values less than the minimun tick value. In wacky cases, use * the default maximum value. */ get_systime(&last); tick = MAXSTEP; for (i = 0; i < MINLOOPS;) { get_systime(&val); diff = val; L_SUB(&diff, &last); last = val; LFPTOD(&diff, dtemp); if (dtemp < MINSTEP) continue; i++; if (dtemp < tick) tick = dtemp; } /* * Find the nearest power of two. */ NLOG(NLOG_SYSEVENT) msyslog(LOG_INFO, "precision = %.3f usec", tick * 1e6); for (i = 0; tick <= 1; i++) tick *= 2; if (tick - 1. > 1. - tick / 2) i--; return (-i); } /* * kod_proto - called once per second to limit kiss-of-death packets */ void kod_proto(void) { sys_kod = sys_kod_rate; } /* * init_proto - initialize the protocol module's data */ void init_proto(void) { l_fp dummy; int i; /* * Fill in the sys_* stuff. Default is don't listen to * broadcasting, authenticate. */ sys_leap = LEAP_NOTINSYNC; sys_stratum = STRATUM_UNSPEC; memcpy(&sys_refid, "INIT", 4); sys_precision = (s_char)default_get_precision(); sys_jitter = LOGTOD(sys_precision); sys_rootdelay = 0; sys_orphandelay = (double)(ntp_random() & 0xffff) / 65536. * sys_maxdist; sys_rootdispersion = 0; L_CLR(&sys_reftime); sys_peer = NULL; sys_survivors = 0; get_systime(&dummy); sys_manycastserver = 0; sys_bclient = 0; sys_bdelay = DEFBROADDELAY; sys_calldelay = BURST_DELAY; sys_authenticate = 1; L_CLR(&sys_authdelay); sys_authdly[0] = sys_authdly[1] = 0; sys_stattime = 0; proto_clr_stats(); for (i = 0; i < MAX_TTL; i++) { sys_ttl[i] = (u_char)((i * 256) / MAX_TTL); sys_ttlmax = i; } #ifdef OPENSSL sys_automax = 1 << NTP_AUTOMAX; #endif /* OPENSSL */ /* * Default these to enable */ ntp_enable = 1; #ifndef KERNEL_FLL_BUG kern_enable = 1; #endif pps_enable = 0; stats_control = 1; } /* * proto_config - configure the protocol module */ void proto_config( int item, u_long value, double dvalue, struct sockaddr_storage* svalue ) { /* * Figure out what he wants to change, then do it */ switch (item) { /* * Turn on/off kernel discipline. */ case PROTO_KERNEL: kern_enable = (int)value; break; /* * Turn on/off clock discipline. */ case PROTO_NTP: ntp_enable = (int)value; break; /* * Turn on/off monitoring. */ case PROTO_MONITOR: if (value) mon_start(MON_ON); else mon_stop(MON_ON); break; /* * Turn on/off statistics. */ case PROTO_FILEGEN: stats_control = (int)value; break; /* * Turn on/off enable broadcasts. */ case PROTO_BROADCLIENT: sys_bclient = (int)value; if (sys_bclient == 0) io_unsetbclient(); else io_setbclient(); break; /* * Turn on/off PPS discipline. */ case PROTO_PPS: pps_enable = (int)value; break; /* * Add muliticast group address. */ case PROTO_MULTICAST_ADD: if (svalue) io_multicast_add(*svalue); sys_bclient = 1; break; /* * Delete multicast group address. */ case PROTO_MULTICAST_DEL: if (svalue) io_multicast_del(*svalue); break; /* * Set default broadcast delay. */ case PROTO_BROADDELAY: sys_bdelay = dvalue; break; /* * Set modem call delay. */ case PROTO_CALLDELAY: sys_calldelay = (int)value; break; /* * Turn on/off authentication to mobilize ephemeral * associations. */ case PROTO_AUTHENTICATE: sys_authenticate = (int)value; break; /* * Set minimum number of survivors. */ case PROTO_MINCLOCK: sys_minclock = (int)dvalue; break; /* * Set maximum number of preemptable associations. */ case PROTO_MAXCLOCK: sys_maxclock = (int)dvalue; break; /* * Set minimum number of survivors. */ case PROTO_MINSANE: sys_minsane = (int)dvalue; break; /* * Set stratum floor. */ case PROTO_FLOOR: sys_floor = (int)dvalue; break; /* * Set stratum ceiling. */ case PROTO_CEILING: sys_ceiling = (int)dvalue; break; /* * Set orphan stratum. */ case PROTO_ORPHAN: sys_orphan = (int)dvalue; break; /* * Set cohort switch. */ case PROTO_COHORT: sys_cohort = (int)dvalue; break; /* * Set minimum dispersion increment. */ case PROTO_MINDISP: sys_mindisp = dvalue; break; /* * Set maximum distance (select threshold). */ case PROTO_MAXDIST: sys_maxdist = dvalue; break; /* * Set anticlockhop threshold. */ case PROTO_MAXHOP: sys_maxhop = (int)dvalue; break; /* * Set adjtime() resolution (s). */ case PROTO_ADJ: sys_tick = dvalue; break; /* * Set manycast beacon interval. */ case PROTO_BEACON: sys_beacon = (int)dvalue; break; #ifdef REFCLOCK /* * Turn on/off refclock calibrate */ case PROTO_CAL: cal_enable = (int)value; break; #endif /* REFCLOCK */ default: /* * Log this error. */ msyslog(LOG_INFO, "proto_config: illegal item %d, value %ld", item, value); } } /* * proto_clr_stats - clear protocol stat counters */ void proto_clr_stats(void) { sys_stattime = current_time; sys_received = 0; sys_processed = 0; sys_newversionpkt = 0; sys_oldversionpkt = 0; sys_unknownversion = 0; sys_restricted = 0; sys_badlength = 0; sys_badauth = 0; sys_limitrejected = 0; } Index: releng/10.1/contrib/ntp/util/ntp-keygen.c =================================================================== --- releng/10.1/contrib/ntp/util/ntp-keygen.c (revision 276158) +++ releng/10.1/contrib/ntp/util/ntp-keygen.c (revision 276159) @@ -1,1890 +1,1890 @@ /* * Program to generate cryptographic keys for NTP clients and servers * * This program generates files "ntpkey__.", * where is the file type, is the generating host and * is the NTP seconds in decimal format. The NTP programs * expect generic names such as "ntpkey__whimsy.udel.edu" with the * association maintained by soft links. * * Files are prefixed with a header giving the name and date of creation * followed by a type-specific descriptive label and PEM-encoded data * string compatible with programs of the OpenSSL library. * * Note that private keys can be password encrypted as per OpenSSL * conventions. * * The file types include * * ntpkey_MD5key_. * MD5 (128-bit) keys used to compute message digests in symmetric * key cryptography * * ntpkey_RSAkey_. * ntpkey_host_ (RSA) link * RSA private/public host key pair used for public key signatures * and data encryption * * ntpkey_DSAkey_. * ntpkey_sign_ (RSA or DSA) link * DSA private/public sign key pair used for public key signatures, * but not data encryption * * ntpkey_IFFpar_. * ntpkey_iff_ (IFF server/client) link * ntpkey_iffkey_ (IFF client) link * Schnorr (IFF) server/client identity parameters * * ntpkey_IFFkey_. * Schnorr (IFF) client identity parameters * * ntpkey_GQpar_., * ntpkey_gq_ (GQ) link * Guillou-Quisquater (GQ) identity parameters * * ntpkey_MVpar_., * Mu-Varadharajan (MV) server identity parameters * * ntpkey_MVkeyX_., * ntpkey_mv_ (MV server) link * ntpkey_mvkey_ (MV client) link * Mu-Varadharajan (MV) client identity parameters * * ntpkey_XXXcert_. * ntpkey_cert_ (RSA or DSA) link * X509v3 certificate using RSA or DSA public keys and signatures. * XXX is a code identifying the message digest and signature * encryption algorithm * * Available digest/signature schemes * * RSA: RSA-MD2, RSA-MD5, RSA-SHA, RSA-SHA1, RSA-MDC2, EVP-RIPEMD160 * DSA: DSA-SHA, DSA-SHA1 * * Note: Once in a while because of some statistical fluke this program * fails to generate and verify some cryptographic data, as indicated by * exit status -1. In this case simply run the program again. If the * program does complete with return code 0, the data are correct as * verified. * * These cryptographic routines are characterized by the prime modulus * size in bits. The default value of 512 bits is a compromise between * cryptographic strength and computing time and is ordinarily * considered adequate for this application. The routines have been * tested with sizes of 256, 512, 1024 and 2048 bits. Not all message * digest and signature encryption schemes work with sizes less than 512 * bits. The computing time for sizes greater than 2048 bits is * prohibitive on all but the fastest processors. An UltraSPARC Blade * 1000 took something over nine minutes to generate and verify the * values with size 2048. An old SPARC IPC would take a week. * * The OpenSSL library used by this program expects a random seed file. * As described in the OpenSSL documentation, the file name defaults to * first the RANDFILE environment variable in the user's home directory * and then .rnd in the user's home directory. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #if HAVE_SYS_TYPES_H # include #endif #include "ntp_types.h" #include "ntp_random.h" #include "l_stdlib.h" #include "ntp-keygen-opts.h" #ifdef SYS_WINNT extern int ntp_getopt P((int, char **, const char *)); #define getopt ntp_getopt #define optarg ntp_optarg #endif #ifdef OPENSSL #include "openssl/bn.h" #include "openssl/evp.h" #include "openssl/err.h" #include "openssl/rand.h" #include "openssl/pem.h" #include "openssl/x509v3.h" #include #endif /* OPENSSL */ /* * Cryptodefines */ #define MD5KEYS 16 /* number of MD5 keys generated */ #define JAN_1970 ULONG_CONST(2208988800) /* NTP seconds */ #define YEAR ((long)60*60*24*365) /* one year in seconds */ #define MAXFILENAME 256 /* max file name length */ #define MAXHOSTNAME 256 /* max host name length */ #ifdef OPENSSL #define PLEN 512 /* default prime modulus size (bits) */ /* * Strings used in X509v3 extension fields */ #define KEY_USAGE "digitalSignature,keyCertSign" #define BASIC_CONSTRAINTS "critical,CA:TRUE" #define EXT_KEY_PRIVATE "private" #define EXT_KEY_TRUST "trustRoot" #endif /* OPENSSL */ /* * Prototypes */ FILE *fheader P((const char *, const char *)); void fslink P((const char *, const char *)); int gen_md5 P((char *)); #ifdef OPENSSL EVP_PKEY *gen_rsa P((char *)); EVP_PKEY *gen_dsa P((char *)); EVP_PKEY *gen_iff P((char *)); EVP_PKEY *gen_gqpar P((char *)); EVP_PKEY *gen_gqkey P((char *, EVP_PKEY *)); EVP_PKEY *gen_mv P((char *)); int x509 P((EVP_PKEY *, const EVP_MD *, char *, char *)); void cb P((int, int, void *)); EVP_PKEY *genkey P((char *, char *)); u_long asn2ntp P((ASN1_TIME *)); #endif /* OPENSSL */ /* * Program variables */ extern char *optarg; /* command line argument */ int debug = 0; /* debug, not de bug */ int rval; /* return status */ #ifdef OPENSSL u_int modulus = PLEN; /* prime modulus size (bits) */ #endif int nkeys = 0; /* MV keys */ time_t epoch; /* Unix epoch (seconds) since 1970 */ char *hostname; /* host name (subject name) */ char *trustname; /* trusted host name (issuer name) */ char filename[MAXFILENAME + 1]; /* file name */ char *passwd1 = NULL; /* input private key password */ char *passwd2 = NULL; /* output private key password */ #ifdef OPENSSL long d0, d1, d2, d3; /* callback counters */ #endif /* OPENSSL */ #ifdef SYS_WINNT BOOL init_randfile(); /* * Don't try to follow symbolic links */ int readlink(char * link, char * file, int len) { return (-1); } /* * Don't try to create a symbolic link for now. * Just move the file to the name you need. */ int symlink(char *filename, char *linkname) { DeleteFile(linkname); MoveFile(filename, linkname); return 0; } void InitWin32Sockets() { WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(2,0); if (WSAStartup(wVersionRequested, &wsaData)) { fprintf(stderr, "No useable winsock.dll"); exit(1); } } #endif /* SYS_WINNT */ /* * Main program */ int main( int argc, /* command line options */ char **argv ) { struct timeval tv; /* initialization vector */ int md5key = 0; /* generate MD5 keys */ #ifdef OPENSSL X509 *cert = NULL; /* X509 certificate */ EVP_PKEY *pkey_host = NULL; /* host key */ EVP_PKEY *pkey_sign = NULL; /* sign key */ EVP_PKEY *pkey_iff = NULL; /* IFF parameters */ EVP_PKEY *pkey_gq = NULL; /* GQ parameters */ EVP_PKEY *pkey_mv = NULL; /* MV parameters */ int hostkey = 0; /* generate RSA keys */ int iffkey = 0; /* generate IFF parameters */ int gqpar = 0; /* generate GQ parameters */ int gqkey = 0; /* update GQ keys */ int mvpar = 0; /* generate MV parameters */ int mvkey = 0; /* update MV keys */ char *sign = NULL; /* sign key */ EVP_PKEY *pkey = NULL; /* temp key */ const EVP_MD *ectx; /* EVP digest */ char pathbuf[MAXFILENAME + 1]; const char *scheme = NULL; /* digest/signature scheme */ char *exten = NULL; /* private extension */ char *grpkey = NULL; /* identity extension */ int nid; /* X509 digest/signature scheme */ FILE *fstr = NULL; /* file handle */ u_int temp; #define iffsw HAVE_OPT(ID_KEY) #endif /* OPENSSL */ char hostbuf[MAXHOSTNAME + 1]; #ifdef SYS_WINNT /* Initialize before OpenSSL checks */ InitWin32Sockets(); if(!init_randfile()) fprintf(stderr, "Unable to initialize .rnd file\n"); #endif #ifdef OPENSSL /* * OpenSSL version numbers: MNNFFPPS: major minor fix patch status * We match major, minor, fix and status (not patch) */ if ((SSLeay() ^ OPENSSL_VERSION_NUMBER) & ~0xff0L) { fprintf(stderr, "OpenSSL version mismatch. Built against %lx, you have %lx\n", OPENSSL_VERSION_NUMBER, SSLeay()); return (-1); } else { fprintf(stderr, "Using OpenSSL version %lx\n", SSLeay()); } #endif /* OPENSSL */ /* * Process options, initialize host name and timestamp. */ gethostname(hostbuf, MAXHOSTNAME); hostname = hostbuf; #ifdef OPENSSL trustname = hostbuf; passwd1 = hostbuf; #endif #ifndef SYS_WINNT gettimeofday(&tv, 0); #else gettimeofday(&tv); #endif epoch = tv.tv_sec; rval = 0; { int optct = optionProcess(&ntp_keygenOptions, argc, argv); argc -= optct; argv += optct; } #ifdef OPENSSL if (HAVE_OPT( CERTIFICATE )) scheme = OPT_ARG( CERTIFICATE ); #endif debug = DESC(DEBUG_LEVEL).optOccCt; #ifdef OPENSSL if (HAVE_OPT( GQ_PARAMS )) gqpar++; if (HAVE_OPT( GQ_KEYS )) gqkey++; if (HAVE_OPT( HOST_KEY )) hostkey++; if (HAVE_OPT( IFFKEY )) iffkey++; if (HAVE_OPT( ISSUER_NAME )) trustname = OPT_ARG( ISSUER_NAME ); #endif if (HAVE_OPT( MD5KEY )) md5key++; #ifdef OPENSSL if (HAVE_OPT( MODULUS )) modulus = OPT_VALUE_MODULUS; if (HAVE_OPT( PVT_CERT )) exten = EXT_KEY_PRIVATE; if (HAVE_OPT( PVT_PASSWD )) passwd2 = OPT_ARG( PVT_PASSWD ); if (HAVE_OPT( GET_PVT_PASSWD )) passwd1 = OPT_ARG( GET_PVT_PASSWD ); if (HAVE_OPT( SIGN_KEY )) sign = OPT_ARG( SIGN_KEY ); if (HAVE_OPT( SUBJECT_NAME )) hostname = OPT_ARG( SUBJECT_NAME ); if (HAVE_OPT( TRUSTED_CERT )) exten = EXT_KEY_TRUST; if (HAVE_OPT( MV_PARAMS )) { mvpar++; nkeys = OPT_VALUE_MV_PARAMS; } if (HAVE_OPT( MV_KEYS )) { mvkey++; nkeys = OPT_VALUE_MV_KEYS; } #endif if (passwd1 != NULL && passwd2 == NULL) passwd2 = passwd1; #ifdef OPENSSL /* * Seed random number generator and grow weeds. */ ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); if (RAND_file_name(pathbuf, MAXFILENAME) == NULL) { fprintf(stderr, "RAND_file_name %s\n", ERR_error_string(ERR_get_error(), NULL)); return (-1); } temp = RAND_load_file(pathbuf, -1); if (temp == 0) { fprintf(stderr, "RAND_load_file %s not found or empty\n", pathbuf); return (-1); } fprintf(stderr, "Random seed file %s %u bytes\n", pathbuf, temp); RAND_add(&epoch, sizeof(epoch), 4.0); #endif /* * Generate new parameters and keys as requested. These replace * any values already generated. */ if (md5key) gen_md5("MD5"); #ifdef OPENSSL if (hostkey) pkey_host = genkey("RSA", "host"); if (sign != NULL) pkey_sign = genkey(sign, "sign"); if (iffkey) pkey_iff = gen_iff("iff"); if (gqpar) pkey_gq = gen_gqpar("gq"); if (mvpar) pkey_mv = gen_mv("mv"); /* * If there is no new host key, look for an existing one. If not * found, create it. */ while (pkey_host == NULL && rval == 0 && !HAVE_OPT(ID_KEY)) { sprintf(filename, "ntpkey_host_%s", hostname); if ((fstr = fopen(filename, "r")) != NULL) { pkey_host = PEM_read_PrivateKey(fstr, NULL, NULL, passwd1); fclose(fstr); readlink(filename, filename, sizeof(filename)); if (pkey_host == NULL) { fprintf(stderr, "Host key\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; } else { fprintf(stderr, "Using host key %s\n", filename); } break; } else if ((pkey_host = genkey("RSA", "host")) == NULL) { rval = -1; break; } } /* * If there is no new sign key, look for an existing one. If not * found, use the host key instead. */ pkey = pkey_sign; while (pkey_sign == NULL && rval == 0 && !HAVE_OPT(ID_KEY)) { sprintf(filename, "ntpkey_sign_%s", hostname); if ((fstr = fopen(filename, "r")) != NULL) { pkey_sign = PEM_read_PrivateKey(fstr, NULL, NULL, passwd1); fclose(fstr); readlink(filename, filename, sizeof(filename)); if (pkey_sign == NULL) { fprintf(stderr, "Sign key\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; } else { fprintf(stderr, "Using sign key %s\n", filename); } break; } else { pkey = pkey_host; fprintf(stderr, "Using host key as sign key\n"); break; } } /* * If there is no new IFF file, look for an existing one. */ if (pkey_iff == NULL && rval == 0) { sprintf(filename, "ntpkey_iff_%s", hostname); if ((fstr = fopen(filename, "r")) != NULL) { pkey_iff = PEM_read_PrivateKey(fstr, NULL, NULL, passwd1); fclose(fstr); readlink(filename, filename, sizeof(filename)); if (pkey_iff == NULL) { fprintf(stderr, "IFF parameters\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; } else { fprintf(stderr, "Using IFF parameters %s\n", filename); } } } /* * If there is no new GQ file, look for an existing one. */ if (pkey_gq == NULL && rval == 0 && !HAVE_OPT(ID_KEY)) { sprintf(filename, "ntpkey_gq_%s", hostname); if ((fstr = fopen(filename, "r")) != NULL) { pkey_gq = PEM_read_PrivateKey(fstr, NULL, NULL, passwd1); fclose(fstr); readlink(filename, filename, sizeof(filename)); if (pkey_gq == NULL) { fprintf(stderr, "GQ parameters\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; } else { fprintf(stderr, "Using GQ parameters %s\n", filename); } } } /* * If there is a GQ parameter file, create GQ private/public * keys and extract the public key for the certificate. */ if (pkey_gq != NULL && rval == 0) { gen_gqkey("gq", pkey_gq); grpkey = BN_bn2hex(pkey_gq->pkey.rsa->q); } /* * Generate a X509v3 certificate. */ while (scheme == NULL && rval == 0 && !HAVE_OPT(ID_KEY)) { sprintf(filename, "ntpkey_cert_%s", hostname); if ((fstr = fopen(filename, "r")) != NULL) { cert = PEM_read_X509(fstr, NULL, NULL, NULL); fclose(fstr); readlink(filename, filename, sizeof(filename)); if (cert == NULL) { fprintf(stderr, "Cert \n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; } else { nid = OBJ_obj2nid( cert->cert_info->signature->algorithm); scheme = OBJ_nid2sn(nid); fprintf(stderr, "Using scheme %s from %s\n", scheme, filename); break; } } scheme = "RSA-MD5"; } if (pkey != NULL && rval == 0 && !HAVE_OPT(ID_KEY)) { ectx = EVP_get_digestbyname(scheme); if (ectx == NULL) { fprintf(stderr, "Invalid digest/signature combination %s\n", scheme); rval = -1; } else { x509(pkey, ectx, grpkey, exten); } } /* * Write the IFF client parameters and keys as a DSA private key * encoded in PEM. Note the private key is obscured. */ if (pkey_iff != NULL && rval == 0 && HAVE_OPT(ID_KEY)) { DSA *dsa; char *sptr; char *tld; sptr = strrchr(filename, '.'); tld = malloc(strlen(sptr)); /* we have an extra byte ... */ strcpy(tld, 1+sptr); /* ... see? */ sprintf(filename, "ntpkey_IFFkey_%s.%s", trustname, tld); free(tld); fprintf(stderr, "Writing new IFF key %s\n", filename); fprintf(stdout, "# %s\n# %s", filename, ctime(&epoch)); dsa = pkey_iff->pkey.dsa; BN_copy(dsa->priv_key, BN_value_one()); pkey = EVP_PKEY_new(); EVP_PKEY_assign_DSA(pkey, dsa); PEM_write_PrivateKey(stdout, pkey, passwd2 ? EVP_des_cbc() : NULL, NULL, 0, NULL, passwd2); fclose(stdout); if (debug) DSA_print_fp(stdout, dsa, 0); } /* * Return the marbles. */ if (grpkey != NULL) OPENSSL_free(grpkey); if (pkey_host != NULL) EVP_PKEY_free(pkey_host); if (pkey_sign != NULL) EVP_PKEY_free(pkey_sign); if (pkey_iff != NULL) EVP_PKEY_free(pkey_iff); if (pkey_gq != NULL) EVP_PKEY_free(pkey_gq); if (pkey_mv != NULL) EVP_PKEY_free(pkey_mv); #endif /* OPENSSL */ return (rval); } #if 0 /* * Generate random MD5 key with password. */ int gen_md5( char *id /* file name id */ ) { BIGNUM *key; BIGNUM *keyid; FILE *str; u_char bin[16]; fprintf(stderr, "Generating MD5 keys...\n"); str = fheader("MD5key", hostname); keyid = BN_new(); key = BN_new(); BN_rand(keyid, 16, -1, 0); BN_rand(key, 128, -1, 0); BN_bn2bin(key, bin); PEM_write_fp(str, MD5, NULL, bin); fclose(str); fslink(id, hostname); return (1); } #else /* * Generate semi-random MD5 keys compatible with NTPv3 and NTPv4 */ int gen_md5( char *id /* file name id */ ) { u_char md5key[16]; /* MD5 key */ FILE *str; u_int temp = 0; /* Initialize to prevent warnings during compile */ int i, j; fprintf(stderr, "Generating MD5 keys...\n"); str = fheader("MD5key", hostname); ntp_srandom(epoch); for (i = 1; i <= MD5KEYS; i++) { for (j = 0; j < 16; j++) { while (1) { - temp = ntp_random() & 0xff; + temp = arc4random() & 0xff; if (temp == '#') continue; if (temp > 0x20 && temp < 0x7f) break; } md5key[j] = (u_char)temp; } md5key[15] = '\0'; fprintf(str, "%2d MD5 %16s # MD5 key\n", i, md5key); } fclose(str); fslink(id, hostname); return (1); } #endif /* OPENSSL */ #ifdef OPENSSL /* * Generate RSA public/private key pair */ EVP_PKEY * /* public/private key pair */ gen_rsa( char *id /* file name id */ ) { EVP_PKEY *pkey; /* private key */ RSA *rsa; /* RSA parameters and key pair */ FILE *str; fprintf(stderr, "Generating RSA keys (%d bits)...\n", modulus); - rsa = RSA_generate_key(modulus, 3, cb, "RSA"); + rsa = RSA_generate_key(modulus, 65537, cb, "RSA"); fprintf(stderr, "\n"); if (rsa == NULL) { fprintf(stderr, "RSA generate keys fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; return (NULL); } /* * For signature encryption it is not necessary that the RSA * parameters be strictly groomed and once in a while the * modulus turns out to be non-prime. Just for grins, we check * the primality. */ if (!RSA_check_key(rsa)) { fprintf(stderr, "Invalid RSA key\n%s\n", ERR_error_string(ERR_get_error(), NULL)); RSA_free(rsa); rval = -1; return (NULL); } /* * Write the RSA parameters and keys as a RSA private key * encoded in PEM. */ str = fheader("RSAkey", hostname); pkey = EVP_PKEY_new(); EVP_PKEY_assign_RSA(pkey, rsa); PEM_write_PrivateKey(str, pkey, passwd2 ? EVP_des_cbc() : NULL, NULL, 0, NULL, passwd2); fclose(str); if (debug) RSA_print_fp(stdout, rsa, 0); fslink(id, hostname); return (pkey); } /* * Generate DSA public/private key pair */ EVP_PKEY * /* public/private key pair */ gen_dsa( char *id /* file name id */ ) { EVP_PKEY *pkey; /* private key */ DSA *dsa; /* DSA parameters */ u_char seed[20]; /* seed for parameters */ FILE *str; /* * Generate DSA parameters. */ fprintf(stderr, "Generating DSA parameters (%d bits)...\n", modulus); RAND_bytes(seed, sizeof(seed)); dsa = DSA_generate_parameters(modulus, seed, sizeof(seed), NULL, NULL, cb, "DSA"); fprintf(stderr, "\n"); if (dsa == NULL) { fprintf(stderr, "DSA generate parameters fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; return (NULL); } /* * Generate DSA keys. */ fprintf(stderr, "Generating DSA keys (%d bits)...\n", modulus); if (!DSA_generate_key(dsa)) { fprintf(stderr, "DSA generate keys fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); DSA_free(dsa); rval = -1; return (NULL); } /* * Write the DSA parameters and keys as a DSA private key * encoded in PEM. */ str = fheader("DSAkey", hostname); pkey = EVP_PKEY_new(); EVP_PKEY_assign_DSA(pkey, dsa); PEM_write_PrivateKey(str, pkey, passwd2 ? EVP_des_cbc() : NULL, NULL, 0, NULL, passwd2); fclose(str); if (debug) DSA_print_fp(stdout, dsa, 0); fslink(id, hostname); return (pkey); } /* * Generate Schnorr (IFF) parameters and keys * * The Schnorr (IFF)identity scheme is intended for use when * certificates are generated by some other trusted certificate * authority and the parameters cannot be conveyed in the certificate * itself. For this purpose, new generations of IFF values must be * securely transmitted to all members of the group before use. There * are two kinds of files: server/client files that include private and * public parameters and client files that include only public * parameters. The scheme is self contained and independent of new * generations of host keys, sign keys and certificates. * * The IFF values hide in a DSA cuckoo structure which uses the same * parameters. The values are used by an identity scheme based on DSA * cryptography and described in Stimson p. 285. The p is a 512-bit * prime, g a generator of Zp* and q a 160-bit prime that divides p - 1 * and is a qth root of 1 mod p; that is, g^q = 1 mod p. The TA rolls a * private random group key b (0 < b < q), then computes public * v = g^(q - a). All values except the group key are known to all group * members; the group key is known to the group servers, but not the * group clients. Alice challenges Bob to confirm identity using the * protocol described below. */ EVP_PKEY * /* DSA cuckoo nest */ gen_iff( char *id /* file name id */ ) { EVP_PKEY *pkey; /* private key */ DSA *dsa; /* DSA parameters */ u_char seed[20]; /* seed for parameters */ BN_CTX *ctx; /* BN working space */ BIGNUM *b, *r, *k, *u, *v, *w; /* BN temp */ FILE *str; u_int temp; /* * Generate DSA parameters for use as IFF parameters. */ fprintf(stderr, "Generating IFF parameters (%d bits)...\n", modulus); RAND_bytes(seed, sizeof(seed)); dsa = DSA_generate_parameters(modulus, seed, sizeof(seed), NULL, NULL, cb, "IFF"); fprintf(stderr, "\n"); if (dsa == NULL) { fprintf(stderr, "DSA generate parameters fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; return (NULL);; } /* * Generate the private and public keys. The DSA parameters and * these keys are distributed to all members of the group. */ fprintf(stderr, "Generating IFF keys (%d bits)...\n", modulus); b = BN_new(); r = BN_new(); k = BN_new(); u = BN_new(); v = BN_new(); w = BN_new(); ctx = BN_CTX_new(); BN_rand(b, BN_num_bits(dsa->q), -1, 0); /* a */ BN_mod(b, b, dsa->q, ctx); BN_sub(v, dsa->q, b); BN_mod_exp(v, dsa->g, v, dsa->p, ctx); /* g^(q - b) mod p */ BN_mod_exp(u, dsa->g, b, dsa->p, ctx); /* g^b mod p */ BN_mod_mul(u, u, v, dsa->p, ctx); temp = BN_is_one(u); fprintf(stderr, "Confirm g^(q - b) g^b = 1 mod p: %s\n", temp == 1 ? "yes" : "no"); if (!temp) { BN_free(b); BN_free(r); BN_free(k); BN_free(u); BN_free(v); BN_free(w); BN_CTX_free(ctx); rval = -1; return (NULL); } dsa->priv_key = BN_dup(b); /* private key */ dsa->pub_key = BN_dup(v); /* public key */ /* * Here is a trial round of the protocol. First, Alice rolls * random r (0 < r < q) and sends it to Bob. She needs only * modulus q. */ BN_rand(r, BN_num_bits(dsa->q), -1, 0); /* r */ BN_mod(r, r, dsa->q, ctx); /* * Bob rolls random k (0 < k < q), computes y = k + b r mod q * and x = g^k mod p, then sends (y, x) to Alice. He needs * moduli p, q and the group key b. */ BN_rand(k, BN_num_bits(dsa->q), -1, 0); /* k, 0 < k < q */ BN_mod(k, k, dsa->q, ctx); BN_mod_mul(v, dsa->priv_key, r, dsa->q, ctx); /* b r mod q */ BN_add(v, v, k); BN_mod(v, v, dsa->q, ctx); /* y = k + b r mod q */ BN_mod_exp(u, dsa->g, k, dsa->p, ctx); /* x = g^k mod p */ /* * Alice computes g^y v^r and verifies the result is equal to x. * She needs modulus p, generator g, and the public key v, as * well as her original r. */ BN_mod_exp(v, dsa->g, v, dsa->p, ctx); /* g^y mod p */ BN_mod_exp(w, dsa->pub_key, r, dsa->p, ctx); /* v^r */ BN_mod_mul(v, w, v, dsa->p, ctx); /* product mod p */ temp = BN_cmp(u, v); fprintf(stderr, "Confirm g^k = g^(k + b r) g^(q - b) r: %s\n", temp == 0 ? "yes" : "no"); BN_free(b); BN_free(r); BN_free(k); BN_free(u); BN_free(v); BN_free(w); BN_CTX_free(ctx); if (temp != 0) { DSA_free(dsa); rval = -1; return (NULL); } /* * Write the IFF server parameters and keys as a DSA private key * encoded in PEM. * * p modulus p * q modulus q * g generator g * priv_key b * public_key v */ str = fheader("IFFpar", trustname); pkey = EVP_PKEY_new(); EVP_PKEY_assign_DSA(pkey, dsa); PEM_write_PrivateKey(str, pkey, passwd2 ? EVP_des_cbc() : NULL, NULL, 0, NULL, passwd2); fclose(str); if (debug) DSA_print_fp(stdout, dsa, 0); fslink(id, trustname); return (pkey); } /* * Generate Guillou-Quisquater (GQ) parameters and keys * * The Guillou-Quisquater (GQ) identity scheme is intended for use when * the parameters, keys and certificates are generated by this program. * The scheme uses a certificate extension field do convey the public * key of a particular group identified by a group key known only to * members of the group. The scheme is self contained and independent of * new generations of host keys and sign keys. * * The GQ parameters hide in a RSA cuckoo structure which uses the same * parameters. The values are used by an identity scheme based on RSA * cryptography and described in Stimson p. 300 (with errors). The 512- * bit public modulus is n = p q, where p and q are secret large primes. * The TA rolls private random group key b as RSA exponent. These values * are known to all group members. * * When rolling new certificates, a member recomputes the private and * public keys. The private key u is a random roll, while the public key * is the inverse obscured by the group key v = (u^-1)^b. These values * replace the private and public keys normally generated by the RSA * scheme. Alice challenges Bob to confirm identity using the protocol * described below. */ EVP_PKEY * /* RSA cuckoo nest */ gen_gqpar( char *id /* file name id */ ) { EVP_PKEY *pkey; /* private key */ RSA *rsa; /* GQ parameters */ BN_CTX *ctx; /* BN working space */ FILE *str; /* * Generate RSA parameters for use as GQ parameters. */ fprintf(stderr, "Generating GQ parameters (%d bits)...\n", modulus); - rsa = RSA_generate_key(modulus, 3, cb, "GQ"); + rsa = RSA_generate_key(modulus, 65537, cb, "GQ"); fprintf(stderr, "\n"); if (rsa == NULL) { fprintf(stderr, "RSA generate keys fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; return (NULL); } /* * Generate the group key b, which is saved in the e member of * the RSA structure. These values are distributed to all * members of the group, but shielded from all other groups. We * don't use all the parameters, but set the unused ones to a * small number to minimize the file size. */ ctx = BN_CTX_new(); BN_rand(rsa->e, BN_num_bits(rsa->n), -1, 0); /* b */ BN_mod(rsa->e, rsa->e, rsa->n, ctx); BN_copy(rsa->d, BN_value_one()); BN_copy(rsa->p, BN_value_one()); BN_copy(rsa->q, BN_value_one()); BN_copy(rsa->dmp1, BN_value_one()); BN_copy(rsa->dmq1, BN_value_one()); BN_copy(rsa->iqmp, BN_value_one()); /* * Write the GQ parameters as a RSA private key encoded in PEM. * The public and private keys are filled in later. * * n modulus n * e group key b * (remaining values are not used) */ str = fheader("GQpar", trustname); pkey = EVP_PKEY_new(); EVP_PKEY_assign_RSA(pkey, rsa); PEM_write_PrivateKey(str, pkey, passwd2 ? EVP_des_cbc() : NULL, NULL, 0, NULL, passwd2); fclose(str); if (debug) RSA_print_fp(stdout, rsa, 0); fslink(id, trustname); return (pkey); } /* * Update Guillou-Quisquater (GQ) parameters */ EVP_PKEY * /* RSA cuckoo nest */ gen_gqkey( char *id, /* file name id */ EVP_PKEY *gqpar /* GQ parameters */ ) { EVP_PKEY *pkey; /* private key */ RSA *rsa; /* RSA parameters */ BN_CTX *ctx; /* BN working space */ BIGNUM *u, *v, *g, *k, *r, *y; /* BN temps */ FILE *str; u_int temp; /* * Generate GQ keys. Note that the group key b is the e member * of * the GQ parameters. */ fprintf(stderr, "Updating GQ keys (%d bits)...\n", modulus); ctx = BN_CTX_new(); u = BN_new(); v = BN_new(); g = BN_new(); k = BN_new(); r = BN_new(); y = BN_new(); /* * When generating his certificate, Bob rolls random private key * u. */ rsa = gqpar->pkey.rsa; BN_rand(u, BN_num_bits(rsa->n), -1, 0); /* u */ BN_mod(u, u, rsa->n, ctx); BN_mod_inverse(v, u, rsa->n, ctx); /* u^-1 mod n */ BN_mod_mul(k, v, u, rsa->n, ctx); /* * Bob computes public key v = (u^-1)^b, which is saved in an * extension field on his certificate. We check that u^b v = * 1 mod n. */ BN_mod_exp(v, v, rsa->e, rsa->n, ctx); BN_mod_exp(g, u, rsa->e, rsa->n, ctx); /* u^b */ BN_mod_mul(g, g, v, rsa->n, ctx); /* u^b (u^-1)^b */ temp = BN_is_one(g); fprintf(stderr, "Confirm u^b (u^-1)^b = 1 mod n: %s\n", temp ? "yes" : "no"); if (!temp) { BN_free(u); BN_free(v); BN_free(g); BN_free(k); BN_free(r); BN_free(y); BN_CTX_free(ctx); RSA_free(rsa); rval = -1; return (NULL); } BN_copy(rsa->p, u); /* private key */ BN_copy(rsa->q, v); /* public key */ /* * Here is a trial run of the protocol. First, Alice rolls * random r (0 < r < n) and sends it to Bob. She needs only * modulus n from the parameters. */ BN_rand(r, BN_num_bits(rsa->n), -1, 0); /* r */ BN_mod(r, r, rsa->n, ctx); /* * Bob rolls random k (0 < k < n), computes y = k u^r mod n and * g = k^b mod n, then sends (y, g) to Alice. He needs modulus n * from the parameters and his private key u. */ BN_rand(k, BN_num_bits(rsa->n), -1, 0); /* k */ BN_mod(k, k, rsa->n, ctx); BN_mod_exp(y, rsa->p, r, rsa->n, ctx); /* u^r mod n */ BN_mod_mul(y, k, y, rsa->n, ctx); /* y = k u^r mod n */ BN_mod_exp(g, k, rsa->e, rsa->n, ctx); /* g = k^b mod n */ /* * Alice computes v^r y^b mod n and verifies the result is equal * to g. She needs modulus n, generator g and group key b from * the parameters and Bob's public key v = (u^-1)^b from his * certificate. */ BN_mod_exp(v, rsa->q, r, rsa->n, ctx); /* v^r mod n */ BN_mod_exp(y, y, rsa->e, rsa->n, ctx); /* y^b mod n */ BN_mod_mul(y, v, y, rsa->n, ctx); /* v^r y^b mod n */ temp = BN_cmp(y, g); fprintf(stderr, "Confirm g^k = v^r y^b mod n: %s\n", temp == 0 ? "yes" : "no"); BN_CTX_free(ctx); BN_free(u); BN_free(v); BN_free(g); BN_free(k); BN_free(r); BN_free(y); if (temp != 0) { RSA_free(rsa); rval = -1; return (NULL); } /* * Write the GQ parameters and keys as a RSA private key encoded * in PEM. * * n modulus n * e group key b * p private key u * q public key (u^-1)^b * (remaining values are not used) */ str = fheader("GQpar", trustname); pkey = EVP_PKEY_new(); EVP_PKEY_assign_RSA(pkey, rsa); PEM_write_PrivateKey(str, pkey, passwd2 ? EVP_des_cbc() : NULL, NULL, 0, NULL, passwd2); fclose(str); if (debug) RSA_print_fp(stdout, rsa, 0); fslink(id, trustname); return (pkey); } /* * Generate Mu-Varadharajan (MV) parameters and keys * * The Mu-Varadharajan (MV) cryptosystem is useful when servers * broadcast messages to clients, but clients never send messages to * servers. There is one encryption key for the server and a separate * decryption key for each client. It operates something like a * pay-per-view satellite broadcasting system where the session key is * encrypted by the broadcaster and the decryption keys are held in a * tamperproof set-top box. We don't use it this way, but read on. * * The MV parameters and private encryption key hide in a DSA cuckoo * structure which uses the same parameters, but generated in a * different way. The values are used in an encryption scheme similar to * El Gamal cryptography and a polynomial formed from the expansion of * product terms (x - x[j]), as described in Mu, Y., and V. * Varadharajan: Robust and Secure Broadcasting, Proc. Indocrypt 2001, * 223-231. The paper has significant errors and serious omissions. * * Let q be the product of n distinct primes s'[j] (j = 1...n), where * each s'[j] has m significant bits. Let p be a prime p = 2 * q + 1, so * that q and each s'[j] divide p - 1 and p has M = n * m + 1 * significant bits. Let g be a generator of Zp; that is, gcd(g, p - 1) * = 1 and g^q = 1 mod p. We do modular arithmetic over Zq and then * project into Zp* as exponents of g. Sometimes we have to compute an * inverse b^-1 of random b in Zq, but for that purpose we require * gcd(b, q) = 1. We expect M to be in the 500-bit range and n * relatively small, like 30. Associated with each s'[j] is an element * s[j] such that s[j] s'[j] = s'[j] mod q. We find s[j] as the quotient * (q + s'[j]) / s'[j]. These are the parameters of the scheme and they * are expensive to compute. * * We set up an instance of the scheme as follows. A set of random * values x[j] mod q (j = 1...n), are generated as the zeros of a * polynomial of order n. The product terms (x - x[j]) are expanded to * form coefficients a[i] mod q (i = 0...n) in powers of x. These are * used as exponents of the generator g mod p to generate the private * encryption key A. The pair (gbar, ghat) of public server keys and the * pairs (xbar[j], xhat[j]) (j = 1...n) of private client keys are used * to construct the decryption keys. The devil is in the details. * * This routine generates a private encryption file including the * private encryption key E and public key (gbar, ghat). It then * generates decryption files including the private key (xbar[j], * xhat[j]) for each client. E is a permutation that encrypts a block * y = E x. The jth client computes the inverse permutation E^-1 = * gbar^xhat[j] ghat^xbar[j] and decrypts the block x = E^-1 y. * * The distinguishing characteristic of this scheme is the capability to * revoke keys. Included in the calculation of E, gbar and ghat is the * product s = prod(s'[j]) (j = 1...n) above. If the factor s'[j] is * subsequently removed from the product and E, gbar and ghat * recomputed, the jth client will no longer be able to compute E^-1 and * thus unable to decrypt the block. */ EVP_PKEY * /* DSA cuckoo nest */ gen_mv( char *id /* file name id */ ) { EVP_PKEY *pkey, *pkey1; /* private key */ DSA *dsa; /* DSA parameters */ DSA *sdsa; /* DSA parameters */ BN_CTX *ctx; /* BN working space */ BIGNUM **x; /* polynomial zeros vector */ BIGNUM **a; /* polynomial coefficient vector */ BIGNUM **g; /* public key vector */ BIGNUM **s, **s1; /* private enabling keys */ BIGNUM **xbar, **xhat; /* private keys vector */ BIGNUM *b; /* group key */ BIGNUM *b1; /* inverse group key */ BIGNUM *ss; /* enabling key */ BIGNUM *biga; /* master encryption key */ BIGNUM *bige; /* session encryption key */ BIGNUM *gbar, *ghat; /* public key */ BIGNUM *u, *v, *w; /* BN scratch */ int i, j, n; FILE *str; u_int temp; char ident[20]; /* * Generate MV parameters. * * The object is to generate a multiplicative group Zp* modulo a * prime p and a subset Zq mod q, where q is the product of n * distinct primes s'[j] (j = 1...n) and q divides p - 1. We * first generate n distinct primes, which may have to be * regenerated later. As a practical matter, it is tough to find * more than 31 distinct primes for modulus 512 or 61 primes for * modulus 1024. The latter can take several hundred iterations * and several minutes on a Sun Blade 1000. */ n = nkeys; fprintf(stderr, "Generating MV parameters for %d keys (%d bits)...\n", n, modulus / n); ctx = BN_CTX_new(); u = BN_new(); v = BN_new(); w = BN_new(); b = BN_new(); b1 = BN_new(); dsa = DSA_new(); dsa->p = BN_new(); dsa->q = BN_new(); dsa->g = BN_new(); s = malloc((n + 1) * sizeof(BIGNUM)); s1 = malloc((n + 1) * sizeof(BIGNUM)); for (j = 1; j <= n; j++) s1[j] = BN_new(); temp = 0; for (j = 1; j <= n; j++) { while (1) { fprintf(stderr, "Birthdays %d\r", temp); BN_generate_prime(s1[j], modulus / n, 0, NULL, NULL, NULL, NULL); for (i = 1; i < j; i++) { if (BN_cmp(s1[i], s1[j]) == 0) break; } if (i == j) break; temp++; } } fprintf(stderr, "Birthday keys rejected %d\n", temp); /* * Compute the modulus q as the product of the primes. Compute * the modulus p as 2 * q + 1 and test p for primality. If p * is composite, replace one of the primes with a new distinct * one and try again. Note that q will hardly be a secret since * we have to reveal p to servers and clients. However, * factoring q to find the primes should be adequately hard, as * this is the same problem considered hard in RSA. Question: is * it as hard to find n small prime factors totalling n bits as * it is to find two large prime factors totalling n bits? * Remember, the bad guy doesn't know n. */ temp = 0; while (1) { fprintf(stderr, "Duplicate keys rejected %d\r", ++temp); BN_one(dsa->q); for (j = 1; j <= n; j++) BN_mul(dsa->q, dsa->q, s1[j], ctx); BN_copy(dsa->p, dsa->q); BN_add(dsa->p, dsa->p, dsa->p); BN_add_word(dsa->p, 1); if (BN_is_prime(dsa->p, BN_prime_checks, NULL, ctx, NULL)) break; j = temp % n + 1; while (1) { BN_generate_prime(u, modulus / n, 0, 0, NULL, NULL, NULL); for (i = 1; i <= n; i++) { if (BN_cmp(u, s1[i]) == 0) break; } if (i > n) break; } BN_copy(s1[j], u); } fprintf(stderr, "Duplicate keys rejected %d\n", temp); /* * Compute the generator g using a random roll such that * gcd(g, p - 1) = 1 and g^q = 1. This is a generator of p, not * q. */ BN_copy(v, dsa->p); BN_sub_word(v, 1); while (1) { BN_rand(dsa->g, BN_num_bits(dsa->p) - 1, 0, 0); BN_mod(dsa->g, dsa->g, dsa->p, ctx); BN_gcd(u, dsa->g, v, ctx); if (!BN_is_one(u)) continue; BN_mod_exp(u, dsa->g, dsa->q, dsa->p, ctx); if (BN_is_one(u)) break; } /* * Compute s[j] such that s[j] * s'[j] = s'[j] for all j. The * easy way to do this is to compute q + s'[j] and divide the * result by s'[j]. Exercise for the student: prove the * remainder is always zero. */ for (j = 1; j <= n; j++) { s[j] = BN_new(); BN_add(s[j], dsa->q, s1[j]); BN_div(s[j], u, s[j], s1[j], ctx); } /* * Setup is now complete. Roll random polynomial roots x[j] * (0 < x[j] < q) for all j. While it may not be strictly * necessary, Make sure each root has no factors in common with * q. */ fprintf(stderr, "Generating polynomial coefficients for %d roots (%d bits)\n", n, BN_num_bits(dsa->q)); x = malloc((n + 1) * sizeof(BIGNUM)); for (j = 1; j <= n; j++) { x[j] = BN_new(); while (1) { BN_rand(x[j], BN_num_bits(dsa->q), 0, 0); BN_mod(x[j], x[j], dsa->q, ctx); BN_gcd(u, x[j], dsa->q, ctx); if (BN_is_one(u)) break; } } /* * Generate polynomial coefficients a[i] (i = 0...n) from the * expansion of root products (x - x[j]) mod q for all j. The * method is a present from Charlie Boncelet. */ a = malloc((n + 1) * sizeof(BIGNUM)); for (i = 0; i <= n; i++) { a[i] = BN_new(); BN_one(a[i]); } for (j = 1; j <= n; j++) { BN_zero(w); for (i = 0; i < j; i++) { BN_copy(u, dsa->q); BN_mod_mul(v, a[i], x[j], dsa->q, ctx); BN_sub(u, u, v); BN_add(u, u, w); BN_copy(w, a[i]); BN_mod(a[i], u, dsa->q, ctx); } } /* * Generate g[i] = g^a[i] mod p for all i and the generator g. */ fprintf(stderr, "Generating g[i] parameters\n"); g = malloc((n + 1) * sizeof(BIGNUM)); for (i = 0; i <= n; i++) { g[i] = BN_new(); BN_mod_exp(g[i], dsa->g, a[i], dsa->p, ctx); } /* * Verify prod(g[i]^(a[i] x[j]^i)) = 1 for all i, j; otherwise, * exit. Note the a[i] x[j]^i exponent is computed mod q, but * the g[i] is computed mod p. also note the expression given in * the paper is incorrect. */ temp = 1; for (j = 1; j <= n; j++) { BN_one(u); for (i = 0; i <= n; i++) { BN_set_word(v, i); BN_mod_exp(v, x[j], v, dsa->q, ctx); BN_mod_mul(v, v, a[i], dsa->q, ctx); BN_mod_exp(v, dsa->g, v, dsa->p, ctx); BN_mod_mul(u, u, v, dsa->p, ctx); } if (!BN_is_one(u)) temp = 0; } fprintf(stderr, "Confirm prod(g[i]^(x[j]^i)) = 1 for all i, j: %s\n", temp ? "yes" : "no"); if (!temp) { rval = -1; return (NULL); } /* * Make private encryption key A. Keep it around for awhile, * since it is expensive to compute. */ biga = BN_new(); BN_one(biga); for (j = 1; j <= n; j++) { for (i = 0; i < n; i++) { BN_set_word(v, i); BN_mod_exp(v, x[j], v, dsa->q, ctx); BN_mod_exp(v, g[i], v, dsa->p, ctx); BN_mod_mul(biga, biga, v, dsa->p, ctx); } } /* * Roll private random group key b mod q (0 < b < q), where * gcd(b, q) = 1 to guarantee b^1 exists, then compute b^-1 * mod q. If b is changed, the client keys must be recomputed. */ while (1) { BN_rand(b, BN_num_bits(dsa->q), 0, 0); BN_mod(b, b, dsa->q, ctx); BN_gcd(u, b, dsa->q, ctx); if (BN_is_one(u)) break; } BN_mod_inverse(b1, b, dsa->q, ctx); /* * Make private client keys (xbar[j], xhat[j]) for all j. Note * that the keys for the jth client involve s[j], but not s'[j] * or the product s = prod(s'[j]) mod q, which is the enabling * key. */ xbar = malloc((n + 1) * sizeof(BIGNUM)); xhat = malloc((n + 1) * sizeof(BIGNUM)); for (j = 1; j <= n; j++) { xbar[j] = BN_new(); xhat[j] = BN_new(); BN_zero(xbar[j]); BN_set_word(v, n); for (i = 1; i <= n; i++) { if (i == j) continue; BN_mod_exp(u, x[i], v, dsa->q, ctx); BN_add(xbar[j], xbar[j], u); } BN_mod_mul(xbar[j], xbar[j], b1, dsa->q, ctx); BN_mod_exp(xhat[j], x[j], v, dsa->q, ctx); BN_mod_mul(xhat[j], xhat[j], s[j], dsa->q, ctx); } /* * The enabling key is initially q by construction. We can * revoke client j by dividing q by s'[j]. The quotient becomes * the enabling key s. Note we always have to revoke one key; * otherwise, the plaintext and cryptotext would be identical. */ ss = BN_new(); BN_copy(ss, dsa->q); BN_div(ss, u, dsa->q, s1[n], ctx); /* * Make private server encryption key E = A^s and public server * keys gbar = g^s mod p and ghat = g^(s b) mod p. The (gbar, * ghat) is the public key provided to the server, which uses it * to compute the session encryption key and public key included * in its messages. These values must be regenerated if the * enabling key is changed. */ bige = BN_new(); gbar = BN_new(); ghat = BN_new(); BN_mod_exp(bige, biga, ss, dsa->p, ctx); BN_mod_exp(gbar, dsa->g, ss, dsa->p, ctx); BN_mod_mul(v, ss, b, dsa->q, ctx); BN_mod_exp(ghat, dsa->g, v, dsa->p, ctx); /* * We produce the key media in three steps. The first step is to * generate the private values that do not depend on the * enabling key. These include the server values p, q, g, b, A * and the client values s'[j], xbar[j] and xhat[j] for each j. * The p, xbar[j] and xhat[j] values are encoded in private * files which are distributed to respective clients. The p, q, * g, A and s'[j] values (will be) written to a secret file to * be read back later. * * The secret file (will be) read back at some later time to * enable/disable individual keys and generate/regenerate the * enabling key s. The p, q, E, gbar and ghat values are written * to a secret file to be read back later by the server. * * The server reads the secret file and rolls the session key * k, which is used only once, then computes E^k, gbar^k and * ghat^k. The E^k is the session encryption key. The encrypted * data, gbar^k and ghat^k are transmtted to clients in an * extension field. The client receives the message and computes * x = (gbar^k)^xbar[j] (ghat^k)^xhat[j], finds the session * encryption key E^k as the inverse x^-1 and decrypts the data. */ BN_copy(dsa->g, bige); dsa->priv_key = BN_dup(gbar); dsa->pub_key = BN_dup(ghat); /* * Write the MV server parameters and keys as a DSA private key * encoded in PEM. * * p modulus p * q modulus q (used only to generate k) * g E mod p * priv_key gbar mod p * pub_key ghat mod p */ str = fheader("MVpar", trustname); pkey = EVP_PKEY_new(); EVP_PKEY_assign_DSA(pkey, dsa); PEM_write_PrivateKey(str, pkey, passwd2 ? EVP_des_cbc() : NULL, NULL, 0, NULL, passwd2); fclose(str); if (debug) DSA_print_fp(stdout, dsa, 0); fslink(id, trustname); /* * Write the parameters and private key (xbar[j], xhat[j]) for * all j as a DSA private key encoded in PEM. It is used only by * the designated recipient(s) who pay a suitably outrageous fee * for its use. */ sdsa = DSA_new(); sdsa->p = BN_dup(dsa->p); sdsa->q = BN_dup(BN_value_one()); sdsa->g = BN_dup(BN_value_one()); sdsa->priv_key = BN_new(); sdsa->pub_key = BN_new(); for (j = 1; j <= n; j++) { BN_copy(sdsa->priv_key, xbar[j]); BN_copy(sdsa->pub_key, xhat[j]); BN_mod_exp(v, dsa->priv_key, sdsa->pub_key, dsa->p, ctx); BN_mod_exp(u, dsa->pub_key, sdsa->priv_key, dsa->p, ctx); BN_mod_mul(u, u, v, dsa->p, ctx); BN_mod_mul(u, u, dsa->g, dsa->p, ctx); BN_free(xbar[j]); BN_free(xhat[j]); BN_free(x[j]); BN_free(s[j]); BN_free(s1[j]); if (!BN_is_one(u)) { fprintf(stderr, "Revoke key %d\n", j); continue; } /* * Write the client parameters as a DSA private key * encoded in PEM. We don't make links for these. * * p modulus p * priv_key xbar[j] mod q * pub_key xhat[j] mod q * (remaining values are not used) */ sprintf(ident, "MVkey%d", j); str = fheader(ident, trustname); pkey1 = EVP_PKEY_new(); EVP_PKEY_set1_DSA(pkey1, sdsa); PEM_write_PrivateKey(str, pkey1, passwd2 ? EVP_des_cbc() : NULL, NULL, 0, NULL, passwd2); fclose(str); fprintf(stderr, "ntpkey_%s_%s.%lu\n", ident, trustname, epoch + JAN_1970); if (debug) DSA_print_fp(stdout, sdsa, 0); EVP_PKEY_free(pkey1); } /* * Free the countries. */ for (i = 0; i <= n; i++) { BN_free(a[i]); BN_free(g[i]); } BN_free(u); BN_free(v); BN_free(w); BN_CTX_free(ctx); BN_free(b); BN_free(b1); BN_free(biga); BN_free(bige); BN_free(ss); BN_free(gbar); BN_free(ghat); DSA_free(sdsa); /* * Free the world. */ free(x); free(a); free(g); free(s); free(s1); free(xbar); free(xhat); return (pkey); } /* * Generate X509v3 scertificate. * * The certificate consists of the version number, serial number, * validity interval, issuer name, subject name and public key. For a * self-signed certificate, the issuer name is the same as the subject * name and these items are signed using the subject private key. The * validity interval extends from the current time to the same time one * year hence. For NTP purposes, it is convenient to use the NTP seconds * of the current time as the serial number. */ int x509 ( EVP_PKEY *pkey, /* generic signature algorithm */ const EVP_MD *md, /* generic digest algorithm */ char *gqpub, /* identity extension (hex string) */ char *exten /* private cert extension */ ) { X509 *cert; /* X509 certificate */ X509_NAME *subj; /* distinguished (common) name */ X509_EXTENSION *ex; /* X509v3 extension */ FILE *str; /* file handle */ ASN1_INTEGER *serial; /* serial number */ const char *id; /* digest/signature scheme name */ char pathbuf[MAXFILENAME + 1]; /* * Generate X509 self-signed certificate. * * Set the certificate serial to the NTP seconds for grins. Set * the version to 3. Set the subject name and issuer name to the * subject name in the request. Set the initial validity to the * current time and the final validity one year hence. */ id = OBJ_nid2sn(md->pkey_type); fprintf(stderr, "Generating certificate %s\n", id); cert = X509_new(); X509_set_version(cert, 2L); serial = ASN1_INTEGER_new(); ASN1_INTEGER_set(serial, epoch + JAN_1970); X509_set_serialNumber(cert, serial); ASN1_INTEGER_free(serial); X509_time_adj(X509_get_notBefore(cert), 0L, &epoch); X509_time_adj(X509_get_notAfter(cert), YEAR, &epoch); subj = X509_get_subject_name(cert); X509_NAME_add_entry_by_txt(subj, "commonName", MBSTRING_ASC, (unsigned char *) hostname, strlen(hostname), -1, 0); subj = X509_get_issuer_name(cert); X509_NAME_add_entry_by_txt(subj, "commonName", MBSTRING_ASC, (unsigned char *) trustname, strlen(trustname), -1, 0); if (!X509_set_pubkey(cert, pkey)) { fprintf(stderr, "Assign key fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); X509_free(cert); rval = -1; return (0); } /* * Add X509v3 extensions if present. These represent the minimum * set defined in RFC3280 less the certificate_policy extension, * which is seriously obfuscated in OpenSSL. */ /* * The basic_constraints extension CA:TRUE allows servers to * sign client certficitates. */ fprintf(stderr, "%s: %s\n", LN_basic_constraints, BASIC_CONSTRAINTS); ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints, BASIC_CONSTRAINTS); if (!X509_add_ext(cert, ex, -1)) { fprintf(stderr, "Add extension field fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; return (0); } X509_EXTENSION_free(ex); /* * The key_usage extension designates the purposes the key can * be used for. */ fprintf(stderr, "%s: %s\n", LN_key_usage, KEY_USAGE); ex = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage, KEY_USAGE); if (!X509_add_ext(cert, ex, -1)) { fprintf(stderr, "Add extension field fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; return (0); } X509_EXTENSION_free(ex); /* * The subject_key_identifier is used for the GQ public key. * This should not be controversial. */ if (gqpub != NULL) { fprintf(stderr, "%s\n", LN_subject_key_identifier); ex = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_key_identifier, gqpub); if (!X509_add_ext(cert, ex, -1)) { fprintf(stderr, "Add extension field fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; return (0); } X509_EXTENSION_free(ex); } /* * The extended key usage extension is used for special purpose * here. The semantics probably do not conform to the designer's * intent and will likely change in future. * * "trustRoot" designates a root authority * "private" designates a private certificate */ if (exten != NULL) { fprintf(stderr, "%s: %s\n", LN_ext_key_usage, exten); ex = X509V3_EXT_conf_nid(NULL, NULL, NID_ext_key_usage, exten); if (!X509_add_ext(cert, ex, -1)) { fprintf(stderr, "Add extension field fails\n%s\n", ERR_error_string(ERR_get_error(), NULL)); rval = -1; return (0); } X509_EXTENSION_free(ex); } /* * Sign and verify. */ X509_sign(cert, pkey, md); if (!X509_verify(cert, pkey)) { fprintf(stderr, "Verify %s certificate fails\n%s\n", id, ERR_error_string(ERR_get_error(), NULL)); X509_free(cert); rval = -1; return (0); } /* * Write the certificate encoded in PEM. */ sprintf(pathbuf, "%scert", id); str = fheader(pathbuf, hostname); PEM_write_X509(str, cert); fclose(str); if (debug) X509_print_fp(stdout, cert); X509_free(cert); fslink("cert", hostname); return (1); } #if 0 /* asn2ntp is not used */ /* * asn2ntp - convert ASN1_TIME time structure to NTP time */ u_long asn2ntp ( ASN1_TIME *asn1time /* pointer to ASN1_TIME structure */ ) { char *v; /* pointer to ASN1_TIME string */ struct tm tm; /* time decode structure time */ /* * Extract time string YYMMDDHHMMSSZ from ASN.1 time structure. * Note that the YY, MM, DD fields start with one, the HH, MM, * SS fiels start with zero and the Z character should be 'Z' * for UTC. Also note that years less than 50 map to years * greater than 100. Dontcha love ASN.1? */ if (asn1time->length > 13) return (-1); v = (char *)asn1time->data; tm.tm_year = (v[0] - '0') * 10 + v[1] - '0'; if (tm.tm_year < 50) tm.tm_year += 100; tm.tm_mon = (v[2] - '0') * 10 + v[3] - '0' - 1; tm.tm_mday = (v[4] - '0') * 10 + v[5] - '0'; tm.tm_hour = (v[6] - '0') * 10 + v[7] - '0'; tm.tm_min = (v[8] - '0') * 10 + v[9] - '0'; tm.tm_sec = (v[10] - '0') * 10 + v[11] - '0'; tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_isdst = 0; return (mktime(&tm) + JAN_1970); } #endif /* * Callback routine */ void cb ( int n1, /* arg 1 */ int n2, /* arg 2 */ void *chr /* arg 3 */ ) { switch (n1) { case 0: d0++; fprintf(stderr, "%s %d %d %lu\r", (char *)chr, n1, n2, d0); break; case 1: d1++; fprintf(stderr, "%s\t\t%d %d %lu\r", (char *)chr, n1, n2, d1); break; case 2: d2++; fprintf(stderr, "%s\t\t\t\t%d %d %lu\r", (char *)chr, n1, n2, d2); break; case 3: d3++; fprintf(stderr, "%s\t\t\t\t\t\t%d %d %lu\r", (char *)chr, n1, n2, d3); break; } } /* * Generate key */ EVP_PKEY * /* public/private key pair */ genkey( char *type, /* key type (RSA or DSA) */ char *id /* file name id */ ) { if (type == NULL) return (NULL); if (strcmp(type, "RSA") == 0) return (gen_rsa(id)); else if (strcmp(type, "DSA") == 0) return (gen_dsa(id)); fprintf(stderr, "Invalid %s key type %s\n", id, type); rval = -1; return (NULL); } #endif /* OPENSSL */ /* * Generate file header */ FILE * fheader ( const char *id, /* file name id */ const char *name /* owner name */ ) { FILE *str; /* file handle */ sprintf(filename, "ntpkey_%s_%s.%lu", id, name, epoch + JAN_1970); if ((str = fopen(filename, "w")) == NULL) { perror("Write"); exit (-1); } fprintf(str, "# %s\n# %s", filename, ctime(&epoch)); return (str); } /* * Generate symbolic links */ void fslink( const char *id, /* file name id */ const char *name /* owner name */ ) { char linkname[MAXFILENAME]; /* link name */ int temp; sprintf(linkname, "ntpkey_%s_%s", id, name); remove(linkname); temp = symlink(filename, linkname); if (temp < 0) perror(id); fprintf(stderr, "Generating new %s file and link\n", id); fprintf(stderr, "%s->%s\n", linkname, filename); } Index: releng/10.1/sys/conf/newvers.sh =================================================================== --- releng/10.1/sys/conf/newvers.sh (revision 276158) +++ releng/10.1/sys/conf/newvers.sh (revision 276159) @@ -1,224 +1,224 @@ #!/bin/sh - # # Copyright (c) 1984, 1986, 1990, 1993 # The Regents of the University of California. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 4. Neither the name of the University nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # @(#)newvers.sh 8.1 (Berkeley) 4/20/94 # $FreeBSD$ TYPE="FreeBSD" REVISION="10.1" -BRANCH="RELEASE-p2" +BRANCH="RELEASE-p3" if [ "X${BRANCH_OVERRIDE}" != "X" ]; then BRANCH=${BRANCH_OVERRIDE} fi RELEASE="${REVISION}-${BRANCH}" VERSION="${TYPE} ${RELEASE}" if [ "X${SYSDIR}" = "X" ]; then SYSDIR=$(dirname $0)/.. fi if [ "X${PARAMFILE}" != "X" ]; then RELDATE=$(awk '/__FreeBSD_version.*propagated to newvers/ {print $3}' \ ${PARAMFILE}) else RELDATE=$(awk '/__FreeBSD_version.*propagated to newvers/ {print $3}' \ ${SYSDIR}/sys/param.h) fi b=share/examples/etc/bsd-style-copyright year=`date '+%Y'` # look for copyright template for bsd_copyright in ../$b ../../$b ../../../$b /usr/src/$b /usr/$b do if [ -r "$bsd_copyright" ]; then COPYRIGHT=`sed \ -e "s/\[year\]/1992-$year/" \ -e 's/\[your name here\]\.* /The FreeBSD Project./' \ -e 's/\[your name\]\.*/The FreeBSD Project./' \ -e '/\[id for your version control system, if any\]/d' \ $bsd_copyright` break fi done # no copyright found, use a dummy if [ X"$COPYRIGHT" = X ]; then COPYRIGHT="/*- * Copyright (c) 1992-$year The FreeBSD Project. * All rights reserved. * */" fi # add newline COPYRIGHT="$COPYRIGHT " LC_ALL=C; export LC_ALL if [ ! -r version ] then echo 0 > version fi touch version v=`cat version` u=${USER:-root} d=`pwd` h=${HOSTNAME:-`hostname`} t=`date` i=`${MAKE:-make} -V KERN_IDENT` compiler_v=$($(${MAKE:-make} -V CC) -v 2>&1 | grep 'version') for dir in /usr/bin /usr/local/bin; do if [ ! -z "${svnversion}" ] ; then break fi if [ -x "${dir}/svnversion" ] && [ -z ${svnversion} ] ; then # Run svnversion from ${dir} on this script; if return code # is not zero, the checkout might not be compatible with the # svnversion being used. ${dir}/svnversion $(realpath ${0}) >/dev/null 2>&1 if [ $? -eq 0 ]; then svnversion=${dir}/svnversion break fi fi done if [ -z "${svnversion}" ] && [ -x /usr/bin/svnliteversion ] ; then /usr/bin/svnliteversion $(realpath ${0}) >/dev/null 2>&1 if [ $? -eq 0 ]; then svnversion=/usr/bin/svnliteversion else svnversion= fi fi for dir in /usr/bin /usr/local/bin; do if [ -x "${dir}/p4" ] && [ -z ${p4_cmd} ] ; then p4_cmd=${dir}/p4 fi done if [ -d "${SYSDIR}/../.git" ] ; then for dir in /usr/bin /usr/local/bin; do if [ -x "${dir}/git" ] ; then git_cmd="${dir}/git --git-dir=${SYSDIR}/../.git" break fi done fi if [ -d "${SYSDIR}/../.hg" ] ; then for dir in /usr/bin /usr/local/bin; do if [ -x "${dir}/hg" ] ; then hg_cmd="${dir}/hg -R ${SYSDIR}/.." break fi done fi if [ -n "$svnversion" ] ; then svn=`cd ${SYSDIR} && $svnversion 2>/dev/null` case "$svn" in [0-9]*) svn=" r${svn}" ;; *) unset svn ;; esac fi if [ -n "$git_cmd" ] ; then git=`$git_cmd rev-parse --verify --short HEAD 2>/dev/null` svn=`$git_cmd svn find-rev $git 2>/dev/null` if [ -n "$svn" ] ; then svn=" r${svn}" git="=${git}" else svn=`$git_cmd log | fgrep 'git-svn-id:' | head -1 | \ sed -n 's/^.*@\([0-9][0-9]*\).*$/\1/p'` if [ -z "$svn" ] ; then svn=`$git_cmd log --format='format:%N' | \ grep '^svn ' | head -1 | \ sed -n 's/^.*revision=\([0-9][0-9]*\).*$/\1/p'` fi if [ -n "$svn" ] ; then svn=" r${svn}" git="+${git}" else git=" ${git}" fi fi git_b=`$git_cmd rev-parse --abbrev-ref HEAD` if [ -n "$git_b" ] ; then git="${git}(${git_b})" fi if $git_cmd --work-tree=${SYSDIR}/.. diff-index \ --name-only HEAD | read dummy; then git="${git}-dirty" fi fi if [ -n "$p4_cmd" ] ; then p4version=`cd ${SYSDIR} && $p4_cmd changes -m1 "./...#have" 2>&1 | \ awk '{ print $2 }'` case "$p4version" in [0-9]*) p4version=" ${p4version}" p4opened=`cd ${SYSDIR} && $p4_cmd opened ./... 2>&1` case "$p4opened" in File*) ;; //*) p4version="${p4version}+edit" ;; esac ;; *) unset p4version ;; esac fi if [ -n "$hg_cmd" ] ; then hg=`$hg_cmd id 2>/dev/null` svn=`$hg_cmd svn info 2>/dev/null | \ awk -F': ' '/Revision/ { print $2 }'` if [ -n "$svn" ] ; then svn=" r${svn}" fi if [ -n "$hg" ] ; then hg=" ${hg}" fi fi cat << EOF > vers.c $COPYRIGHT #define SCCSSTR "@(#)${VERSION} #${v}${svn}${git}${hg}${p4version}: ${t}" #define VERSTR "${VERSION} #${v}${svn}${git}${hg}${p4version}: ${t}\\n ${u}@${h}:${d}\\n" #define RELSTR "${RELEASE}" char sccs[sizeof(SCCSSTR) > 128 ? sizeof(SCCSSTR) : 128] = SCCSSTR; char version[sizeof(VERSTR) > 256 ? sizeof(VERSTR) : 256] = VERSTR; char compiler_version[] = "${compiler_v}"; char ostype[] = "${TYPE}"; char osrelease[sizeof(RELSTR) > 32 ? sizeof(RELSTR) : 32] = RELSTR; int osreldate = ${RELDATE}; char kern_ident[] = "${i}"; EOF echo $((v + 1)) > version Index: releng/10.1/usr.sbin/freebsd-update/freebsd-update.sh =================================================================== --- releng/10.1/usr.sbin/freebsd-update/freebsd-update.sh (revision 276158) +++ releng/10.1/usr.sbin/freebsd-update/freebsd-update.sh (revision 276159) @@ -1,3263 +1,3264 @@ #!/bin/sh #- # Copyright 2004-2007 Colin Percival # All rights reserved # # Redistribution and use in source and binary forms, with or without # modification, are permitted providing that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # $FreeBSD$ #### Usage function -- called from command-line handling code. # Usage instructions. Options not listed: # --debug -- don't filter output from utilities # --no-stats -- don't show progress statistics while fetching files usage () { cat < ${LINE}" exit 1 fi done < ${CONFFILE} # Merge the settings read from the configuration file with those # provided at the command line. mergeconfig } # Provide some default parameters default_params () { # Save any parameters already configured, and clear the slate saveconfig nullconfig # Default configurations config_WorkDir /var/db/freebsd-update config_MailTo root config_AllowAdd yes config_AllowDelete yes config_KeepModifiedMetadata yes config_BaseDir / config_VerboseLevel stats config_StrictComponents no config_BackupKernel yes config_BackupKernelDir /boot/kernel.old config_BackupKernelSymbolFiles no # Merge these defaults into the earlier-configured settings mergeconfig } # Set utility output filtering options, based on ${VERBOSELEVEL} fetch_setup_verboselevel () { case ${VERBOSELEVEL} in debug) QUIETREDIR="/dev/stderr" QUIETFLAG=" " STATSREDIR="/dev/stderr" DDSTATS=".." XARGST="-t" NDEBUG=" " ;; nostats) QUIETREDIR="" QUIETFLAG="" STATSREDIR="/dev/null" DDSTATS=".." XARGST="" NDEBUG="" ;; stats) QUIETREDIR="/dev/null" QUIETFLAG="-q" STATSREDIR="/dev/stdout" DDSTATS="" XARGST="" NDEBUG="-n" ;; esac } # Perform sanity checks and set some final parameters # in preparation for fetching files. Figure out which # set of updates should be downloaded: If the user is # running *-p[0-9]+, strip off the last part; if the # user is running -SECURITY, call it -RELEASE. Chdir # into the working directory. fetchupgrade_check_params () { export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)" _SERVERNAME_z=\ "SERVERNAME must be given via command line or configuration file." _KEYPRINT_z="Key must be given via -k option or configuration file." _KEYPRINT_bad="Invalid key fingerprint: " _WORKDIR_bad="Directory does not exist or is not writable: " if [ -z "${SERVERNAME}" ]; then echo -n "`basename $0`: " echo "${_SERVERNAME_z}" exit 1 fi if [ -z "${KEYPRINT}" ]; then echo -n "`basename $0`: " echo "${_KEYPRINT_z}" exit 1 fi if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then echo -n "`basename $0`: " echo -n "${_KEYPRINT_bad}" echo ${KEYPRINT} exit 1 fi if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then echo -n "`basename $0`: " echo -n "${_WORKDIR_bad}" echo ${WORKDIR} exit 1 fi chmod 700 ${WORKDIR} cd ${WORKDIR} || exit 1 # Generate release number. The s/SECURITY/RELEASE/ bit exists # to provide an upgrade path for FreeBSD Update 1.x users, since # the kernels provided by FreeBSD Update 1.x are always labelled # as X.Y-SECURITY. RELNUM=`uname -r | sed -E 's,-p[0-9]+,,' | sed -E 's,-SECURITY,-RELEASE,'` ARCH=`uname -m` FETCHDIR=${RELNUM}/${ARCH} PATCHDIR=${RELNUM}/${ARCH}/bp # Figure out what directory contains the running kernel BOOTFILE=`sysctl -n kern.bootfile` KERNELDIR=${BOOTFILE%/kernel} if ! [ -d ${KERNELDIR} ]; then echo "Cannot identify running kernel" exit 1 fi # Figure out what kernel configuration is running. We start with # the output of `uname -i`, and then make the following adjustments: # 1. Replace "SMP-GENERIC" with "SMP". Why the SMP kernel config # file says "ident SMP-GENERIC", I don't know... # 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64" # _and_ `sysctl kern.version` contains a line which ends "/SMP", then # we're running an SMP kernel. This mis-identification is a bug # which was fixed in 6.2-STABLE. KERNCONF=`uname -i` if [ ${KERNCONF} = "SMP-GENERIC" ]; then KERNCONF=SMP fi if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then if sysctl kern.version | grep -qE '/SMP$'; then KERNCONF=SMP fi fi # Define some paths BSPATCH=/usr/bin/bspatch SHA256=/sbin/sha256 PHTTPGET=/usr/libexec/phttpget # Set up variables relating to VERBOSELEVEL fetch_setup_verboselevel # Construct a unique name from ${BASEDIR} BDHASH=`echo ${BASEDIR} | sha256 -q` } # Perform sanity checks etc. before fetching updates. fetch_check_params () { fetchupgrade_check_params if ! [ -z "${TARGETRELEASE}" ]; then echo -n "`basename $0`: " echo -n "-r option is meaningless with 'fetch' command. " echo "(Did you mean 'upgrade' instead?)" exit 1 fi } # Perform sanity checks etc. before fetching upgrades. upgrade_check_params () { fetchupgrade_check_params # Unless set otherwise, we're upgrading to the same kernel config. NKERNCONF=${KERNCONF} # We need TARGETRELEASE set _TARGETRELEASE_z="Release target must be specified via -r option." if [ -z "${TARGETRELEASE}" ]; then echo -n "`basename $0`: " echo "${_TARGETRELEASE_z}" exit 1 fi # The target release should be != the current release. if [ "${TARGETRELEASE}" = "${RELNUM}" ]; then echo -n "`basename $0`: " echo "Cannot upgrade from ${RELNUM} to itself" exit 1 fi # Turning off AllowAdd or AllowDelete is a bad idea for upgrades. if [ "${ALLOWADD}" = "no" ]; then echo -n "`basename $0`: " echo -n "WARNING: \"AllowAdd no\" is a bad idea " echo "when upgrading between releases." echo fi if [ "${ALLOWDELETE}" = "no" ]; then echo -n "`basename $0`: " echo -n "WARNING: \"AllowDelete no\" is a bad idea " echo "when upgrading between releases." echo fi # Set EDITOR to /usr/bin/vi if it isn't already set : ${EDITOR:='/usr/bin/vi'} } # Perform sanity checks and set some final parameters in # preparation for installing updates. install_check_params () { # Check that we are root. All sorts of things won't work otherwise. if [ `id -u` != 0 ]; then echo "You must be root to run this." exit 1 fi # Check that securelevel <= 0. Otherwise we can't update schg files. if [ `sysctl -n kern.securelevel` -gt 0 ]; then echo "Updates cannot be installed when the system securelevel" echo "is greater than zero." exit 1 fi # Check that we have a working directory _WORKDIR_bad="Directory does not exist or is not writable: " if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then echo -n "`basename $0`: " echo -n "${_WORKDIR_bad}" echo ${WORKDIR} exit 1 fi cd ${WORKDIR} || exit 1 # Construct a unique name from ${BASEDIR} BDHASH=`echo ${BASEDIR} | sha256 -q` # Check that we have updates ready to install if ! [ -L ${BDHASH}-install ]; then echo "No updates are available to install." echo "Run '$0 fetch' first." exit 1 fi if ! [ -f ${BDHASH}-install/INDEX-OLD ] || ! [ -f ${BDHASH}-install/INDEX-NEW ]; then echo "Update manifest is corrupt -- this should never happen." echo "Re-run '$0 fetch'." exit 1 fi # Figure out what directory contains the running kernel BOOTFILE=`sysctl -n kern.bootfile` KERNELDIR=${BOOTFILE%/kernel} if ! [ -d ${KERNELDIR} ]; then echo "Cannot identify running kernel" exit 1 fi } # Perform sanity checks and set some final parameters in # preparation for UNinstalling updates. rollback_check_params () { # Check that we are root. All sorts of things won't work otherwise. if [ `id -u` != 0 ]; then echo "You must be root to run this." exit 1 fi # Check that we have a working directory _WORKDIR_bad="Directory does not exist or is not writable: " if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then echo -n "`basename $0`: " echo -n "${_WORKDIR_bad}" echo ${WORKDIR} exit 1 fi cd ${WORKDIR} || exit 1 # Construct a unique name from ${BASEDIR} BDHASH=`echo ${BASEDIR} | sha256 -q` # Check that we have updates ready to rollback if ! [ -L ${BDHASH}-rollback ]; then echo "No rollback directory found." exit 1 fi if ! [ -f ${BDHASH}-rollback/INDEX-OLD ] || ! [ -f ${BDHASH}-rollback/INDEX-NEW ]; then echo "Update manifest is corrupt -- this should never happen." exit 1 fi } # Perform sanity checks and set some final parameters # in preparation for comparing the system against the # published index. Figure out which index we should # compare against: If the user is running *-p[0-9]+, # strip off the last part; if the user is running # -SECURITY, call it -RELEASE. Chdir into the working # directory. IDS_check_params () { export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)" _SERVERNAME_z=\ "SERVERNAME must be given via command line or configuration file." _KEYPRINT_z="Key must be given via -k option or configuration file." _KEYPRINT_bad="Invalid key fingerprint: " _WORKDIR_bad="Directory does not exist or is not writable: " if [ -z "${SERVERNAME}" ]; then echo -n "`basename $0`: " echo "${_SERVERNAME_z}" exit 1 fi if [ -z "${KEYPRINT}" ]; then echo -n "`basename $0`: " echo "${_KEYPRINT_z}" exit 1 fi if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then echo -n "`basename $0`: " echo -n "${_KEYPRINT_bad}" echo ${KEYPRINT} exit 1 fi if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then echo -n "`basename $0`: " echo -n "${_WORKDIR_bad}" echo ${WORKDIR} exit 1 fi cd ${WORKDIR} || exit 1 # Generate release number. The s/SECURITY/RELEASE/ bit exists # to provide an upgrade path for FreeBSD Update 1.x users, since # the kernels provided by FreeBSD Update 1.x are always labelled # as X.Y-SECURITY. RELNUM=`uname -r | sed -E 's,-p[0-9]+,,' | sed -E 's,-SECURITY,-RELEASE,'` ARCH=`uname -m` FETCHDIR=${RELNUM}/${ARCH} PATCHDIR=${RELNUM}/${ARCH}/bp # Figure out what directory contains the running kernel BOOTFILE=`sysctl -n kern.bootfile` KERNELDIR=${BOOTFILE%/kernel} if ! [ -d ${KERNELDIR} ]; then echo "Cannot identify running kernel" exit 1 fi # Figure out what kernel configuration is running. We start with # the output of `uname -i`, and then make the following adjustments: # 1. Replace "SMP-GENERIC" with "SMP". Why the SMP kernel config # file says "ident SMP-GENERIC", I don't know... # 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64" # _and_ `sysctl kern.version` contains a line which ends "/SMP", then # we're running an SMP kernel. This mis-identification is a bug # which was fixed in 6.2-STABLE. KERNCONF=`uname -i` if [ ${KERNCONF} = "SMP-GENERIC" ]; then KERNCONF=SMP fi if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then if sysctl kern.version | grep -qE '/SMP$'; then KERNCONF=SMP fi fi # Define some paths SHA256=/sbin/sha256 PHTTPGET=/usr/libexec/phttpget # Set up variables relating to VERBOSELEVEL fetch_setup_verboselevel } #### Core functionality -- the actual work gets done here # Use an SRV query to pick a server. If the SRV query doesn't provide # a useful answer, use the server name specified by the user. # Put another way... look up _http._tcp.${SERVERNAME} and pick a server # from that; or if no servers are returned, use ${SERVERNAME}. # This allows a user to specify "portsnap.freebsd.org" (in which case # portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org" # (in which case portsnap will use that particular server, since there # won't be an SRV entry for that name). # # We ignore the Port field, since we are always going to use port 80. # Fetch the mirror list, but do not pick a mirror yet. Returns 1 if # no mirrors are available for any reason. fetch_pick_server_init () { : > serverlist_tried # Check that host(1) exists (i.e., that the system wasn't built with the # WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist. if ! which -s host; then : > serverlist_full return 1 fi echo -n "Looking up ${SERVERNAME} mirrors... " # Issue the SRV query and pull out the Priority, Weight, and Target fields. # BIND 9 prints "$name has SRV record ..." while BIND 8 prints # "$name server selection ..."; we allow either format. MLIST="_http._tcp.${SERVERNAME}" host -t srv "${MLIST}" | sed -nE "s/${MLIST} (has SRV record|server selection) //p" | cut -f 1,2,4 -d ' ' | sed -e 's/\.$//' | sort > serverlist_full # If no records, give up -- we'll just use the server name we were given. if [ `wc -l < serverlist_full` -eq 0 ]; then echo "none found." return 1 fi # Report how many mirrors we found. echo `wc -l < serverlist_full` "mirrors found." # Generate a random seed for use in picking mirrors. If HTTP_PROXY # is set, this will be used to generate the seed; otherwise, the seed # will be random. if [ -n "${HTTP_PROXY}${http_proxy}" ]; then RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" | tr -d 'a-f' | cut -c 1-9` else RANDVALUE=`jot -r 1 0 999999999` fi } # Pick a mirror. Returns 1 if we have run out of mirrors to try. fetch_pick_server () { # Generate a list of not-yet-tried mirrors sort serverlist_tried | comm -23 serverlist_full - > serverlist # Have we run out of mirrors? if [ `wc -l < serverlist` -eq 0 ]; then echo "No mirrors remaining, giving up." return 1 fi # Find the highest priority level (lowest numeric value). SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1` # Add up the weights of the response lines at that priority level. SRV_WSUM=0; while read X; do case "$X" in ${SRV_PRIORITY}\ *) SRV_W=`echo $X | cut -f 2 -d ' '` SRV_WSUM=$(($SRV_WSUM + $SRV_W)) ;; esac done < serverlist # If all the weights are 0, pretend that they are all 1 instead. if [ ${SRV_WSUM} -eq 0 ]; then SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l` SRV_W_ADD=1 else SRV_W_ADD=0 fi # Pick a value between 0 and the sum of the weights - 1 SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}` # Read through the list of mirrors and set SERVERNAME. Write the line # corresponding to the mirror we selected into serverlist_tried so that # we won't try it again. while read X; do case "$X" in ${SRV_PRIORITY}\ *) SRV_W=`echo $X | cut -f 2 -d ' '` SRV_W=$(($SRV_W + $SRV_W_ADD)) if [ $SRV_RND -lt $SRV_W ]; then SERVERNAME=`echo $X | cut -f 3 -d ' '` echo "$X" >> serverlist_tried break else SRV_RND=$(($SRV_RND - $SRV_W)) fi ;; esac done < serverlist } # Take a list of ${oldhash}|${newhash} and output a list of needed patches, # i.e., those for which we have ${oldhash} and don't have ${newhash}. fetch_make_patchlist () { grep -vE "^([0-9a-f]{64})\|\1$" | tr '|' ' ' | while read X Y; do if [ -f "files/${Y}.gz" ] || [ ! -f "files/${X}.gz" ]; then continue fi echo "${X}|${Y}" done | uniq } # Print user-friendly progress statistics fetch_progress () { LNC=0 while read x; do LNC=$(($LNC + 1)) if [ $(($LNC % 10)) = 0 ]; then echo -n $LNC elif [ $(($LNC % 2)) = 0 ]; then echo -n . fi done echo -n " " } # Function for asking the user if everything is ok continuep () { while read -p "Does this look reasonable (y/n)? " CONTINUE; do case "${CONTINUE}" in y*) return 0 ;; n*) return 1 ;; esac done } # Initialize the working directory workdir_init () { mkdir -p files touch tINDEX.present } # Check that we have a public key with an appropriate hash, or # fetch the key if it doesn't exist. Returns 1 if the key has # not yet been fetched. fetch_key () { if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then return 0 fi echo -n "Fetching public key from ${SERVERNAME}... " rm -f pub.ssl fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/pub.ssl \ 2>${QUIETREDIR} || true if ! [ -r pub.ssl ]; then echo "failed." return 1 fi if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then echo "key has incorrect hash." rm -f pub.ssl return 1 fi echo "done." } # Fetch metadata signature, aka "tag". fetch_tag () { echo -n "Fetching metadata signature " echo ${NDEBUG} "for ${RELNUM} from ${SERVERNAME}... " rm -f latest.ssl fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/latest.ssl \ 2>${QUIETREDIR} || true if ! [ -r latest.ssl ]; then echo "failed." return 1 fi openssl rsautl -pubin -inkey pub.ssl -verify \ < latest.ssl > tag.new 2>${QUIETREDIR} || true rm latest.ssl if ! [ `wc -l < tag.new` = 1 ] || ! grep -qE \ "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \ tag.new; then echo "invalid signature." return 1 fi echo "done." RELPATCHNUM=`cut -f 4 -d '|' < tag.new` TINDEXHASH=`cut -f 5 -d '|' < tag.new` EOLTIME=`cut -f 6 -d '|' < tag.new` } # Sanity-check the patch number in a tag, to make sure that we're not # going to "update" backwards and to prevent replay attacks. fetch_tagsanity () { # Check that we're not going to move from -pX to -pY with Y < X. RELPX=`uname -r | sed -E 's,.*-,,'` if echo ${RELPX} | grep -qE '^p[0-9]+$'; then RELPX=`echo ${RELPX} | cut -c 2-` else RELPX=0 fi if [ "${RELPATCHNUM}" -lt "${RELPX}" ]; then echo echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})" echo " appear older than what" echo "we are currently running (`uname -r`)!" echo "Cowardly refusing to proceed any further." return 1 fi # If "tag" exists and corresponds to ${RELNUM}, make sure that # it contains a patch number <= RELPATCHNUM, in order to protect # against rollback (replay) attacks. if [ -f tag ] && grep -qE \ "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \ tag; then LASTRELPATCHNUM=`cut -f 4 -d '|' < tag` if [ "${RELPATCHNUM}" -lt "${LASTRELPATCHNUM}" ]; then echo echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})" echo " are older than the" echo -n "most recently seen updates" echo " (${RELNUM}-p${LASTRELPATCHNUM})." echo "Cowardly refusing to proceed any further." return 1 fi fi } # Fetch metadata index file fetch_metadata_index () { echo ${NDEBUG} "Fetching metadata index... " rm -f ${TINDEXHASH} fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/t/${TINDEXHASH} 2>${QUIETREDIR} if ! [ -f ${TINDEXHASH} ]; then echo "failed." return 1 fi if [ `${SHA256} -q ${TINDEXHASH}` != ${TINDEXHASH} ]; then echo "update metadata index corrupt." return 1 fi echo "done." } # Print an error message about signed metadata being bogus. fetch_metadata_bogus () { echo echo "The update metadata$1 is correctly signed, but" echo "failed an integrity check." echo "Cowardly refusing to proceed any further." return 1 } # Construct tINDEX.new by merging the lines named in $1 from ${TINDEXHASH} # with the lines not named in $@ from tINDEX.present (if that file exists). fetch_metadata_index_merge () { for METAFILE in $@; do if [ `grep -E "^${METAFILE}\|" ${TINDEXHASH} | wc -l` \ -ne 1 ]; then fetch_metadata_bogus " index" return 1 fi grep -E "${METAFILE}\|" ${TINDEXHASH} done | sort > tINDEX.wanted if [ -f tINDEX.present ]; then join -t '|' -v 2 tINDEX.wanted tINDEX.present | sort -m - tINDEX.wanted > tINDEX.new rm tINDEX.wanted else mv tINDEX.wanted tINDEX.new fi } # Sanity check all the lines of tINDEX.new. Even if more metadata lines # are added by future versions of the server, this won't cause problems, # since the only lines which appear in tINDEX.new are the ones which we # specifically grepped out of ${TINDEXHASH}. fetch_metadata_index_sanity () { if grep -qvE '^[0-9A-Z.-]+\|[0-9a-f]{64}$' tINDEX.new; then fetch_metadata_bogus " index" return 1 fi } # Sanity check the metadata file $1. fetch_metadata_sanity () { # Some aliases to save space later: ${P} is a character which can # appear in a path; ${M} is the four numeric metadata fields; and # ${H} is a sha256 hash. P="[-+./:=%@_[~[:alnum:]]" M="[0-9]+\|[0-9]+\|[0-9]+\|[0-9]+" H="[0-9a-f]{64}" # Check that the first four fields make sense. if gunzip -c < files/$1.gz | grep -qvE "^[a-z]+\|[0-9a-z]+\|${P}+\|[fdL-]\|"; then fetch_metadata_bogus "" return 1 fi # Remove the first three fields. gunzip -c < files/$1.gz | cut -f 4- -d '|' > sanitycheck.tmp # Sanity check entries with type 'f' if grep -E '^f' sanitycheck.tmp | grep -qvE "^f\|${M}\|${H}\|${P}*\$"; then fetch_metadata_bogus "" return 1 fi # Sanity check entries with type 'd' if grep -E '^d' sanitycheck.tmp | grep -qvE "^d\|${M}\|\|\$"; then fetch_metadata_bogus "" return 1 fi # Sanity check entries with type 'L' if grep -E '^L' sanitycheck.tmp | grep -qvE "^L\|${M}\|${P}*\|\$"; then fetch_metadata_bogus "" return 1 fi # Sanity check entries with type '-' if grep -E '^-' sanitycheck.tmp | grep -qvE "^-\|\|\|\|\|\|"; then fetch_metadata_bogus "" return 1 fi # Clean up rm sanitycheck.tmp } # Fetch the metadata index and metadata files listed in $@, # taking advantage of metadata patches where possible. fetch_metadata () { fetch_metadata_index || return 1 fetch_metadata_index_merge $@ || return 1 fetch_metadata_index_sanity || return 1 # Generate a list of wanted metadata patches join -t '|' -o 1.2,2.2 tINDEX.present tINDEX.new | fetch_make_patchlist > patchlist if [ -s patchlist ]; then # Attempt to fetch metadata patches echo -n "Fetching `wc -l < patchlist | tr -d ' '` " echo ${NDEBUG} "metadata patches.${DDSTATS}" tr '|' '-' < patchlist | lam -s "${FETCHDIR}/tp/" - -s ".gz" | xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 2>${STATSREDIR} | fetch_progress echo "done." # Attempt to apply metadata patches echo -n "Applying metadata patches... " tr '|' ' ' < patchlist | while read X Y; do if [ ! -f "${X}-${Y}.gz" ]; then continue; fi gunzip -c < ${X}-${Y}.gz > diff gunzip -c < files/${X}.gz > diff-OLD # Figure out which lines are being added and removed grep -E '^-' diff | cut -c 2- | while read PREFIX; do look "${PREFIX}" diff-OLD done | sort > diff-rm grep -E '^\+' diff | cut -c 2- > diff-add # Generate the new file comm -23 diff-OLD diff-rm | sort - diff-add > diff-NEW if [ `${SHA256} -q diff-NEW` = ${Y} ]; then mv diff-NEW files/${Y} gzip -n files/${Y} else mv diff-NEW ${Y}.bad fi rm -f ${X}-${Y}.gz diff rm -f diff-OLD diff-NEW diff-add diff-rm done 2>${QUIETREDIR} echo "done." fi # Update metadata without patches cut -f 2 -d '|' < tINDEX.new | while read Y; do if [ ! -f "files/${Y}.gz" ]; then echo ${Y}; fi done | sort -u > filelist if [ -s filelist ]; then echo -n "Fetching `wc -l < filelist | tr -d ' '` " echo ${NDEBUG} "metadata files... " lam -s "${FETCHDIR}/m/" - -s ".gz" < filelist | xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 2>${QUIETREDIR} while read Y; do if ! [ -f ${Y}.gz ]; then echo "failed." return 1 fi if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then mv ${Y}.gz files/${Y}.gz else echo "metadata is corrupt." return 1 fi done < filelist echo "done." fi # Sanity-check the metadata files. cut -f 2 -d '|' tINDEX.new > filelist while read X; do fetch_metadata_sanity ${X} || return 1 done < filelist # Remove files which are no longer needed cut -f 2 -d '|' tINDEX.present | sort > oldfiles cut -f 2 -d '|' tINDEX.new | sort | comm -13 - oldfiles | lam -s "files/" - -s ".gz" | xargs rm -f rm patchlist filelist oldfiles rm ${TINDEXHASH} # We're done! mv tINDEX.new tINDEX.present mv tag.new tag return 0 } # Extract a subset of a downloaded metadata file containing only the parts # which are listed in COMPONENTS. fetch_filter_metadata_components () { METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'` gunzip -c < files/${METAHASH}.gz > $1.all # Fish out the lines belonging to components we care about. for C in ${COMPONENTS}; do look "`echo ${C} | tr '/' '|'`|" $1.all done > $1 # Remove temporary file. rm $1.all } # Generate a filtered version of the metadata file $1 from the downloaded # file, by fishing out the lines corresponding to components we're trying # to keep updated, and then removing lines corresponding to paths we want # to ignore. fetch_filter_metadata () { # Fish out the lines belonging to components we care about. fetch_filter_metadata_components $1 # Canonicalize directory names by removing any trailing / in # order to avoid listing directories multiple times if they # belong to multiple components. Turning "/" into "" doesn't # matter, since we add a leading "/" when we use paths later. cut -f 3- -d '|' $1 | sed -e 's,/|d|,|d|,' | + sed -e 's,/|-|,|-|,' | sort -u > $1.tmp # Figure out which lines to ignore and remove them. for X in ${IGNOREPATHS}; do grep -E "^${X}" $1.tmp done | sort -u | comm -13 - $1.tmp > $1 # Remove temporary files. rm $1.tmp } # Filter the metadata file $1 by adding lines with "/boot/$2" # replaced by ${KERNELDIR} (which is `sysctl -n kern.bootfile` minus the # trailing "/kernel"); and if "/boot/$2" does not exist, remove # the original lines which start with that. # Put another way: Deal with the fact that the FOO kernel is sometimes # installed in /boot/FOO/ and is sometimes installed elsewhere. fetch_filter_kernel_names () { grep ^/boot/$2 $1 | sed -e "s,/boot/$2,${KERNELDIR},g" | sort - $1 > $1.tmp mv $1.tmp $1 if ! [ -d /boot/$2 ]; then grep -v ^/boot/$2 $1 > $1.tmp mv $1.tmp $1 fi } # For all paths appearing in $1 or $3, inspect the system # and generate $2 describing what is currently installed. fetch_inspect_system () { # No errors yet... rm -f .err # Tell the user why his disk is suddenly making lots of noise echo -n "Inspecting system... " # Generate list of files to inspect cat $1 $3 | cut -f 1 -d '|' | sort -u > filelist # Examine each file and output lines of the form # /path/to/file|type|device-inum|user|group|perm|flags|value # sorted by device and inode number. while read F; do # If the symlink/file/directory does not exist, record this. if ! [ -e ${BASEDIR}/${F} ]; then echo "${F}|-||||||" continue fi if ! [ -r ${BASEDIR}/${F} ]; then echo "Cannot read file: ${BASEDIR}/${F}" \ >/dev/stderr touch .err return 1 fi # Otherwise, output an index line. if [ -L ${BASEDIR}/${F} ]; then echo -n "${F}|L|" stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F}; readlink ${BASEDIR}/${F}; elif [ -f ${BASEDIR}/${F} ]; then echo -n "${F}|f|" stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F}; sha256 -q ${BASEDIR}/${F}; elif [ -d ${BASEDIR}/${F} ]; then echo -n "${F}|d|" stat -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F}; else echo "Unknown file type: ${BASEDIR}/${F}" \ >/dev/stderr touch .err return 1 fi done < filelist | sort -k 3,3 -t '|' > $2.tmp rm filelist # Check if an error occurred during system inspection if [ -f .err ]; then return 1 fi # Convert to the form # /path/to/file|type|user|group|perm|flags|value|hlink # by resolving identical device and inode numbers into hard links. cut -f 1,3 -d '|' $2.tmp | sort -k 1,1 -t '|' | sort -s -u -k 2,2 -t '|' | join -1 2 -2 3 -t '|' - $2.tmp | awk -F \| -v OFS=\| \ '{ if (($2 == $3) || ($4 == "-")) print $3,$4,$5,$6,$7,$8,$9,"" else print $3,$4,$5,$6,$7,$8,$9,$2 }' | sort > $2 rm $2.tmp # We're finished looking around echo "done." } # For any paths matching ${MERGECHANGES}, compare $1 and $2 and find any # files which differ; generate $3 containing these paths and the old hashes. fetch_filter_mergechanges () { # Pull out the paths and hashes of the files matching ${MERGECHANGES}. for F in $1 $2; do for X in ${MERGECHANGES}; do grep -E "^${X}" ${F} done | cut -f 1,2,7 -d '|' | sort > ${F}-values done # Any line in $2-values which doesn't appear in $1-values and is a # file means that we should list the path in $3. comm -13 $1-values $2-values | fgrep '|f|' | cut -f 1 -d '|' > $2-paths # For each path, pull out one (and only one!) entry from $1-values. # Note that we cannot distinguish which "old" version the user made # changes to; but hopefully any changes which occur due to security # updates will exist in both the "new" version and the version which # the user has installed, so the merging will still work. while read X; do look "${X}|" $1-values | head -1 done < $2-paths > $3 # Clean up rm $1-values $2-values $2-paths } # For any paths matching ${UPDATEIFUNMODIFIED}, remove lines from $[123] # which correspond to lines in $2 with hashes not matching $1 or $3, unless # the paths are listed in $4. For entries in $2 marked "not present" # (aka. type -), remove lines from $[123] unless there is a corresponding # entry in $1. fetch_filter_unmodified_notpresent () { # Figure out which lines of $1 and $3 correspond to bits which # should only be updated if they haven't changed, and fish out # the (path, type, value) tuples. # NOTE: We don't consider a file to be "modified" if it matches # the hash from $3. for X in ${UPDATEIFUNMODIFIED}; do grep -E "^${X}" $1 grep -E "^${X}" $3 done | cut -f 1,2,7 -d '|' | sort > $1-values # Do the same for $2. for X in ${UPDATEIFUNMODIFIED}; do grep -E "^${X}" $2 done | cut -f 1,2,7 -d '|' | sort > $2-values # Any entry in $2-values which is not in $1-values corresponds to # a path which we need to remove from $1, $2, and $3, unless it # that path appears in $4. comm -13 $1-values $2-values | sort -t '|' -k 1,1 > mlines.tmp cut -f 1 -d '|' $4 | sort | join -v 2 -t '|' - mlines.tmp | sort > mlines rm $1-values $2-values mlines.tmp # Any lines in $2 which are not in $1 AND are "not present" lines # also belong in mlines. comm -13 $1 $2 | cut -f 1,2,7 -d '|' | fgrep '|-|' >> mlines # Remove lines from $1, $2, and $3 for X in $1 $2 $3; do sort -t '|' -k 1,1 ${X} > ${X}.tmp cut -f 1 -d '|' < mlines | sort | join -v 2 -t '|' - ${X}.tmp | sort > ${X} rm ${X}.tmp done # Store a list of the modified files, for future reference fgrep -v '|-|' mlines | cut -f 1 -d '|' > modifiedfiles rm mlines } # For each entry in $1 of type -, remove any corresponding # entry from $2 if ${ALLOWADD} != "yes". Remove all entries # of type - from $1. fetch_filter_allowadd () { cut -f 1,2 -d '|' < $1 | fgrep '|-' | cut -f 1 -d '|' > filesnotpresent if [ ${ALLOWADD} != "yes" ]; then sort < $2 | join -v 1 -t '|' - filesnotpresent | sort > $2.tmp mv $2.tmp $2 fi sort < $1 | join -v 1 -t '|' - filesnotpresent | sort > $1.tmp mv $1.tmp $1 rm filesnotpresent } # If ${ALLOWDELETE} != "yes", then remove any entries from $1 # which don't correspond to entries in $2. fetch_filter_allowdelete () { # Produce a lists ${PATH}|${TYPE} for X in $1 $2; do cut -f 1-2 -d '|' < ${X} | sort -u > ${X}.nodes done # Figure out which lines need to be removed from $1. if [ ${ALLOWDELETE} != "yes" ]; then comm -23 $1.nodes $2.nodes > $1.badnodes else : > $1.badnodes fi # Remove the relevant lines from $1 while read X; do look "${X}|" $1 done < $1.badnodes | comm -13 - $1 > $1.tmp mv $1.tmp $1 rm $1.badnodes $1.nodes $2.nodes } # If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in $2 # with metadata not matching any entry in $1, replace the corresponding # line of $3 with one having the same metadata as the entry in $2. fetch_filter_modified_metadata () { # Fish out the metadata from $1 and $2 for X in $1 $2; do cut -f 1-6 -d '|' < ${X} > ${X}.metadata done # Find the metadata we need to keep if [ ${KEEPMODIFIEDMETADATA} = "yes" ]; then comm -13 $1.metadata $2.metadata > keepmeta else : > keepmeta fi # Extract the lines which we need to remove from $3, and # construct the lines which we need to add to $3. : > $3.remove : > $3.add while read LINE; do NODE=`echo "${LINE}" | cut -f 1-2 -d '|'` look "${NODE}|" $3 >> $3.remove look "${NODE}|" $3 | cut -f 7- -d '|' | lam -s "${LINE}|" - >> $3.add done < keepmeta # Remove the specified lines and add the new lines. sort $3.remove | comm -13 - $3 | sort -u - $3.add > $3.tmp mv $3.tmp $3 rm keepmeta $1.metadata $2.metadata $3.add $3.remove } # Remove lines from $1 and $2 which are identical; # no need to update a file if it isn't changing. fetch_filter_uptodate () { comm -23 $1 $2 > $1.tmp comm -13 $1 $2 > $2.tmp mv $1.tmp $1 mv $2.tmp $2 } # Fetch any "clean" old versions of files we need for merging changes. fetch_files_premerge () { # We only need to do anything if $1 is non-empty. if [ -s $1 ]; then # Tell the user what we're doing echo -n "Fetching files from ${OLDRELNUM} for merging... " # List of files wanted fgrep '|f|' < $1 | cut -f 3 -d '|' | sort -u > files.wanted # Only fetch the files we don't already have while read Y; do if [ ! -f "files/${Y}.gz" ]; then echo ${Y}; fi done < files.wanted > filelist # Actually fetch them lam -s "${OLDFETCHDIR}/f/" - -s ".gz" < filelist | xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 2>${QUIETREDIR} # Make sure we got them all, and move them into /files/ while read Y; do if ! [ -f ${Y}.gz ]; then echo "failed." return 1 fi if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then mv ${Y}.gz files/${Y}.gz else echo "${Y} has incorrect hash." return 1 fi done < filelist echo "done." # Clean up rm filelist files.wanted fi } # Prepare to fetch files: Generate a list of the files we need, # copy the unmodified files we have into /files/, and generate # a list of patches to download. fetch_files_prepare () { # Tell the user why his disk is suddenly making lots of noise echo -n "Preparing to download files... " # Reduce indices to ${PATH}|${HASH} pairs for X in $1 $2 $3; do cut -f 1,2,7 -d '|' < ${X} | fgrep '|f|' | cut -f 1,3 -d '|' | sort > ${X}.hashes done # List of files wanted cut -f 2 -d '|' < $3.hashes | sort -u | while read HASH; do if ! [ -f files/${HASH}.gz ]; then echo ${HASH} fi done > files.wanted # Generate a list of unmodified files comm -12 $1.hashes $2.hashes | sort -k 1,1 -t '|' > unmodified.files # Copy all files into /files/. We only need the unmodified files # for use in patching; but we'll want all of them if the user asks # to rollback the updates later. while read LINE; do F=`echo "${LINE}" | cut -f 1 -d '|'` HASH=`echo "${LINE}" | cut -f 2 -d '|'` # Skip files we already have. if [ -f files/${HASH}.gz ]; then continue fi # Make sure the file hasn't changed. cp "${BASEDIR}/${F}" tmpfile if [ `sha256 -q tmpfile` != ${HASH} ]; then echo echo "File changed while FreeBSD Update running: ${F}" return 1 fi # Place the file into storage. gzip -c < tmpfile > files/${HASH}.gz rm tmpfile done < $2.hashes # Produce a list of patches to download sort -k 1,1 -t '|' $3.hashes | join -t '|' -o 2.2,1.2 - unmodified.files | fetch_make_patchlist > patchlist # Garbage collect rm unmodified.files $1.hashes $2.hashes $3.hashes # We don't need the list of possible old files any more. rm $1 # We're finished making noise echo "done." } # Fetch files. fetch_files () { # Attempt to fetch patches if [ -s patchlist ]; then echo -n "Fetching `wc -l < patchlist | tr -d ' '` " echo ${NDEBUG} "patches.${DDSTATS}" tr '|' '-' < patchlist | lam -s "${PATCHDIR}/" - | xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 2>${STATSREDIR} | fetch_progress echo "done." # Attempt to apply patches echo -n "Applying patches... " tr '|' ' ' < patchlist | while read X Y; do if [ ! -f "${X}-${Y}" ]; then continue; fi gunzip -c < files/${X}.gz > OLD bspatch OLD NEW ${X}-${Y} if [ `${SHA256} -q NEW` = ${Y} ]; then mv NEW files/${Y} gzip -n files/${Y} fi rm -f diff OLD NEW ${X}-${Y} done 2>${QUIETREDIR} echo "done." fi # Download files which couldn't be generate via patching while read Y; do if [ ! -f "files/${Y}.gz" ]; then echo ${Y}; fi done < files.wanted > filelist if [ -s filelist ]; then echo -n "Fetching `wc -l < filelist | tr -d ' '` " echo ${NDEBUG} "files... " lam -s "${FETCHDIR}/f/" - -s ".gz" < filelist | xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 2>${QUIETREDIR} while read Y; do if ! [ -f ${Y}.gz ]; then echo "failed." return 1 fi if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then mv ${Y}.gz files/${Y}.gz else echo "${Y} has incorrect hash." return 1 fi done < filelist echo "done." fi # Clean up rm files.wanted filelist patchlist } # Create and populate install manifest directory; and report what updates # are available. fetch_create_manifest () { # If we have an existing install manifest, nuke it. if [ -L "${BDHASH}-install" ]; then rm -r ${BDHASH}-install/ rm ${BDHASH}-install fi # Report to the user if any updates were avoided due to local changes if [ -s modifiedfiles ]; then echo echo -n "The following files are affected by updates, " echo "but no changes have" echo -n "been downloaded because the files have been " echo "modified locally:" cat modifiedfiles fi | $PAGER rm modifiedfiles # If no files will be updated, tell the user and exit if ! [ -s INDEX-PRESENT ] && ! [ -s INDEX-NEW ]; then rm INDEX-PRESENT INDEX-NEW echo echo -n "No updates needed to update system to " echo "${RELNUM}-p${RELPATCHNUM}." return fi # Divide files into (a) removed files, (b) added files, and # (c) updated files. cut -f 1 -d '|' < INDEX-PRESENT | sort > INDEX-PRESENT.flist cut -f 1 -d '|' < INDEX-NEW | sort > INDEX-NEW.flist comm -23 INDEX-PRESENT.flist INDEX-NEW.flist > files.removed comm -13 INDEX-PRESENT.flist INDEX-NEW.flist > files.added comm -12 INDEX-PRESENT.flist INDEX-NEW.flist > files.updated rm INDEX-PRESENT.flist INDEX-NEW.flist # Report removed files, if any if [ -s files.removed ]; then echo echo -n "The following files will be removed " echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:" cat files.removed fi | $PAGER rm files.removed # Report added files, if any if [ -s files.added ]; then echo echo -n "The following files will be added " echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:" cat files.added fi | $PAGER rm files.added # Report updated files, if any if [ -s files.updated ]; then echo echo -n "The following files will be updated " echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:" cat files.updated fi | $PAGER rm files.updated # Create a directory for the install manifest. MDIR=`mktemp -d install.XXXXXX` || return 1 # Populate it mv INDEX-PRESENT ${MDIR}/INDEX-OLD mv INDEX-NEW ${MDIR}/INDEX-NEW # Link it into place ln -s ${MDIR} ${BDHASH}-install } # Warn about any upcoming EoL fetch_warn_eol () { # What's the current time? NOWTIME=`date "+%s"` # When did we last warn about the EoL date? if [ -f lasteolwarn ]; then LASTWARN=`cat lasteolwarn` else LASTWARN=`expr ${NOWTIME} - 63072000` fi # If the EoL time is past, warn. if [ ${EOLTIME} -lt ${NOWTIME} ]; then echo cat <<-EOF WARNING: `uname -sr` HAS PASSED ITS END-OF-LIFE DATE. Any security issues discovered after `date -r ${EOLTIME}` will not have been corrected. EOF return 1 fi # Figure out how long it has been since we last warned about the # upcoming EoL, and how much longer we have left. SINCEWARN=`expr ${NOWTIME} - ${LASTWARN}` TIMELEFT=`expr ${EOLTIME} - ${NOWTIME}` # Don't warn if the EoL is more than 3 months away if [ ${TIMELEFT} -gt 7884000 ]; then return 0 fi # Don't warn if the time remaining is more than 3 times the time # since the last warning. if [ ${TIMELEFT} -gt `expr ${SINCEWARN} \* 3` ]; then return 0 fi # Figure out what time units to use. if [ ${TIMELEFT} -lt 604800 ]; then UNIT="day" SIZE=86400 elif [ ${TIMELEFT} -lt 2678400 ]; then UNIT="week" SIZE=604800 else UNIT="month" SIZE=2678400 fi # Compute the right number of units NUM=`expr ${TIMELEFT} / ${SIZE}` if [ ${NUM} != 1 ]; then UNIT="${UNIT}s" fi # Print the warning echo cat <<-EOF WARNING: `uname -sr` is approaching its End-of-Life date. It is strongly recommended that you upgrade to a newer release within the next ${NUM} ${UNIT}. EOF # Update the stored time of last warning echo ${NOWTIME} > lasteolwarn } # Do the actual work involved in "fetch" / "cron". fetch_run () { workdir_init || return 1 # Prepare the mirror list. fetch_pick_server_init && fetch_pick_server # Try to fetch the public key until we run out of servers. while ! fetch_key; do fetch_pick_server || return 1 done # Try to fetch the metadata index signature ("tag") until we run # out of available servers; and sanity check the downloaded tag. while ! fetch_tag; do fetch_pick_server || return 1 done fetch_tagsanity || return 1 # Fetch the latest INDEX-NEW and INDEX-OLD files. fetch_metadata INDEX-NEW INDEX-OLD || return 1 # Generate filtered INDEX-NEW and INDEX-OLD files containing only # the lines which (a) belong to components we care about, and (b) # don't correspond to paths we're explicitly ignoring. fetch_filter_metadata INDEX-NEW || return 1 fetch_filter_metadata INDEX-OLD || return 1 # Translate /boot/${KERNCONF} into ${KERNELDIR} fetch_filter_kernel_names INDEX-NEW ${KERNCONF} fetch_filter_kernel_names INDEX-OLD ${KERNCONF} # For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the # system and generate an INDEX-PRESENT file. fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1 # Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which # correspond to lines in INDEX-PRESENT with hashes not appearing # in INDEX-OLD or INDEX-NEW. Also remove lines where the entry in # INDEX-PRESENT has type - and there isn't a corresponding entry in # INDEX-OLD with type -. fetch_filter_unmodified_notpresent \ INDEX-OLD INDEX-PRESENT INDEX-NEW /dev/null # For each entry in INDEX-PRESENT of type -, remove any corresponding # entry from INDEX-NEW if ${ALLOWADD} != "yes". Remove all entries # of type - from INDEX-PRESENT. fetch_filter_allowadd INDEX-PRESENT INDEX-NEW # If ${ALLOWDELETE} != "yes", then remove any entries from # INDEX-PRESENT which don't correspond to entries in INDEX-NEW. fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW # If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in # INDEX-PRESENT with metadata not matching any entry in INDEX-OLD, # replace the corresponding line of INDEX-NEW with one having the # same metadata as the entry in INDEX-PRESENT. fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW # Remove lines from INDEX-PRESENT and INDEX-NEW which are identical; # no need to update a file if it isn't changing. fetch_filter_uptodate INDEX-PRESENT INDEX-NEW # Prepare to fetch files: Generate a list of the files we need, # copy the unmodified files we have into /files/, and generate # a list of patches to download. fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1 # Fetch files. fetch_files || return 1 # Create and populate install manifest directory; and report what # updates are available. fetch_create_manifest || return 1 # Warn about any upcoming EoL fetch_warn_eol || return 1 } # If StrictComponents is not "yes", generate a new components list # with only the components which appear to be installed. upgrade_guess_components () { if [ "${STRICTCOMPONENTS}" = "no" ]; then # Generate filtered INDEX-ALL with only the components listed # in COMPONENTS. fetch_filter_metadata_components $1 || return 1 # Tell the user why his disk is suddenly making lots of noise echo -n "Inspecting system... " # Look at the files on disk, and assume that a component is # supposed to be present if it is more than half-present. cut -f 1-3 -d '|' < INDEX-ALL | tr '|' ' ' | while read C S F; do if [ -e ${BASEDIR}/${F} ]; then echo "+ ${C}|${S}" fi echo "= ${C}|${S}" done | sort | uniq -c | sed -E 's,^ +,,' > compfreq grep ' = ' compfreq | cut -f 1,3 -d ' ' | sort -k 2,2 -t ' ' > compfreq.total grep ' + ' compfreq | cut -f 1,3 -d ' ' | sort -k 2,2 -t ' ' > compfreq.present join -t ' ' -1 2 -2 2 compfreq.present compfreq.total | while read S P T; do if [ ${P} -gt `expr ${T} / 2` ]; then echo ${S} fi done > comp.present cut -f 2 -d ' ' < compfreq.total > comp.total rm INDEX-ALL compfreq compfreq.total compfreq.present # We're done making noise. echo "done." # Sometimes the kernel isn't installed where INDEX-ALL # thinks that it should be: In particular, it is often in # /boot/kernel instead of /boot/GENERIC or /boot/SMP. To # deal with this, if "kernel|X" is listed in comp.total # (i.e., is a component which would be upgraded if it is # found to be present) we will add it to comp.present. # If "kernel|" is in comp.total but "kernel|X" is # not, we print a warning -- the user is running a kernel # which isn't part of the release. KCOMP=`echo ${KERNCONF} | tr 'A-Z' 'a-z'` grep -E "^kernel\|${KCOMP}\$" comp.total >> comp.present if grep -qE "^kernel\|" comp.total && ! grep -qE "^kernel\|${KCOMP}\$" comp.total; then cat <<-EOF WARNING: This system is running a "${KCOMP}" kernel, which is not a kernel configuration distributed as part of FreeBSD ${RELNUM}. This kernel will not be updated: you MUST update the kernel manually before running "$0 install". EOF fi # Re-sort the list of installed components and generate # the list of non-installed components. sort -u < comp.present > comp.present.tmp mv comp.present.tmp comp.present comm -13 comp.present comp.total > comp.absent # Ask the user to confirm that what we have is correct. To # reduce user confusion, translate "X|Y" back to "X/Y" (as # subcomponents must be listed in the configuration file). echo echo -n "The following components of FreeBSD " echo "seem to be installed:" tr '|' '/' < comp.present | fmt -72 echo echo -n "The following components of FreeBSD " echo "do not seem to be installed:" tr '|' '/' < comp.absent | fmt -72 echo continuep || return 1 echo # Suck the generated list of components into ${COMPONENTS}. # Note that comp.present.tmp is used due to issues with # pipelines and setting variables. COMPONENTS="" tr '|' '/' < comp.present > comp.present.tmp while read C; do COMPONENTS="${COMPONENTS} ${C}" done < comp.present.tmp # Delete temporary files rm comp.present comp.present.tmp comp.absent comp.total fi } # If StrictComponents is not "yes", COMPONENTS contains an entry # corresponding to the currently running kernel, and said kernel # does not exist in the new release, add "kernel/generic" to the # list of components. upgrade_guess_new_kernel () { if [ "${STRICTCOMPONENTS}" = "no" ]; then # Grab the unfiltered metadata file. METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'` gunzip -c < files/${METAHASH}.gz > $1.all # If "kernel/${KCOMP}" is in ${COMPONENTS} and that component # isn't in $1.all, we need to add kernel/generic. for C in ${COMPONENTS}; do if [ ${C} = "kernel/${KCOMP}" ] && ! grep -qE "^kernel\|${KCOMP}\|" $1.all; then COMPONENTS="${COMPONENTS} kernel/generic" NKERNCONF="GENERIC" cat <<-EOF WARNING: This system is running a "${KCOMP}" kernel, which is not a kernel configuration distributed as part of FreeBSD ${RELNUM}. As part of upgrading to FreeBSD ${RELNUM}, this kernel will be replaced with a "generic" kernel. EOF continuep || return 1 fi done # Don't need this any more... rm $1.all fi } # Convert INDEX-OLD (last release) and INDEX-ALL (new release) into # INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades). upgrade_oldall_to_oldnew () { # For each ${F}|... which appears in INDEX-ALL but does not appear # in INDEX-OLD, add ${F}|-|||||| to INDEX-OLD. cut -f 1 -d '|' < $1 | sort -u > $1.paths cut -f 1 -d '|' < $2 | sort -u | comm -13 $1.paths - | lam - -s "|-||||||" | sort - $1 > $1.tmp mv $1.tmp $1 # Remove lines from INDEX-OLD which also appear in INDEX-ALL comm -23 $1 $2 > $1.tmp mv $1.tmp $1 # Remove lines from INDEX-ALL which have a file name not appearing # anywhere in INDEX-OLD (since these must be files which haven't # changed -- if they were new, there would be an entry of type "-"). cut -f 1 -d '|' < $1 | sort -u > $1.paths sort -k 1,1 -t '|' < $2 | join -t '|' - $1.paths | sort > $2.tmp rm $1.paths mv $2.tmp $2 # Rename INDEX-ALL to INDEX-NEW. mv $2 $3 } # Helper for upgrade_merge: Return zero true iff the two files differ only # in the contents of their $FreeBSD$ tags. samef () { X=`sed -E 's/\\$FreeBSD.*\\$/\$FreeBSD\$/' < $1 | ${SHA256}` Y=`sed -E 's/\\$FreeBSD.*\\$/\$FreeBSD\$/' < $2 | ${SHA256}` if [ $X = $Y ]; then return 0; else return 1; fi } # From the list of "old" files in $1, merge changes in $2 with those in $3, # and update $3 to reflect the hashes of merged files. upgrade_merge () { # We only need to do anything if $1 is non-empty. if [ -s $1 ]; then cut -f 1 -d '|' $1 | sort > $1-paths # Create staging area for merging files rm -rf merge/ while read F; do D=`dirname ${F}` mkdir -p merge/old/${D} mkdir -p merge/${OLDRELNUM}/${D} mkdir -p merge/${RELNUM}/${D} mkdir -p merge/new/${D} done < $1-paths # Copy in files while read F; do # Currently installed file V=`look "${F}|" $2 | cut -f 7 -d '|'` gunzip < files/${V}.gz > merge/old/${F} # Old release if look "${F}|" $1 | fgrep -q "|f|"; then V=`look "${F}|" $1 | cut -f 3 -d '|'` gunzip < files/${V}.gz \ > merge/${OLDRELNUM}/${F} fi # New release if look "${F}|" $3 | cut -f 1,2,7 -d '|' | fgrep -q "|f|"; then V=`look "${F}|" $3 | cut -f 7 -d '|'` gunzip < files/${V}.gz \ > merge/${RELNUM}/${F} fi done < $1-paths # Attempt to automatically merge changes echo -n "Attempting to automatically merge " echo -n "changes in files..." : > failed.merges while read F; do # If the file doesn't exist in the new release, # the result of "merging changes" is having the file # not exist. if ! [ -f merge/${RELNUM}/${F} ]; then continue fi # If the file didn't exist in the old release, we're # going to throw away the existing file and hope that # the version from the new release is what we want. if ! [ -f merge/${OLDRELNUM}/${F} ]; then cp merge/${RELNUM}/${F} merge/new/${F} continue fi # Some files need special treatment. case ${F} in /etc/spwd.db | /etc/pwd.db | /etc/login.conf.db) # Don't merge these -- we're rebuild them # after updates are installed. cp merge/old/${F} merge/new/${F} ;; *) if ! merge -p -L "current version" \ -L "${OLDRELNUM}" -L "${RELNUM}" \ merge/old/${F} \ merge/${OLDRELNUM}/${F} \ merge/${RELNUM}/${F} \ > merge/new/${F} 2>/dev/null; then echo ${F} >> failed.merges fi ;; esac done < $1-paths echo " done." # Ask the user to handle any files which didn't merge. while read F; do # If the installed file differs from the version in # the old release only due to $FreeBSD$ tag expansion # then just use the version in the new release. if samef merge/old/${F} merge/${OLDRELNUM}/${F}; then cp merge/${RELNUM}/${F} merge/new/${F} continue fi cat <<-EOF The following file could not be merged automatically: ${F} Press Enter to edit this file in ${EDITOR} and resolve the conflicts manually... EOF read dummy files/${V}.gz echo "${F}|${V}" fi done < $1-paths > newhashes # Pull lines out from $3 which need to be updated to # reflect merged files. while read F; do look "${F}|" $3 done < $1-paths > $3-oldlines # Update lines to reflect merged files join -t '|' -o 1.1,1.2,1.3,1.4,1.5,1.6,2.2,1.8 \ $3-oldlines newhashes > $3-newlines # Remove old lines from $3 and add new lines. sort $3-oldlines | comm -13 - $3 | sort - $3-newlines > $3.tmp mv $3.tmp $3 # Clean up rm $1-paths newhashes $3-oldlines $3-newlines rm -rf merge/ fi # We're done with merging files. rm $1 } # Do the work involved in fetching upgrades to a new release upgrade_run () { workdir_init || return 1 # Prepare the mirror list. fetch_pick_server_init && fetch_pick_server # Try to fetch the public key until we run out of servers. while ! fetch_key; do fetch_pick_server || return 1 done # Try to fetch the metadata index signature ("tag") until we run # out of available servers; and sanity check the downloaded tag. while ! fetch_tag; do fetch_pick_server || return 1 done fetch_tagsanity || return 1 # Fetch the INDEX-OLD and INDEX-ALL. fetch_metadata INDEX-OLD INDEX-ALL || return 1 # If StrictComponents is not "yes", generate a new components list # with only the components which appear to be installed. upgrade_guess_components INDEX-ALL || return 1 # Generate filtered INDEX-OLD and INDEX-ALL files containing only # the components we want and without anything marked as "Ignore". fetch_filter_metadata INDEX-OLD || return 1 fetch_filter_metadata INDEX-ALL || return 1 # Merge the INDEX-OLD and INDEX-ALL files into INDEX-OLD. sort INDEX-OLD INDEX-ALL > INDEX-OLD.tmp mv INDEX-OLD.tmp INDEX-OLD rm INDEX-ALL # Adjust variables for fetching files from the new release. OLDRELNUM=${RELNUM} RELNUM=${TARGETRELEASE} OLDFETCHDIR=${FETCHDIR} FETCHDIR=${RELNUM}/${ARCH} # Try to fetch the NEW metadata index signature ("tag") until we run # out of available servers; and sanity check the downloaded tag. while ! fetch_tag; do fetch_pick_server || return 1 done # Fetch the new INDEX-ALL. fetch_metadata INDEX-ALL || return 1 # If StrictComponents is not "yes", COMPONENTS contains an entry # corresponding to the currently running kernel, and said kernel # does not exist in the new release, add "kernel/generic" to the # list of components. upgrade_guess_new_kernel INDEX-ALL || return 1 # Filter INDEX-ALL to contain only the components we want and without # anything marked as "Ignore". fetch_filter_metadata INDEX-ALL || return 1 # Convert INDEX-OLD (last release) and INDEX-ALL (new release) into # INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades). upgrade_oldall_to_oldnew INDEX-OLD INDEX-ALL INDEX-NEW # Translate /boot/${KERNCONF} or /boot/${NKERNCONF} into ${KERNELDIR} fetch_filter_kernel_names INDEX-NEW ${NKERNCONF} fetch_filter_kernel_names INDEX-OLD ${KERNCONF} # For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the # system and generate an INDEX-PRESENT file. fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1 # Based on ${MERGECHANGES}, generate a file tomerge-old with the # paths and hashes of old versions of files to merge. fetch_filter_mergechanges INDEX-OLD INDEX-PRESENT tomerge-old # Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which # correspond to lines in INDEX-PRESENT with hashes not appearing # in INDEX-OLD or INDEX-NEW. Also remove lines where the entry in # INDEX-PRESENT has type - and there isn't a corresponding entry in # INDEX-OLD with type -. fetch_filter_unmodified_notpresent \ INDEX-OLD INDEX-PRESENT INDEX-NEW tomerge-old # For each entry in INDEX-PRESENT of type -, remove any corresponding # entry from INDEX-NEW if ${ALLOWADD} != "yes". Remove all entries # of type - from INDEX-PRESENT. fetch_filter_allowadd INDEX-PRESENT INDEX-NEW # If ${ALLOWDELETE} != "yes", then remove any entries from # INDEX-PRESENT which don't correspond to entries in INDEX-NEW. fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW # If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in # INDEX-PRESENT with metadata not matching any entry in INDEX-OLD, # replace the corresponding line of INDEX-NEW with one having the # same metadata as the entry in INDEX-PRESENT. fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW # Remove lines from INDEX-PRESENT and INDEX-NEW which are identical; # no need to update a file if it isn't changing. fetch_filter_uptodate INDEX-PRESENT INDEX-NEW # Fetch "clean" files from the old release for merging changes. fetch_files_premerge tomerge-old # Prepare to fetch files: Generate a list of the files we need, # copy the unmodified files we have into /files/, and generate # a list of patches to download. fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1 # Fetch patches from to-${RELNUM}/${ARCH}/bp/ PATCHDIR=to-${RELNUM}/${ARCH}/bp fetch_files || return 1 # Merge configuration file changes. upgrade_merge tomerge-old INDEX-PRESENT INDEX-NEW || return 1 # Create and populate install manifest directory; and report what # updates are available. fetch_create_manifest || return 1 # Leave a note behind to tell the "install" command that the kernel # needs to be installed before the world. touch ${BDHASH}-install/kernelfirst # Remind the user that they need to run "freebsd-update install" # to install the downloaded bits, in case they didn't RTFM. echo "To install the downloaded upgrades, run \"$0 install\"." } # Make sure that all the file hashes mentioned in $@ have corresponding # gzipped files stored in /files/. install_verify () { # Generate a list of hashes cat $@ | cut -f 2,7 -d '|' | grep -E '^f' | cut -f 2 -d '|' | sort -u > filelist # Make sure all the hashes exist while read HASH; do if ! [ -f files/${HASH}.gz ]; then echo -n "Update files missing -- " echo "this should never happen." echo "Re-run '$0 fetch'." return 1 fi done < filelist # Clean up rm filelist } # Remove the system immutable flag from files install_unschg () { # Generate file list cat $@ | cut -f 1 -d '|' > filelist # Remove flags while read F; do if ! [ -e ${BASEDIR}/${F} ]; then continue fi chflags noschg ${BASEDIR}/${F} || return 1 done < filelist # Clean up rm filelist } # Decide which directory name to use for kernel backups. backup_kernel_finddir () { CNT=0 while true ; do # Pathname does not exist, so it is OK use that name # for backup directory. if [ ! -e $BACKUPKERNELDIR ]; then return 0 fi # If directory do exist, we only use if it has our # marker file. if [ -d $BACKUPKERNELDIR -a \ -e $BACKUPKERNELDIR/.freebsd-update ]; then return 0 fi # We could not use current directory name, so add counter to # the end and try again. CNT=$((CNT + 1)) if [ $CNT -gt 9 ]; then echo "Could not find valid backup dir ($BACKUPKERNELDIR)" exit 1 fi BACKUPKERNELDIR="`echo $BACKUPKERNELDIR | sed -Ee 's/[0-9]\$//'`" BACKUPKERNELDIR="${BACKUPKERNELDIR}${CNT}" done } # Backup the current kernel using hardlinks, if not disabled by user. # Since we delete all files in the directory used for previous backups # we create a marker file called ".freebsd-update" in the directory so # we can determine on the next run that the directory was created by # freebsd-update and we then do not accidentally remove user files in # the unlikely case that the user has created a directory with a # conflicting name. backup_kernel () { # Only make kernel backup is so configured. if [ $BACKUPKERNEL != yes ]; then return 0 fi # Decide which directory name to use for kernel backups. backup_kernel_finddir # Remove old kernel backup files. If $BACKUPKERNELDIR was # "not ours", backup_kernel_finddir would have exited, so # deleting the directory content is as safe as we can make it. if [ -d $BACKUPKERNELDIR ]; then rm -fr $BACKUPKERNELDIR fi # Create directories for backup. mkdir -p $BACKUPKERNELDIR mtree -cdn -p "${KERNELDIR}" | \ mtree -Ue -p "${BACKUPKERNELDIR}" > /dev/null # Mark the directory as having been created by freebsd-update. touch $BACKUPKERNELDIR/.freebsd-update if [ $? -ne 0 ]; then echo "Could not create kernel backup directory" exit 1 fi # Disable pathname expansion to be sure *.symbols is not # expanded. set -f # Use find to ignore symbol files, unless disabled by user. if [ $BACKUPKERNELSYMBOLFILES = yes ]; then FINDFILTER="" else FINDFILTER=-"a ! -name *.symbols" fi # Backup all the kernel files using hardlinks. (cd $KERNELDIR && find . -type f $FINDFILTER -exec \ cp -pl '{}' ${BACKUPKERNELDIR}/'{}' \;) # Re-enable patchname expansion. set +f } # Install new files install_from_index () { # First pass: Do everything apart from setting file flags. We # can't set flags yet, because schg inhibits hard linking. sort -k 1,1 -t '|' $1 | tr '|' ' ' | while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do case ${TYPE} in d) # Create a directory install -d -o ${OWNER} -g ${GROUP} \ -m ${PERM} ${BASEDIR}/${FPATH} ;; f) if [ -z "${LINK}" ]; then # Create a file, without setting flags. gunzip < files/${HASH}.gz > ${HASH} install -S -o ${OWNER} -g ${GROUP} \ -m ${PERM} ${HASH} ${BASEDIR}/${FPATH} rm ${HASH} else # Create a hard link. ln -f ${BASEDIR}/${LINK} ${BASEDIR}/${FPATH} fi ;; L) # Create a symlink ln -sfh ${HASH} ${BASEDIR}/${FPATH} ;; esac done # Perform a second pass, adding file flags. tr '|' ' ' < $1 | while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do if [ ${TYPE} = "f" ] && ! [ ${FLAGS} = "0" ]; then chflags ${FLAGS} ${BASEDIR}/${FPATH} fi done } # Remove files which we want to delete install_delete () { # Generate list of new files cut -f 1 -d '|' < $2 | sort > newfiles # Generate subindex of old files we want to nuke sort -k 1,1 -t '|' $1 | join -t '|' -v 1 - newfiles | sort -r -k 1,1 -t '|' | cut -f 1,2 -d '|' | tr '|' ' ' > killfiles # Remove the offending bits while read FPATH TYPE; do case ${TYPE} in d) rmdir ${BASEDIR}/${FPATH} ;; f) rm ${BASEDIR}/${FPATH} ;; L) rm ${BASEDIR}/${FPATH} ;; esac done < killfiles # Clean up rm newfiles killfiles } # Install new files, delete old files, and update linker.hints install_files () { # If we haven't already dealt with the kernel, deal with it. if ! [ -f $1/kerneldone ]; then grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW # Backup current kernel before installing a new one backup_kernel || return 1 # Install new files install_from_index INDEX-NEW || return 1 # Remove files which need to be deleted install_delete INDEX-OLD INDEX-NEW || return 1 # Update linker.hints if necessary if [ -s INDEX-OLD -o -s INDEX-NEW ]; then kldxref -R /boot/ 2>/dev/null fi # We've finished updating the kernel. touch $1/kerneldone # Do we need to ask for a reboot now? if [ -f $1/kernelfirst ] && [ -s INDEX-OLD -o -s INDEX-NEW ]; then cat <<-EOF Kernel updates have been installed. Please reboot and run "$0 install" again to finish installing updates. EOF exit 0 fi fi # If we haven't already dealt with the world, deal with it. if ! [ -f $1/worlddone ]; then # Create any necessary directories first grep -vE '^/boot/' $1/INDEX-NEW | grep -E '^[^|]+\|d\|' > INDEX-NEW install_from_index INDEX-NEW || return 1 # Install new shared libraries next grep -vE '^/boot/' $1/INDEX-NEW | grep -vE '^[^|]+\|d\|' | grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW install_from_index INDEX-NEW || return 1 # Deal with everything else grep -vE '^/boot/' $1/INDEX-OLD | grep -vE '^[^|]+\|d\|' | grep -vE '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-OLD grep -vE '^/boot/' $1/INDEX-NEW | grep -vE '^[^|]+\|d\|' | grep -vE '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW install_from_index INDEX-NEW || return 1 install_delete INDEX-OLD INDEX-NEW || return 1 # Rebuild /etc/spwd.db and /etc/pwd.db if necessary. if [ /etc/master.passwd -nt /etc/spwd.db ] || [ /etc/master.passwd -nt /etc/pwd.db ]; then pwd_mkdb /etc/master.passwd fi # Rebuild /etc/login.conf.db if necessary. if [ /etc/login.conf -nt /etc/login.conf.db ]; then cap_mkdb /etc/login.conf fi # We've finished installing the world and deleting old files # which are not shared libraries. touch $1/worlddone # Do we need to ask the user to portupgrade now? grep -vE '^/boot/' $1/INDEX-NEW | grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' | cut -f 1 -d '|' | sort > newfiles if grep -vE '^/boot/' $1/INDEX-OLD | grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' | cut -f 1 -d '|' | sort | join -v 1 - newfiles | grep -q .; then cat <<-EOF Completing this upgrade requires removing old shared object files. Please rebuild all installed 3rd party software (e.g., programs installed from the ports tree) and then run "$0 install" again to finish installing updates. EOF rm newfiles exit 0 fi rm newfiles fi # Remove old shared libraries grep -vE '^/boot/' $1/INDEX-NEW | grep -vE '^[^|]+\|d\|' | grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW grep -vE '^/boot/' $1/INDEX-OLD | grep -vE '^[^|]+\|d\|' | grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-OLD install_delete INDEX-OLD INDEX-NEW || return 1 # Remove old directories grep -vE '^/boot/' $1/INDEX-NEW | grep -E '^[^|]+\|d\|' > INDEX-NEW grep -vE '^/boot/' $1/INDEX-OLD | grep -E '^[^|]+\|d\|' > INDEX-OLD install_delete INDEX-OLD INDEX-NEW || return 1 # Remove temporary files rm INDEX-OLD INDEX-NEW } # Rearrange bits to allow the installed updates to be rolled back install_setup_rollback () { # Remove the "reboot after installing kernel", "kernel updated", and # "finished installing the world" flags if present -- they are # irrelevant when rolling back updates. if [ -f ${BDHASH}-install/kernelfirst ]; then rm ${BDHASH}-install/kernelfirst rm ${BDHASH}-install/kerneldone fi if [ -f ${BDHASH}-install/worlddone ]; then rm ${BDHASH}-install/worlddone fi if [ -L ${BDHASH}-rollback ]; then mv ${BDHASH}-rollback ${BDHASH}-install/rollback fi mv ${BDHASH}-install ${BDHASH}-rollback } # Actually install updates install_run () { echo -n "Installing updates..." # Make sure we have all the files we should have install_verify ${BDHASH}-install/INDEX-OLD \ ${BDHASH}-install/INDEX-NEW || return 1 # Remove system immutable flag from files install_unschg ${BDHASH}-install/INDEX-OLD \ ${BDHASH}-install/INDEX-NEW || return 1 # Install new files, delete old files, and update linker.hints install_files ${BDHASH}-install || return 1 # Rearrange bits to allow the installed updates to be rolled back install_setup_rollback echo " done." } # Rearrange bits to allow the previous set of updates to be rolled back next. rollback_setup_rollback () { if [ -L ${BDHASH}-rollback/rollback ]; then mv ${BDHASH}-rollback/rollback rollback-tmp rm -r ${BDHASH}-rollback/ rm ${BDHASH}-rollback mv rollback-tmp ${BDHASH}-rollback else rm -r ${BDHASH}-rollback/ rm ${BDHASH}-rollback fi } # Install old files, delete new files, and update linker.hints rollback_files () { # Install old shared library files which don't have the same path as # a new shared library file. grep -vE '^/boot/' $1/INDEX-NEW | grep -E '/lib/.*\.so\.[0-9]+\|' | cut -f 1 -d '|' | sort > INDEX-NEW.libs.flist grep -vE '^/boot/' $1/INDEX-OLD | grep -E '/lib/.*\.so\.[0-9]+\|' | sort -k 1,1 -t '|' - | join -t '|' -v 1 - INDEX-NEW.libs.flist > INDEX-OLD install_from_index INDEX-OLD || return 1 # Deal with files which are neither kernel nor shared library grep -vE '^/boot/' $1/INDEX-OLD | grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD grep -vE '^/boot/' $1/INDEX-NEW | grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW install_from_index INDEX-OLD || return 1 install_delete INDEX-NEW INDEX-OLD || return 1 # Install any old shared library files which we didn't install above. grep -vE '^/boot/' $1/INDEX-OLD | grep -E '/lib/.*\.so\.[0-9]+\|' | sort -k 1,1 -t '|' - | join -t '|' - INDEX-NEW.libs.flist > INDEX-OLD install_from_index INDEX-OLD || return 1 # Delete unneeded shared library files grep -vE '^/boot/' $1/INDEX-OLD | grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD grep -vE '^/boot/' $1/INDEX-NEW | grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW install_delete INDEX-NEW INDEX-OLD || return 1 # Deal with kernel files grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW install_from_index INDEX-OLD || return 1 install_delete INDEX-NEW INDEX-OLD || return 1 if [ -s INDEX-OLD -o -s INDEX-NEW ]; then kldxref -R /boot/ 2>/dev/null fi # Remove temporary files rm INDEX-OLD INDEX-NEW INDEX-NEW.libs.flist } # Actually rollback updates rollback_run () { echo -n "Uninstalling updates..." # If there are updates waiting to be installed, remove them; we # want the user to re-run 'fetch' after rolling back updates. if [ -L ${BDHASH}-install ]; then rm -r ${BDHASH}-install/ rm ${BDHASH}-install fi # Make sure we have all the files we should have install_verify ${BDHASH}-rollback/INDEX-NEW \ ${BDHASH}-rollback/INDEX-OLD || return 1 # Remove system immutable flag from files install_unschg ${BDHASH}-rollback/INDEX-NEW \ ${BDHASH}-rollback/INDEX-OLD || return 1 # Install old files, delete new files, and update linker.hints rollback_files ${BDHASH}-rollback || return 1 # Remove the rollback directory and the symlink pointing to it; and # rearrange bits to allow the previous set of updates to be rolled # back next. rollback_setup_rollback echo " done." } # Compare INDEX-ALL and INDEX-PRESENT and print warnings about differences. IDS_compare () { # Get all the lines which mismatch in something other than file # flags. We ignore file flags because sysinstall doesn't seem to # set them when it installs FreeBSD; warning about these adds a # very large amount of noise. cut -f 1-5,7-8 -d '|' $1 > $1.noflags sort -k 1,1 -t '|' $1.noflags > $1.sorted cut -f 1-5,7-8 -d '|' $2 | comm -13 $1.noflags - | fgrep -v '|-|||||' | sort -k 1,1 -t '|' | join -t '|' $1.sorted - > INDEX-NOTMATCHING # Ignore files which match IDSIGNOREPATHS. for X in ${IDSIGNOREPATHS}; do grep -E "^${X}" INDEX-NOTMATCHING done | sort -u | comm -13 - INDEX-NOTMATCHING > INDEX-NOTMATCHING.tmp mv INDEX-NOTMATCHING.tmp INDEX-NOTMATCHING # Go through the lines and print warnings. while read LINE; do FPATH=`echo "${LINE}" | cut -f 1 -d '|'` TYPE=`echo "${LINE}" | cut -f 2 -d '|'` OWNER=`echo "${LINE}" | cut -f 3 -d '|'` GROUP=`echo "${LINE}" | cut -f 4 -d '|'` PERM=`echo "${LINE}" | cut -f 5 -d '|'` HASH=`echo "${LINE}" | cut -f 6 -d '|'` LINK=`echo "${LINE}" | cut -f 7 -d '|'` P_TYPE=`echo "${LINE}" | cut -f 8 -d '|'` P_OWNER=`echo "${LINE}" | cut -f 9 -d '|'` P_GROUP=`echo "${LINE}" | cut -f 10 -d '|'` P_PERM=`echo "${LINE}" | cut -f 11 -d '|'` P_HASH=`echo "${LINE}" | cut -f 12 -d '|'` P_LINK=`echo "${LINE}" | cut -f 13 -d '|'` # Warn about different object types. if ! [ "${TYPE}" = "${P_TYPE}" ]; then echo -n "${FPATH} is a " case "${P_TYPE}" in f) echo -n "regular file, " ;; d) echo -n "directory, " ;; L) echo -n "symlink, " ;; esac echo -n "but should be a " case "${TYPE}" in f) echo -n "regular file." ;; d) echo -n "directory." ;; L) echo -n "symlink." ;; esac echo # Skip other tests, since they don't make sense if # we're comparing different object types. continue fi # Warn about different owners. if ! [ "${OWNER}" = "${P_OWNER}" ]; then echo -n "${FPATH} is owned by user id ${P_OWNER}, " echo "but should be owned by user id ${OWNER}." fi # Warn about different groups. if ! [ "${GROUP}" = "${P_GROUP}" ]; then echo -n "${FPATH} is owned by group id ${P_GROUP}, " echo "but should be owned by group id ${GROUP}." fi # Warn about different permissions. We do not warn about # different permissions on symlinks, since some archivers # don't extract symlink permissions correctly and they are # ignored anyway. if ! [ "${PERM}" = "${P_PERM}" ] && ! [ "${TYPE}" = "L" ]; then echo -n "${FPATH} has ${P_PERM} permissions, " echo "but should have ${PERM} permissions." fi # Warn about different file hashes / symlink destinations. if ! [ "${HASH}" = "${P_HASH}" ]; then if [ "${TYPE}" = "L" ]; then echo -n "${FPATH} is a symlink to ${P_HASH}, " echo "but should be a symlink to ${HASH}." fi if [ "${TYPE}" = "f" ]; then echo -n "${FPATH} has SHA256 hash ${P_HASH}, " echo "but should have SHA256 hash ${HASH}." fi fi # We don't warn about different hard links, since some # some archivers break hard links, and as long as the # underlying data is correct they really don't matter. done < INDEX-NOTMATCHING # Clean up rm $1 $1.noflags $1.sorted $2 INDEX-NOTMATCHING } # Do the work involved in comparing the system to a "known good" index IDS_run () { workdir_init || return 1 # Prepare the mirror list. fetch_pick_server_init && fetch_pick_server # Try to fetch the public key until we run out of servers. while ! fetch_key; do fetch_pick_server || return 1 done # Try to fetch the metadata index signature ("tag") until we run # out of available servers; and sanity check the downloaded tag. while ! fetch_tag; do fetch_pick_server || return 1 done fetch_tagsanity || return 1 # Fetch INDEX-OLD and INDEX-ALL. fetch_metadata INDEX-OLD INDEX-ALL || return 1 # Generate filtered INDEX-OLD and INDEX-ALL files containing only # the components we want and without anything marked as "Ignore". fetch_filter_metadata INDEX-OLD || return 1 fetch_filter_metadata INDEX-ALL || return 1 # Merge the INDEX-OLD and INDEX-ALL files into INDEX-ALL. sort INDEX-OLD INDEX-ALL > INDEX-ALL.tmp mv INDEX-ALL.tmp INDEX-ALL rm INDEX-OLD # Translate /boot/${KERNCONF} to ${KERNELDIR} fetch_filter_kernel_names INDEX-ALL ${KERNCONF} # Inspect the system and generate an INDEX-PRESENT file. fetch_inspect_system INDEX-ALL INDEX-PRESENT /dev/null || return 1 # Compare INDEX-ALL and INDEX-PRESENT and print warnings about any # differences. IDS_compare INDEX-ALL INDEX-PRESENT } #### Main functions -- call parameter-handling and core functions # Using the command line, configuration file, and defaults, # set all the parameters which are needed later. get_params () { init_params parse_cmdline $@ parse_conffile default_params } # Fetch command. Make sure that we're being called # interactively, then run fetch_check_params and fetch_run cmd_fetch () { if [ ! -t 0 ]; then echo -n "`basename $0` fetch should not " echo "be run non-interactively." echo "Run `basename $0` cron instead." exit 1 fi fetch_check_params fetch_run || exit 1 } # Cron command. Make sure the parameters are sensible; wait # rand(3600) seconds; then fetch updates. While fetching updates, # send output to a temporary file; only print that file if the # fetching failed. cmd_cron () { fetch_check_params sleep `jot -r 1 0 3600` TMPFILE=`mktemp /tmp/freebsd-update.XXXXXX` || exit 1 if ! fetch_run >> ${TMPFILE} || ! grep -q "No updates needed" ${TMPFILE} || [ ${VERBOSELEVEL} = "debug" ]; then mail -s "`hostname` security updates" ${MAILTO} < ${TMPFILE} fi rm ${TMPFILE} } # Fetch files for upgrading to a new release. cmd_upgrade () { upgrade_check_params upgrade_run || exit 1 } # Install downloaded updates. cmd_install () { install_check_params install_run || exit 1 } # Rollback most recently installed updates. cmd_rollback () { rollback_check_params rollback_run || exit 1 } # Compare system against a "known good" index. cmd_IDS () { IDS_check_params IDS_run || exit 1 } #### Entry point # Make sure we find utilities from the base system export PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH} # Set a pager if the user doesn't if [ -z "$PAGER" ]; then PAGER=/usr/bin/more fi # Set LC_ALL in order to avoid problems with character ranges like [A-Z]. export LC_ALL=C get_params $@ for COMMAND in ${COMMANDS}; do cmd_${COMMAND} done