示例#1
0
static int
nsldapi_sasl_bind_s(
    LDAP                *ld,
    const char          *dn,
    const char          *mechanism,
    const struct berval *cred,
    LDAPControl         **serverctrls,
    LDAPControl         **clientctrls,
    struct berval       **servercredp,
    LDAPControl         ***responsectrls
)
{
        int             err, msgid;
        LDAPMessage     *result;

        LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_sasl_bind_s\n", 0, 0, 0 );

        if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
                return( LDAP_PARAM_ERROR );
        }

        if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3 ) {
                LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL );
                return( LDAP_NOT_SUPPORTED );
        }

        if ( ( err = ldap_sasl_bind( ld, dn, mechanism, cred, serverctrls,
            clientctrls, &msgid )) != LDAP_SUCCESS )
                return( err );

        if ( ldap_result( ld, msgid, 1, (struct timeval *) 0, &result ) == -1 )
                return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );

        /* Get the controls sent by the server if requested */
        if ( responsectrls ) {
                if ( ( err = ldap_parse_result( ld, result, &err, NULL, NULL,
                       NULL, responsectrls, 0 )) != LDAP_SUCCESS )
                    return( err );
        }

        err = ldap_parse_sasl_bind_result( ld, result, servercredp, 0 );
        if (err != LDAP_SUCCESS  && err != LDAP_SASL_BIND_IN_PROGRESS) {
                ldap_msgfree( result );
                return( err );
        }

        return( ldap_result2error( ld, result, 1 ) );
}
示例#2
0
/*
 * ldap_sasl_bind - authenticate to the ldap server.  The dn, mechanism,
 * and credentials of the entry to which to bind are supplied. An LDAP
 * error code is returned and if LDAP_SUCCESS is returned *msgidp is set
 * to the id of the request initiated.
 *
 * Example:
 *	struct berval	creds;
 *	LDAPControl	**ctrls;
 *	int		err, msgid;
 *	... fill in creds with credentials ...
 *	... fill in ctrls with server controls ...
 *	err = ldap_sasl_bind( ld, "cn=manager, o=university of michigan, c=us",
 *	    "mechanismname", &creds, ctrls, NULL, &msgid );
 */
