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 ) ); }
/* * 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 ); }
/* 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 ); } }
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 ); }
/* * 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 ); }
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 ); }
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 )); }
/* * 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 ); }