From: Juergen Perlinger Date: Wed, 14 Oct 2020 16:40:09 +0000 (+0200) Subject: [Bug 3692] /dev/gpsN requirement prevents KPPS X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=16c523a2e237f3dfa43fbf9be1e6644811592c8a;p=thirdparty%2Fntp.git [Bug 3692] /dev/gpsN requirement prevents KPPS - resolve symlinks in device names for GPSD_JSON driver bk: 5f8729e9muDcx7Vq6XoBDT_eNmfM1Q --- diff --git a/ChangeLog b/ChangeLog index eeceaa9f1..be97b2a2f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +--- +* [Bug 3692] /dev/gpsN requirement prevents KPPS + - implement/wrap 'realpath()' to resolve symlinks in device names +* Implement NTP_FUNC_REALPATH. + --- (4.2.8p15) 2020/06/23 Released by Harlan Stenn diff --git a/configure.ac b/configure.ac index 5dc6aee02..4e1c1dc67 100644 --- a/configure.ac +++ b/configure.ac @@ -900,6 +900,7 @@ case "$host" in ;; esac AC_CHECK_FUNCS([nice plock pututline pututxline readlink rtprio]) +NTP_FUNC_REALPATH case "$host" in *-*-aix[[4-9]]*) # XXX only verified thru AIX6. diff --git a/include/ntp_stdlib.h b/include/ntp_stdlib.h index 265aafa73..cf8c4f279 100644 --- a/include/ntp_stdlib.h +++ b/include/ntp_stdlib.h @@ -40,6 +40,8 @@ extern void setup_logfile (const char *); extern void errno_to_str(int, char *, size_t); #endif +extern char * ntp_realpath(const char * fsname); + extern int xvsbprintf(char**, char* const, char const*, va_list) NTP_PRINTF(3, 0); extern int xsbprintf(char**, char* const, char const*, ...) NTP_PRINTF(3, 4); diff --git a/libntp/Makefile.am b/libntp/Makefile.am index ddd1e01dd..54dbdf1e8 100644 --- a/libntp/Makefile.am +++ b/libntp/Makefile.am @@ -87,6 +87,7 @@ libntp_a_SRCS = \ ntp_libopts.c \ ntp_lineedit.c \ ntp_random.c \ + ntp_realpath.c \ ntp_rfc2553.c \ ntp_worker.c \ numtoa.c \ diff --git a/libntp/ntp_realpath.c b/libntp/ntp_realpath.c new file mode 100644 index 000000000..716c7b517 --- /dev/null +++ b/libntp/ntp_realpath.c @@ -0,0 +1,273 @@ +/* + * ntp_realpath.c - get real path for a file + * Juergen Perlinger (perlinger@ntp.org) for the NTP project. + * Feb 11, 2014 for the NTP project. + * + * This is a butchered version of FreeBSD's implementation of 'realpath()', + * and the following copyright applies: + *---------------------------------------------------------------------- + */ + +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2003 Constantin S. Svintsoff + * + * 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. + * 3. The names of the authors may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include "ntp_stdlib.h" + +/* ================================================================== */ +#if !defined(SYS_WINNT) && !defined(HAVE_FUNC_POSIX_REALPATH) + +#include +#include +#include +#include +#include +#include + +/* The following definitions are to avoid system settings with excessive + * values for maxmimum path length and symlink chains/loops. Adjust with + * care, if that's ever needed: some buffers are on the stack! + */ +#define NTP_PATH_MAX 1024 +#define NTP_MAXSYMLINKS 16 + +/* + * Find the real name of path, by removing all ".", ".." and symlink + * components. Returns (resolved) on success, or (NULL) on failure, + * in which case the path which caused trouble is left in (resolved). + */ +static char * +realpath1(const char *path, char *resolved) +{ + struct stat sb; + char *p, *q; + size_t left_len, resolved_len, next_token_len; + unsigned symlinks; + ssize_t slen; + char left[NTP_PATH_MAX], next_token[NTP_PATH_MAX], symlink[NTP_PATH_MAX]; + + symlinks = 0; + if (path[0] == '/') { + resolved[0] = '/'; + resolved[1] = '\0'; + if (path[1] == '\0') + return (resolved); + resolved_len = 1; + left_len = strlcpy(left, path + 1, sizeof(left)); + } else { + if (getcwd(resolved, NTP_PATH_MAX) == NULL) { + resolved[0] = '.'; + resolved[1] = '\0'; + return (NULL); + } + resolved_len = strlen(resolved); + left_len = strlcpy(left, path, sizeof(left)); + } + if (left_len >= sizeof(left) || resolved_len >= NTP_PATH_MAX) { + errno = ENAMETOOLONG; + return (NULL); + } + + /* + * Iterate over path components in `left'. + */ + while (left_len != 0) { + /* + * Extract the next path component and adjust `left' + * and its length. + */ + p = strchr(left, '/'); + + next_token_len = p != NULL ? (size_t)(p - left) : left_len; + memcpy(next_token, left, next_token_len); + next_token[next_token_len] = '\0'; + + if (p != NULL) { + left_len -= next_token_len + 1; + memmove(left, p + 1, left_len + 1); + } else { + left[0] = '\0'; + left_len = 0; + } + + if (resolved[resolved_len - 1] != '/') { + if (resolved_len + 1 >= NTP_PATH_MAX) { + errno = ENAMETOOLONG; + return (NULL); + } + resolved[resolved_len++] = '/'; + resolved[resolved_len] = '\0'; + } + if (next_token[0] == '\0') { + /* Handle consequential slashes. */ + continue; + } else if (strcmp(next_token, ".") == 0) { + continue; + } else if (strcmp(next_token, "..") == 0) { + /* + * Strip the last path component except when we have + * single "/" + */ + if (resolved_len > 1) { + resolved[resolved_len - 1] = '\0'; + q = strrchr(resolved, '/') + 1; + *q = '\0'; + resolved_len = q - resolved; + } + continue; + } + + /* + * Append the next path component and lstat() it. + */ + resolved_len = strlcat(resolved, next_token, NTP_PATH_MAX); + if (resolved_len >= NTP_PATH_MAX) { + errno = ENAMETOOLONG; + return (NULL); + } + if (lstat(resolved, &sb) != 0) + return (NULL); + if (S_ISLNK(sb.st_mode)) { + if (symlinks++ > NTP_MAXSYMLINKS) { + errno = ELOOP; + return (NULL); + } + slen = readlink(resolved, symlink, sizeof(symlink)); + if (slen <= 0 || slen >= (ssize_t)sizeof(symlink)) { + if (slen < 0) + ; /* keep errno from readlink(2) call */ + else if (slen == 0) + errno = ENOENT; + else + errno = ENAMETOOLONG; + return (NULL); + } + symlink[slen] = '\0'; + if (symlink[0] == '/') { + resolved[1] = 0; + resolved_len = 1; + } else { + /* Strip the last path component. */ + q = strrchr(resolved, '/') + 1; + *q = '\0'; + resolved_len = q - resolved; + } + + /* + * If there are any path components left, then + * append them to symlink. The result is placed + * in `left'. + */ + if (p != NULL) { + if (symlink[slen - 1] != '/') { + if (slen + 1 >= (ssize_t)sizeof(symlink)) { + errno = ENAMETOOLONG; + return (NULL); + } + symlink[slen] = '/'; + symlink[slen + 1] = 0; + } + left_len = strlcat(symlink, left, + sizeof(symlink)); + if (left_len >= sizeof(symlink)) { + errno = ENAMETOOLONG; + return (NULL); + } + } + left_len = strlcpy(left, symlink, sizeof(left)); + } else if (!S_ISDIR(sb.st_mode) && p != NULL) { + errno = ENOTDIR; + return (NULL); + } + } + + /* + * Remove trailing slash except when the resolved pathname + * is a single "/". + */ + if (resolved_len > 1 && resolved[resolved_len - 1] == '/') + resolved[resolved_len - 1] = '\0'; + return (resolved); +} + +#endif /* !defined(SYS_WINNT) && !defined(HAVE_POSIX_REALPATH) */ +/* ================================================================== */ + +char * +ntp_realpath(const char * path) +{ + char *res, *m; + +# if defined(SYS_WINNT) + + (void)m; + if (path == NULL) { + errno = EINVAL; + return (NULL); + } + if (path[0] == '\0') { + errno = ENOENT; + return (NULL); + } + + return strdup(path); /* this clearly needs some work! */ + +# elif defined(HAVE_FUNC_POSIX_REALPATH) + + (void)m; + return realpath(path, NULL); + +# else + + if (path == NULL) { + errno = EINVAL; + return (NULL); + } + if (path[0] == '\0') { + errno = ENOENT; + return (NULL); + } + + m = malloc(NTP_PATH_MAX); + if (m == NULL) + return (NULL); + + res = realpath1(path, m); + if (res != NULL) + res = realloc(res, strlen(res) + 1); + if (res == NULL) + free(m); + +# endif + + return (res); +} diff --git a/ntpd/refclock_gpsdjson.c b/ntpd/refclock_gpsdjson.c index 0cb2fc408..fa8bc29d7 100644 --- a/ntpd/refclock_gpsdjson.c +++ b/ntpd/refclock_gpsdjson.c @@ -501,9 +501,10 @@ gpsd_start( { clockprocT * const pp = peer->procptr; gpsd_unitT * up; - gpsd_unitT ** uscan = &s_clock_units; + gpsd_unitT ** uscan = &s_clock_units; - struct stat sb; + struct stat sb; + char * devname = NULL; /* check if we can proceed at all or if init failed */ if ( ! gpsd_init_check()) @@ -531,17 +532,30 @@ gpsd_start( * practicable, we will have to read the symlink, if * any, so we can get the true device file.) */ - if (-1 == myasprintf(&up->device, "%s%u", + if (-1 == myasprintf(&devname, "%s%u", s_dev_stem, up->unit)) { msyslog(LOG_ERR, "%s: clock device name too long", up->logname); goto dev_fail; } - if (-1 == stat(up->device, &sb) || !S_ISCHR(sb.st_mode)) { + up->device = ntp_realpath(devname); + if (NULL == up->device) { + msyslog(LOG_ERR, "%s: '%s' has no absolute path", + up->logname, devname); + goto dev_fail; + } + if (-1 == lstat(up->device, &sb)) { + msyslog(LOG_ERR, "%s: '%s' not accessible", + up->logname, up->device); + goto dev_fail; + } + if (!S_ISCHR(sb.st_mode)) { msyslog(LOG_ERR, "%s: '%s' is not a character device", up->logname, up->device); goto dev_fail; } + free(devname); + devname = NULL; } else { /* All set up, just increment use count. */ ++up->refcount; @@ -585,7 +599,7 @@ gpsd_start( dev_fail: /* On failure, remove all UNIT ressources and declare defeat. */ - + free(devname); INSIST (up); if (!--up->refcount) { *uscan = up->next_unit; @@ -2205,6 +2219,7 @@ log_data( } } + #else NONEMPTY_TRANSLATION_UNIT #endif /* REFCLOCK && CLOCK_GPSDJSON */ diff --git a/ntpsnmpd/ntpsnmpd.c b/ntpsnmpd/ntpsnmpd.c index d96ad3af4..f2534ba55 100644 --- a/ntpsnmpd/ntpsnmpd.c +++ b/ntpsnmpd/ntpsnmpd.c @@ -67,7 +67,7 @@ main (int argc, char **argv) { snmp_enable_stderrlog(); /* Become Subagent */ - netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1); + netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1); /* go into background mode, if requested */ if (background && netsnmp_daemonize(1, !use_syslog)) diff --git a/sntp/m4/realpath.m4 b/sntp/m4/realpath.m4 new file mode 100644 index 000000000..4deaaed2b --- /dev/null +++ b/sntp/m4/realpath.m4 @@ -0,0 +1,49 @@ +# +# SYNOPSIS +# +# NTP_FUNC_REALPATH +# +# DESCRIPTION +# +# This macro defines HAVE_FUNC_REALPATH if we have a realpath() +# function that accepts NULL as the 2nd argument. +# +# LICENSE +# +# Copyright (c) 2020 Network Time Foundation +# +# Author: Harlan Stenn +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 1 + +AC_DEFUN([NTP_FUNC_REALPATH], [ + AC_MSG_CHECKING([for POSIX-2008 compliant realpath()]) + AC_REQUIRE([AC_PROG_CC_C99]) + + AC_LANG_PUSH([C]) + + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ + #include + #include + int main() { return (NULL == realpath(".", NULL)); } + ]])], + ans="yes", + ans="no", + ans="CROSS COMPILE!" + ) + AC_MSG_RESULT([$ans]) + case "$ans" in + yes) + AC_DEFINE([HAVE_FUNC_POSIX_REALPATH], [1], + [Define to 1 if we have realpath() that supports NULL as the 2nd argument]) + ;; + esac + + AC_LANG_POP([C]) + ]);