/*++ Copyright (c) 1997-2000 Microsoft Corporation Module Name: rndldap.cpp Abstract: This module contains implementation of ldap helper functions. --*/ #include "stdafx.h" #include "rndldap.h" #include "ntldap.h" HRESULT GetAttributeValue( IN LDAP * pLdap, IN LDAPMessage * pEntry, IN const WCHAR * pName, OUT BSTR * pValue ) { *pValue = NULL; TCHAR **p = ldap_get_values(pLdap, pEntry, (WCHAR *)pName); if (p != NULL) { if (p[0] != NULL) { *pValue = SysAllocString(p[0]); } ldap_value_free(p); } return (*pValue == NULL) ? E_FAIL : S_OK; } HRESULT GetAttributeValueBer( IN LDAP * pLdap, IN LDAPMessage * pEntry, IN const WCHAR * pName, OUT char ** pValue, OUT DWORD * pdwSize ) { *pValue = NULL; struct berval **p = ldap_get_values_len(pLdap, pEntry, (WCHAR *)pName); if (p != NULL) { if (p[0] != NULL) { *pValue = new CHAR[p[0]->bv_len]; if (*pValue == NULL) { return E_OUTOFMEMORY; } memcpy(*pValue, p[0]->bv_val, p[0]->bv_len); *pdwSize = p[0]->bv_len; } ldap_value_free_len(p); } return (*pValue == NULL) ? E_FAIL : S_OK; } HRESULT GetNamingContext(LDAP *hLdap, TCHAR **ppNamingContext) { // send a search (base level, base dn = "", filter = "objectclass=*") // ask only for the defaultNamingContext attribute PTCHAR Attributes[] = {(WCHAR *)DEFAULT_NAMING_CONTEXT, NULL}; LDAPMessage *SearchResult; ULONG res = DoLdapSearch( hLdap, // ldap handle L"", // empty base dn LDAP_SCOPE_BASE, // base level search (WCHAR *)ANY_OBJECT_CLASS, // instance of any object class Attributes, // array of attribute names FALSE, // also return the attribute values &SearchResult // search results ); BAIL_IF_LDAP_FAIL(res, "Search for oganization"); // associate the ldap handle with the search message holder, so that the // search message may be released when the instance goes out of scope CLdapMsgPtr MessageHolder(SearchResult); TCHAR **NamingContext; LDAPMessage *EntryMessage = ldap_first_entry(hLdap, SearchResult); while ( NULL != EntryMessage ) { // look for the value for the namingContexts attribute NamingContext = ldap_get_values( hLdap, EntryMessage, (WCHAR *)DEFAULT_NAMING_CONTEXT ); // the first entry contains the naming context and its a single // value(null terminated) if a value is found, create memory for // the directory path, set the dir path length if ( (NULL != NamingContext) && (NULL != NamingContext[0]) && (NULL == NamingContext[1]) ) { // the naming context value is released when the ValueHolder // instance goes out of scope CLdapValuePtr ValueHolder(NamingContext); *ppNamingContext = new TCHAR [lstrlen(NamingContext[0]) + 1]; BAIL_IF_NULL(*ppNamingContext, E_OUTOFMEMORY); lstrcpy(*ppNamingContext, NamingContext[0]); // return success return S_OK; } // Get next entry. EntryMessage = ldap_next_entry(hLdap, EntryMessage); } // none found, return error return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); } ULONG DoLdapSearch ( LDAP *ld, PWCHAR base, ULONG scope, PWCHAR filter, PWCHAR attrs[], ULONG attrsonly, LDAPMessage **res, BOOL bSACL /*=TRUE */ ) { LDAP_TIMEVAL Timeout; Timeout.tv_sec = REND_LDAP_TIMELIMIT; Timeout.tv_usec = 0; // // Without SACLs // SECURITY_INFORMATION seInfo = DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION; // // Ber val // BYTE berValue[2*sizeof(ULONG)]; berValue[0] = 0x30; berValue[1] = 0x03; berValue[2] = 0x02; berValue[3] = 0x01; berValue[4] = (BYTE)(seInfo & 0xF); // // LDAP server control // LDAPControlW seInfoControl = { LDAP_SERVER_SD_FLAGS_OID_W, {5, (PCHAR)berValue}, TRUE }; // // LDAP Server controls list // PLDAPControlW serverControls[2] = { &seInfoControl, NULL}; PLDAPControlW* pServerControls = NULL; if( !bSACL ) { pServerControls = serverControls; } ULONG ulRes = ldap_search_ext_sW(ld, base, scope, filter, attrs, attrsonly, pServerControls, // server controls NULL, // client controls &Timeout, // timeout value 0, // maximum size res); // // The ldap_search* APIs are quirky in that they require you to free the // result even if the call fails. ldap_msgfree() checks its argument, so // this also doesn't break if the result *wasn't* left around. This is // inconsistent with pretty much all other Windows system APIs; to keep // from obfuscating the callers of DoLdapSearch, we free the result here // in the failure case. That way, callers can treat DoLdapSearch like any // other function that cleans up after itself on failure. // // Some callers use smart pointers that would free the message on destruction, // and some do not. Therefore we set *res to NULL after freeing *res, to // protect ourselves in either case. Any subsequent ldap_msgfree(NULL) will // do nothing (and will not fault). // if ( ulRes != LDAP_SUCCESS ) { ldap_msgfree( *res ); *res = NULL; } return ulRes; } // // Translates the result of an ldap_result call to an ldap error code. // ULONG LdapErrorFromLdapResult(ULONG res, LDAPMessage * pResultMessage) { ULONG ulCode; if ( res == 0 ) { ulCode = LDAP_TIMEOUT; } else if ( res == (ULONG) -1 ) { ulCode = LDAP_LOCAL_ERROR; } else { // ulCode = LDAP_SUCCESS; ulCode = pResultMessage->lm_returncode; } ldap_msgfree( pResultMessage ); return ulCode; } ULONG DoLdapAdd ( LDAP *ld, PWCHAR dn, LDAPModW *attrs[] ) { // // Ask to add an object.We get back an error/success code and a // message number so that we can refer to this pending message. // ULONG ulMessageNumber; ULONG res1 = ldap_add_extW(ld, dn, attrs, NULL, // server controls NULL, // client controls &ulMessageNumber); BAIL_IF_LDAP_FAIL(res1, "ldap_add_extW"); // // Wait for the result, specifying a timeout. We get // back an error/success code and a result message. // LDAP_TIMEVAL Timeout; Timeout.tv_sec = REND_LDAP_TIMELIMIT; Timeout.tv_usec = 0; LDAPMessage * pResultMessage; ULONG res2 = ldap_result(ld, ulMessageNumber, LDAP_MSG_ALL, &Timeout, &pResultMessage); // // Extract return code and free message. // return LdapErrorFromLdapResult(res2, pResultMessage); } ULONG DoLdapModify ( BOOL fChase, LDAP *ld, PWCHAR dn, LDAPModW *attrs[], BOOL bSACL /*=TRUE */ ) { // // Chase referrals. These is only used if fChase is set, but we must not // stick them in an "if" block or they will not have appropriate scope. // LDAPControlW control; LDAPControlW * controls [] = {&control, NULL}; ULONG ulValue = LDAP_CHASE_EXTERNAL_REFERRALS | LDAP_CHASE_SUBORDINATE_REFERRALS; if ( fChase ) { control.ldctl_iscritical = 1; control.ldctl_oid = LDAP_CONTROL_REFERRALS_W; control.ldctl_value.bv_len = sizeof(ULONG); control.ldctl_value.bv_val = (char *) &ulValue; } // // Without SACLs // SECURITY_INFORMATION seInfo = DACL_SECURITY_INFORMATION ; // // Ber val // BYTE berValue[2*sizeof(ULONG)]; berValue[0] = 0x30; berValue[1] = 0x03; berValue[2] = 0x02; berValue[3] = 0x01; berValue[4] = (BYTE)(seInfo & 0xF); // // LDAP server control // LDAPControlW seInfoControl = { LDAP_SERVER_SD_FLAGS_OID_W, {5, (PCHAR)berValue}, TRUE }; // // LDAP Server controls list // PLDAPControlW serverControls[2] = { &seInfoControl, NULL}; PLDAPControlW* pServerControls = NULL; if( !bSACL ) { pServerControls = serverControls; } // // Ask to modify an object.We get back an error/success code and a // message number so that we can refer to this pending message. // ULONG ulMessageNumber; ULONG res1 = ldap_modify_extW(ld, dn, attrs, pServerControls, // server controls fChase ? controls : NULL, // client controls &ulMessageNumber); BAIL_IF_LDAP_FAIL(res1, "ldap_modify_extW"); // // Wait for the result, specifying a timeout. We get // back an error/success code and a result message. // LDAP_TIMEVAL Timeout; Timeout.tv_sec = REND_LDAP_TIMELIMIT; Timeout.tv_usec = 0; LDAPMessage * pResultMessage; ULONG res2 = ldap_result(ld, ulMessageNumber, LDAP_MSG_ALL, &Timeout, &pResultMessage); // // Extract return code and free message. // return LdapErrorFromLdapResult(res2, pResultMessage); } ULONG DoLdapDelete ( LDAP *ld, PWCHAR dn ) { // // Ask to delete an object.We get back an error/success code and a // message number so that we can refer to this pending message. // ULONG ulMessageNumber; ULONG res1 = ldap_delete_extW(ld, dn, NULL, // server controls NULL, // client controls &ulMessageNumber); BAIL_IF_LDAP_FAIL(res1, "ldap_delete_extW"); // // Wait for the result, specifying a timeout. We get // back an error/success code and a result message. // LDAP_TIMEVAL Timeout; Timeout.tv_sec = REND_LDAP_TIMELIMIT; Timeout.tv_usec = 0; LDAPMessage * pResultMessage; ULONG res2 = ldap_result(ld, ulMessageNumber, LDAP_MSG_ALL, &Timeout, &pResultMessage); // // Extract return code and free message. // return LdapErrorFromLdapResult(res2, pResultMessage); } HRESULT SetTTL( IN LDAP * pLdap, IN const WCHAR * pDN, IN DWORD dwTTL ) /*++ Routine Description: Set the TTL of a dynamic object for either ILS or NDNC. Arguments: pLdap - The Ldap connection. pDN - The DN of a object on the ILS server. dwTTL - The time to live value. Return Value: HRESULT. --*/ { TCHAR strTTL[32]; // The attribute value is a DWORD in string. wsprintf(strTTL, _T("%d"), dwTTL); TCHAR * ttl[] = {strTTL, NULL}; LDAPMod mod; // The modify sturctures used by LDAP mod.mod_values = ttl; mod.mod_op = LDAP_MOD_REPLACE; mod.mod_type = (WCHAR *)ENTRYTTL; LDAPMod* mods[] = {&mod, NULL}; // only one attribute is modified. LOG((MSP_INFO, "setting TTL for %S", pDN)); BAIL_IF_LDAP_FAIL(DoLdapModify(FALSE, pLdap, (WCHAR *)pDN, mods), "set TTL"); return S_OK; } HRESULT UglyIPtoIP( BSTR pUglyIP, BSTR * pIP ) // This function converts NM's IP address format to the right format. { #define IPADDRLEN 16 WCHAR buffer[IPADDRLEN + 1]; DWORD dwIP; dwIP = _wtoi(pUglyIP); if (dwIP == 0) { return E_FAIL; } dwIP = ntohl(dwIP); // format the four bytes in the dword into an ip address string swprintf(buffer, L"%d.%d.%d.%d", HIBYTE(HIWORD(dwIP)), LOBYTE(HIWORD(dwIP)), HIBYTE(LOWORD(dwIP)), LOBYTE(LOWORD(dwIP)) ); *pIP = SysAllocString(buffer); BAIL_IF_NULL(*pIP, E_OUTOFMEMORY); return S_OK; } HRESULT ParseUserName( BSTR pName, BSTR * ppAddress ) { WCHAR * pCloseBracket = wcschr(pName, CLOSE_BRACKET_CHARACTER); if ( pCloseBracket == NULL ) { // this is not the format generated by us. return S_FALSE; } *ppAddress = SysAllocString(pCloseBracket + 1); BAIL_IF_NULL(*ppAddress, E_OUTOFMEMORY); *pCloseBracket = NULL_CHARACTER; return S_OK; } // eof