int
LDAP_CALL
ldap_sasl_bind(
    LDAP		*ld,
    const char		*dn,
    const char		*mechanism,
    const struct berval	*cred,
    LDAPControl		**serverctrls,
    LDAPControl		**clientctrls,
    int			*msgidp
)
{
	BerElement	*ber;
	int		rc, simple, msgid, ldapversion;

	/*
	 * The ldapv3 bind request looks like this:
	 *	BindRequest ::= SEQUENCE {
	 *		version		INTEGER,
	 *		name		DistinguishedName,	 -- who
	 *		authentication	CHOICE {
	 *			simple		[0] OCTET STRING, -- passwd
	 *			sasl		[3] SaslCredentials -- v3 only
	 *		}
	 *	}
	 *	SaslCredentials ::= SEQUENCE {
	 *		mechanism	LDAPString,
	 * 		credentials	OCTET STRING
	 *	}
	 * all wrapped up in an LDAPMessage sequence.
	 */

	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_sasl_bind\n", 0, 0, 0 );

	if ( msgidp == NULL ) {
		LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
                return( LDAP_PARAM_ERROR );
	}

	simple = ( mechanism == LDAP_SASL_SIMPLE );
	ldapversion = NSLDAPI_LDAP_VERSION( ld );

	/* only ldapv3 or higher can do sasl binds */
	if ( !simple && ldapversion < LDAP_VERSION3 ) {
		LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL );
		return( LDAP_NOT_SUPPORTED );
	}

	LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK );
	msgid = ++ld->ld_msgid;
	LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK );

	if ( dn == NULL )
		dn = "";

	if ( ld->ld_cache_on && ld->ld_cache_bind != NULL ) {
		LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK );
		if ( (rc = (ld->ld_cache_bind)( ld, msgid, LDAP_REQ_BIND, dn,
		    cred, LDAP_AUTH_SASL )) != 0 ) {
			*msgidp = rc;
			LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK );
			return( LDAP_SUCCESS );
		}
		LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK );
	}

	/* create a message to send */
	if (( rc = nsldapi_alloc_ber_with_options( ld, &ber ))
	    != LDAP_SUCCESS ) {
		return( rc );
	}

	/* fill it in */
	if ( simple ) {		/* simple bind; works in LDAPv2 or v3 */
		struct berval	tmpcred;

		if ( cred == NULL ) {
			tmpcred.bv_val = "";
			tmpcred.bv_len = 0;
			cred = &tmpcred;
		}
		rc = ber_printf( ber, "{it{isto}", msgid, LDAP_REQ_BIND,
		    ldapversion, dn, LDAP_AUTH_SIMPLE, cred->bv_val,
		    (int)cred->bv_len /* XXX lossy cast */ );

	} else {		/* SASL bind; requires LDAPv3 or better */
		if ( cred == NULL ) {
			rc = ber_printf( ber, "{it{ist{s}}", msgid,
			    LDAP_REQ_BIND, ldapversion, dn, LDAP_AUTH_SASL,
			    mechanism );
		} else {
			rc = ber_printf( ber, "{it{ist{so}}", msgid,
			    LDAP_REQ_BIND, ldapversion, dn, LDAP_AUTH_SASL,
			    mechanism, cred->bv_val,
			    (int)cred->bv_len /* XXX lossy cast */ );
		}
	}

	if ( rc == -1 ) {
		LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL );
		ber_free( ber, 1 );
		return( LDAP_ENCODING_ERROR );
	}

	if ( (rc = nsldapi_put_controls( ld, serverctrls, 1, ber ))
	    != LDAP_SUCCESS ) {
		ber_free( ber, 1 );
		return( rc );
	}

	/* send the message */
	rc = nsldapi_send_initial_request( ld, msgid, LDAP_REQ_BIND,
		(char *)dn, ber );
	*msgidp = rc;
	return( rc < 0 ? LDAP_GET_LDERRNO( ld, NULL, NULL ) : LDAP_SUCCESS );
}
示例#3
0
/* returns an LDAP error code that indicates if parse succeeded or not */
int
LDAP_CALL
ldap_parse_sasl_bind_result(
    LDAP		*ld,
    LDAPMessage		*res,
    struct berval	**servercredp,
    int			freeit
)
{
	BerElement	ber;
	int		rc, err;
	long		along;
	unsigned long	len;
	char		*m, *e;

	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_parse_sasl_bind_result\n", 0, 0, 0 );

	/*
	 * the ldapv3 SASL bind response looks like this:
	 *
	 *	BindResponse ::= [APPLICATION 1] SEQUENCE {
	 *		COMPONENTS OF LDAPResult,
	 *		serverSaslCreds [7] OCTET STRING OPTIONAL
	 *	}
	 *
	 * all wrapped up in an LDAPMessage sequence.
	 */

	if ( !NSLDAPI_VALID_LDAP_POINTER( ld ) ||
	    !NSLDAPI_VALID_LDAPMESSAGE_BINDRESULT_POINTER( res )) {
		return( LDAP_PARAM_ERROR );
	}

	/* only ldapv3 or higher can do sasl binds */
	if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3 ) {
		LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL );
		return( LDAP_NOT_SUPPORTED );
	}

	if ( servercredp != NULL ) {
		*servercredp = NULL;
	}

	ber = *(res->lm_ber);	/* struct copy */

	/* skip past message id, matched dn, error message ... */
	rc = ber_scanf( &ber, "{iaa}", &along, &m, &e );

	if ( rc != LBER_ERROR &&
	    ber_peek_tag( &ber, &len ) == LDAP_TAG_SASL_RES_CREDS ) {
		rc = ber_get_stringal( &ber, servercredp );
	}

	if ( freeit ) {
		ldap_msgfree( res );
	}

	if ( rc == LBER_ERROR ) {
		err = LDAP_DECODING_ERROR;
	} else {
		err = (int) along;
	}

	LDAP_SET_LDERRNO( ld, err, m, e );
	/* this is a little kludge for the 3.0 Barracuda/hammerhead relese */
	/* the docs state that the return is either LDAP_DECODING_ERROR */
	/* or LDAP_SUCCESS.  Here we match the docs...  it's cleaner in 3.1 */

	if ( LDAP_DECODING_ERROR == err ) {
		return (LDAP_DECODING_ERROR);
	} else {
		return( LDAP_SUCCESS );
	}
}
示例#4
0
int
LDAP_CALL
ldap_extended_operation(
    LDAP		*ld,
    const char		*exoid,
    const struct berval	*exdata,
    LDAPControl		**serverctrls,
    LDAPControl		**clientctrls,
    int			*msgidp
)
{
	BerElement	*ber;
	int		rc, msgid;

	/*
	 * the ldapv3 extended operation request looks like this:
	 *
	 *	ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
	 *		requestName	LDAPOID,
	 *		requestValue	OCTET STRING
	 *	}
	 *
	 * all wrapped up in an LDAPMessage sequence.
	 */

	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_extended_operation\n", 0, 0, 0 );

	if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
		return( LDAP_PARAM_ERROR );
	}


	/* only ldapv3 or higher can do extended operations */
	if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3 ) {
		rc = LDAP_NOT_SUPPORTED;
		LDAP_SET_LDERRNO( ld, rc, NULL, NULL );
		return( rc );
	}

	if ( msgidp == NULL || exoid == NULL || *exoid == '\0' ) {
		rc = LDAP_PARAM_ERROR;
		LDAP_SET_LDERRNO( ld, rc, NULL, NULL );
		return( rc );
	}

	LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK );
	msgid = ++ld->ld_msgid;
	LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK );

#if 0
	if ( ld->ld_cache_on && ld->ld_cache_extendedop != NULL ) {
		LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK );
		if ( (rc = (ld->ld_cache_extendedop)( ld, msgid,
		    LDAP_REQ_EXTENDED, exoid, cred )) != 0 ) {
			LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK );
			return( rc );
		}
		LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK );
	}
#endif

	/* create a message to send */
	if (( rc = nsldapi_alloc_ber_with_options( ld, &ber ))
	    != LDAP_SUCCESS ) {
		return( rc );
	}

	/* fill it in */
	if ( ber_printf( ber, "{it{tsto}", msgid, LDAP_REQ_EXTENDED,
	    LDAP_TAG_EXOP_REQ_OID, exoid, LDAP_TAG_EXOP_REQ_VALUE,
	    exdata->bv_val, (int)exdata->bv_len /* XXX lossy cast */ ) == -1 ) {
		rc = LDAP_ENCODING_ERROR;
		LDAP_SET_LDERRNO( ld, rc, NULL, NULL );
		ber_free( ber, 1 );
		return( rc );
	}

	if (( rc = nsldapi_put_controls( ld, serverctrls, 1, ber ))
	    != LDAP_SUCCESS ) {
		ber_free( ber, 1 );
		return( rc );
	}

	/* send the message */
	rc = nsldapi_send_initial_request( ld, msgid, LDAP_REQ_EXTENDED, NULL,
		ber );
	*msgidp = rc;
	return( rc < 0 ? LDAP_GET_LDERRNO( ld, NULL, NULL ) : LDAP_SUCCESS );
}
示例#5
0
/*
 * Pull the oid returned by the server and the data out of an extended
 * operation result.  Return an LDAP error code.
 */
int
LDAP_CALL
ldap_parse_extended_result(
    LDAP		*ld,
    LDAPMessage		*res,
    char		**retoidp,	/* may be NULL */
    struct berval	**retdatap,	/* may be NULL */
    int			freeit
)
{
	struct berelement	ber;
	unsigned long		len;
	long			err;
	char			*m, *e, *roid;
	struct berval		*rdata;

	LDAPDebug( LDAP_DEBUG_TRACE, "ldap_parse_extended_result\n", 0, 0, 0 );

	if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
		return( LDAP_PARAM_ERROR );
	}

        if ( !NSLDAPI_VALID_LDAPMESSAGE_EXRESULT_POINTER( res )) {
		return( LDAP_PARAM_ERROR );
	}

	m = e = NULL;
	ber = *(res->lm_ber);
	if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3 ) {
		LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL );
		return( LDAP_NOT_SUPPORTED );
	}

	if ( ber_scanf( &ber, "{iaa", &err, &m, &e ) == LBER_ERROR ) {
		goto decoding_error;
	}
	roid = NULL;
	if ( ber_peek_tag( &ber, &len ) == LDAP_TAG_EXOP_RES_OID ) {
		if ( ber_scanf( &ber, "a", &roid ) == LBER_ERROR ) {
			goto decoding_error;
		}
	}
	if ( retoidp != NULL ) {
		*retoidp = roid;
	} else if ( roid != NULL ) {
		NSLDAPI_FREE( roid );
	}

	rdata = NULL;
	if ( ber_peek_tag( &ber, &len ) == LDAP_TAG_EXOP_RES_VALUE ) {
		if ( ber_scanf( &ber, "O", &rdata ) == LBER_ERROR ) {
			goto decoding_error;
		}
	}
	if ( retdatap != NULL ) {
		*retdatap = rdata;
	} else if ( rdata != NULL ) {
		ber_bvfree( rdata );
	}

	LDAP_SET_LDERRNO( ld, err, m, e );

	if ( freeit ) {
		ldap_msgfree( res );
	}

	return( LDAP_SUCCESS );

decoding_error:;
	LDAP_SET_LDERRNO( ld, LDAP_DECODING_ERROR, NULL, NULL );
	return( LDAP_DECODING_ERROR );
}
示例#6
0
static int
nsldapi_sasl_do_bind( LDAP *ld, const char *dn,
        const char *mechs, unsigned flags,
        LDAP_SASL_INTERACT_PROC *callback, void *defaults,
        LDAPControl **sctrl, LDAPControl **cctrl, LDAPControl ***rctrl )
{
        sasl_interact_t *prompts = NULL;
        sasl_conn_t     *ctx = NULL;
        sasl_ssf_t      *ssf = NULL;
        const char      *mech = NULL;
        int             saslrc, rc;
        struct berval   ccred;
        unsigned        credlen;
        int stepnum = 1;
        char *sasl_username = NULL;

        if (rctrl) {
            /* init to NULL so we can call ldap_controls_free below */
            *rctrl = NULL;
        }

        if (NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3) {
                LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL );
                return( LDAP_NOT_SUPPORTED );
        }

        /* shouldn't happen */
        if (callback == NULL) {
                return( LDAP_LOCAL_ERROR );
        }

        if ( (rc = nsldapi_sasl_open(ld, NULL, &ctx, 0)) != LDAP_SUCCESS ) {
            return( rc );
        }

        ccred.bv_val = NULL;
        ccred.bv_len = 0;

        LDAPDebug(LDAP_DEBUG_TRACE, "Starting SASL/%s authentication\n",
                  (mechs ? mechs : ""), 0, 0 );

        do {
                saslrc = sasl_client_start( ctx,
                        mechs,
                        &prompts,
                        (const char **)&ccred.bv_val,
                        &credlen,
                        &mech );

                LDAPDebug(LDAP_DEBUG_TRACE, "Doing step %d of client start for SASL/%s authentication\n",
                          stepnum, (mech ? mech : ""), 0 );
                stepnum++;

                if( saslrc == SASL_INTERACT &&
                    (callback)(ld, flags, defaults, prompts) != LDAP_SUCCESS ) {
                        break;
                }
        } while ( saslrc == SASL_INTERACT );

        ccred.bv_len = credlen;

        if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
                return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) );
        }

        stepnum = 1;

        do {
                struct berval *scred;
                int clientstepnum = 1;

                scred = NULL;

                if (rctrl) {
                    /* if we're looping again, we need to free any controls set
                       during the previous loop */
                    /* NOTE that this assumes we only care about the controls
                       returned by the last call to nsldapi_sasl_bind_s - if
                       we care about _all_ controls, we will have to figure out
                       some way to append them each loop go round */
                    ldap_controls_free(*rctrl);
                    *rctrl = NULL;
                }

                LDAPDebug(LDAP_DEBUG_TRACE, "Doing step %d of bind for SASL/%s authentication\n",
                          stepnum, (mech ? mech : ""), 0 );
                stepnum++;

                /* notify server of a sasl bind step */
                rc = nsldapi_sasl_bind_s(ld, dn, mech, &ccred,
                                      sctrl, cctrl, &scred, rctrl); 

                if ( ccred.bv_val != NULL ) {
                        ccred.bv_val = NULL;
                }

                if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
                        ber_bvfree( scred );
                        return( rc );
                }

                if( rc == LDAP_SUCCESS && saslrc == SASL_OK ) {
                        /* we're done, no need to step */
                        if( scred ) {
                            if ( scred->bv_len == 0 ) { /* MS AD sends back empty screds */
                                LDAPDebug(LDAP_DEBUG_ANY,
                                          "SASL BIND complete - ignoring empty credential response\n",
                                          0, 0, 0);
                                ber_bvfree( scred );
                            } else {
                                /* but server provided us with data! */
                                LDAPDebug(LDAP_DEBUG_TRACE,
                                          "SASL BIND complete but invalid because server responded with credentials - length [%u]\n",
                                          scred->bv_len, 0, 0);
                                ber_bvfree( scred );
                                LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR,
                                                  NULL, "Error during SASL handshake - invalid server credential response" );
                                return( LDAP_LOCAL_ERROR );
                            }
                        }
                        break;
                }

                /* perform the next step of the sasl bind */
                do {
                        LDAPDebug(LDAP_DEBUG_TRACE, "Doing client step %d of bind step %d for SASL/%s authentication\n",
                                  clientstepnum, stepnum, (mech ? mech : "") );
                        clientstepnum++;
                        saslrc = sasl_client_step( ctx,
                                (scred == NULL) ? NULL : scred->bv_val,
                                (scred == NULL) ? 0 : scred->bv_len,
                                &prompts,
                                (const char **)&ccred.bv_val,
                                &credlen );

                        if( saslrc == SASL_INTERACT &&
                            (callback)(ld, flags, defaults, prompts)
                                                        != LDAP_SUCCESS ) {
                                break;
                        }
                } while ( saslrc == SASL_INTERACT );

                ccred.bv_len = credlen;
                ber_bvfree( scred );

                if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
                        return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) );
                }
        } while ( rc == LDAP_SASL_BIND_IN_PROGRESS );

        if ( rc != LDAP_SUCCESS ) {
                return( rc );
        }

        if ( saslrc != SASL_OK ) {
                return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) );
        }

        saslrc = sasl_getprop( ctx, SASL_USERNAME, (const void **) &sasl_username );
        if ( (saslrc == SASL_OK) && sasl_username ) {
                LDAPDebug(LDAP_DEBUG_TRACE, "SASL identity: %s\n", sasl_username, 0, 0);
        }

        saslrc = sasl_getprop( ctx, SASL_SSF, (const void **) &ssf );
        if( saslrc == SASL_OK ) {
                if( ssf && *ssf ) {
                        LDAPDebug(LDAP_DEBUG_TRACE,
                                "SASL install encryption, for SSF: %lu\n",
                                (unsigned long) *ssf, 0, 0 );
                        nsldapi_sasl_install( ld, NULL );
                }
        }

        return( rc );
}
示例#7
0
static int
simple_bind_nolock( LDAP *ld, const char *dn, const char *passwd,
    int unlock_permitted )
{
	BerElement	*ber;
	int		rc, msgid;

	/*
	 * The bind request looks like this:
	 *	BindRequest ::= SEQUENCE {
	 *		version		INTEGER,
	 *		name		DistinguishedName,	 -- who
	 *		authentication	CHOICE {
	 *			simple		[0] OCTET STRING -- passwd
	 *		}
	 *	}
	 * all wrapped up in an LDAPMessage sequence.
	 */

	LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK );
	msgid = ++ld->ld_msgid;
	LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK );

	if ( dn == NULL )
		dn = "";
	if ( passwd == NULL )
		passwd = "";

	if ( ld->ld_cache_on && ld->ld_cache_bind != NULL ) {
		struct berval	bv;

		bv.bv_val = (char *)passwd;
		bv.bv_len = strlen( passwd );
		/* if ( unlock_permitted ) LDAP_MUTEX_UNLOCK( ld ); */
		LDAP_MUTEX_LOCK( ld, LDAP_CACHE_LOCK );
		rc = (ld->ld_cache_bind)( ld, msgid, LDAP_REQ_BIND, dn, &bv,
		    LDAP_AUTH_SIMPLE );
		LDAP_MUTEX_UNLOCK( ld, LDAP_CACHE_LOCK );
		/* if ( unlock_permitted ) LDAP_MUTEX_LOCK( ld ); */
		if ( rc != 0 ) {
			return( rc );
		}
	}

	/* create a message to send */
	if (( rc = nsldapi_alloc_ber_with_options( ld, &ber ))
	    != LDAP_SUCCESS ) {
		return( -1 );
	}

	/* fill it in */
	if ( ber_printf( ber, "{it{ists}", msgid, LDAP_REQ_BIND,
	    NSLDAPI_LDAP_VERSION( ld ), dn, LDAP_AUTH_SIMPLE, passwd ) == -1 ) {
		LDAP_SET_LDERRNO( ld, LDAP_ENCODING_ERROR, NULL, NULL );
		ber_free( ber, 1 );
		return( -1 );
	}

	if ( nsldapi_put_controls( ld, NULL, 1, ber ) != LDAP_SUCCESS ) {
		ber_free( ber, 1 );
		return( -1 );
	}

	/* send the message */
	return( nsldapi_send_initial_request( ld, msgid, LDAP_REQ_BIND,
		(char *)dn, ber ));
}
示例#8
0
/*
 * Append a list of LDAPv3 controls to ber.  If ctrls is NULL, use default
 * set of controls from ld.
 * Return an LDAP error code (LDAP_SUCCESS if all goes well).
 * If closeseq is non-zero, we do an extra ber_put_seq() as well.
 */
int
nsldapi_put_controls( LDAP *ld, LDAPControl **ctrls, int closeseq,
    BerElement *ber )
{
	LDAPControl	*c;
	int		rc, i;

	rc = LDAP_ENCODING_ERROR;	/* the most popular error */

	/* if no controls were passed in, use global list from LDAP * */
	LDAP_MUTEX_LOCK( ld, LDAP_CTRL_LOCK );
	if ( ctrls == NULL ) {
		ctrls = ld->ld_servercontrols;
	}

	/* if there are no controls then we are done */
	if ( ctrls == NULL || ctrls[ 0 ] == NULL ) {
		goto clean_exit;
	}

	/*
	 * If we're using LDAPv2 or earlier we can't send any controls, so
	 * we just ignore them unless one is marked critical, in which case
	 * we return an error.
	 */
	if ( NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3 ) {
		for ( i = 0; ctrls != NULL && ctrls[i] != NULL; i++ ) {
			if ( ctrls[i]->ldctl_iscritical ) {
				rc = LDAP_NOT_SUPPORTED;
				goto error_exit;
			}
		}
		goto clean_exit;
	}

	/*
	 * encode the controls as a Sequence of Sequence
	 */
	if ( ber_printf( ber, "t{", LDAP_TAG_CONTROLS ) == -1 ) {
		goto error_exit;
	}

	for ( i = 0; ctrls[i] != NULL; i++ ) {
		c = ctrls[i];

		if ( ber_printf( ber, "{s", c->ldctl_oid ) == -1 ) {
			goto error_exit;
		}

		/* criticality is "BOOLEAN DEFAULT FALSE" */
		/* therefore, it should only be encoded if it exists AND is TRUE */
		if ( c->ldctl_iscritical ) {
			if ( ber_printf( ber, "b", (int)c->ldctl_iscritical )
			    == -1 ) {
				goto error_exit;
			}
		}

		if ( c->ldctl_value.bv_val != NULL ) {
			if ( ber_printf( ber, "o", c->ldctl_value.bv_val,
			    (int)c->ldctl_value.bv_len /* XXX lossy cast */ )
			    == -1 ) {
				goto error_exit;
			}
		}

		if ( ber_put_seq( ber ) == -1 ) {
			goto error_exit;
		}
	}

	if ( ber_put_seq( ber ) == -1 ) {
		goto error_exit;
	}

clean_exit:
	LDAP_MUTEX_UNLOCK( ld, LDAP_CTRL_LOCK );
	if ( closeseq && ber_put_seq( ber ) == -1 ) {
		goto error_exit;
	}
	return( LDAP_SUCCESS );

error_exit:
	LDAP_MUTEX_UNLOCK( ld, LDAP_CTRL_LOCK );
	LDAP_SET_LDERRNO( ld, rc, NULL, NULL );
	return( rc );
}