Ticket #8173: patch_master_password.patch

File patch_master_password.patch, 20.1 KB (added by Theo D., 7 years ago)

First version of a clean patch.

  • configure.in

     
    159159  #include <sys/types.h>
    160160  #include <utmp.h>])
    161161
     162 
     163  # -------------- Start of @td ----------------
     164
     165  # AC_CHECK_HEADERS([cryptlib.h osrng.h sha.h aes.h hex.h integer.h pwdbased.h modes.h],
     166  # [cryptopp_found_headers=yes; break;])
     167 
     168   
     169  # Check for Crypto++
     170  AM_OPTIONS_CRYPTO
     171
     172  CHECK_CRYPTO(5.1)
     173
     174  CXXFLAGS="$CXXFLAGS $CRYPTO_PP_CXXFLAGS"
     175  LDFLAGS="$LDFLAGS $CRYPTO_PP_LDFLAGS"
     176  CRYPTOLIBS="-l$CRYPTO_PP_LIB_NAME"
     177 
     178  case "$CRYPTO_PP_STYLE" in
     179    gentoo_debian | installed | sources)
     180      ;;
     181    *) AC_MSG_ERROR([
     182            WARNING: crypto++ >= $min_crypto_version is not found.
     183            Please check that cryptopp-headers are in your default include path,
     184      check out LD_LIBRARY_PATH or equivalent variable.
     185            Or this might also be that your cryptopp is installed on other path.
     186            Please try again with --with-crypto-prefix=/my_crypto_prefix
     187            (replace /my_crypto_prefix with a valid path to your crypto directory).
     188            To download the latest version check http://www.cryptopp.com for sources.
     189      ])
     190      ;;
     191  esac
     192
     193  AC_SUBST(CRYPTOLIBS)
     194 
     195  # AS_IF([test "x$cryptopp_found_headers" != "xyes"],
     196  # [AC_MSG_ERROR([Unable to find the Crypto++ library headers])])
     197 
     198  # ----------------- End of @td -----------------
     199
    162200  AC_SEARCH_LIBS([socket], [xnet])
    163201  AC_SEARCH_LIBS([getaddrinfo], [xnet])
    164202  AC_SEARCH_LIBS([in6addr_loopback], [socket])
  • src/interface/settings/optionspage_interface.cpp

     
    66#include "../Mainfrm.h"
    77#include "../power_management.h"
    88
     9// Start of @td
     10#define DEFAULTVALUE ""
     11#include "../engine/crypto.h"
     12#include "xmlfunctions.h"
     13#include "../interface/ipcmutex.h"
     14
     15// End of @td
     16
    917BEGIN_EVENT_TABLE(COptionsPageInterface, COptionsPage)
    1018EVT_CHECKBOX(XRCID("ID_FILEPANESWAP"), COptionsPageInterface::OnLayoutChange)
     19// Start of @td
     20EVT_CHECKBOX(XRCID("ID_ENCRYPT_PASSWORDS"), COptionsPageInterface::OnEncryptPasswordsChanged)
     21// End of @td
    1122EVT_CHOICE(XRCID("ID_FILEPANELAYOUT"), COptionsPageInterface::OnLayoutChange)
    1223EVT_CHOICE(XRCID("ID_MESSAGELOGPOS"), COptionsPageInterface::OnLayoutChange)
    1324END_EVENT_TABLE()
     
    4152        SetCheckFromOption(XRCID("ID_DONT_SAVE_PASSWORDS"), OPTION_DEFAULT_KIOSKMODE, failure);
    4253
    4354    SetCheckFromOption(XRCID("ID_INTERFACE_SITEMANAGER_ON_STARTUP"), OPTION_INTERFACE_SITEMANAGER_ON_STARTUP, failure);
     55   
     56    // Start of @td
     57    SetCheckFromOption(XRCID("ID_ENCRYPT_PASSWORDS"), OPTION_ENCRYPT_PASSWORDS, failure);
     58    wxString stars = wxString(DEFAULTVALUE, wxConvUTF8);
     59    SetText(XRCID("ID_MASTER_PASSWORD"), stars, failure); // @TODO : Better display...
     60    // End of @td
     61   
     62    if (!failure)
     63    {
     64        SetCtrlState();
     65    }
    4466
    4567    return !failure;
    4668}
     
    5981    SetOptionFromCheck(XRCID("ID_PREVENT_IDLESLEEP"), OPTION_PREVENT_IDLESLEEP);
    6082   
    6183    SetOptionFromCheck(XRCID("ID_SPEED_DISPLAY"), OPTION_SPEED_DISPLAY);
     84   
     85    // Start of @td
     86    bool enablingEnc = false; // Tells us if we are "ENABLING" encryption (disabled before, enabled now)
     87    if (GetCheck(XRCID("ID_ENCRYPT_PASSWORDS")) != m_pOptions->GetOptionVal(OPTION_ENCRYPT_PASSWORDS))
     88    {
     89        if (!GetCheck(XRCID("ID_ENCRYPT_PASSWORDS"))) // We are DISABLING encryption, let's decrypt everything already stored:m_pOptions->
     90        {
     91            CInterProcessMutex mutex(MUTEX_SITEMANAGER);
     92            CXmlFile file(_T("sitemanager"));
     93            TiXmlElement* pDocument = file.Load();
     94            if (!pDocument)
     95            {
     96                wxMessageBox(file.GetError(), _("Error loading xml file"), wxICON_ERROR);
     97            }
    6298
     99            TiXmlElement* pElement = pDocument->FirstChildElement("Servers");
     100            if (pElement)
     101            {
     102                for (TiXmlElement* pServer = pElement->FirstChildElement("Server"); pServer; pServer = pServer->NextSiblingElement("Server"))
     103                {
     104                    CServer s;
     105                    if (::GetServer(pServer, s))
     106                    {
     107                        wxString pass = s.GetPass(); // decrypts before returning
     108                        m_pOptions->SetOption(OPTION_ENCRYPT_PASSWORDS, 0); //* Disabling encryption so that SetPass() does not encrypt the passwd
     109                        s.SetPass(pass); // will not encrypt
     110                        ::SetServer(pServer, s);
     111                        m_pOptions->SetOption(OPTION_ENCRYPT_PASSWORDS, 1); //* Enabling encryption so that next GetPass() does decrypt the pass before returning it
     112                    }
     113                }
     114                file.Save();
     115            }
     116            m_pOptions->SetOption(OPTION_MASTER_PASSWORD, _T(""));// Resetting the master password when encryption is disabled
     117            CCrypto::SetMasterPassword(_T(""));
     118        } else {
     119            enablingEnc = true;
     120        }
     121        SetOptionFromCheck(XRCID("ID_ENCRYPT_PASSWORDS"), OPTION_ENCRYPT_PASSWORDS);
     122    }
     123
     124   
     125    wxString defaultval = wxString(DEFAULTVALUE, wxConvUTF8);
     126    wxString newPassword = GetText(XRCID("ID_MASTER_PASSWORD"));
     127    if(GetCheck(XRCID("ID_ENCRYPT_PASSWORDS")) && newPassword != defaultval)
     128    {
     129        wxString currentMPasswd = CCrypto::GetMasterPassword();
     130        if (newPassword != _T("") && newPassword != currentMPasswd) // We just changed the master password (and it is not empty), so we have to "migrate" the existing encrypted password from former encryption to new one
     131        {
     132            CInterProcessMutex mutex(MUTEX_SITEMANAGER);
     133            CXmlFile file(_T("sitemanager"));
     134            TiXmlElement* pDocument = file.Load();
     135            if (!pDocument)
     136            {
     137                wxMessageBox(file.GetError(), _("Error loading xml file"), wxICON_ERROR);
     138            }
     139
     140            TiXmlElement* pElement = pDocument->FirstChildElement("Servers");
     141            if (pElement)
     142            {
     143                for (TiXmlElement* pServer = pElement->FirstChildElement("Server"); pServer; pServer = pServer->NextSiblingElement("Server"))
     144                {
     145                    CServer s;
     146                    if (enablingEnc)
     147                    {
     148                        //* Encryption was previously disabled, so, disable it just the time to LOAD up the data
     149                        m_pOptions->SetOption(OPTION_ENCRYPT_PASSWORDS, 0);
     150                    }
     151                    if (::GetServer(pServer, s))
     152                    {
     153                        wxString pass = s.GetPass(); // uses current master passwd
     154                        CCrypto::SetMasterPassword(newPassword);
     155                        //* Ensure encryption is enabled when we SAVE
     156                        m_pOptions->SetOption(OPTION_ENCRYPT_PASSWORDS, 1);
     157                        s.SetPass(pass); // will encrypt using new master password
     158                        CCrypto::SetMasterPassword(currentMPasswd); // back to former one for next loop
     159                        ::SetServer(pServer, s);
     160                    }
     161                }
     162                file.Save();
     163            }
     164        }
     165        CCrypto::SetMasterPassword(newPassword);
     166        wxString passwdEncrypted = wxString(CCrypto::Encrypt(newPassword).c_str(), wxConvUTF8);
     167        m_pOptions->SetOption(OPTION_MASTER_PASSWORD, passwdEncrypted);
     168    }
     169   
     170    // End of @td
     171
    63172    if (!m_pOptions->OptionFromFzDefaultsXml(OPTION_DEFAULT_KIOSKMODE) && m_pOptions->GetOptionVal(OPTION_DEFAULT_KIOSKMODE) != 2)
    64173        SetOptionFromCheck(XRCID("ID_DONT_SAVE_PASSWORDS"), OPTION_DEFAULT_KIOSKMODE);
    65174
     
    80189
    81190    m_pOwner->m_pMainFrame->UpdateLayout(layout, swap, GetChoice(XRCID("ID_MESSAGELOGPOS")));
    82191}
     192
     193// Start of @td
     194
     195void COptionsPageInterface::SetCtrlState()
     196{
     197    bool enabled = XRCCTRL(*this, "ID_ENCRYPT_PASSWORDS", wxCheckBox)->GetValue() == 1;
     198
     199    XRCCTRL(*this, "ID_MASTER_PASSWORD", wxTextCtrl)->Enable(enabled);
     200}
     201
     202void COptionsPageInterface::OnEncryptPasswordsChanged(wxCommandEvent& event)
     203{
     204    SetCtrlState();
     205}
     206
     207// End of @td
     208 No newline at end of file
  • src/interface/settings/optionspage_interface.h

     
    99    virtual bool SavePage();
    1010    virtual bool Validate();
    1111
     12    // Start of  @td
     13    void SetCtrlState();
     14
     15    void OnEncryptPasswordsChanged(wxCommandEvent& event);
     16    // End of @td
     17
    1218    DECLARE_EVENT_TABLE();
    1319    void OnLayoutChange(wxCommandEvent& event);
    1420};
  • src/interface/Options.h

     
    8282    OPTION_TOOLBAR_HIDDEN,
    8383    OPTION_STRIP_VMS_REVISION,
    8484    OPTION_INTERFACE_SITEMANAGER_ON_STARTUP,
    85 
     85   
     86    OPTION_ENCRYPT_PASSWORDS, // @td
     87    OPTION_MASTER_PASSWORD,// @td
     88    OPTION_ENCRYPT_ITERATIONS, // @td
     89   
    8690    // Default/internal options
    8791    OPTION_DEFAULT_SETTINGSDIR,
    8892    OPTION_DEFAULT_KIOSKMODE,
  • src/interface/resources/settings.xrc

     
    13051305                  <label>D&amp;o not save passwords</label>
    13061306                </object>
    13071307              </object>
     1308              <!-- Start of @td -->
    13081309              <object class="sizeritem">
     1310                <object class="wxCheckBox" name="ID_ENCRYPT_PASSWORDS">
     1311                  <label>&amp;Encrypt passwords</label>
     1312                </object>
     1313                <flag>wxALIGN_CENTRE_VERTICAL</flag>
     1314              </object>             
     1315              <object class="sizeritem">
     1316                <object class="wxTextCtrl" name="ID_MASTER_PASSWORD" >
     1317                  <style>wxTE_PASSWORD</style>
     1318                </object>
     1319                <flag>wxALIGN_CENTRE_VERTICAL|wxGROW</flag>
     1320              </object>
     1321              <!-- End of @td -->
     1322              <object class="sizeritem">
    13091323                <object class="wxCheckBox" name="ID_MINIMIZE_TRAY">
    13101324                  <label>&amp;Minimize to tray</label>
    13111325                </object>
  • src/interface/resources/dialogs.xrc

     
    14511451      </object>
    14521452    </object>
    14531453  </object>
     1454  <!-- Start of @td -->
     1455  <object class="wxDialog" name="ID_ENTERMASTERPASSWORD">
     1456    <title>Enter master password</title>
     1457    <object class="wxBoxSizer">
     1458      <orient>wxVERTICAL</orient>
     1459      <object class="sizeritem">
     1460        <object class="wxStaticText" name="ID_HEADER_PASS">
     1461          <label>Please enter the FileZilla master password :</label>
     1462        </object>
     1463        <flag>wxTOP|wxLEFT|wxRIGHT</flag>
     1464        <border>5</border>
     1465      </object>
     1466      <object class="sizeritem">
     1467        <object class="wxTextCtrl" name="ID_PASSWD">
     1468          <style>wxTE_PASSWORD</style>
     1469        </object>
     1470        <flag>wxALL|wxGROW</flag>
     1471        <border>5</border>
     1472      </object>
     1473      <object class="sizeritem">
     1474        <object class="wxBoxSizer">
     1475          <orient>wxHORIZONTAL</orient>
     1476          <object class="sizeritem">
     1477            <object class="wxButton" name="wxID_OK">
     1478              <label>&amp;OK</label>
     1479              <default>1</default>
     1480            </object>
     1481            <flag>wxALL|wxGROW</flag>
     1482            <border>5</border>
     1483          </object>
     1484          <object class="sizeritem">
     1485            <object class="wxButton" name="wxID_CANCEL">
     1486              <label>&amp;Cancel</label>
     1487            </object>
     1488            <flag>wxALL| wxGROW</flag>
     1489            <border>5</border>
     1490          </object>
     1491        </object>
     1492        <flag>wxALIGN_CENTRE_HORIZONTAL</flag>
     1493      </object>
     1494    </object>
     1495  </object>
     1496  <!-- End of @td -->
    14541497  <object class="wxDialog" name="ID_INPUTDIALOG">
    14551498    <object class="wxBoxSizer">
    14561499      <orient>wxVERTICAL</orient>
  • src/interface/Options.cpp

     
    177177    { "Toolbar hidden", number, _T("0"), normal },
    178178    { "Strip VMS revisions", number, _T("0"), normal },
    179179    { "Show Site Manager on startup", number, _T("0"), normal },
     180   
     181    // Start of @td
     182    { "Use password encryption", number, _T("0"), normal },
     183    { "Master password", string, _T(""), normal },
     184    { "Encryption iterations", number, _T("0"), normal },
     185    // End of @td
    180186
    181187    // Default/internal options
    182188    { "Config Location", string, _T(""), default_only },
  • src/interface/FileZilla.cpp

     
    7070IMPLEMENT_APP_NO_MAIN(CFileZillaApp);
    7171#endif //__WXGTK__
    7272
     73// Start of @td
     74#include "../engine/crypto.h"
     75// End of @td
    7376CFileZillaApp::CFileZillaApp()
    7477{
    7578    m_pWrapEngine = 0;
     
    316319    CSessionManager::Init();
    317320#endif
    318321
     322    // Start of @td
     323    if (COptions::Get()->GetOptionVal(OPTION_ENCRYPT_PASSWORDS))
     324    {
     325        wxDialog pwdDlg;
     326        wxXmlResource::Get()->LoadDialog(&pwdDlg, wxGetApp().GetTopWindow(), _T("ID_ENTERMASTERPASSWORD"));
     327        XRCCTRL(pwdDlg, "wxID_OK", wxButton)->SetId(wxID_OK);
     328        XRCCTRL(pwdDlg, "wxID_CANCEL", wxButton)->SetId(wxID_CANCEL);
     329        pwdDlg.GetSizer()->Fit(&pwdDlg);
     330        pwdDlg.GetSizer()->SetSizeHints(&pwdDlg);
     331
     332        wxString passwd;
     333        while (passwd == _T(""))
     334        {
     335            if (pwdDlg.ShowModal() != wxID_OK)
     336                exit(0);
     337            passwd = XRCCTRL(pwdDlg, "ID_PASSWD", wxTextCtrl)->GetValue();
     338            if (passwd == _T(""))
     339            {
     340                wxMessageBox(_("No password provided provided."), _("Invalid input"), wxICON_EXCLAMATION);
     341                continue;
     342            } else if (!CCrypto::IsPassword(passwd))
     343            {
     344                wxMessageBox(_("Wrong master password."), _("Invalid input"), wxICON_EXCLAMATION);
     345                passwd = _T("");
     346                continue;
     347            }
     348           
     349        }
     350    }
     351    // End of @td
     352   
     353   
    319354    // Load the text wrapping engine
    320355    m_pWrapEngine = new CWrapEngine();
    321356    m_pWrapEngine->LoadCache();
  • src/interface/xmlfunctions.cpp

     
    594594        if ((long)NORMAL == logonType || (long)ACCOUNT == logonType)
    595595            pass = GetTextElement(node, "Pass");
    596596
    597         if (!server.SetUser(user, pass))
     597        if (!server.SetUser(user, pass, true)) // @td
    598598            return false;
    599599
    600600        if ((long)ACCOUNT == logonType)
     
    697697                logonType = ASK;
    698698            else
    699699            {
    700                 AddTextElement(node, "Pass", server.GetPass());
     700                AddTextElement(node, "Pass", server.GetPass(false));
    701701
    702702                if (server.GetLogonType() == ACCOUNT)
    703703                    AddTextElement(node, "Account", server.GetAccount());
  • src/include/server.h

     
    7878    unsigned int GetPort() const;
    7979    enum LogonType GetLogonType() const;
    8080    wxString GetUser() const;
    81     wxString GetPass() const;
     81    wxString GetPass(bool decrypt=true) const;
    8282    wxString GetAccount() const;
    8383    int GetTimezoneOffset() const;
    8484    enum PasvMode GetPasvMode() const;
     
    9494    bool SetHost(wxString Host, unsigned int port);
    9595
    9696    void SetLogonType(enum LogonType logonType);
    97     bool SetUser(const wxString& user, const wxString& pass = _T(""));
     97    bool SetUser(const wxString& user, const wxString& pass = _T(""), bool alreadyEncrypted = false);// @td
     98    bool SetPass(const wxString& pass, bool alreadyEncrypted = false);// @td
    9899    bool SetAccount(const wxString& account);
    99100
    100101    CServer& operator=(const CServer &op);
  • src/engine/server.cpp

     
    11#include <filezilla.h>
     2#include "server.h"
    23
     4// Start of @td
     5#include "../interface/Options.h"
     6#include "crypto.h"
     7// End of @td
     8
    39struct t_protocolInfo
    410{
    511    const enum ServerProtocol protocol;
     
    227233
    228234    m_port = port;
    229235    m_user = user;
    230     m_pass = pass;
     236    // Start of @td
     237    SetPass(pass);
     238    // End of @td
    231239    m_account = _T("");
    232240    if (m_logonType != ASK && m_logonType != INTERACTIVE)
    233241    {
    234         if (m_user == _T(""))
     242        if (m_user == _T("")) {
    235243            m_logonType = ANONYMOUS;
    236         else if (m_user == _T("anonymous"))
    237             if (m_pass.IsEmpty() || m_pass == _T("anonymous@example.com"))
     244        } else if (m_user == _T("anonymous")) {
     245            wxString pwd = GetPass(); // @td
     246            if (pwd.IsEmpty() || pwd == _T("anonymous@example.com")) // @td
    238247                m_logonType = ANONYMOUS;
    239248            else
    240249                m_logonType = NORMAL;
    241         else
     250        } else {
    242251            m_logonType = NORMAL;
     252        }
    243253    }
    244254
    245255    if (m_protocol == UNKNOWN)
     
    276286    return m_user;
    277287}
    278288
    279 wxString CServer::GetPass() const
     289// Start of @td
     290wxString CServer::GetPass(bool decrypt/*=true*/) const
    280291{
    281     if (m_logonType == ANONYMOUS)
     292    if (m_logonType == ANONYMOUS) {
    282293        return _T("anon@localhost");
     294    }
    283295
     296    if(COptions::Get()->GetOptionVal(OPTION_ENCRYPT_PASSWORDS) && decrypt) { // @td
     297        wxString result = wxString(CCrypto::Decrypt(m_pass).c_str(), wxConvUTF8);
     298        return result;
     299    }
    284300    return m_pass;
    285301}
     302// End of @td
    286303
    287304wxString CServer::GetAccount() const
    288305{
     
    300317    m_port = op.m_port;
    301318    m_logonType = op.m_logonType;
    302319    m_user = op.m_user;
    303     m_pass = op.m_pass;
     320    // Start of @td
     321    SetPass(op.GetPass());
     322    // End of @td
    304323    m_account = op.m_account;
    305324    m_timezoneOffset = op.m_timezoneOffset;
    306325    m_pasvMode = op.m_pasvMode;
     
    333352
    334353        if (m_logonType == NORMAL)
    335354        {
    336             if (m_pass != op.m_pass)
     355            if (GetPass() != op.GetPass()) // @td
    337356                return false;
    338357        }
    339358        else if (m_logonType == ACCOUNT)
    340359        {
    341             if (m_pass != op.m_pass)
     360            if (GetPass() != op.GetPass()) // @td
    342361                return false;
    343362            if (m_account != op.m_account)
    344363                return false;
     
    403422
    404423        if (m_logonType == NORMAL)
    405424        {
    406             cmp = m_pass.Cmp(op.m_pass);
     425            cmp = GetPass().Cmp(op.GetPass()); // @td
    407426            if (cmp < 0)
    408427                return true;
    409428            else if (cmp > 0)
     
    411430        }
    412431        else if (m_logonType == ACCOUNT)
    413432        {
    414             cmp = m_pass.Cmp(op.m_pass);
     433            cmp = GetPass().Cmp(op.GetPass()); // @td
    415434            if (cmp < 0)
    416435                return true;
    417436            else if (cmp > 0)
     
    513532    m_port = port;
    514533    m_logonType = NORMAL;
    515534    m_user = user;
    516     m_pass = pass;
     535    // Start of @td
     536    SetPass(pass);
     537    // End of @td
    517538    m_account = account;
    518539}
    519540
     
    569590    return true;
    570591}
    571592
    572 bool CServer::SetUser(const wxString& user, const wxString& pass /*=_T("")*/)
     593bool CServer::SetUser(const wxString& user, const wxString& pass /*=_T("")*/, bool alreadyEncrypted/* = false*/) // @td
    573594{
    574595    if (m_logonType == ANONYMOUS)
    575596        return true;
     
    578599    {
    579600        if (m_logonType != ASK && m_logonType != INTERACTIVE)
    580601            return false;
    581         m_pass = _T("");
     602        SetPass(_T(""), alreadyEncrypted);
     603    } else {
     604        SetPass(pass, alreadyEncrypted);
    582605    }
    583     else
    584         m_pass = pass;
    585606   
    586607    m_user = user;
    587608   
    588609    return true;
    589610}
     611// start of @td
     612bool CServer::SetPass(const wxString& pass, bool alreadyEncrypted/* = false*/) {
     613    if(COptions::Get()->GetOptionVal(OPTION_ENCRYPT_PASSWORDS) && !alreadyEncrypted) {
     614        m_pass = wxString(CCrypto::Encrypt(pass).c_str(), wxConvUTF8);
     615    } else {
     616        m_pass = pass;
     617    }
     618    return true;
     619}
     620// end of @td
    590621
    591622bool CServer::SetAccount(const wxString& account)
    592623{
     
    676707    m_port = 21;
    677708    m_logonType = ANONYMOUS;
    678709    m_user = _T("");
    679     m_pass = _T("");
     710    // Start of @td
     711    SetPass(_T(""));
     712    // End of @td
    680713    m_account = _T("");
    681714    m_timezoneOffset = 0;
    682715    m_pasvMode = MODE_DEFAULT;
  • src/engine/Makefile.am

     
    3838        tlssocket.cpp \
    3939        threadex.cpp \
    4040        timeex.cpp \
    41         transfersocket.cpp
     41        transfersocket.cpp \
     42        crypto.cpp # @td
    4243
    4344noinst_HEADERS = backend.h \
    4445        ControlSocket.h \
     
    5455        servercapabilities.h \
    5556        sftpcontrolsocket.h \
    5657        tlssocket.h \
    57         transfersocket.h
     58        transfersocket.h \
     59        crypto.h # @td
    5860
    5961dist_noinst_DATA = engine.vcproj
    6062
  • data/Makefile.am

     
    3636    rm -f $@
    3737    touch test_libtool_exedir.c
    3838    $(CC) -c -o test_libtool_exedir.o test_libtool_exedir.c
     39    echo "Pouet1"
    3940    exedir=`$(LIBTOOL) -n --mode=link $(CC) -o test_libtool_exedir$(EXEEXT) test_libtool_exedir.o`; \
    4041    if echo "$$exedir" | grep -- '-o \.libs' > /dev/null; then \
    4142      echo "!define LT_EXEDIR \".libs\\\"" > libtoolexecutablesubdir.nsh; \