On Sun, Jan 24, 2016 at 05:11:34PM -0500, Josh Grosse wrote: > Additional pledge() calls added for Self-Extracting archives. These > use a separate executable, which has now been pledged. > > Mitigation for CVE-2015-1038, which upstream has not implemented. > This is Debian's proposed solution. > > Debug tokens added to pledge() patches. Today, brynet@ proposed > several secondary pledges to reduce the pledge to stdio rpath, > and in some cases possibly stdio alone. These are still in > development. Testing is complete on the reduced pledges.
The test, hash, list, and extract to stdout (x -so) functions are pledged "stdio rpath", while the info and benchmark functions are now pledged "stdio" only. These were devised by brynet@, and I'm very grateful for the time Bryan devoted to analyzing the appropriate pledge points. Revised patches below. -Josh- Index: Makefile =================================================================== RCS file: /systems/cvs/ports/archivers/p7zip/Makefile,v retrieving revision 1.28 diff -u -p -r1.28 Makefile --- Makefile 22 Jan 2016 13:39:08 -0000 1.28 +++ Makefile 25 Jan 2016 00:45:03 -0000 @@ -6,7 +6,7 @@ COMMENT-main= file archiver with high co COMMENT-rar= rar modules for p7zip V= 15.09 -REVISION= 1 +REVISION= 2 DISTNAME= p7zip_${V}_src_all PKGNAME= p7zip-${V} PKGNAME-main= p7zip-${V} Index: patches/patch-CPP_7zip_Bundles_SFXCon_SfxCon_cpp =================================================================== RCS file: patches/patch-CPP_7zip_Bundles_SFXCon_SfxCon_cpp diff -N patches/patch-CPP_7zip_Bundles_SFXCon_SfxCon_cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-CPP_7zip_Bundles_SFXCon_SfxCon_cpp 24 Jan 2016 21:02:56 -0000 @@ -0,0 +1,48 @@ +$OpenBSD$ + +Pledge self-extracting archives + +--- CPP/7zip/Bundles/SFXCon/SfxCon.cpp.orig Sat Sep 5 16:22:56 2015 ++++ CPP/7zip/Bundles/SFXCon/SfxCon.cpp Sun Jan 24 15:59:24 2016 +@@ -250,6 +250,21 @@ int Main2( + #endif + ) + { ++ ++#ifndef EXTERNAL_CODECS ++ ++#ifdef PLEDGE_DEBUG ++ printf("pledge: 7za 7zr SFX\n"); ++#endif //PLEDGE_DEBUG ++ ++ if (pledge("stdio rpath wpath cpath fattr", NULL) == -1) { ++ perror("pledge"); ++ exit(2); ++ } ++ ++#endif ++ ++ + #if defined(_WIN32) && !defined(UNDER_CE) + SetFileApisToOEM(); + #endif +@@ -371,6 +386,19 @@ int Main2( + HRESULT result = codecs->Load(); + if (result != S_OK) + throw CSystemException(result); ++ ++#ifdef EXTERNAL_CODECS ++ ++#ifdef PLEDGE_DEBUG ++ printf("pledge: 7z SFX\n"); ++#endif //PLEDGE_DEBUG ++ ++ if (pledge("stdio rpath wpath cpath fattr", NULL) == -1) { ++ perror("pledge"); ++ exit(2); ++ } ++ ++#endif + + if (command.CommandType != NCommandType::kList) + { Index: patches/patch-CPP_7zip_UI_Agent_Agent_cpp =================================================================== RCS file: patches/patch-CPP_7zip_UI_Agent_Agent_cpp diff -N patches/patch-CPP_7zip_UI_Agent_Agent_cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-CPP_7zip_UI_Agent_Agent_cpp 24 Jan 2016 18:24:54 -0000 @@ -0,0 +1,19 @@ +$OpenBSD$ + +Adapted for p7zip 15.09 by Ismail Donmez: +http://sourceforge.net/p/p7zip/discussion/383043/thread/53f8df4f/ + +Author: Ben Hutchings <b...@decadent.org.uk> +Description: Delay creation of symlinks to prevent arbitrary file writes (CVE-2015-1038) + +--- CPP/7zip/UI/Agent/Agent.cpp.orig Thu Sep 17 15:02:35 2015 ++++ CPP/7zip/UI/Agent/Agent.cpp Sun Jan 24 13:20:58 2016 +@@ -1515,7 +1515,7 @@ STDMETHODIMP CAgentFolder::Extract(const UInt32 *indic + HRESULT result = _agentSpec->GetArchive()->Extract(&realIndices.Front(), + realIndices.Size(), testMode, extractCallback); + if (result == S_OK) +- result = extractCallbackSpec->SetDirsTimes(); ++ result = extractCallbackSpec->SetFinalAttribs(); + return result; + COM_TRY_END + } Index: patches/patch-CPP_7zip_UI_Client7z_Client7z_cpp =================================================================== RCS file: patches/patch-CPP_7zip_UI_Client7z_Client7z_cpp diff -N patches/patch-CPP_7zip_UI_Client7z_Client7z_cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-CPP_7zip_UI_Client7z_Client7z_cpp 24 Jan 2016 18:24:47 -0000 @@ -0,0 +1,57 @@ +$OpenBSD$ + +Adapted for p7zip 15.09 by Ismail Donmez: +http://sourceforge.net/p/p7zip/discussion/383043/thread/53f8df4f/ + +Author: Ben Hutchings <b...@decadent.org.uk> +Description: Delay creation of symlinks to prevent arbitrary file writes (CVE-2015-1038) + +--- CPP/7zip/UI/Client7z/Client7z.cpp.orig Sat Oct 17 10:52:30 2015 ++++ CPP/7zip/UI/Client7z/Client7z.cpp Sun Jan 24 13:20:58 2016 +@@ -230,8 +230,11 @@ class CArchiveExtractCallback: (private) + COutFileStream *_outFileStreamSpec; + CMyComPtr<ISequentialOutStream> _outFileStream; + ++ CObjectVector<NWindows::NFile::NDir::CDelayedSymLink> _delayedSymLinks; ++ + public: + void Init(IInArchive *archiveHandler, const FString &directoryPath); ++ HRESULT SetFinalAttribs(); + + UInt64 NumErrors; + bool PasswordIsDefined; +@@ -449,12 +452,24 @@ STDMETHODIMP CArchiveExtractCallback::SetOperationResu + } + _outFileStream.Release(); + if (_extractMode && _processedFileInfo.AttribDefined) +- SetFileAttrib(_diskFilePath, _processedFileInfo.Attrib); ++ SetFileAttrib(_diskFilePath, _processedFileInfo.Attrib, &_delayedSymLinks); + PrintNewLine(); + return S_OK; + } + ++HRESULT CArchiveExtractCallback::SetFinalAttribs() ++{ ++ HRESULT result = S_OK; + ++ for (int i = 0; i != _delayedSymLinks.Size(); ++i) ++ if (!_delayedSymLinks[i].Create()) ++ result = E_FAIL; ++ ++ _delayedSymLinks.Clear(); ++ ++ return result; ++} ++ + STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password) + { + if (!PasswordIsDefined) +@@ -914,6 +929,8 @@ int MY_CDECL main(int numArgs, const char *args[]) + // extractCallbackSpec->PasswordIsDefined = true; + // extractCallbackSpec->Password = L"1"; + HRESULT result = archive->Extract(NULL, (UInt32)(Int32)(-1), false, extractCallback); ++ if (result == S_OK) ++ result = extractCallbackSpec->SetFinalAttribs(); + if (result != S_OK) + { + PrintError("Extract Error"); Index: patches/patch-CPP_7zip_UI_Common_ArchiveExtractCallback_cpp =================================================================== RCS file: patches/patch-CPP_7zip_UI_Common_ArchiveExtractCallback_cpp diff -N patches/patch-CPP_7zip_UI_Common_ArchiveExtractCallback_cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-CPP_7zip_UI_Common_ArchiveExtractCallback_cpp 24 Jan 2016 18:24:14 -0000 @@ -0,0 +1,44 @@ +$OpenBSD$ + +Adapted for p7zip 15.09 by Ismail Donmez: +http://sourceforge.net/p/p7zip/discussion/383043/thread/53f8df4f/ + +Author: Ben Hutchings <b...@decadent.org.uk> +Description: Delay creation of symlinks to prevent arbitrary file writes (CVE-2015-1038) + +--- CPP/7zip/UI/Common/ArchiveExtractCallback.cpp.orig Sat Oct 3 04:49:15 2015 ++++ CPP/7zip/UI/Common/ArchiveExtractCallback.cpp Sun Jan 24 13:20:58 2016 +@@ -1502,7 +1502,7 @@ STDMETHODIMP CArchiveExtractCallback::SetOperationResu + NumFiles++; + + if (!_stdOutMode && _extractMode && _fi.AttribDefined) +- SetFileAttrib(_diskFilePath, _fi.Attrib); ++ SetFileAttrib(_diskFilePath, _fi.Attrib, &_delayedSymLinks); + + RINOK(_extractCallback2->SetOperationResult(opRes, BoolToInt(_encrypted))); + +@@ -1584,8 +1584,9 @@ static unsigned GetNumSlashes(const FChar *s) + } + } + +-HRESULT CArchiveExtractCallback::SetDirsTimes() ++HRESULT CArchiveExtractCallback::SetFinalAttribs() + { ++ HRESULT result = S_OK; + CRecordVector<CExtrRefSortPair> pairs; + pairs.ClearAndSetSize(_extractedFolderPaths.Size()); + unsigned i; +@@ -1622,5 +1623,12 @@ HRESULT CArchiveExtractCallback::SetDirsTimes() + (WriteATime && ATimeDefined) ? &ATime : NULL, + (WriteMTime && MTimeDefined) ? &MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL)); + } +- return S_OK; ++ ++ for (int i = 0; i != _delayedSymLinks.Size(); ++i) ++ if (!_delayedSymLinks[i].Create()) ++ result = E_FAIL; ++ ++ _delayedSymLinks.Clear(); ++ ++ return result; + } Index: patches/patch-CPP_7zip_UI_Common_ArchiveExtractCallback_h =================================================================== RCS file: patches/patch-CPP_7zip_UI_Common_ArchiveExtractCallback_h diff -N patches/patch-CPP_7zip_UI_Common_ArchiveExtractCallback_h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-CPP_7zip_UI_Common_ArchiveExtractCallback_h 24 Jan 2016 18:24:26 -0000 @@ -0,0 +1,37 @@ +$OpenBSD$ + +Adapted for p7zip 15.09 by Ismail Donmez: +http://sourceforge.net/p/p7zip/discussion/383043/thread/53f8df4f/ + +Author: Ben Hutchings <b...@decadent.org.uk> +Description: Delay creation of symlinks to prevent arbitrary file writes (CVE-2015-1038) + +--- CPP/7zip/UI/Common/ArchiveExtractCallback.h.orig Sat Oct 3 06:29:09 2015 ++++ CPP/7zip/UI/Common/ArchiveExtractCallback.h Sun Jan 24 13:20:58 2016 +@@ -6,6 +6,8 @@ + #include "../../../Common/MyCom.h" + #include "../../../Common/Wildcard.h" + ++#include "../../../Windows/FileDir.h" ++ + #include "../../IPassword.h" + + #include "../../Common/FileStreams.h" +@@ -237,6 +239,8 @@ class CArchiveExtractCallback: + bool _saclEnabled; + #endif + ++ CObjectVector<NWindows::NFile::NDir::CDelayedSymLink> _delayedSymLinks; ++ + void CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath); + HRESULT GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined); + HRESULT GetUnpackSize(); +@@ -330,7 +334,7 @@ class CArchiveExtractCallback: + } + #endif + +- HRESULT SetDirsTimes(); ++ HRESULT SetFinalAttribs(); + }; + + bool CensorNode_CheckPath(const NWildcard::CCensorNode &node, const CReadArcItem &item); Index: patches/patch-CPP_7zip_UI_Common_Extract_cpp =================================================================== RCS file: patches/patch-CPP_7zip_UI_Common_Extract_cpp diff -N patches/patch-CPP_7zip_UI_Common_Extract_cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-CPP_7zip_UI_Common_Extract_cpp 24 Jan 2016 18:24:33 -0000 @@ -0,0 +1,19 @@ +$OpenBSD$ + +Adapted for p7zip 15.09 by Ismail Donmez: +http://sourceforge.net/p/p7zip/discussion/383043/thread/53f8df4f/ + +Author: Ben Hutchings <b...@decadent.org.uk> +Description: Delay creation of symlinks to prevent arbitrary file writes (CVE-2015-1038) + +--- CPP/7zip/UI/Common/Extract.cpp.orig Mon Sep 7 15:47:32 2015 ++++ CPP/7zip/UI/Common/Extract.cpp Sun Jan 24 13:20:58 2016 +@@ -207,7 +207,7 @@ static HRESULT DecompressArchive( + else + result = archive->Extract(&realIndices.Front(), realIndices.Size(), testMode, ecs); + if (result == S_OK && !options.StdInMode) +- result = ecs->SetDirsTimes(); ++ result = ecs->SetFinalAttribs(); + return callback->ExtractResult(result); + } + Index: patches/patch-CPP_7zip_UI_Console_Main_cpp =================================================================== RCS file: /systems/cvs/ports/archivers/p7zip/patches/patch-CPP_7zip_UI_Console_Main_cpp,v retrieving revision 1.1 diff -u -p -r1.1 patch-CPP_7zip_UI_Console_Main_cpp --- patches/patch-CPP_7zip_UI_Console_Main_cpp 22 Jan 2016 13:38:37 -0000 1.1 +++ patches/patch-CPP_7zip_UI_Console_Main_cpp 25 Jan 2016 02:52:34 -0000 @@ -1,21 +1,23 @@ $OpenBSD: patch-CPP_7zip_UI_Console_Main_cpp,v 1.1 2016/01/22 13:38:37 sthen Exp $ -Pledge archivers/p7zip binaries +Pledge archivers/p7zip main executables --- CPP/7zip/UI/Console/Main.cpp.orig Sat Oct 17 11:20:22 2015 -+++ CPP/7zip/UI/Console/Main.cpp Mon Jan 18 10:05:31 2016 -@@ -484,6 +484,18 @@ int Main2( ++++ CPP/7zip/UI/Console/Main.cpp Sun Jan 24 21:51:01 2016 +@@ -484,6 +484,20 @@ int Main2( #endif ) { + -+// pledge 7za and 7zr at this point, they take different paths than 7z. -+ +#ifndef EXTERNAL_CODECS + ++#ifdef PLEDGE_DEBUG ++ printf("initial pledge: 7za 7zr\n"); ++#endif ++ + if (pledge("stdio rpath wpath cpath fattr", NULL) == -1) { + perror("pledge"); -+ exit(1); ++ exit(2); + } + +#endif @@ -23,21 +25,118 @@ Pledge archivers/p7zip binaries #if defined(_WIN32) && !defined(UNDER_CE) SetFileApisToOEM(); #endif -@@ -579,6 +591,17 @@ int Main2( - codecs->CaseSensitiveChange = options.CaseSensitiveChange; +@@ -580,6 +594,19 @@ int Main2( codecs->CaseSensitive = options.CaseSensitive; ThrowException_if_Error(codecs->Load()); -+ -+// pledge 7z here -+ + +#ifdef EXTERNAL_CODECS + ++#ifdef PLEDGE_DEBUG ++ printf("initial pledge: 7z\n"); ++#endif ++ + if (pledge("stdio rpath wpath cpath fattr", NULL) == -1) { + perror("pledge"); -+ exit(1); ++ exit(2); + } + +#endif - ++ bool isExtractGroupCommand = options.Command.IsFromExtractGroup(); + if (codecs->Formats.Size() == 0 && +@@ -636,6 +663,16 @@ int Main2( + + if (options.Command.CommandType == NCommandType::kInfo) + { ++ ++#ifdef PLEDGE_DEBUG ++ printf("reduced pledge: info\n"); ++#endif ++ ++ if (pledge("stdio", NULL) == -1) { ++ perror("pledge"); ++ exit(2); ++ } ++ + CStdOutStream &so = (g_StdStream ? *g_StdStream : g_StdOut); + unsigned i; + +@@ -802,6 +839,16 @@ int Main2( + } + else if (options.Command.CommandType == NCommandType::kBenchmark) + { ++ ++#ifdef PLEDGE_DEBUG ++ printf("reduced pledge: benchmark\n"); ++#endif ++ ++ if (pledge("stdio", NULL) == -1) { ++ perror("pledge"); ++ exit(2); ++ } ++ + CStdOutStream &so = (g_StdStream ? *g_StdStream : g_StdOut); + hresultMain = BenchCon(EXTERNAL_CODECS_VARS_L + options.Properties, options.NumIterations, (FILE *)so); +@@ -869,6 +916,17 @@ int Main2( + if (hresultMain == S_OK) + if (isExtractGroupCommand) + { ++ ++ if (options.Command.IsTestCommand() || options.StdOutMode) { ++#ifdef PLEDGE_DEBUG ++ printf("reduced pledge: test stdout\n"); ++#endif ++ if (pledge("stdio rpath", NULL) == -1) { ++ perror("pledge"); ++ exit(2); ++ } ++ } ++ + CExtractCallbackConsole *ecs = new CExtractCallbackConsole; + CMyComPtr<IFolderArchiveExtractCallback> extractCallback = ecs; + +@@ -893,7 +951,7 @@ int Main2( + #ifndef _NO_CRYPTO + openCallback.PasswordIsDefined = options.PasswordEnabled; + openCallback.Password = options.Password; +- #endif ++ #endiF + */ + + CExtractOptions eo; +@@ -1029,6 +1087,16 @@ int Main2( + } + else + { ++ ++#ifdef PLEDGE_DEBUG ++ printf("reduced pledge: list\n"); ++#endif ++ ++ if (pledge("stdio rpath", NULL) == -1) { ++ perror("pledge"); ++ exit(2); ++ } ++ + UInt64 numErrors = 0; + UInt64 numWarnings = 0; + +@@ -1139,6 +1207,16 @@ int Main2( + } + else if (options.Command.CommandType == NCommandType::kHash) + { ++ ++#ifdef PLEDGE_DEBUG ++ printf("reduced pledge: hash\n"); ++#endif ++ ++ if (pledge("stdio rpath", NULL) == -1) { ++ perror("pledge"); ++ exit(2); ++ } ++ + const CHashOptions &uo = options.HashOptions; + + CHashCallbackConsole callback; Index: patches/patch-CPP_Windows_FileDir_cpp =================================================================== RCS file: patches/patch-CPP_Windows_FileDir_cpp diff -N patches/patch-CPP_Windows_FileDir_cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-CPP_Windows_FileDir_cpp 24 Jan 2016 18:24:59 -0000 @@ -0,0 +1,75 @@ +$OpenBSD$ + +Adapted for p7zip 15.09 by Ismail Donmez: +http://sourceforge.net/p/p7zip/discussion/383043/thread/53f8df4f/ + +Author: Ben Hutchings <b...@decadent.org.uk> +Description: Delay creation of symlinks to prevent arbitrary file writes (CVE-2015-1038) + +--- CPP/Windows/FileDir.cpp.orig Sat Oct 10 08:37:41 2015 ++++ CPP/Windows/FileDir.cpp Sun Jan 24 13:20:58 2016 +@@ -347,7 +347,8 @@ static int convert_to_symlink(const char * name) { + return -1; + } + +-bool SetFileAttrib(CFSTR fileName, DWORD fileAttributes) ++bool SetFileAttrib(CFSTR fileName, DWORD fileAttributes, ++ CObjectVector<CDelayedSymLink> *delayedSymLinks) + { + if (!fileName) { + SetLastError(ERROR_PATH_NOT_FOUND); +@@ -379,7 +380,9 @@ bool SetFileAttrib(CFSTR fileName, DWORD fileAttribute + stat_info.st_mode = fileAttributes >> 16; + #ifdef ENV_HAVE_LSTAT + if (S_ISLNK(stat_info.st_mode)) { +- if ( convert_to_symlink(name) != 0) { ++ if (delayedSymLinks) ++ delayedSymLinks->Add(CDelayedSymLink(name)); ++ else if ( convert_to_symlink(name) != 0) { + TRACEN((printf("SetFileAttrib(%s,%d) : false-3\n",(const char *)name,fileAttributes))) + return false; + } +@@ -813,6 +816,43 @@ bool CTempDir::Remove() + _mustBeDeleted = !RemoveDirectoryWithSubItems(_path); + return !_mustBeDeleted; + } ++ ++#ifdef ENV_UNIX ++ ++CDelayedSymLink::CDelayedSymLink(const char * source) ++ : _source(source) ++{ ++ struct stat st; ++ ++ if (lstat(_source, &st) == 0) { ++ _dev = st.st_dev; ++ _ino = st.st_ino; ++ } else { ++ _dev = 0; ++ } ++} ++ ++bool CDelayedSymLink::Create() ++{ ++ struct stat st; ++ ++ if (_dev == 0) { ++ errno = EPERM; ++ return false; ++ } ++ if (lstat(_source, &st) != 0) ++ return false; ++ if (_dev != st.st_dev || _ino != st.st_ino) { ++ // Placeholder file has been overwritten or moved by another ++ // symbolic link creation ++ errno = EPERM; ++ return false; ++ } ++ ++ return convert_to_symlink(_source) == 0; ++} ++ ++#endif // ENV_UNIX + + }}} + Index: patches/patch-CPP_Windows_FileDir_h =================================================================== RCS file: patches/patch-CPP_Windows_FileDir_h diff -N patches/patch-CPP_Windows_FileDir_h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-CPP_Windows_FileDir_h 24 Jan 2016 18:25:12 -0000 @@ -0,0 +1,66 @@ +$OpenBSD$ + +Adapted for p7zip 15.09 by Ismail Donmez: +http://sourceforge.net/p/p7zip/discussion/383043/thread/53f8df4f/ + +Author: Ben Hutchings <b...@decadent.org.uk> +Description: Delay creation of symlinks to prevent arbitrary file writes (CVE-2015-1038) + +--- CPP/Windows/FileDir.h.orig Fri Jun 19 06:52:06 2015 ++++ CPP/Windows/FileDir.h Sun Jan 24 13:20:58 2016 +@@ -4,6 +4,7 @@ + #define __WINDOWS_FILE_DIR_H + + #include "../Common/MyString.h" ++#include "../Common/MyVector.h" + + #include "FileIO.h" + +@@ -11,11 +12,14 @@ namespace NWindows { + namespace NFile { + namespace NDir { + ++class CDelayedSymLink; ++ + bool GetWindowsDir(FString &path); + bool GetSystemDir(FString &path); + + bool SetDirTime(CFSTR path, const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime); +-bool SetFileAttrib(CFSTR path, DWORD attrib); ++bool SetFileAttrib(CFSTR path, DWORD attrib, ++ CObjectVector<CDelayedSymLink> *delayedSymLinks = 0); + bool MyMoveFile(CFSTR existFileName, CFSTR newFileName); + + #ifndef UNDER_CE +@@ -74,6 +78,31 @@ class CTempDir (public) + void DisableDeleting() { _mustBeDeleted = false; } + bool Create(CFSTR namePrefix) ; + bool Remove(); ++}; ++ ++// Symbolic links must be created last so that they can't be used to ++// create or overwrite files above the extraction directory. ++class CDelayedSymLink ++{ ++#ifdef ENV_UNIX ++ // Where the symlink should be created. The target is specified in ++ // the placeholder file. ++ AString _source; ++ ++ // Device and inode of the placeholder file. Before creating the ++ // symlink, we must check that these haven't been changed by creation ++ // of another symlink. ++ dev_t _dev; ++ ino_t _ino; ++ ++public: ++ explicit CDelayedSymLink(const char * source); ++ bool Create(); ++#else // !ENV_UNIX ++public: ++ CDelayedSymLink(const char * source) {} ++ bool Create() { return true; } ++#endif // ENV_UNIX + }; + + #if !defined(UNDER_CE)