From: Juergen Perlinger Date: Sun, 5 Mar 2017 13:56:43 +0000 (+0100) Subject: [Sec 3384] NTP-01-009 NTP: Privileged execution of User Library code (Pentest report... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=742d374ee9d1e2084eda2ad5f0eaa72e02fd2cc1;p=thirdparty%2Fntp.git [Sec 3384] NTP-01-009 NTP: Privileged execution of User Library code (Pentest report 01.2017) - load PPS providers from registry - disable PPSAPI_HACK envvar - major cleanup of 'instsrv' bk: 58bc191bDZd_XnGYlOxrP6OMXLbkOw --- diff --git a/ChangeLog b/ChangeLog index aef57d7f7..b4827c2c4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +--- +* [Sec 3384] NTP-01-009: Privileged execution of User Library code + (Pentest report 01.2017) + --- (4.2.8p10) diff --git a/include/ntp_stdlib.h b/include/ntp_stdlib.h index 3213900ea..a4e857425 100644 --- a/include/ntp_stdlib.h +++ b/include/ntp_stdlib.h @@ -131,9 +131,13 @@ extern void * oreallocarrayxz (void *optr, size_t nmemb, size_t size, __FILE__, __LINE__) #define erealloc_zero(p, n, o) ereallocz((p), (n), (o), TRUE, \ __FILE__, __LINE__) -#define ereallocarray(p, n, s) oreallocarray((p), (n), (s), \ +#define ereallocarray(p, n, s) oreallocarrayxz((p), (n), (s), 0, \ __FILE__, __LINE__) -#define eallocarray(n, s) oreallocarray(NULL, (n), (s), \ +#define eallocarray(n, s) oreallocarrayxz(NULL, (n), (s), 0, \ + __FILE__, __LINE__) +#define ereallocarrayxz(p, n, s, x) oreallocarrayxz((p), (n), (s), (x), \ + __FILE__, __LINE__) +#define eallocarrayxz(n, s, x) oreallocarrayxz(NULL, (n), (s), (x), \ __FILE__, __LINE__) extern char * estrdup_impl(const char *, const char *, int); #define estrdup(s) estrdup_impl((s), __FILE__, __LINE__) diff --git a/include/ntpd.h b/include/ntpd.h index 0026a0fff..f944235cd 100644 --- a/include/ntpd.h +++ b/include/ntpd.h @@ -495,7 +495,7 @@ extern int sys_minsane; /* minimum candidates */ extern int sys_floor; /* cluster stratum floor */ extern int sys_ceiling; /* cluster stratum ceiling */ extern u_char sys_ttl[MAX_TTL]; /* ttl mapping vector */ -extern int sys_ttlmax; /* max ttl mapping vector index */ +extern u_int sys_ttlmax; /* max ttl mapping vector index */ /* * Statistics counters diff --git a/ntpd/ntp_config.c b/ntpd/ntp_config.c index 1355e7343..428ab9f46 100644 --- a/ntpd/ntp_config.c +++ b/ntpd/ntp_config.c @@ -3262,7 +3262,7 @@ config_ttl( "ttl: Number of TTL entries exceeds %zu. Ignoring TTL %d...", COUNTOF(sys_ttl), curr_ttl->i); } - sys_ttlmax = i - 1; + sys_ttlmax = (i) ? (i - 1) : 0; } #endif /* !SIM */ diff --git a/ntpd/ntp_proto.c b/ntpd/ntp_proto.c index 94411cf8f..c5d7cc6db 100644 --- a/ntpd/ntp_proto.c +++ b/ntpd/ntp_proto.c @@ -147,7 +147,7 @@ int sys_cohort = 0; /* cohort switch */ int sys_orphan = STRATUM_UNSPEC + 1; /* orphan stratum */ int sys_orphwait = NTP_ORPHWAIT; /* orphan wait */ int sys_beacon = BEACON; /* manycast beacon interval */ -int sys_ttlmax; /* max ttl mapping vector index */ +u_int sys_ttlmax; /* max ttl mapping vector index */ u_char sys_ttl[MAX_TTL]; /* ttl mapping vector */ /* @@ -401,7 +401,7 @@ transmit( peer_xmit(peer); } else if ( sys_survivors < sys_minclock || peer_associations < sys_maxclock) { - if (peer->ttl < (u_int32)sys_ttlmax) + if (peer->ttl < sys_ttlmax) peer->ttl++; peer_xmit(peer); } diff --git a/ports/winnt/include/ntp_iocpltypes.h b/ports/winnt/include/ntp_iocpltypes.h index b3e320b4d..502237892 100644 --- a/ports/winnt/include/ntp_iocpltypes.h +++ b/ports/winnt/include/ntp_iocpltypes.h @@ -87,6 +87,7 @@ typedef struct DeviceContext DevCtx_t; struct DeviceContext { volatile u_long ref_count; volatile u_long cov_count; + int pps_active; PPSData_t pps_data; PPSDataEx_t pps_buff[PPS_QUEUE_LEN]; }; diff --git a/ports/winnt/instsrv/instsrv.c b/ports/winnt/instsrv/instsrv.c index 2c93e257e..3487c218e 100644 --- a/ports/winnt/instsrv/instsrv.c +++ b/ports/winnt/instsrv/instsrv.c @@ -4,86 +4,153 @@ * */ #ifndef __RPCASYNC_H__ -#define __RPCASYNC_H__ /* Skip asynch rpc inclusion */ +#define __RPCASYNC_H__ /* Skip asynch rpc inclusion */ #endif #include #include #define PERR(api) printf("\n%s: Error %d from %s on line %d", \ - __FILE__, GetLastError(), api, __LINE__); + __FILE__, GetLastError(), api, __LINE__); #define MSG_FOR_ACCESS_DENIED "You aren't authorized to do this - please see your system Administrator" #define MSG_1_FOR_BAD_PATH "The fully qualified path name to the .exe must be given, and" #define MSG_2_FOR_BAD_PATH " the drive letter must be for a fixed disk (e.g., not a net drive)" +/* Building an initialised REG_MULTISZ needs a bit of care: The array must be big enough +** to hold the closing double-NUL and should have *exactly* the required size. Since the +** dependencies are fixed here, we take exactly the required 11 bytes: +*/ +static const char s_acSvcDeps[11] = "TcpIp\0Afd\0\0"; +/* likewise we do with the PPAS API list... */ +static const char s_acPpsDlls[30] = "loopback-ppsapi-provider.dll\0\0"; + SC_HANDLE schService; SC_HANDLE schSCManager; int ok2; -VOID DisplayHelp(VOID); +void DisplayHelp(void); /* --------------------------------------------------------------------------------------- */ - -int InstallService(LPCTSTR serviceName, LPCTSTR displayName, LPCTSTR serviceExe) +static const char * +getServicePath( + const char * const * const argv, + const int argc) /* MUST be >= 1 !! */ { - LPCTSTR lpszBinaryPathName = serviceExe; - TCHAR lpszRootPathName[] ="?:\\"; - - if ( (':' != *(lpszBinaryPathName+1)) || ('\\' != *(lpszBinaryPathName+2)) ) - { printf("\n%s",MSG_1_FOR_BAD_PATH); - printf("\n%s\n",MSG_2_FOR_BAD_PATH); - return 1; - } - - #define DRIVE_TYPE_INDETERMINATE 0 - #define ROOT_DIR_DOESNT_EXIST 1 - - *lpszRootPathName = *(lpszBinaryPathName+0) ; - - switch ( GetDriveType(lpszRootPathName) ) - { - case DRIVE_FIXED : - { // OK - break; + static const char * const s_chars_to_quote = " []()"; + size_t minsize = argc * 3; /* may need separator/NUL + quotes */ + int i; + const char *execPath = argv[0]; + char *cbuf, *cpos; + + /* if just the executable and no dangerous chars, return the exe path. */ + if (argc == 1 && !strpbrk(execPath, s_chars_to_quote)) + return execPath; + + /* calculate buffer size and get the buffer */ + for (i = 0; i < argc; ++i) + minsize += strlen(argv[i]); + + cpos = cbuf = malloc(minsize); + if (NULL == cbuf) { + printf("malloc() failed\n"); + exit(2); } - case ROOT_DIR_DOESNT_EXIST : - { printf("\n%s",MSG_1_FOR_BAD_PATH); - printf("\n the root directory where the .exe is specified to be must exist, and"); - printf("\n%s\n",MSG_2_FOR_BAD_PATH); - return 1; + + /* program name must be quoted in all cases */ + *cpos++ = '"'; + strcpy(cpos, execPath); + cpos += strlen(cpos); + *cpos++ = '"'; + + /* append additional args */ + for (i = 1; i < argc; ++i) { + *cpos++ = ' '; + if (strpbrk(argv[i], s_chars_to_quote)) { /* needs quotes? */ + *cpos++ = '"'; + strcpy(cpos, argv[i]); + cpos += strlen(cpos); + *cpos++ = '"'; + } else { + strcpy(cpos, argv[i]); + cpos += strlen(cpos); + } } - case DRIVE_TYPE_INDETERMINATE : - case DRIVE_REMOVABLE : - case DRIVE_REMOTE : - case DRIVE_CDROM : - case DRIVE_RAMDISK : - { printf("\n%s",MSG_1_FOR_BAD_PATH); - printf("\n%s\n",MSG_2_FOR_BAD_PATH); - return 1; + *cpos = '\0'; + return cbuf; +} + +/* --------------------------------------------------------------------------------------- */ +BOOL +validateExeName( + const char *exePath) +{ + char rootPath[] = "?:\\"; + + /* check for absolute path */ + if ((':' != exePath[1]) || ('\\' != exePath[2])) { + printf("\n%s", MSG_1_FOR_BAD_PATH); + printf("\n%s\n", MSG_2_FOR_BAD_PATH); + return 1; } - default : - { printf("\n%s",MSG_1_FOR_BAD_PATH); - printf("\n%s\n",MSG_2_FOR_BAD_PATH); - return 1; + +#define DRIVE_TYPE_INDETERMINATE 0 +#define ROOT_DIR_DOESNT_EXIST 1 + + /* check drive type -- must be local HDD! */ + rootPath[0] = exePath[0]; + switch (GetDriveTypeA(rootPath)) + { + case DRIVE_FIXED: + // OK + break; + + case ROOT_DIR_DOESNT_EXIST: + printf("\n%s", MSG_1_FOR_BAD_PATH); + printf("\n the root directory where the .exe is specified to be must exist, and"); + printf("\n%s\n", MSG_2_FOR_BAD_PATH); + return 1; + + case DRIVE_TYPE_INDETERMINATE: + case DRIVE_REMOVABLE: + case DRIVE_REMOTE: + case DRIVE_CDROM: + case DRIVE_RAMDISK: + printf("\n%s", MSG_1_FOR_BAD_PATH); + printf("\n%s\n", MSG_2_FOR_BAD_PATH); + return 1; + + default: + printf("\n%s", MSG_1_FOR_BAD_PATH); + printf("\n%s\n", MSG_2_FOR_BAD_PATH); + return 1; } - } - - if (INVALID_HANDLE_VALUE == CreateFile(lpszBinaryPathName, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL)) - { - printf("\n%s",MSG_1_FOR_BAD_PATH); - printf("\n the file must exist, and"); - printf("\n%s\n",MSG_2_FOR_BAD_PATH); - return 1; - } + + /* check if file exists. We just drop the handle. This is a one-shot program! */ + if (INVALID_HANDLE_VALUE == CreateFileA( + exePath, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) + { + printf("\n%s", MSG_1_FOR_BAD_PATH); + printf("\n the file must exist, and"); + printf("\n%s\n", MSG_2_FOR_BAD_PATH); + return 1; + } + + return 0; +} +/* --------------------------------------------------------------------------------------- */ - schService = CreateService( +int +InstallService( + LPCSTR serviceName, + LPCSTR displayName, + LPCSTR serviceExe , + LPCSTR servDepends) +{ + + /* create the service now. */ + schService = CreateServiceA( schSCManager, // SCManager database serviceName, // name of service displayName, // name to display @@ -91,368 +158,332 @@ int InstallService(LPCTSTR serviceName, LPCTSTR displayName, LPCTSTR serviceExe) SERVICE_WIN32_OWN_PROCESS, // service type SERVICE_AUTO_START, // start type SERVICE_ERROR_NORMAL, // error control type - lpszBinaryPathName, // service's binary + serviceExe, // service's binary NULL, // no load ordering group NULL, // no tag identifier - NULL, // no dependencies + servDepends, // possible dependencies NULL, // Local System account NULL); // null password - if (NULL == schService) - { switch (GetLastError()) - { - case ERROR_ACCESS_DENIED : - { printf("\n%s",MSG_FOR_ACCESS_DENIED); - break; - } - case ERROR_SERVICE_EXISTS : - { printf("\nThe %s service is already installed",serviceName); - printf("\nRemove it first if you need to re-install a new version\n"); - break; - } - default : - { PERR("CreateService"); - } + if (NULL == schService) { + switch (GetLastError()) + { + case ERROR_ACCESS_DENIED : + printf("\n%s",MSG_FOR_ACCESS_DENIED); + break; + + case ERROR_SERVICE_EXISTS : + printf("\nThe %s service is already installed",serviceName); + printf("\nRemove it first if you need to re-install a new version\n"); + break; + + default : + PERR("CreateService"); + break; + } + return 1; } - return 1; - } - else - CloseServiceHandle(schService); - return 0; + CloseServiceHandle(schService); + return 0; } /* --------------------------------------------------------------------------------------- */ -int RemoveService(LPCTSTR serviceName) +int +RemoveService( + LPCSTR serviceName) { - { - #define SZ_ENUM_BUF 4096 - ENUM_SERVICE_STATUS essServiceStatus[SZ_ENUM_BUF]; - DWORD dwBufSize = sizeof(essServiceStatus); - DWORD dwBytesNeeded = 0; - DWORD dwServicesReturned = 0; - DWORD dwResumeHandle = 0; - DWORD dwI = 0; - BOOLEAN bFound = FALSE; - - if (!EnumServicesStatus(schSCManager, - SERVICE_WIN32, - SERVICE_ACTIVE, - (LPENUM_SERVICE_STATUS)&essServiceStatus, - dwBufSize, - &dwBytesNeeded, - &dwServicesReturned, - &dwResumeHandle)) - { switch (GetLastError()) - { - case ERROR_ACCESS_DENIED : - { printf("\n%s",MSG_FOR_ACCESS_DENIED); - break; + { +# define SZ_ENUM_BUF 4096 + ENUM_SERVICE_STATUS essServiceStatus[SZ_ENUM_BUF]; + DWORD dwBufSize = sizeof(essServiceStatus); + DWORD dwBytesNeeded = 0; + DWORD dwServicesReturned = 0; + DWORD dwResumeHandle = 0; + DWORD dwI = 0; + BOOLEAN bFound = FALSE; + + if (!EnumServicesStatusA(schSCManager, SERVICE_WIN32, SERVICE_ACTIVE, + (LPENUM_SERVICE_STATUS)&essServiceStatus, + dwBufSize, &dwBytesNeeded, &dwServicesReturned, + &dwResumeHandle)) + { + switch (GetLastError()) + { + case ERROR_ACCESS_DENIED : + printf("\n%s", MSG_FOR_ACCESS_DENIED); + break; + + default : + PERR("EnumServicesStatus"); + break; + } + return 1; } - default : - { PERR("EnumServicesStatus"); + + for (dwI = 0; dwI < dwServicesReturned; ++dwI) { + if(0 == _stricmp(essServiceStatus[dwI].lpServiceName, serviceName)) { + bFound = TRUE; + break; + } + } + + if (bFound) { + printf("\nThe %s service cannot be removed until it has been stopped.", serviceName); + printf("\nTo stop the %s service, use the Stop button in the Control" , serviceName); + printf("\n Panel Services applet\n"); + return 1; } - } - return 1; } + + schService = OpenServiceA(schSCManager, serviceName, SERVICE_ALL_ACCESS); + if (NULL == schService) { + switch (GetLastError()) + { + case ERROR_ACCESS_DENIED : + printf("\n%s", MSG_FOR_ACCESS_DENIED); + break; - for (dwI=0; dwI= sizeof(regarray)) - { - fputs("addSourceToRegistry: buffer overrun(app name)", stderr); - return 1; - } - /* Create a new key for our application */ - bSuccess = RegCreateKeyA(HKEY_LOCAL_MACHINE, regarray, &hk); - if(bSuccess != ERROR_SUCCESS) - { - PERR("RegCreateKey"); - return 1; - } + HKEY hk; /* registry key handle */ + DWORD dwData; + BOOL bSuccess; + char regarray[200]; + int rc; + + /* When an application uses the RegisterEventSource or OpenEventLog function to get a + handle of an event log, the event loggging service searches for the specified + source name in the registry. You can add a new source name to the registry by + opening a new registry subkey under the Application key and adding registry values + to the new subkey. + */ + + rc = snprintf(regarray, sizeof(regarray), "%s%s", + "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\", + pszAppname); + if (rc < 0 || rc >= sizeof(regarray)) { + fputs("addSourceToRegistry: buffer overrun(app name)", stderr); + return 1; + } + /* Create a new key for our application */ + bSuccess = RegCreateKeyA(HKEY_LOCAL_MACHINE, regarray, &hk); + if(bSuccess != ERROR_SUCCESS) { + PERR("RegCreateKey"); + return 1; + } - /* Add the Event-ID message-file name to the subkey. */ - bSuccess = RegSetValueExA(hk, /* subkey handle */ - "EventMessageFile", /* value name */ - 0, /* must be zero */ - REG_EXPAND_SZ, /* value type */ - (LPBYTE)pszMsgDLL, /* address of value data */ - (DWORD)(strlen(pszMsgDLL) + 1)); /* length of value data */ - if(bSuccess != ERROR_SUCCESS) - { - PERR("RegSetValueEx"); - return 1; - } + /* Add the Event-ID message-file name to the subkey. */ + bSuccess = RegSetValueExA(hk, /* subkey handle */ + "EventMessageFile", /* value name */ + 0, /* must be zero */ + REG_EXPAND_SZ, /* value type */ + (LPBYTE)pszMsgDLL, /* address of value data */ + (DWORD)(strlen(pszMsgDLL) + 1)); /* length of value data */ + if(bSuccess != ERROR_SUCCESS) { + PERR("RegSetValueEx"); + return 1; + } - /* Set the supported types flags and addit to the subkey. */ - dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | - EVENTLOG_INFORMATION_TYPE; - bSuccess = RegSetValueExA(hk, /* subkey handle */ - "TypesSupported", /* value name */ - 0, /* must be zero */ - REG_DWORD, /* value type */ - (LPBYTE) &dwData, /* address of value data */ - sizeof(DWORD)); /* length of value data */ - if(bSuccess != ERROR_SUCCESS) - { - PERR("RegSetValueEx"); - return 1; - } - RegCloseKey(hk); - return 0; + /* Set the supported types flags and addit to the subkey. */ + dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; + bSuccess = RegSetValueExA(hk, /* subkey handle */ + "TypesSupported", /* value name */ + 0, /* must be zero */ + REG_DWORD, /* value type */ + (LPBYTE) &dwData, /* address of value data */ + sizeof(DWORD)); /* length of value data */ + if(bSuccess != ERROR_SUCCESS) { + PERR("RegSetValueEx"); + return 1; + } + + RegCloseKey(hk); + return 0; } /* --------------------------------------------------------------------------------------- */ -int addKeysToRegistry() +int +addKeysToRegistry(void) { - HKEY hk; /* registry key handle */ - BOOL bSuccess; - - /* Building an initialised REG_MULTISZ needs a bit of care: The array must be big enough - ** to hold the closing double-NUL and should have *exactly* the required size. Since the - ** dependencies are fixed here, we take exactly the required 11 bytes: - */ - static const char s_acSvcDeps[11] = "TcpIp\0Afd\0\0"; - /* now add the depends on service key */ + HKEY hk; /* registry key handle */ + BOOL bSuccess; + + /* now add the depends on service key */ - /* Create a new key for our application */ - bSuccess = RegCreateKeyA(HKEY_LOCAL_MACHINE, - "SYSTEM\\CurrentControlSet\\Services\\NTP", &hk); - if(bSuccess != ERROR_SUCCESS) { - PERR("RegCreateKey"); - return 1; - } - - bSuccess = RegSetValueExA(hk, /* subkey handle */ - "DependOnService", /* value name */ - 0, /* must be zero */ - REG_MULTI_SZ, /* value type */ - (LPBYTE)s_acSvcDeps, /* address of value data */ - (DWORD)sizeof(s_acSvcDeps)); /* length of value data */ - if(bSuccess != ERROR_SUCCESS) - { - PERR("RegSetValueEx"); - return 1; - } + /* Create a new key for our application */ + bSuccess = RegCreateKeyA(HKEY_LOCAL_MACHINE, + "SYSTEM\\CurrentControlSet\\Services\\NTP", &hk); + if(bSuccess != ERROR_SUCCESS) { + PERR("RegCreateKey"); + return 1; + } + + bSuccess = RegSetValueExA(hk, /* subkey handle */ + "PPSProviders", /* value name */ + 0, /* must be zero */ + REG_MULTI_SZ, /* value type */ + (LPBYTE)s_acPpsDlls, /* address of value data */ + (DWORD)sizeof(s_acPpsDlls)); /* length of value data */ + if (bSuccess != ERROR_SUCCESS) { + PERR("RegSetValueEx"); + return 1; + } - RegCloseKey(hk); - return 0; + RegCloseKey(hk); + return 0; } /* --------------------------------------------------------------------------------------- */ int main(int argc, char *argv[]) { - #define SZ_NAME_BUF 270 // 256 is max, add a little - UCHAR ucNameBuf[SZ_NAME_BUF] = "NTP"; - LPTSTR lpszServName = (LPTSTR)&ucNameBuf; - - UCHAR ucDNameBuf[SZ_NAME_BUF] = "Network Time Protocol"; - LPTSTR lpszDispName = (LPTSTR)&ucDNameBuf; + static const char * const szServiceName = "NTP"; + static const char * const szDisplayName = "Network Time Protocol"; + BOOL bRemovingService = FALSE; + char *p; + int ok = 0; + + // check if Win32s, if so, display notice and terminate + if (GetVersion() & 0x80000000) { + MessageBoxA(NULL, + "This application cannot run on Windows 3.1.\n" + "This application will now terminate.", + "NAMED", + MB_OK | MB_ICONSTOP | MB_SETFOREGROUND ); + return 1; + } - UCHAR ucExeNBuf[SZ_NAME_BUF] = ""; - LPTSTR lpszExeName = (LPTSTR)&ucExeNBuf; + if (argc >= 2) + bRemovingService = (!stricmp(argv[1], "remove")); - BOOL bRemovingService = FALSE; - char *p; + if ((bRemovingService && argc != 2) || (!bRemovingService && argc < 2)) { + DisplayHelp(); + return 1; + } - int ok = 0; - - // check if Win32s, if so, display notice and terminate - if( GetVersion() & 0x80000000 ) - { - MessageBox( NULL, - "This application cannot run on Windows 3.1.\n" - "This application will now terminate.", - "NAMED", - MB_OK | MB_ICONSTOP | MB_SETFOREGROUND ); - return( 1 ); - } - if (argc == 2) - bRemovingService = (!stricmp(argv[1], "remove")); - - if(!bRemovingService) - { + if(!bRemovingService) { + p = argv[1]; + if (('/' == *p) || ('-' == *p) || validateExeName(p)) { + DisplayHelp(); + return 1; + } + } - - if (argc != 2) - { - DisplayHelp(); - return(1); - } - - p=argv[1]; - if ( ('/' == *p) - || ('-' == *p) ) - { - DisplayHelp(); - return(1); - } - - - } - if (strlen(argv[1]) > 256) - { - printf("\nThe service name cannot be longer than 256 characters\n"); - return(1); + if (strlen(argv[1]) > 256) { + printf("\nThe service name cannot be longer than 256 characters\n"); + return 1; } + schSCManager = OpenSCManagerA( + NULL, // machine (NULL == local) + NULL, // database (NULL == default) + SC_MANAGER_ALL_ACCESS); // access required + if (NULL == schSCManager) { + switch (GetLastError()) + { + case ERROR_ACCESS_DENIED : + printf("\n%s", MSG_FOR_ACCESS_DENIED); + break; - bRemovingService = (!stricmp(argv[1], "remove")); - schSCManager = OpenSCManager( - NULL, // machine (NULL == local) - NULL, // database (NULL == default) - SC_MANAGER_ALL_ACCESS); // access required - - if (NULL == schSCManager) - { switch (GetLastError()) - { - case ERROR_ACCESS_DENIED : - { printf("\n%s",MSG_FOR_ACCESS_DENIED); - break; - } - default : - { PERR("OpenSCManager"); - } + default : + PERR("OpenSCManager"); + break; + } + return 0; } - return (0); - } - if (bRemovingService) - { - ok = RemoveService(lpszServName); - } - else - { - /* get the exe name */ - strcpy(lpszExeName,argv[1]); - ok = InstallService(lpszServName, lpszDispName, lpszExeName); - } - - CloseServiceHandle(schSCManager); - - if (!bRemovingService) - { - if (ok == 0) - { /* Set the Event-ID message-file name. */ - ok = addSourceToRegistry("NTP", lpszExeName); - if (ok == 0) - ok = addKeysToRegistry(); - else return ok; - - if (ok == 0) - { - printf("\nThe \"Network Time Protocol\" service was successfully created.\n"); - printf("\nDon't forget!!! You must now go to the Control Panel and"); - printf("\n use the Services applet to change the account name and"); - printf("\n password that the NTP Service will use when"); - printf("\n it starts."); - printf("\nTo do this: use the Startup button in the Services applet,"); - printf("\n and (for example) specify the desired account and"); - printf("\n correct password."); - printf("\nAlso, use the Services applet to ensure this newly installed"); - printf("\n service starts automatically on bootup.\n"); - return 0; + if (bRemovingService) + ok = RemoveService(szServiceName); + else + ok = InstallService(szServiceName, szDisplayName, + getServicePath(argv+1, argc-1), s_acSvcDeps); + + CloseServiceHandle(schSCManager); + + if (!bRemovingService) { + if (ok == 0) + ok = addSourceToRegistry("NTP", argv[1]);/* Set the Event-ID message-file name. */ + if (ok == 0) + ok = addKeysToRegistry(); /* add other stuff */ + + if (ok == 0) + { + static const char s_msg[] = + "\nThe \"Network Time Protocol\" service was successfully created.\n" + "\nDon't forget!!! You must now go to the Control Panel and" + "\n use the Services applet to change the account name and" + "\n password that the NTP Service will use when" + "\n it starts." + "\nTo do this: use the Startup button in the Services applet," + "\n and (for example) specify the desired account and" + "\n correct password." + "\nAlso, use the Services applet to ensure this newly installed" + "\n service starts automatically on bootup.\n"; + fputs(s_msg, stdout); + } } - } - else return ok; - } - return 0; + return ok; } /* --------------------------------------------------------------------------------------- */ -VOID DisplayHelp(VOID) +void +DisplayHelp(void) { - printf("Installs or removes the NTP service.\n"); - printf("To install the NTP service,\n"); - printf("type INSTSRV \n"); - printf("Where:\n"); - printf(" path Absolute path to the NTP service, name.exe. You must\n"); - printf(" use a fully qualified path and the drive letter must be for a\n"); - printf(" fixed, local drive.\n\n"); - printf("For example, INSTSRV i:\\winnt\\system32\\ntpd.exe\n"); - printf("To remove the NTP service,\n"); - printf("type INSTSRV remove \n"); - + static const char s_hlpmsg[] = + "Installs or removes the NTP service.\n" + "To install the NTP service,\n" + "type INSTSRV [args]\n" + "Where:\n" + " path Absolute path to the NTP service. (ntpd.exe) You must\n" + " use a fully qualified path and the drive letter must be for a\n" + " fixed, local drive.\n\n" + " args Additional command line arguments for the service\n" + "For example, INSTSRV i:\\winnt\\system32\\ntpd.exe\n" + "To remove the NTP service,\n" + "type INSTSRV remove \n"; + fputs(s_hlpmsg, stdout); } /* EOF */ diff --git a/ports/winnt/ntpd/nt_ppsimpl.c b/ports/winnt/ntpd/nt_ppsimpl.c index d3b3c5daa..2a7c0d88f 100644 --- a/ports/winnt/ntpd/nt_ppsimpl.c +++ b/ports/winnt/ntpd/nt_ppsimpl.c @@ -21,10 +21,135 @@ #include "timepps.h" #include "ntp_stdlib.h" #include "lib_strbuf.h" +#include "ntp_iocpltypes.h" +#include "ntp_iocplmem.h" +struct InstListNode { + struct InstListNode * next; + pps_handle_t ppsu; + DevCtx_t * devu; +}; +typedef struct ProvListNode ProvListNode_t; + +static struct InstListNode * g_active_units; static ppsapi_provider * g_provider_list; static ppsapi_provider * g_curr_provider; +static void +ppsu_register( + pps_handle_t ppsu, + DevCtx_t * devu) +{ + struct InstListNode * node; + + if (devu && (node = IOCPLPoolAlloc(sizeof(*node), "PPS registration"))) { + node->next = g_active_units; + node->ppsu = ppsu; + node->devu = DevCtxAttach(devu); + devu->pps_active = TRUE; + g_active_units = node; + } +} + +static void +ppsu_remove( + pps_handle_t ppsu) +{ + struct InstListNode ** link; + struct InstListNode * node; + + link = &g_active_units; + while (NULL != (node = *link)) { + if (node->ppsu == ppsu) { + node->devu->pps_active = FALSE; + DevCtxDetach(node->devu); + *link = node->next; + IOCPLPoolFree(node, "PPS registration"); + } else { + link = &node->next; + } + } +} + +static HKEY +myRegOpenKey( + const char * szSubKey) +{ + static const char * const s_RegKey = + "SYSTEM\\CurrentControlSet\\services\\NTP"; + + HKEY hkey1 = NULL; + HKEY hkey2 = NULL; + DWORD rc; + + rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, s_RegKey, 0, KEY_READ, &hkey1); + if (ERROR_SUCCESS != rc) + return NULL; + if (!(szSubKey && *szSubKey)) + return hkey1; + + rc = RegOpenKeyExA(hkey1, szSubKey, 0, KEY_READ, &hkey2); + RegCloseKey(hkey1); + if (ERROR_SUCCESS != rc) + return NULL; + return hkey2; +} + +static char* +myRegReadMultiString( + HKEY hKey , + const char *szValue, + DWORD *pSize ) +{ + char * endp; + char * retv = NULL; + DWORD rSize = 0, rType = REG_NONE, rc; + + /* take two turns: one to get the size, another one to malloc & read */ + do { + if (rType != REG_NONE) { + retv = malloc(rSize += 2); + if (NULL == retv) + goto fail; + } + rc = RegQueryValueExA(hKey, szValue, NULL, &rType, retv, &rSize); + if (ERROR_SUCCESS != rc || (REG_SZ != rType && REG_MULTI_SZ != rType)) + goto fail; + } while (NULL == retv); + + /* trim trailing NULs and ensure two of them */ + endp = retv + rSize; + while (endp != retv && endp[-1]) + --endp; + if (endp != retv) { + endp[0] = endp[1] = '\0'; + if (NULL != pSize) + *pSize = (DWORD)(endp - retv); + return retv; + } +fail: + free(retv); + if (NULL != pSize) + *pSize = 0; + return NULL; +} + +static DWORD +myRegReadDWord( + HKEY hKey , + const char *szValue, + DWORD Default) +{ + DWORD rc, rSize, rType, rValue; + + rSize = sizeof(rValue); + rc = RegQueryValueExA(hKey, szValue, NULL, &rType, (PBYTE)&rValue, &rSize); + if (rc != ERROR_SUCCESS || rSize != sizeof(rValue) || rType != REG_DWORD) + rValue = Default; + return rValue; +} + + static pps_handle_t internal_create_pps_handle( void * prov_context @@ -169,6 +294,82 @@ fail: return NULL; } +static char * +get_provider_list(void) +{ + static char * s_Value = NULL; + + HKEY hKey; + DWORD rSize; + char *cp, *op; + + if (s_Value != NULL) + return (*s_Value) ? s_Value : NULL; + + /* + ** try registry first + */ + hKey = myRegOpenKey(NULL); + if (NULL == hKey) + goto regfail; + + s_Value = myRegReadMultiString(hKey, "PPSProviders", &rSize); + if (NULL == s_Value) + goto regfail; + + /* make sure we have backslashes in the path */ + for (cp = s_Value; rSize; --rSize, ++cp) + if (*cp == '/') + *cp = '\\'; +regfail: + if (NULL != hKey) + RegCloseKey(hKey); + + if (s_Value && *s_Value) + return s_Value; + + /* + ** try environment next. + */ + free(s_Value); + s_Value = NULL; + + /* try to get env var */ + cp = getenv("PPSAPI_DLLS"); + if (!(cp && *cp)) + goto envfail; + + /* get size & allocate buffer */ + rSize = strlen(cp); + s_Value = malloc(rSize + 2); + if (s_Value == NULL) + goto envfail; + + /* copy string value and convert to MULTI_SZ. + * Converts sequences of ';' to a single NUL byte, and rplaces + * slashes by backslashes on the fly. + */ + for (op = s_Value; *cp; ++cp) { + if (*cp == '/') { + *op++ = '\\'; + } else if (*cp == ';') { + if (op != s_Value && op[-1]) + *op++ = '\0'; + } else { + *op++ = *cp; + } + } + cp[0] = '\0'; + cp[1] = '\0'; + return s_Value; + +envfail: + free(s_Value); + s_Value = calloc(2, 1); + return s_Value; +} + + /* Iteration helper for the provider list. Naked names (without *any* * path) will be prepended with path to the executable running this * code. While this was not necessary until Win7, newer versions of @@ -177,38 +378,48 @@ fail: */ static char* provlist_next_item( - const char ** const ppath + const char ** iter ) { static char * s_modpath /* = NULL */; static char s_nullstr[1] /* = { '\0' } */; - const char * phead; - const char * ptail; - char * retv; - char * endp; + const char *phead, *phold; + char *retv, *endp; int/*BOOL*/ nodir; - int ch; - DWORD slen; + DWORD slen, mlen; - /* check if we're already done */ - if (NULL == (phead = *ppath)) + /* get next item -- might be start of a new round or the end */ +again: + if (*iter == NULL) + *iter = phead = get_provider_list(); + else + *iter = phead = *iter + strlen(*iter) + 1; + if (!(phead && *phead)) { + *iter = NULL; return NULL; - /* Skip any leading ';' -- should not happen, but this is - * user-provided input after all... + } + + /* Inspect the next section of input string. It must be + * either an absolute path or just a name. */ - while (*phead == ';') - ++phead; - /* check if we're already done */ - if (!*phead) { - *ppath = NULL; - return NULL; + if (isalpha((u_char)phead[0]) && phead[1] == ':' && phead[2] == '\\') { + nodir = FALSE; + } else { + nodir = TRUE; + phold = phead; + while (NULL != (endp = strpbrk(phold, "\\:"))) + phold = endp + 1; + if (phead != phold) { + msyslog(LOG_WARNING, + "pps api: path component(s) of '%s' ignored, use '%s'", + phead, phold); + phead = phold; + } } - /* Inspect the next section of input string. */ - nodir = TRUE; - for (ptail = phead; (ch = *ptail) && ch != ';'; ++ptail) - if (ch == '\\' || ch == '/') - nodir = FALSE; + if (!*phead || strchr("\\.:", (u_char)phead[strlen(phead) - 1])) + goto again; /* empty or looks like a directory! */ + /* Make sure we have a proper module path when we need one. */ if (nodir && NULL == s_modpath) { s_modpath = get_module_path(); @@ -217,27 +428,23 @@ provlist_next_item( } /* Prepare buffer for copy of file name. */ - slen = (DWORD)(ptail - phead); /* 4GB string should be enough... */ + slen = (DWORD)strlen(phead); /* 4GB string should be enough... */ if (nodir && NULL != s_modpath) { /* Prepend full path to executable to the name. */ - endp = retv = malloc(strlen(s_modpath) + slen + 1); + mlen = (DWORD)strlen(s_modpath); + endp = retv = malloc(mlen + slen + 1); if (NULL != endp) { - strcpy(endp, s_modpath); - endp += strlen(endp); + memcpy(endp, s_modpath, mlen); + endp += mlen; } } else { endp = retv = malloc(slen + 1u); } /* Copy with conversion from '/' to '\\' */ if (NULL != endp) { - while (phead != ptail) { - ch = (u_char)*phead++; - *endp++ = (ch != '/') ? ch : '\\'; - } - *endp = '\0'; + memcpy(endp, phead, slen); + endp[slen] = '\0'; } - /*Update scan pointer & return result. */ - *ppath = (*ptail) ? (ptail + 1) : NULL; return retv; } @@ -327,6 +534,49 @@ load_pps_provider( } +static ppsapi_provider* +get_first_provider(void) +{ + const char * itpos; + char * dll; + ppsapi_provider *prov, *hold; + int err; + + /* check if we have done our work so far... */ + if (g_provider_list == INVALID_HANDLE_VALUE) + return NULL; + if (g_provider_list != NULL) + return g_provider_list; + + itpos = NULL; + while (NULL != (dll = provlist_next_item(&itpos))) { + err = load_pps_provider(dll); + if (err) + msyslog(LOG_ERR, "time_pps_create: load failed (%s) --> %d / %s", + dll, err, strerror(err)); + else + msyslog(LOG_INFO, "time_pps_create: loaded '%s'", + dll); + free(dll); + } + + /* reverse the list, possibly mark as EMPTY */ + prov = g_provider_list; + if (NULL != prov) { + g_provider_list = NULL; + do { + hold = prov; + prov = hold->next; + hold->next = g_provider_list; + g_provider_list = hold; + } while (prov); + prov = g_provider_list; + } else { + g_provider_list = INVALID_HANDLE_VALUE; + } + return prov; +} + int time_pps_create( int filedes,/* device file descriptor */ @@ -334,8 +584,6 @@ time_pps_create( ) { HANDLE winhandle; - const char * dlls; - char * dll; ppsapi_provider * prov; pps_handle_t ppshandle; int err; @@ -347,37 +595,6 @@ time_pps_create( if (INVALID_HANDLE_VALUE == winhandle) return set_pps_errno(EBADF); - /* For initial testing the list of PPSAPI backend providers is - * provided by the environment variable PPSAPI_DLLS, separated by - * semicolons such as - * PPSAPI_DLLS=c:\ntp\serial_ppsapi.dll;..\parport_ppsapi.dll - * There are a million better ways, such as a well-known registry - * key under which a value is created for each provider DLL - * installed, or even a platform-specific ntp.conf directive or - * command-line switch. - * - * [Bug 3139] Since nothing is more durable than a provisional - * implementation, we're still stuck with that... - */ - dlls = getenv("PPSAPI_DLLS"); - if (NULL != dlls && NULL == g_provider_list) { - msyslog(LOG_INFO, "time_pps_create: load list '%s'", - dlls); - } else - dlls = NULL; - - while (NULL != (dll = provlist_next_item(&dlls))) { - err = load_pps_provider(dll); - if (err) { - msyslog(LOG_ERR, "time_pps_create: load failed (%s) --> %d / %s", - dll, err, strerror(err)); - } else { - msyslog(LOG_INFO, "time_pps_create: loaded '%s'", - dll); - } - free(dll); - } - /* Hand off to each provider in turn until one returns a PPS * handle or they've all declined. * @@ -388,23 +605,25 @@ time_pps_create( * this provides slightly more information. */ err = ENOEXEC; - if (NULL == g_provider_list) { + prov = get_first_provider(); + if (NULL == prov) { msyslog(LOG_ERR, "time_pps_create: %s", "no providers available"); return set_pps_errno(err); - } - for (prov = g_provider_list; NULL != prov; prov = prov->next) { + } else do { ppshandle = 0; g_curr_provider = prov; err = (*prov->ptime_pps_create)(winhandle, &ppshandle); g_curr_provider = NULL; if (!err && ppshandle) { *phandle = ppshandle; + ppsu_register(ppshandle, serial_devctx(winhandle)); return 0; } msyslog(LOG_INFO, "time_pps_create: provider '%s' failed: %d / %s", prov->short_name, err, strerror(err)); - } + } while (NULL != (prov = prov->next)); + msyslog(LOG_ERR, "time_pps_create: %s", "all providers failed"); return set_pps_errno(err); @@ -423,6 +642,7 @@ time_pps_destroy( if (NULL == punit) return set_pps_errno(EBADF); /* Call provider. Note the handle is gone anyway... */ + ppsu_remove(handle); err = (*punit->provider->ptime_pps_destroy)(punit, punit->context); free(punit); if (err) diff --git a/ports/winnt/ntpd/ntp_iocompletionport.c b/ports/winnt/ntpd/ntp_iocompletionport.c index a52e7f860..333bd2fb4 100644 --- a/ports/winnt/ntpd/ntp_iocompletionport.c +++ b/ports/winnt/ntpd/ntp_iocompletionport.c @@ -119,7 +119,6 @@ static void free_io_completion_port_mem(void); static HANDLE hndIOCPLPort; static HANDLE hMainThread; static HANDLE hMainRpcDone; -static BOOL DoPPShack; DWORD ActiveWaitHandles; HANDLE WaitHandles[4]; @@ -227,26 +226,11 @@ void init_io_completion_port(void) { OSVERSIONINFO vi; - const char * envp; # ifdef DEBUG atexit(&free_io_completion_port_mem); # endif - /* TODO: this should not be done via environment; - * It would be much better to have this as proper config option. - * (The same is true for the PPS API DLL list...) - */ - if (NULL != (envp = getenv("PPSAPI_HACK"))) - /* check for [Tt]{rue}, [Yy]{es}, or '1' as first char*/ - DoPPShack = !!strchr("yYtT1", (u_char)*envp); - else if (NULL != (envp = getenv("PPSAPI_DLLS"))) - /* any non-empty list disables PPS hack */ - DoPPShack = !*envp; - else - /* otherwise use the PPS hack */ - DoPPShack = TRUE; - memset(&vi, 0, sizeof(vi)); vi.dwOSVersionInfoSize = sizeof(vi); @@ -819,11 +803,15 @@ OnSerialWaitComplete( * more like the behaviour under a UN*Xish OS. On the other hand, it * will give a nasty surprise for people which have until now happily * taken the pps hack for granted, and after the first complaint, I have - * decided to keep the old implementation unconditionally. So here it is: + * decided to keep the old implementation. + * + * perlinger@ntp.org, 2017-03-04 + * If the loopback PPS API provider is active on this channel, the + * PPS hack will be *disabled*. * * backward compat: 'usermode-pps-hack' */ - if ((MS_RLSD_ON & modem_status) && DoPPShack) { + if ((MS_RLSD_ON & modem_status) && !(dev && dev->pps_active)) { lpo->aux.DCDSTime = lpo->aux.RecvTime; lpo->aux.flTsDCDS = 1; DPRINTF(2, ("upps-hack: fd %d DCD PPS Rise at %s\n", @@ -1301,7 +1289,7 @@ ntp_pps_attach_device( DevCtx_t * dev = NULL; dev = DevCtxAttach(serial_devctx(hndIo)); - if ( NULL == dev) + if (NULL == dev) SetLastError(ERROR_INVALID_HANDLE); return dev; }