⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ldap.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 3 页
字号:
	/* Allocate room for the LDAPMod structure and the data pointers.
	   mod_values and mod_bvalues members have the same representation so we
	   can allocate them with the same malloc */
	if( ( ldapModPtr = ( LDAPMod * ) clAlloc( "copyAttribute", \
											  sizeof( LDAPMod ) ) ) == NULL )
		return( NULL );
	if( ( ldapModPtr->mod_values = clAlloc( "copyAttribute", \
											2 * sizeof( void * ) ) ) == NULL )
		{
		clFree( "copyAttribute", ldapModPtr );
		return( NULL );
		}

	/* Set up the pointers to the attribute information.  This differs
	   slightly depending on whether we're adding text or binary data */
	if( !attributeLength )
		{
		ldapModPtr->mod_op = LDAP_MOD_ADD;
		ldapModPtr->mod_type = ( char * ) attributeName;
		ldapModPtr->mod_values[ 0 ] = ( char * ) attributeValue;
		ldapModPtr->mod_values[ 1 ] = NULL;
		}
	else
		{
		if( ( ldapModPtr->mod_bvalues[ 0 ] = \
				clAlloc( "copyAttribute", sizeof( struct berval ) ) ) == NULL )
			{
			clFree( "copyAttribute", ldapModPtr->mod_values );
			clFree( "copyAttribute", ldapModPtr );
			return( NULL );
			}
		ldapModPtr->mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
		ldapModPtr->mod_type = ( char * ) attributeName;
		ldapModPtr->mod_bvalues[ 0 ]->bv_len = attributeLength;
		ldapModPtr->mod_bvalues[ 0 ]->bv_val = ( char * ) attributeValue;
		ldapModPtr->mod_bvalues[ 1 ] = NULL;
		}

	return( ldapModPtr );
	}

/* Encode DN information in the RFC 1779 reversed format.  We don't have to
   check for overflows because the cert.management code limits the size of
   each component to a small fraction of the total buffer size */

static void copyComponent( char *dest, char *src )
	{
	while( *src )
		{
		const char ch = *src++;

		if( ch == ',' )
			*dest++ = '\\';
		*dest++ = ch;
		}
	*dest++ = '\0';
	}

static int encodeDN( char *dn, char *C, char *SP, char *L, char *O, char *OU,
					 char *CN )
	{
	char *bufPtr = dn;

	strcpy( dn, "CN=" );
	strcpy( dn + 3, CN );
	bufPtr += strlen( bufPtr );
	if( *OU )
		{
		strcpy( bufPtr, ",OU=" );
		copyComponent( bufPtr + 4, OU );
		bufPtr += strlen( bufPtr );
		}
	if( *O )
		{
		strcpy( bufPtr, ",O=" );
		copyComponent( bufPtr + 3, O );
		bufPtr += strlen( bufPtr );
		}
	if( *L )
		{
		strcpy( bufPtr, ",L=" );
		copyComponent( bufPtr + 3, L );
		bufPtr += strlen( bufPtr );
		}
	if( *SP )
		{
		strcpy( bufPtr, ",ST=" );	/* Not to be confused with ST=street */
		copyComponent( bufPtr + 4, SP );
		bufPtr += strlen( bufPtr );
		}
	strcpy( bufPtr, ",C=" );
	copyComponent( bufPtr + 3, C );

	return( CRYPT_OK );
	}

/* Decompose an LDAP URL of the general form ldap://server:port/user into its
   various components */

static int parseURL( char *ldapServer, char **ldapUser, int *ldapPort )
	{
	char *strPtr;

	/* Clear return value */
	*ldapUser = NULL;
	*ldapPort = LDAP_PORT;

	/* Handle a leading URL specifier if this is present */
	if( !strCompare( ldapServer, "ldaps://", 8 ) )
		/* We can't do LDAP over SSL without a lot of extra work */
		return( CRYPT_ERROR_BADDATA );
	if( !strCompare( ldapServer, "ldap://", 7 ) )
		memmove( ldapServer, ldapServer + 7, strlen( ldapServer ) - 6 );

	/* Decompose what's left into a FQDN, port, and user name */
	if( ( strPtr = strchr( ldapServer, '/' ) ) != NULL )
		{
		*strPtr++ = '\0';
		*ldapUser = strPtr;
		}
	if( ( strPtr = strchr( ldapServer, ':' ) ) != NULL )
		{
		*strPtr++ = '\0';
		*ldapPort = aToI( strPtr );
		if( *ldapPort < 26 || *ldapPort > 65534L )
			return( CRYPT_ERROR_BADDATA );
		}

	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*						 	Directory Open/Close Routines					*
*																			*
****************************************************************************/

/* Close a previously-opened LDAP connection.  We have to have this before
   the init function since it may be called by it if the open process fails.
   This is necessary because the complex LDAP open may require a fairly
   extensive cleanup afterwards */

static void shutdownFunction( KEYSET_INFO *keysetInfo )
	{
	LDAP_INFO *ldapInfo = keysetInfo->keysetLDAP;

	ldap_unbind( ldapInfo->ld );
	ldapInfo->ld = NULL;
	}

/* Open a connection to an LDAP directory */

static int initFunction( KEYSET_INFO *keysetInfo, const char *server,
						 const CRYPT_KEYOPT_TYPE options )
	{
	LDAP_INFO *ldapInfo = keysetInfo->keysetLDAP;
	char ldapServer[ MAX_URL_SIZE ], *ldapUser;
	int maxEntries = 2, timeout, ldapPort, ldapStatus = LDAP_OTHER, status;

	/* Check the URL.  The Netscape API provides the function
	   ldap_is_ldap_url() for this, but this requires a complete LDAP URL
	   rather than just a server name and port */
	if( strlen( server ) > MAX_URL_SIZE - 1 )
		return( CRYPT_ARGERROR_STR1 );
	strcpy( ldapServer, server );
	status = parseURL( ldapServer, &ldapUser, &ldapPort );
	if( cryptStatusError( status ) )
		return( CRYPT_ARGERROR_STR1 );

	/* Open the connection to the server */
	if( ( ldapInfo->ld = ldap_init( ldapServer, ldapPort ) ) == NULL )
		return( CRYPT_ERROR_OPEN );
	if( ( ldapStatus = ldap_simple_bind_s( ldapInfo->ld, ldapUser, 
										   NULL ) ) != LDAP_SUCCESS )
		{
		getErrorInfo( keysetInfo, ldapStatus );
		ldap_unbind( ldapInfo->ld );
		ldapInfo->ld = NULL;
		return( mapLDAPerror( ldapStatus, CRYPT_ERROR_OPEN ) );
		}

	/* Set the search timeout and limit the maximum number of returned
	   entries to 2 (setting the search timeout is mostly redundant since we
	   use search_st anyway, however there may be other operations which also
	   require some sort of timeout which can't be explicitly specified */
	krnlSendMessage( keysetInfo->ownerHandle, IMESSAGE_GETATTRIBUTE,
					 &timeout, CRYPT_OPTION_NET_READTIMEOUT );
	if( timeout < 15 )
		/* Network I/O may be set to be nonblocking, so we make sure we try
		   for at least 15s before timing out */
		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( keysetInfo->ownerHandle, ldapInfo->nameObjectClass,
					 CRYPT_OPTION_KEYS_LDAP_OBJECTCLASS );
	assignFieldName( keysetInfo->ownerHandle, ldapInfo->nameFilter,
					 CRYPT_OPTION_KEYS_LDAP_FILTER );
	assignFieldName( keysetInfo->ownerHandle, ldapInfo->nameCACert,
					 CRYPT_OPTION_KEYS_LDAP_CACERTNAME );
	assignFieldName( keysetInfo->ownerHandle, ldapInfo->nameCert,
					 CRYPT_OPTION_KEYS_LDAP_CERTNAME );
	assignFieldName( keysetInfo->ownerHandle, ldapInfo->nameCRL,
					 CRYPT_OPTION_KEYS_LDAP_CRLNAME );
	assignFieldName( keysetInfo->ownerHandle, ldapInfo->nameEmail,
					 CRYPT_OPTION_KEYS_LDAP_EMAILNAME );
	krnlSendMessage( keysetInfo->ownerHandle, IMESSAGE_GETATTRIBUTE,
					 &ldapInfo->objectType,
					 CRYPT_OPTION_KEYS_LDAP_OBJECTTYPE );

	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*						 	Directory Access Routines						*
*																			*
****************************************************************************/

/* Retrieve a key attribute from an LDAP directory */

static int getItemFunction( KEYSET_INFO *keysetInfo,
							CRYPT_HANDLE *iCryptHandle,
							const KEYMGMT_ITEM_TYPE itemType,
							const CRYPT_KEYID_TYPE keyIDtype,
							const void *keyID,  const int keyIDlength,
							void *auxInfo, int *auxInfoLength,
							const int flags )
	{
	LDAP_INFO *ldapInfo = keysetInfo->keysetLDAP;
	LDAPMessage *result, *resultEntry;
	BerElement *ber;
	struct berval **valuePtrs;
	char *attributePtr;
	int status = CRYPT_OK;

	assert( keyIDtype != CRYPT_KEYID_NONE || iCryptHandle != NULL );
	assert( itemType == KEYMGMT_ITEM_PUBLICKEY );
	assert( auxInfo == NULL ); assert( *auxInfoLength == 0 );

	/* If we're not in the middle of an ongoing fetch, send the query to the
	   server */
	if( !ldapInfo->queryInProgress )
		{
		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 };
		char dn[ MAX_DN_STRINGSIZE ];
		int ldapStatus = LDAP_OTHER, timeout;

		assert( keyIDtype == CRYPT_KEYID_NAME || \
				keyIDtype == CRYPT_KEYID_URI );

		/* Network I/O may be set to be nonblocking, so we make sure we try
		   for at least 15s before timing out */
		krnlSendMessage( keysetInfo->ownerHandle, IMESSAGE_GETATTRIBUTE, 
						 &timeout, CRYPT_OPTION_NET_READTIMEOUT );
		ldapTimeout.tv_sec = max( timeout, 15 );

		/* 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';

		/* 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( dn ) )
			ldapStatus = ldap_url_search_st( ldapInfo->ld, dn, FALSE, 
											 &ldapTimeout, &result );
		else
			{
			/* 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 certs 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 cert isn't found but can't
			   really be avoided since there's no way to tell in advance
			   whether a cert will be an end entity or a CA cert.  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, &result );
			if( ldapStatus != LDAP_SUCCESS && \
				( objectType == CRYPT_CERTTYPE_NONE || \
				  objectType == CRYPT_CERTTYPE_CERTIFICATE ) )
				ldapStatus = ldap_search_st( ldapInfo->ld, dn, LDAP_SCOPE_BASE,
											 ldapInfo->nameFilter,
											 ( char ** ) caCertAttributes, 0,
											 &ldapTimeout, &result );
			if( ldapStatus != LDAP_SUCCESS && \
				( objectType == CRYPT_CERTTYPE_NONE || \
				  objectType == CRYPT_CERTTYPE_CRL ) )
				ldapStatus = ldap_search_st( ldapInfo->ld, dn, LDAP_SCOPE_BASE,
											 ldapInfo->nameFilter,
											 ( char ** ) crlAttributes, 0,
											 &ldapTimeout, &result );
			}
		if( ldapStatus != LDAP_SUCCESS )
			{
			getErrorInfo( keysetInfo, 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 );
			}

		/* If we've been passed a null crypt handle, this is the start of a
		   general-purpose query rather than a single cert fetch, save the
		   query state and record the fact that we're in the middle of a
		   query */
		if( iCryptHandle == NULL )
			{
			ldapInfo->result = result;
			ldapInfo->queryInProgress = TRUE;
			}
		}
	else
		{
		/* We're in an ongoing query, try and fetch the next set of results */
		if( ( resultEntry = ldap_next_entry( ldapInfo->ld,
											 ldapInfo->result ) ) == NULL )
			{
			/* No more results, wrap up the processing */
			ldap_msgfree( ldapInfo->result );
			ldapInfo->result = NULL;
			return( CRYPT_ERROR_COMPLETE );
			}
		}

	/* Copy out the certificate */
	if( ( attributePtr = ldap_first_attribute( ldapInfo->ld, resultEntry, 
											   &ber ) ) == NULL )
		{
		if( ldapInfo->queryInProgress )
			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) and 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).  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-

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -