📄 ldap.c
字号:
timeout = 15;
ldap_set_option( ldapInfo->ld, LDAP_OPT_TIMELIMIT, &timeout );
ldap_set_option( ldapInfo->ld, LDAP_OPT_SIZELIMIT, &maxEntries );
/* Set up the names of the objects and attributes */
assignFieldName( keysetInfoPtr->ownerHandle, ldapInfo->nameObjectClass,
CRYPT_OPTION_KEYS_LDAP_OBJECTCLASS );
assignFieldName( keysetInfoPtr->ownerHandle, ldapInfo->nameFilter,
CRYPT_OPTION_KEYS_LDAP_FILTER );
assignFieldName( keysetInfoPtr->ownerHandle, ldapInfo->nameCACert,
CRYPT_OPTION_KEYS_LDAP_CACERTNAME );
assignFieldName( keysetInfoPtr->ownerHandle, ldapInfo->nameCert,
CRYPT_OPTION_KEYS_LDAP_CERTNAME );
assignFieldName( keysetInfoPtr->ownerHandle, ldapInfo->nameCRL,
CRYPT_OPTION_KEYS_LDAP_CRLNAME );
assignFieldName( keysetInfoPtr->ownerHandle, ldapInfo->nameEmail,
CRYPT_OPTION_KEYS_LDAP_EMAILNAME );
krnlSendMessage( keysetInfoPtr->ownerHandle, IMESSAGE_GETATTRIBUTE,
&ldapInfo->objectType,
CRYPT_OPTION_KEYS_LDAP_OBJECTTYPE );
return( CRYPT_OK );
}
/****************************************************************************
* *
* Directory Access Routines *
* *
****************************************************************************/
/* Send a query to an LDAP server */
static int sendLdapQuery( LDAP_INFO *ldapInfo, LDAPMessage **resultPtr,
const CRYPT_HANDLE iOwnerHandle, const char *dn )
{
const CRYPT_CERTTYPE_TYPE objectType = ldapInfo->objectType;
const char *certAttributes[] = { ldapInfo->nameCert, NULL };
const char *caCertAttributes[] = { ldapInfo->nameCACert, NULL };
const char *crlAttributes[] = { ldapInfo->nameCRL, NULL };
struct timeval ldapTimeout = { 0, 0 };
int ldapStatus = LDAP_OTHER, timeout;
/* Network I/O may be set to be nonblocking, so we make sure we try for
at least 15s before timing out */
krnlSendMessage( iOwnerHandle, IMESSAGE_GETATTRIBUTE, &timeout,
CRYPT_OPTION_NET_READTIMEOUT );
ldapTimeout.tv_sec = max( timeout, 15 );
/* If the LDAP search-by-URL functions are available and the key ID is
an LDAP URL, perform a search by URL */
if( ldap_is_ldap_url != NULL && ldap_is_ldap_url( ( char * ) dn ) )
return( ldap_url_search_st( ldapInfo->ld, ( char * ) dn, FALSE,
&ldapTimeout, resultPtr ) );
/* Try and retrieve the entry for this DN from the directory. We use a
base specified by the DN, a chop of 0 (to return only the current
entry), any object class (to get around the problem of
implementations which stash certificates in whatever they feel like),
and look for a certificate attribute. If the search fails for this
attribute, we try again but this time go for a CA certificate
attribute which unfortunately slows down the search somewhat when the
certificate isn't found but can't really be avoided since there's no
way to tell in advance whether a certificate will be an end entity or
a CA certificate. To complicate things even further, we may also
need to check for a CRL in case this is what the user is after */
if( objectType == CRYPT_CERTTYPE_NONE || \
objectType == CRYPT_CERTTYPE_CERTIFICATE )
{
ldapStatus = ldap_search_st( ldapInfo->ld, dn, LDAP_SCOPE_BASE,
ldapInfo->nameFilter,
( char ** ) certAttributes, 0,
&ldapTimeout, resultPtr );
if( ldapStatus == LDAP_SUCCESS )
return( ldapStatus );
}
if( objectType == CRYPT_CERTTYPE_NONE || \
objectType == CRYPT_CERTTYPE_CERTIFICATE )
{
ldapStatus = ldap_search_st( ldapInfo->ld, dn, LDAP_SCOPE_BASE,
ldapInfo->nameFilter,
( char ** ) caCertAttributes, 0,
&ldapTimeout, resultPtr );
if( ldapStatus == LDAP_SUCCESS )
return( ldapStatus );
}
if( objectType == CRYPT_CERTTYPE_NONE || \
objectType == CRYPT_CERTTYPE_CRL )
{
ldapStatus = ldap_search_st( ldapInfo->ld, dn, LDAP_SCOPE_BASE,
ldapInfo->nameFilter,
( char ** ) crlAttributes, 0,
&ldapTimeout, resultPtr );
if( ldapStatus == LDAP_SUCCESS )
return( ldapStatus );
}
return( ldapStatus );
}
/* Retrieve a key attribute from an LDAP directory */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 5 ) ) \
static int getItemFunction( INOUT KEYSET_INFO *keysetInfoPtr,
OUT_HANDLE_OPT CRYPT_HANDLE *iCryptHandle,
IN_ENUM( KEYMGMT_ITEM ) \
const KEYMGMT_ITEM_TYPE itemType,
IN_KEYID const CRYPT_KEYID_TYPE keyIDtype,
IN_BUFFER( keyIDlength ) const void *keyID,
IN_LENGTH_KEYID const int keyIDlength,
IN_OPT void *auxInfo,
INOUT_OPT int *auxInfoLength,
IN_FLAGS_Z( KEYMGMT ) const int flags )
{
LDAP_INFO *ldapInfo = keysetInfoPtr->keysetLDAP;
LDAPMessage *result = DUMMY_INIT_PTR, *resultEntry;
BerElement *ber;
struct berval **valuePtrs;
char dn[ MAX_DN_STRINGSIZE + 8 ];
char *attributePtr;
int ldapStatus, status = CRYPT_OK;
assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );
assert( isWritePtr( iCryptHandle, sizeof( CRYPT_HANDLE ) ) );
assert( isReadPtr( keyID, keyIDlength ) );
REQUIRES( keysetInfoPtr->type == KEYSET_LDAP );
REQUIRES( itemType == KEYMGMT_ITEM_PUBLICKEY );
REQUIRES( keyIDtype != CRYPT_KEYID_NONE || iCryptHandle != NULL );
REQUIRES( keyIDlength >= MIN_NAME_LENGTH && \
keyIDlength < MAX_ATTRIBUTE_SIZE );
REQUIRES( auxInfo == NULL && *auxInfoLength == 0 );
REQUIRES( flags >= KEYMGMT_FLAG_NONE && flags < KEYMGMT_FLAG_MAX );
/* Convert the DN into a null-terminated form */
if( keyIDlength > MAX_DN_STRINGSIZE - 1 )
return( CRYPT_ARGERROR_STR1 );
memcpy( dn, keyID, keyIDlength );
dn[ keyIDlength ] = '\0';
/* Send the LDAP query to the server */
ldapStatus = sendLdapQuery( ldapInfo, &result,
keysetInfoPtr->ownerHandle, dn );
if( ldapStatus != LDAP_SUCCESS )
{
getErrorInfo( keysetInfoPtr, ldapStatus );
return( mapLdapError( ldapStatus, CRYPT_ERROR_READ ) );
}
/* We got something, start fetching the results */
if( ( resultEntry = ldap_first_entry( ldapInfo->ld, result ) ) == NULL )
{
ldap_msgfree( result );
return( CRYPT_ERROR_NOTFOUND );
}
/* Copy out the certificate */
if( ( attributePtr = ldap_first_attribute( ldapInfo->ld, resultEntry,
&ber ) ) == NULL )
{
ldap_msgfree( result );
return( CRYPT_ERROR_NOTFOUND );
}
valuePtrs = ldap_get_values_len( ldapInfo->ld, resultEntry,
attributePtr );
if( valuePtrs != NULL )
{
MESSAGE_CREATEOBJECT_INFO createInfo;
/* Create a certificate object from the returned data */
setMessageCreateObjectIndirectInfo( &createInfo, valuePtrs[ 0 ]->bv_val,
valuePtrs[ 0 ]->bv_len,
CRYPT_CERTTYPE_NONE );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_DEV_CREATEOBJECT_INDIRECT,
&createInfo, OBJECT_TYPE_CERTIFICATE );
if( cryptStatusOK( status ) )
*iCryptHandle = createInfo.cryptHandle;
ldap_value_free_len( valuePtrs );
}
else
status = CRYPT_ERROR_NOTFOUND;
/* Clean up. The ber_free() function is rather problematic because
Netscape uses the nonstandard ldap_ber_free() name (which can be fixed
with proprocessor trickery), Microsoft first omitted it entirely (up
to NT4 SP4) and then later added it as a stub (Win2K, rumour has it
that the only reason this function even exists is because the Netscape
client required it), and OpenLDAP doesn't use it at all. Because it
may or may not exist in the MS client, we call it if we resolved its
address, otherwise we skip it.
The function is further complicated by the fact that LDAPv3 says the
second parameter should be 0, however the Netscape client docs used to
require it to be 1 and the MS client was supposed to ignore it so the
code passed in a 1. Actually the way the MS implementation handles
the BER data is that the BerElement returned by ldap_first_attribute()
is (despite what the MSDN docs claim) just a data structure pointed to
by lm_ber in the LDAPMessage structure, all that
ldap_first_attribute() does is redirect the lm_ber pointer inside the
LDAPMessage, so actually freeing this wouldn't be a good idea).
Later, the Netscape docs were updated to require a 0, presumably to
align them with the LDAPv3 spec. On some systems it makes no
difference whether you pass in a 0 or 1 to the call, but on others it
can cause an access violation. Presumably eventually everyone will
move to something which implements the new rather than old Netscape-
documented behaviour, so we pass in 0 as the argument.
It gets worse than this though. Calling ber_free() with newer
versions of the Windows LDAP client with any argument at all causes
internal data corruption which typically first results in a soft
failure (e.g. a data fetch fails) and then eventually a hard failure
such as an access violation after further calls are made. The only
real way to fix this is to avoid calling it entirely, this doesn't
seem to leak any more memory than Winsock leaks anyway (that is,
there are a considerable number of memory and handle leaks, but the
number doesn't increase if ber_free() isn't called).
There have been reports that with some older versions of the Windows
LDAP client (e.g. the one in Win95) the ldap_msgfree() call generates
an exception in wldap.dll, if this is a problem you need to either
install a newer LDAP DLL or switch to the Netscape one.
The reason for some of the Windows problems are because the
wldap32.lib shipped with VC++ uses different ordinals than the
wldap32.dll which comes with the OS (see MSKB article Q283199), so
that simply using the out-of-the-box development tools with the out-
of-the-box OS can result in access violations and assorted other
problems */
#ifdef NETSCAPE_CLIENT
if( ber_free != NULL )
ber_free( ber, 0 );
#endif /* NETSCAPE_CLIENT */
ldap_memfree( attributePtr );
ldap_msgfree( result );
return( status );
}
/* Add an entry/attribute to an LDAP directory. The LDAP behaviour differs
somewhat from DAP in that assigning a value to a nonexistant attribute
implicitly creates the required attribute. In addition deleting the last
value automatically deletes the entire attribute, the delete item code
assumes the user is requesting a superset of this behaviour and deletes
the entire entry */
static int addCert( KEYSET_INFO *keysetInfoPtr,
const CRYPT_HANDLE iCryptHandle )
{
LDAP_INFO *ldapInfo = keysetInfoPtr->keysetLDAP;
LDAPMod *ldapMod[ MAX_LDAP_ATTRIBUTES + 8 ];
MESSAGE_DATA msgData;
BYTE keyData[ MAX_CERT_SIZE + 8 ];
char dn[ MAX_DN_STRINGSIZE + 8 ];
char C[ CRYPT_MAX_TEXTSIZE + 1 + 8 ], SP[ CRYPT_MAX_TEXTSIZE + 1 + 8 ],
L[ CRYPT_MAX_TEXTSIZE + 1 + 8 ], O[ CRYPT_MAX_TEXTSIZE + 1 + 8 ],
OU[ CRYPT_MAX_TEXTSIZE + 1 + 8 ], CN[ CRYPT_MAX_TEXTSIZE + 1 + 8 ],
email[ CRYPT_MAX_TEXTSIZE + 1 + 8 ];
int keyDataLength, ldapModIndex = 1, status = CRYPT_OK;
*C = *SP = *L = *O = *OU = *CN = *email = '\0';
/* Extract the DN and altName components. This changes the currently
selected DN components, but this is OK since we've got the
certificate locked and the prior state will be restored when we
unlock it */
krnlSendMessage( iCryptHandle, IMESSAGE_SETATTRIBUTE,
MESSAGE_VALUE_UNUSED, CRYPT_CERTINFO_SUBJECTNAME );
setMessageData( &msgData, C, CRYPT_MAX_TEXTSIZE );
status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_CERTINFO_COUNTRYNAME );
if( cryptStatusOK( status ) )
C[ msgData.length ] = '\0';
if( cryptStatusOK( status ) || status == CRYPT_ERROR_NOTFOUND )
{
setMessageData( &msgData, SP, CRYPT_MAX_TEXTSIZE );
status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_CERTINFO_STATEORPROVINCENAME );
}
if( cryptStatusOK( status ) )
SP[ msgData.length ] = '\0';
if( cryptStatusOK( status ) || status == CRYPT_ERROR_NOTFOUND )
{
setMessageData( &msgData, L, CRYPT_MAX_TEXTSIZE );
status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_CERTINFO_LOCALITYNAME );
}
if( cryptStatusOK( status ) )
L[ msgData.length ] = '\0';
if( cryptStatusOK( status ) || status == CRYPT_ERROR_NOTFOUND )
{
setMessageData( &msgData, O, CRYPT_MAX_TEXTSIZE );
status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_CERTINFO_ORGANIZATIONNAME );
}
if( cryptStatusOK( status ) )
O[ msgData.length ] = '\0';
if( cryptStatusOK( status ) || status == CRYPT_ERROR_NOTFOUND )
{
setMessageData( &msgData, OU, CRYPT_MAX_TEXTSIZE );
status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_CERTINFO_ORGANIZATIONALUNITNAME );
}
if( cryptStatusOK( status ) )
OU[ msgData.length ] = '\0';
if( cryptStatusOK( status ) || status == CRYPT_ERROR_NOTFOUND )
{
setMessageData( &msgData, CN, CRYPT_MAX_TEXTSIZE );
status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_CERTINFO_COMMONNAME );
}
if( cryptStatusOK( status ) )
CN[ msgData.length ] = '\0';
if( cryptStatusOK( status ) || status == CRYPT_ERROR_NOTFOUND )
{
/* Get the string form of the DN */
status = encodeDN( dn, MAX_DN_STRINGSIZE, C, SP, L, O, OU, CN );
}
if( cryptStatusError( status ) )
{
/* Convert any low-level certificate-specific error into something
generic which makes a bit more sense to the caller */
return( CRYPT_ARGERROR_NUM1 );
}
/* Get the certificate data */
setMessageData( &msgData, keyData, MAX_CERT_SIZE );
status = krnlSendMessage( iCryptHandle, IMESSAGE_CRT_EXPORT, &msgData,
CRYPT_CERTFORMAT_CERTIFICATE );
keyDataLength = msgData.length;
if( cryptStatusError( status ) )
{
/* Convert any low-level certificate-specific error into something
generic which makes a bit more sense to the caller */
return( CRYPT_ARGERROR_NUM1 );
}
/* Set up the fixed attributes and certificate data. This currently
always adds a certificate as a standard certificate rather than a CA
certificate because of uncertainty over what other implementations
will try and look for, once enough other software uses the CA
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -