// Map11Pge.cpp : implementation file // // Certificate mappings storage has changed for IIS6. // CERTMAP.OCX however has to support downlevel admin // of IIS5, IIS5.1 // // NSEPM support was removed from IIS6 and 1to1 client certificate // mapping information is now stored under /Cert11/Mappings // node directly in the metabase // Functions with IIS6 postfix access new location // for 1to1 client certificate mappings storage #include "stdafx.h" #include #include "certmap.h" // persistence and mapping includes #include "WrapMaps.h" //#include "wrpprsis.h" //#include "admutil.h" #include "ListRow.h" #include "ChkLstCt.h" #include "wrapmb.h" // mapping page includes #include "brwsdlg.h" #include "EdtOne11.h" #include "Ed11Maps.h" #include "Map11Pge.h" #include "CrackCrt.h" #include //#include "WrpMBWrp.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define COL_NUM_ENABLED 0 #define COL_NUM_NAME 1 #define COL_NUM_NTACCOUNT 2 #define MB_EXTEND_KEY _T("Cert11") #define MB_EXTEND_KEY_MAPS _T("Cert11/Mappings") ///////////////////////////////////////////////////////////////////////////// // CMap11Page property page IMPLEMENT_DYNCREATE(CMap11Page, CPropertyPage) CMap11Page::CMap11Page() : CPropertyPage(CMap11Page::IDD), m_MapsInMetabase( 0 ) { //{{AFX_DATA_INIT(CMap11Page) m_csz_i_c = _T(""); m_csz_i_o = _T(""); m_csz_i_ou = _T(""); m_csz_s_c = _T(""); m_csz_s_cn = _T(""); m_csz_s_l = _T(""); m_csz_s_o = _T(""); m_csz_s_ou = _T(""); m_csz_s_s = _T(""); m_fIsIIS6 = TRUE; //}}AFX_DATA_INIT } CMap11Page::~CMap11Page() { ResetMappingList(); } void CMap11Page::DoDataExchange(CDataExchange* pDX) { CPropertyPage::DoDataExchange(pDX); //{{AFX_DATA_MAP(CMap11Page) DDX_Control(pDX, IDC_ADD, m_cbutton_add); DDX_Control(pDX, IDC_ISSUER, m_cbutton_grp_issuer); DDX_Control(pDX, IDC_ISSUED_TO, m_cbutton_grp_issuedto); DDX_Control(pDX, IDC_LIST, m_clistctrl_list); DDX_Control(pDX, IDC_EDIT_11MAP, m_cbutton_editmap); DDX_Control(pDX, IDC_DELETE, m_cbutton_delete); DDX_Text(pDX, IDC_I_C, m_csz_i_c); DDX_Text(pDX, IDC_I_O, m_csz_i_o); DDX_Text(pDX, IDC_I_OU, m_csz_i_ou); DDX_Text(pDX, IDC_S_C, m_csz_s_c); DDX_Text(pDX, IDC_S_CN, m_csz_s_cn); DDX_Text(pDX, IDC_S_L, m_csz_s_l); DDX_Text(pDX, IDC_S_O, m_csz_s_o); DDX_Text(pDX, IDC_S_OU, m_csz_s_ou); DDX_Text(pDX, IDC_S_S, m_csz_s_s); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CMap11Page, CPropertyPage) //{{AFX_MSG_MAP(CMap11Page) ON_BN_CLICKED(IDC_ADD, OnAdd) ON_BN_CLICKED(IDC_DELETE, OnDelete) ON_BN_CLICKED(IDC_EDIT_11MAP, OnEdit11map) ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST, OnItemchangedList) ON_NOTIFY(NM_DBLCLK, IDC_LIST, OnDblclkList) //}}AFX_MSG_MAP ON_COMMAND(ID_HELP_FINDER, DoHelp) ON_COMMAND(ID_HELP, DoHelp) ON_COMMAND(ID_CONTEXT_HELP, DoHelp) ON_COMMAND(ID_DEFAULT_HELP, DoHelp) END_MESSAGE_MAP() //--------------------------------------------------------------------------- void CMap11Page::DoHelp() { WinHelpDebug(HIDD_CERTMAP_MAIN_BASIC); WinHelp( HIDD_CERTMAP_MAIN_BASIC ); } ///////////////////////////////////////////////////////////////////////////// // initialization routines //--------------------------------------------------------------------------- // FInitMapper is called by the routine instantiating this page. After the object // is first created is when it is called. It allows us to fail gracefully. BOOL CMap11Page::FInit(IMSAdminBase* pMB) { m_pMB = pMB; // check if it's for older than iis6 version m_fIsIIS6 = TRUE; if (IsLegacyMetabase(pMB)){m_fIsIIS6 = FALSE;} if ( !m_fIsIIS6 ) { // IIS5.1 and older saved cert mappings through NSEPM m_szMBPath = m_szMBPath + SZ_NAMESPACE_EXTENTION; } // this has become a simple place return TRUE; } //--------------------------------------------------------------------------- BOOL CMap11Page::OnInitDialog() { //call the parental oninitdialog BOOL f = CPropertyPage::OnInitDialog(); // if the initinalization (sp?) succeeded, init the list and other items if ( f ) { // init the contents of the list FInitMappingList(); // Fill the mapping list with the stored items FillMappingList(); // set the initial button states EnableDependantButtons(); } // set any changes in the info into place UpdateData(FALSE); // return the answer return f; } //--------------------------------------------------------------------------- BOOL CMap11Page::FInitMappingList() { CString sz; int i; // setup the friendly name column sz.Empty(); i = m_clistctrl_list.InsertColumn( COL_NUM_ENABLED, sz, LVCFMT_LEFT, 20 ); // setup the friendly name column sz.LoadString( IDS_LIST11_NAME ); i = m_clistctrl_list.InsertColumn( COL_NUM_NAME, sz, LVCFMT_LEFT, 105 ); // setup the account column sz.LoadString( IDS_LIST11_ACCOUNT ); i = m_clistctrl_list.InsertColumn( COL_NUM_NTACCOUNT, sz, LVCFMT_LEFT, 195 ); return TRUE; } //--------------------------------------------------------------------------- BOOL CMap11Page::FillMappingList() { // reset the mapping list - get rid of anything in there now ResetMappingList(); // read in the mappings - it adds them to the list FReadMappings(); return TRUE; } //--------------------------------------------------------------------------- //BOOL CMap11Page::FAddMappingToList( C11Mapping* pMap, DWORD iList ) BOOL CMap11Page::FAddMappingToList( C11Mapping* pMap ) { CString sz; int i; DWORD iList; // if requested, make sure the mapping is added to the end of the list iList = m_clistctrl_list.GetItemCount(); // get the appropriate "enabled" string BOOL fEnabled; pMap->GetMapEnabled( &fEnabled ); if ( fEnabled ) sz.LoadString( IDS_ENABLED ); else sz.Empty(); // add the friendly name of the mapping // create the new entry in the list box. Do not sort on this entry - yet i = m_clistctrl_list.InsertItem( iList, sz ); // add the friendly name of the mapping pMap->GetMapName( sz ); // create the new entry in the list box. Do not sort on this entry - yet m_clistctrl_list.SetItemText( i, COL_NUM_NAME, sz ); // add the account name of the mapping pMap->GetNTAccount( sz ); m_clistctrl_list.SetItemText( i, COL_NUM_NTACCOUNT, sz ); // attach the pointer to the mapping as the private data in the list. m_clistctrl_list.SetItemData( i, (UINT_PTR)pMap ); // return whether or not the insertion succeeded return TRUE; } //--------------------------------------------------------------------------- void CMap11Page::EnableDependantButtons() { // the whole purpose of this routine is to gray or activate // the edit and delete buttons depending on whether or not anything // is selected. So start by getting the selection count if ( m_clistctrl_list.GetSelectedCount() > 0 ) { // there are items selected m_cbutton_editmap.EnableWindow( TRUE ); m_cbutton_delete.EnableWindow( TRUE ); EnableCrackDisplay( TRUE ); } else { // nope. Nothing selected m_cbutton_editmap.EnableWindow( FALSE ); m_cbutton_delete.EnableWindow( FALSE ); } // always enable the add button m_cbutton_add.EnableWindow( TRUE ); } //--------------------------------------------------------------------------- BOOL CMap11Page::EditOneMapping( C11Mapping* pUpdateMap ) { CEditOne11MapDlg mapdlg; // prepare the mapping dialog pUpdateMap->GetMapName( mapdlg.m_sz_mapname ); pUpdateMap->GetMapEnabled( &mapdlg.m_bool_enable ); pUpdateMap->GetNTAccount( mapdlg.m_sz_accountname ); pUpdateMap->GetNTPassword( mapdlg.m_sz_password ); // run the mapping dialog if ( mapdlg.DoModal() == IDOK ) { // update its friendly name pUpdateMap->SetMapName( mapdlg.m_sz_mapname ); // set the NT account field of the mapping object pUpdateMap->SetNTAccount( mapdlg.m_sz_accountname ); // set the NT account password field of the mapping object CString csTempString; mapdlg.m_sz_password.CopyTo(csTempString); pUpdateMap->SetNTPassword( csTempString ); // set whether or not the mapping is enabled pUpdateMap->SetMapEnabled( mapdlg.m_bool_enable ); // NOTE: the caller is resposible for calling UpdateMappingInDispList // as the mapping in question may not yet be in the display list // this mapping has changed. Mark it to be saved MarkToSave( pUpdateMap ); // return true because the user said "OK" return TRUE; } // return FALSE because the user did not say "OK" return FALSE; } //--------------------------------------------------------------------------- BOOL CMap11Page::EditMultipleMappings() { CEdit11Mappings mapdlg; C11Mapping* pUpdate11Map; BOOL fSetInitialState = FALSE; BOOL fEnable; // scan the list of seleted items for the proper initial enable button state // loop through the selected items, setting each one's mapping int iList = -1; while( (iList = m_clistctrl_list.GetNextItem( iList, LVNI_SELECTED )) >= 0 ) { // get the mapping item for updating purposes pUpdate11Map = GetMappingInDisplay( iList ); ASSERT( pUpdate11Map ); if ( !pUpdate11Map ) { AfxMessageBox( IDS_ERR_ACCESS_MAPPING ); break; } // get the enable state of the mapping pUpdate11Map->GetMapEnabled( &fEnable ); // if this is the first time, just set the initial state if ( !fSetInitialState ) { mapdlg.m_int_enable = fEnable; fSetInitialState = TRUE; } else { // if it is different, then go indeterminate and break if ( fEnable != mapdlg.m_int_enable ) { mapdlg.m_int_enable = 2; break; } } } // run the mapping dialog if ( mapdlg.DoModal() == IDOK ) { // loop through the selected items, setting each one's mapping int iList = -1; while( (iList = m_clistctrl_list.GetNextItem( iList, LVNI_SELECTED )) >= 0 ) { // get the mapping item for updating purposes pUpdate11Map = GetMappingInDisplay( iList ); if ( !pUpdate11Map ) { AfxMessageBox( IDS_ERR_ACCESS_MAPPING ); break; } // set the enable flag if requested switch ( mapdlg.m_int_enable ) { case 0: // disable pUpdate11Map->SetMapEnabled( FALSE ); break; case 1: // enable pUpdate11Map->SetMapEnabled( TRUE ); break; } // set the NT account field of the mapping object pUpdate11Map->SetNTAccount( mapdlg.m_sz_accountname ); // set the NT account password field of the mapping object CString csTempString; mapdlg.m_sz_password.CopyTo(csTempString); pUpdate11Map->SetNTPassword( csTempString ); // update it in the list control too UpdateMappingInDispList( iList, pUpdate11Map ); // this mapping has changed. Mark it to be saved MarkToSave( pUpdate11Map ); } // activate the apply button SetModified(); // return true because the user said "OK" return TRUE; } // return FALSE because the user did not say "OK" return FALSE; } //--------------------------------------------------------------------------- void CMap11Page::UpdateMappingInDispList( DWORD iList, C11Mapping* pMap ) { CString sz; // verify the index and the pointer! ASSERT( pMap == GetMappingInDisplay(iList) ); // get the appropriate "enabled" string BOOL fEnabled; pMap->GetMapEnabled( &fEnabled ); if ( fEnabled ) sz.LoadString( IDS_ENABLED ); else sz.Empty(); // update the "Enabled" indicator m_clistctrl_list.SetItemText( iList, COL_NUM_ENABLED, sz ); // update the mapping name pMap->GetMapName( sz ); m_clistctrl_list.SetItemText( iList, COL_NUM_NAME, sz ); // update the account name pMap->GetNTAccount( sz ); m_clistctrl_list.SetItemText( iList, COL_NUM_NTACCOUNT, sz ); } //--------------------------------------------------------------------------- void CMap11Page::ResetMappingList() { // first, delete all the mapping objects in the list DWORD cbList = m_clistctrl_list.GetItemCount(); for ( DWORD iList = 0; iList < cbList; iList++ ) DeleteMapping( GetMappingInDisplay(iList) ); // reset the mapping list - get rid of anything in there now m_clistctrl_list.DeleteAllItems(); } //--------------------------------------------------------------------------- void CMap11Page::MarkToSave( C11Mapping* pSaveMap, BOOL fSave ) { // first, we see if it is already in the list. If it is, we have nothing to do // unless fSave is set to false, then we remove it from the list DWORD cbItemsInList = (DWORD)m_rgbSave.GetSize(); for ( DWORD i = 0; i < cbItemsInList; i++ ) { if ( pSaveMap == (C11Mapping*)m_rgbSave[i] ) { // go away if fSave, otherwise, double check it isn't // anywhere else in the list if ( fSave ) { return; } else { // remove the item from the list m_rgbSave.RemoveAt(i); // don't skip now as the list slid down cbItemsInList--; i--; } } } // since it is not there, we should add it, if fSave is true if ( fSave ) m_rgbSave.Add( (CObject*)pSaveMap ); } ///////////////////////////////////////////////////////////////////////////// // CMap11Page message handlers //--------------------------------------------------------------------------- void CMap11Page::OnOK() { // this has gotten much simpler FWriteMappings(); CPropertyPage::OnOK(); } //--------------------------------------------------------------------------- BOOL CMap11Page::OnApply() { // this has gotten much simpler BOOL f = FWriteMappings(); // rebuild the display FillMappingList(); return f; } //--------------------------------------------------------------------------- // when the user pushes the add button, ask them to load a certificate, then // add it to the list as a mapping void CMap11Page::OnAdd() { // put this in a try/catch to make errors easier to deal with try { CString szFilter; szFilter.LoadString( IDS_KEY_OR_CERT_FILE_FILTER ); // prepare the file dialog variables CFileDialog cfdlg(TRUE, NULL, NULL, OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY, (LPCTSTR)szFilter); // Disable hook to get Windows 2000 style dialog cfdlg.m_ofn.Flags &= ~(OFN_ENABLEHOOK); // run the dialog if ( cfdlg.DoModal() == IDOK ) { // add the certificate to the mapping list if ( FAddCertificateFile( cfdlg.GetPathName() ) ) { // activate the apply button SetModified(); } } } catch ( CException * pException ) { pException->Delete(); } } //--------------------------------------------------------------------------- void CMap11Page::OnDelete() { C11Mapping* pKillMap; // ask the user to confirm this decision if ( AfxMessageBox(IDS_CONFIRM_DELETE, MB_OKCANCEL) != IDOK ) return; // loop through the selected items. Remove each from the list, // then mark it to be deleted. int iList = -1; while( (iList = m_clistctrl_list.GetNextItem( -1, LVNI_SELECTED )) >= 0 ) { // get the mapping pKillMap = GetMappingInDisplay( iList ); // remove it from the list m_clistctrl_list.DeleteItem( iList ); // if it has not yet been applied to the metabase, continue if ( (!m_fIsIIS6 && pKillMap->iMD == NEW_OBJECT ) || ( m_fIsIIS6 && pKillMap->QueryNodeName() == "" /*NEW_OBJECT*/ ) ) { // since this mapping never existed, we can just remove it from the add/edit lists MarkToSave( pKillMap, FALSE ); // go to the next selected object continue; } // mark the item to be deleted from the metabase m_rgbDelete.Add( (CObject*)pKillMap ); } // activate the apply button SetModified(); } //--------------------------------------------------------------------------- void CMap11Page::OnEdit11map() { int iList; C11Mapping* pUpdateMap; // what happens here depends on if just one mapping is selected, or many switch( m_clistctrl_list.GetSelectedCount() ) { case 0: // do nothing - should not get here because button grays out ASSERT( FALSE ); break; case 1: // get the mapping for update and run single edit dialog // get index of the selected list item iList = m_clistctrl_list.GetNextItem( -1, LVNI_SELECTED ); ASSERT( iList >= 0 ); // get the mapping item for updating purposes pUpdateMap = GetMappingInDisplay( iList ); if ( !pUpdateMap ) { AfxMessageBox( IDS_ERR_ACCESS_MAPPING ); break; } // edit the mapping, update it if successful, delete if not if ( EditOneMapping(pUpdateMap) ) { UpdateMappingInDispList( iList, pUpdateMap ); // activate the apply button SetModified(); } break; default: // run the multi edit dialog EditMultipleMappings(); break; } } //--------------------------------------------------------------------------- void CMap11Page::OnDblclkList(NMHDR* pNMHDR, LRESULT* pResult) { *pResult = 0; // if something in the list was double clicked, edit it if ( m_clistctrl_list.GetSelectedCount() > 0 ) OnEdit11map(); } //--------------------------------------------------------------------------- void CMap11Page::OnItemchangedList(NMHDR* pNMHDR, LRESULT* pResult) { C11Mapping* pSelMap; NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; *pResult = 0; // enable the correct items EnableDependantButtons(); // fill in the cracked information for the selected mapping - if there is only one if ( m_clistctrl_list.GetSelectedCount() == 1 ) { // get index of the selected list item int i = m_clistctrl_list.GetNextItem( -1, LVNI_SELECTED ); ASSERT( i >= 0 ); // get the mapper index for the item pSelMap = GetMappingInDisplay( i ); if ( pSelMap ) { DisplayCrackedMap( pSelMap ); } } else { // either multiple, or no mappings selected EnableCrackDisplay( FALSE ); } } //================================================================================ // special display //--------------------------------------------------------------------------- BOOL CMap11Page::DisplayCrackedMap( C11Mapping* pMap ) { PUCHAR pCert; DWORD cbCert; CString sz; // obtain a reference to the certificate if ( !pMap->GetCertificate( &pCert, &cbCert ) ) return FALSE; // crack the certificate CCrackedCert cracker; if ( !cracker.CrackCert( pCert, cbCert ) ) return FALSE; // fill in all the fields cracker.GetIssuerCountry( sz ); m_csz_i_c = sz; cracker.GetIssuerOrganization( sz ); m_csz_i_o = sz; cracker.GetIssuerUnit( sz ); m_csz_i_ou = sz; cracker.GetSubjectCountry( sz ); m_csz_s_c = sz; cracker.GetSubjectCommonName( sz ); m_csz_s_cn = sz; cracker.GetSubjectLocality( sz ); m_csz_s_l = sz; cracker.GetSubjectOrganization( sz ); m_csz_s_o = sz; cracker.GetSubjectUnit( sz ); m_csz_s_ou = sz; cracker.GetSubjectState( sz ); m_csz_s_s = sz; UpdateData( FALSE ); // return success return TRUE; } //--------------------------------------------------------------------------- void CMap11Page::ClearCrackDisplay() { m_csz_i_c.Empty(); m_csz_i_o.Empty(); m_csz_i_ou.Empty(); m_csz_s_c.Empty(); m_csz_s_cn.Empty(); m_csz_s_l.Empty(); m_csz_s_o.Empty(); m_csz_s_ou.Empty(); m_csz_s_s.Empty(); UpdateData( FALSE ); } //--------------------------------------------------------------------------- void CMap11Page::EnableCrackDisplay( BOOL fEnable ) { if ( !fEnable ) ClearCrackDisplay(); m_cbutton_grp_issuer.EnableWindow( fEnable ); m_cbutton_grp_issuedto.EnableWindow( fEnable ); } //--------------------------------------------------------------------------- BOOL CMap11Page::FReadMappings() { if ( m_fIsIIS6 ) { return FReadMappingsIIS6(); } BOOL f; C11Mapping* pMap; DWORD cbData; PVOID pData; DWORD fEnabled; CString sz; BOOL fRet = TRUE; // before messing with the metabase, prepare the strings we will need CString szBasePath = m_szMBPath + _T('/'); CString szRelativePath = MB_EXTEND_KEY_MAPS; CString szObjectPath = m_szMBPath + _T('/') + szRelativePath; CString szMapPath; // prepare the metabase wrappers CWrapMetaBase mbWrap; f = mbWrap.FInit(m_pMB); // open the base object f = mbWrap.Open( szObjectPath, METADATA_PERMISSION_READ ); ASSERT( f ); if ( !f ) { return FALSE; } // for now, at least, we are reading in all the mappings. reset the m_nNamer counter // so that we end up with a somewhat accurate reading of the last number-name in the list. m_MapsInMetabase = 0; // Loop the items in the metabase, adding each to the napper. DWORD index = 0; CString szEnum; while ( mbWrap.EnumObjects(_T(""), szEnum.GetBuffer(MAX_PATH*sizeof(WCHAR)), MAX_PATH*sizeof(WCHAR), index) ) { szEnum.ReleaseBuffer(); // keep track of the number of mappings we encounter m_MapsInMetabase++; // build the final mapping object path szMapPath.Format( _T("/%s"), szEnum ); // make a new mapping object pMap = PNewMapping(); if (pMap == NULL) { SetLastError(E_OUTOFMEMORY); fRet = FALSE; break; } // install the object name into the mapping pMap->iMD = m_MapsInMetabase; // get the certificate pData = mbWrap.GetData( szMapPath, MD_MAPCERT, IIS_MD_UT_SERVER, BINARY_METADATA, &cbData ); if ( pData ) { // set the data into place pMap->SetCertificate( (PUCHAR)pData, cbData ); // free the buffer mbWrap.FreeWrapData( pData ); } // get the NT Account - a string cbData = METADATA_MAX_NAME_LEN; if ( Get11String( &mbWrap, szMapPath, MD_MAPNTACCT, sz) ) { pMap->SetNTAccount( sz ); } // get the NT Password cbData = METADATA_MAX_NAME_LEN; if ( Get11String( &mbWrap, szMapPath, MD_MAPNTPWD, sz) ) { pMap->SetNTPassword( sz ); } // get the Enabled flag if ( mbWrap.GetDword( szMapPath, MD_MAPENABLED, IIS_MD_UT_SERVER, &fEnabled) ) pMap->SetMapEnabled( (fEnabled > 0) ); // get the mapping name cbData = METADATA_MAX_NAME_LEN; if ( Get11String( &mbWrap, szMapPath, MD_MAPNAME, sz) ) { pMap->SetMapName( sz ); } // add the mapping to the list FAddMappingToList( pMap ); // increment the index index++; } szEnum.ReleaseBuffer(); // close the mapping object mbWrap.Close(); // return success return fRet; } //--------------------------------------------------------------------------- // IMPORTANT: There is a bug in the mapping namespace extension where, even // though we are using the unicode metabase interface, all the strings are // expected to be ansi. This means that we cannont use the wrapmb getstring // and setstring calls with regards to the nsmp extetention. That is why // there are these two string wrapper classes that // also, all the strings used here are IIS_MD_UT_SERVER, so we can elimiate that parameter. BOOL CMap11Page::Get11String(CWrapMetaBase* pmb, LPCTSTR pszPath, DWORD dwPropID, CString& sz) { DWORD dwcb; BOOL fAnswer = FALSE; // get the string using the self-allocating get data process // that that it is cast as ANSI so the sz gets it right. // NOTE: This must be gotten as an ANSI string! PCHAR pchar = (PCHAR)pmb->GetData( pszPath, dwPropID, IIS_MD_UT_SERVER, STRING_METADATA, &dwcb ); if ( pchar ) { // set the answer sz = pchar; fAnswer = TRUE; // clean up pmb->FreeWrapData( pchar ); } // return the answer return fAnswer; } //--------------------------------------------------------------------------- /* INTRINSA suppress=null_pointers, uninitialized */ BOOL CMap11Page::Set11String(CWrapMetaBase* pmb, LPCTSTR pszPath, DWORD dwPropID, CString& sz, DWORD dwFlags ) { USES_CONVERSION; // Easy. Just set it as data // Make sure it is set back as an ANSI string though LPSTR pA = T2A((LPTSTR)(LPCTSTR)sz); return pmb->SetData( pszPath, dwPropID, IIS_MD_UT_SERVER, STRING_METADATA, (PVOID)pA, strlen(pA)+1, dwFlags ); } //--------------------------------------------------------------------------- // we only need to write out the mappings that have been either changed or added. // Thoughts on further optimizations: The bare minimum info about where to find // a mapping in the metabase could be stored in the metabase. Then, the mappings // would only be loaded when they were added to be edited or displayed in the // cracked list. The private data for each item in the list would have to have // some sort of reference to a position in the metabase. BOOL CMap11Page::FWriteMappings() { if ( m_fIsIIS6 ) { return FWriteMappingsIIS6(); } BOOL f; DWORD i,j; DWORD cMappings; C11Mapping* pMap; C11Mapping* pMapTemp; CString sz; DWORD dwEnabled; PUCHAR pCert; DWORD cbCert; DWORD iList; CStrPassword cspTempPassword; // before messing with the metabase, prepare the strings we will need CString szTempPath; CString szBasePath = m_szMBPath + _T("/Cert11"); CString szRelativePath = _T("/Mappings"); CString szObjectPath = szRelativePath + _T('/'); // prepare the base metabase wrapper CWrapMetaBase mbBase; f = mbBase.FInit(m_pMB); if ( !f ) { AfxMessageBox( IDS_ERR_ACCESS_MAPPING ); return FALSE; } // first, we have to open the Cert11 object. If it doesn't exist // then we have to add it tothe metabase if ( !mbBase.Open( szBasePath, METADATA_PERMISSION_READ|METADATA_PERMISSION_WRITE ) ) { // Cert11 does not exist - open the namespace base and add it szTempPath = m_szMBPath + _T('/'); if ( !mbBase.Open( szTempPath, METADATA_PERMISSION_READ|METADATA_PERMISSION_WRITE ) ) { AfxMessageBox( IDS_ERR_ACCESS_MAPPING ); return FALSE; // serious problems if we can't open the base } // add the Cert11 object szTempPath = _T("Cert11"); f = mbBase.AddObject( szTempPath ); mbBase.Close(); if ( !f ) { AfxMessageBox( IDS_ERR_CANTADD ); return FALSE; } // try again to open the Cert11. Fail if it doesn't work if ( !mbBase.Open( szBasePath, METADATA_PERMISSION_READ|METADATA_PERMISSION_WRITE ) ) { AfxMessageBox( IDS_ERR_ACCESS_MAPPING ); return FALSE; } } //========== // start by deleting all the mappings in the to-be-deleted list cMappings = (DWORD)m_rgbDelete.GetSize(); // only bother if there are items waiting to be deleted if ( cMappings > 0 ) { // get the count of mappings in the display list DWORD cList = m_clistctrl_list.GetItemCount(); // sort the mappings, in decending order for ( i = 0; i < cMappings-1; i++ ) { pMap = (C11Mapping*)m_rgbDelete[i]; for ( j = i; j < cMappings; j++ ) { pMapTemp = (C11Mapping*)m_rgbDelete[j]; if ( pMap->iMD < pMapTemp->iMD ) { m_rgbDelete.SetAt( i, (CObject*)pMapTemp ); m_rgbDelete.SetAt( j, (CObject*)pMap ); pMap = pMapTemp; } } } // loop the mappings, deleting each from the metabase for ( i = 0; i < cMappings; i++ ) { // get the mapping object pMap = (C11Mapping*)m_rgbDelete[i]; if ( !pMap || (pMap->iMD == NEW_OBJECT) ) continue; // build the relative path to the object in question. szObjectPath.Format( _T("%s/%d"), szRelativePath, pMap->iMD ); // delete that mapping's object from the metabase f = mbBase.DeleteObject( szObjectPath ); // decrement the number of maps in the metabase m_MapsInMetabase--; // loop the items in the list, decrementing the index of those // that are above it. Yes - this is non-optimal, but its what // has to be done for now for ( iList = 0; iList < cList; iList++ ) { pMapTemp = GetMappingInDisplay(iList); if ( (pMapTemp->iMD > pMap->iMD) && (pMapTemp->iMD != NEW_OBJECT) ) pMapTemp->iMD--; } // since we will no longer be needing this mapping, delete it DeleteMapping( pMap ); } // reset the to-be-deleted list m_rgbDelete.RemoveAll(); } //========== // get the number mappings in the to-be-saved list cMappings = (DWORD)m_rgbSave.GetSize(); // loop the mappings, adding each to the metabase for ( i = 0; i < cMappings; i++ ) { // get the mapping object pMap = (C11Mapping*)m_rgbSave[i]; ASSERT( pMap ); // if the object is already in the metabase, just open it. if ( pMap->iMD != NEW_OBJECT ) { // build the relative path to the object szObjectPath.Format( _T("%s/%d"), szRelativePath, pMap->iMD ); } else { // set up the name of the new mapping as one higher // than the number of mappings in the metabase pMap->iMD = m_MapsInMetabase + 1; // build the relative path to the object szObjectPath.Format( _T("%s/%d"), szRelativePath, pMap->iMD ); // add the mapping object to the base f = mbBase.AddObject( szObjectPath ); if ( f ) { // increment the number of maps in the metabase m_MapsInMetabase++; } } // write the object's parameters if ( f ) { // save the certificate if ( pMap->GetCertificate(&pCert, &cbCert) ) { // set the data into place in the object f = mbBase.SetData( szObjectPath, MD_MAPCERT, IIS_MD_UT_SERVER, BINARY_METADATA, pCert, cbCert, METADATA_SECURE | METADATA_INHERIT ); } // save the NTAccount if ( pMap->GetNTAccount(sz) ) { // set the data into place in the object f = Set11String(&mbBase, szObjectPath, MD_MAPNTACCT, sz, METADATA_SECURE); } // save the password - secure if ( pMap->GetNTPassword(cspTempPassword) ) { // set the data into place in the object CString csTempPassword; cspTempPassword.CopyTo(csTempPassword); f = Set11String(&mbBase, szObjectPath, MD_MAPNTPWD, csTempPassword, METADATA_SECURE); } // save the map's name if ( pMap->GetMapName(sz) ) { // set the data into place in the object f = Set11String(&mbBase, szObjectPath, MD_MAPNAME, sz); } // save the Enabled flag // server reads the flag as the value of the dword if ( pMap->GetMapEnabled(&f) ) { dwEnabled = (DWORD)f; f = mbBase.SetDword( szObjectPath, MD_MAPENABLED, IIS_MD_UT_SERVER, dwEnabled ); } } } // close the base object mbBase.Close(); // save the metabase mbBase.Save(); // reset the to-be-saved list m_rgbSave.RemoveAll(); // return success return TRUE; } //--------------------------------------------------------------------------- BOOL CMap11Page::FReadMappingsIIS6() { BOOL f; C11Mapping* pMap; DWORD cbData; PVOID pData; DWORD fEnabled; WCHAR * pChar = NULL; BOOL fRet = TRUE; // before messing with the metabase, prepare the strings we will need CString szBasePath = m_szMBPath + _T('/'); CString szRelativePath = MB_EXTEND_KEY_MAPS; CString szObjectPath = m_szMBPath + _T('/') + szRelativePath; CString szMapPath; // prepare the metabase wrappers CWrapMetaBase mbWrap; f = mbWrap.FInit(m_pMB); // open the base object f = mbWrap.Open( szObjectPath, METADATA_PERMISSION_READ ); ASSERT( f ); if ( !f ) { return FALSE; } // for now, at least, we are reading in all the mappings. reset the m_nNamer counter // so that we end up with a somewhat accurate reading of the last number-name in the list. m_MapsInMetabase = 0; // Loop the items in the metabase, adding each to the napper. DWORD index = 0; CString szEnum; while ( mbWrap.EnumObjects(_T(""), szEnum.GetBuffer(MAX_PATH*sizeof(WCHAR)), MAX_PATH*sizeof(WCHAR), index) ) { szEnum.ReleaseBuffer(); // keep track of the number of mappings we encounter m_MapsInMetabase++; // build the final mapping object path szMapPath.Format( _T("/%s"), szEnum ); // make a new mapping object pMap = PNewMapping(); if (pMap == NULL) { SetLastError(E_OUTOFMEMORY); fRet = FALSE; break; } // install the object name into the mapping pMap->SetNodeName( szEnum ); // get the certificate pData = mbWrap.GetData( szMapPath, MD_MAPCERT, IIS_MD_UT_SERVER, BINARY_METADATA, &cbData ); if ( pData ) { // set the data into place pMap->SetCertificate( (PUCHAR)pData, cbData ); // free the buffer mbWrap.FreeWrapData( pData ); } // get the NT Account - a string cbData = METADATA_MAX_NAME_LEN; if ( pChar = (WCHAR *) mbWrap.GetData( szMapPath, MD_MAPNTACCT, IIS_MD_UT_SERVER, STRING_METADATA, &cbData ) ) { pMap->SetNTAccount( pChar ); // free the buffer mbWrap.FreeWrapData( pChar ); } // get the NT Password cbData = METADATA_MAX_NAME_LEN; if ( pChar = (WCHAR *) mbWrap.GetData( szMapPath, MD_MAPNTPWD, IIS_MD_UT_SERVER, STRING_METADATA, &cbData ) ) { pMap->SetNTPassword( pChar ); // free the buffer mbWrap.FreeWrapData( pChar ); } // get the Enabled flag if ( mbWrap.GetDword( szMapPath, MD_MAPENABLED, IIS_MD_UT_SERVER, &fEnabled) ) pMap->SetMapEnabled( (fEnabled > 0) ); // get the mapping name cbData = METADATA_MAX_NAME_LEN; if ( pChar = (WCHAR *) mbWrap.GetData( szMapPath, MD_MAPNAME, IIS_MD_UT_SERVER, STRING_METADATA, &cbData ) ) { pMap->SetMapName( pChar ); // free the buffer mbWrap.FreeWrapData( pChar ); } // add the mapping to the list FAddMappingToList( pMap ); // increment the index index++; } szEnum.ReleaseBuffer(); // close the mapping object mbWrap.Close(); // return success return fRet; } //--------------------------------------------------------------------------- // we only need to write out the mappings that have been either changed or added. // This function is based on FWriteMappings() function. Minimum changes were made // to support IIS6 format of IIS 1to1 client certificate mappings BOOL CMap11Page::FWriteMappingsIIS6() { BOOL f; DWORD i,j; DWORD cMappings; C11Mapping* pMap; C11Mapping* pMapTemp; CString sz; DWORD dwEnabled; PUCHAR pCert; DWORD cbCert; DWORD iList; CStrPassword cspTempPassword; // before messing with the metabase, prepare the strings we will need CString szTempPath; CString szBasePath = m_szMBPath + _T('/')+ MB_EXTEND_KEY_MAPS; CString szObjectPath = ""; // prepare the base metabase wrapper CWrapMetaBase mbBase; f = mbBase.FInit(m_pMB); if ( !f ) { AfxMessageBox( IDS_ERR_ACCESS_MAPPING ); return FALSE; } // first, we have to open the Cert11Mappings object. If it doesn't exist // then we have to add it to the metabase if ( !mbBase.Open( szBasePath, METADATA_PERMISSION_READ|METADATA_PERMISSION_WRITE ) ) { // Cert11Mappings does not exist - open the namespace base and add it szTempPath = m_szMBPath + _T('/'); if ( !mbBase.Open( szTempPath, METADATA_PERMISSION_READ|METADATA_PERMISSION_WRITE ) ) { AfxMessageBox( IDS_ERR_ACCESS_MAPPING ); return FALSE; // serious problems if we can't open the base } // add the Cert11Mappings object szTempPath = MB_EXTEND_KEY_MAPS; f = mbBase.AddObject( szTempPath ); mbBase.Close(); if ( !f ) { AfxMessageBox( IDS_ERR_CANTADD ); return FALSE; } // try again to open the Cert11Mappings. Fail if it doesn't work if ( !mbBase.Open( szBasePath, METADATA_PERMISSION_READ|METADATA_PERMISSION_WRITE ) ) { AfxMessageBox( IDS_ERR_ACCESS_MAPPING ); return FALSE; } } //========== // start by deleting all the mappings in the to-be-deleted list cMappings = (DWORD)m_rgbDelete.GetSize(); // only bother if there are items waiting to be deleted if ( cMappings > 0 ) { // get the count of mappings in the display list DWORD cList = m_clistctrl_list.GetItemCount(); // loop the mappings, deleting each from the metabase for ( i = 0; i < cMappings; i++ ) { // get the mapping object pMap = (C11Mapping*)m_rgbDelete[i]; if ( !pMap || (pMap->QueryNodeName() == "" /*NEW_OBJECT*/) ) continue; // build the relative path to the object in question. szObjectPath.Format( _T("%s"), pMap->QueryNodeName() ); // delete that mapping's object from the metabase f = mbBase.DeleteObject( szObjectPath ); // decrement the number of maps in the metabase m_MapsInMetabase--; // since we will no longer be needing this mapping, delete it DeleteMapping( pMap ); } // reset the to-be-deleted list m_rgbDelete.RemoveAll(); } //========== // get the number mappings in the to-be-saved list cMappings = (DWORD)m_rgbSave.GetSize(); // loop the mappings, adding each to the metabase for ( i = 0; i < cMappings; i++ ) { // get the mapping object pMap = (C11Mapping*)m_rgbSave[i]; ASSERT( pMap ); // if the object is already in the metabase, just open it. if ( pMap->QueryNodeName() != "" /*NEW_OBJECT*/ ) { // build the relative path to the object szObjectPath.Format( _T("%s"), pMap->QueryNodeName() ); } else { // set up the name of the new mapping node to be equal // to SHA1 hash of the certificate pMap->SetNodeName( pMap->QueryCertHash() ); // build the relative path to the object szObjectPath.Format( _T("%s"), pMap->QueryNodeName() ); if ( !szObjectPath.IsEmpty() ) { // add the mapping object to the base f = mbBase.AddObject( szObjectPath ); if ( f ) { // increment the number of maps in the metabase m_MapsInMetabase++; } // reset error of Adding object // most likely node already exists // CODEWORK: 04/08/02 jaroslad - this function is very optimistic // it ignores errors that happen during process of saving mapping data f = TRUE; } } // write the object's parameters if ( f && !szObjectPath.IsEmpty() ) { // save the certificate if ( pMap->GetCertificate(&pCert, &cbCert) ) { // set the data into place in the object f = mbBase.SetData( szObjectPath, MD_MAPCERT, IIS_MD_UT_SERVER, BINARY_METADATA, pCert, cbCert, 0 ); } // save the NTAccount if ( pMap->GetNTAccount(sz) ) { // set the data into place in the object f = mbBase.SetString(szObjectPath, MD_MAPNTACCT, IIS_MD_UT_SERVER, sz, 0 ); } // save the password - secure if ( pMap->GetNTPassword(cspTempPassword) ) { // set the data into place in the object CString csTempPassword; cspTempPassword.CopyTo(csTempPassword); f = mbBase.SetString(szObjectPath, MD_MAPNTPWD, IIS_MD_UT_SERVER, csTempPassword, METADATA_SECURE); } // save the map's name if ( pMap->GetMapName(sz) ) { // set the data into place in the object f = mbBase.SetString(szObjectPath, MD_MAPNAME, IIS_MD_UT_SERVER, sz, 0); } // save the Enabled flag // server reads the flag as the value of the dword if ( pMap->GetMapEnabled(&f) ) { dwEnabled = (DWORD)f; f = mbBase.SetDword( szObjectPath, MD_MAPENABLED, IIS_MD_UT_SERVER, dwEnabled, 0 ); } } } // close the base object mbBase.Close(); // save the metabase mbBase.Save(); // reset the to-be-saved list m_rgbSave.RemoveAll(); // return success return TRUE; } //--------------------------------------------------------------------------- C11Mapping* CMap11Page::PNewMapping() { // the way it should be return new C11Mapping(); } //--------------------------------------------------------------------------- void CMap11Page::DeleteMapping( C11Mapping* pMap ) { // the way it should be delete pMap; }