X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=servers%2Fslapd%2Fback-ldap%2Fextended.c;h=b0cc19fb48d1dee590bc7a0a82a575c5016df261;hb=473e2c997f6b1f226d35da186db8033c922001f3;hp=3673800fa2c4ecd79156bef6dec901ffe20c4376;hpb=ab3ab80ecdb4e09087172125eda62b7dca9ead59;p=openldap diff --git a/servers/slapd/back-ldap/extended.c b/servers/slapd/back-ldap/extended.c index 3673800fa2..b0cc19fb48 100644 --- a/servers/slapd/back-ldap/extended.c +++ b/servers/slapd/back-ldap/extended.c @@ -1,8 +1,22 @@ /* extended.c - ldap backend extended routines */ /* $OpenLDAP$ */ -/* - * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved. - * COPYING RESTRICTIONS APPLY, see COPYRIGHT file +/* This work is part of OpenLDAP Software . + * + * Copyright 2003-2011 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by the Howard Chu for inclusion + * in OpenLDAP Software and subsequently enhanced by Pierangelo + * Masarati. */ #include "portable.h" @@ -14,146 +28,375 @@ #include "back-ldap.h" #include "lber_pvt.h" -BI_op_extended ldap_back_exop_passwd; +typedef int (ldap_back_exop_f)( Operation *op, SlapReply *rs, ldapconn_t **lc ); + +static ldap_back_exop_f ldap_back_exop_passwd; +static ldap_back_exop_f ldap_back_exop_generic; static struct exop { - struct berval *oid; - BI_op_extended *extended; + struct berval oid; + ldap_back_exop_f *extended; } exop_table[] = { - { (struct berval *)&slap_EXOP_MODIFY_PASSWD, ldap_back_exop_passwd }, - { NULL, NULL } + { BER_BVC(LDAP_EXOP_MODIFY_PASSWD), ldap_back_exop_passwd }, + { BER_BVNULL, NULL } }; +static int +ldap_back_extended_one( Operation *op, SlapReply *rs, ldap_back_exop_f exop ) +{ + ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; + + ldapconn_t *lc = NULL; + LDAPControl **ctrls = NULL, **oldctrls = NULL; + int rc; + + /* FIXME: this needs to be called here, so it is + * called twice; maybe we could avoid the + * ldap_back_dobind() call inside each extended() + * call ... */ + if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) { + return -1; + } + + ctrls = op->o_ctrls; + if ( ldap_back_controls_add( op, rs, lc, &ctrls ) ) + { + op->o_ctrls = oldctrls; + send_ldap_extended( op, rs ); + rs->sr_text = NULL; + /* otherwise frontend resends result */ + rc = rs->sr_err = SLAPD_ABANDON; + goto done; + } + + op->o_ctrls = ctrls; + rc = exop( op, rs, &lc ); + + op->o_ctrls = oldctrls; + (void)ldap_back_controls_free( op, rs, &ctrls ); + +done:; + if ( lc != NULL ) { + ldap_back_release_conn( li, lc ); + } + + return rc; +} + int ldap_back_extended( - Operation *op, - SlapReply *rs ) + Operation *op, + SlapReply *rs ) { - int i; + int i; - for( i=0; exop_table[i].extended != NULL; i++ ) { - if( ber_bvcmp( exop_table[i].oid, &op->oq_extended.rs_reqoid ) == 0 ) { - return (exop_table[i].extended)( op, rs ); + RS_ASSERT( !(rs->sr_flags & REP_ENTRY_MASK) ); + rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia */ + + for ( i = 0; exop_table[i].extended != NULL; i++ ) { + if ( bvmatch( &exop_table[i].oid, &op->oq_extended.rs_reqoid ) ) + { + return ldap_back_extended_one( op, rs, exop_table[i].extended ); } } - rs->sr_text = "not supported within naming context"; - return LDAP_UNWILLING_TO_PERFORM; + /* if we get here, the exop is known; the best that we can do + * is pass it thru as is */ + /* FIXME: maybe a list of OIDs to pass thru would be safer */ + return ldap_back_extended_one( op, rs, ldap_back_exop_generic ); } -int +static int ldap_back_exop_passwd( - Operation *op, - SlapReply *rs ) + Operation *op, + SlapReply *rs, + ldapconn_t **lcp ) { - struct ldapinfo *li = (struct ldapinfo *) op->o_bd->be_private; - struct ldapconn *lc; - struct berval id = { 0, NULL }; - struct berval old = { 0, NULL }; - struct berval new = { 0, NULL }; - struct berval dn, mdn = { 0, NULL }, newpw; - LDAPMessage *res; - ber_int_t msgid; - int rc; - - lc = ldap_back_getconn(op, rs); - if (!lc || !ldap_back_dobind(lc, op, rs) ) { - return -1; + ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; + + ldapconn_t *lc = *lcp; + req_pwdexop_s *qpw = &op->oq_pwdexop; + LDAPMessage *res; + ber_int_t msgid; + int rc, isproxy, freedn = 0; + int do_retry = 1; + char *text = NULL; + struct berval dn = op->o_req_dn, + ndn = op->o_req_ndn; + + assert( lc != NULL ); + assert( rs->sr_ctrls == NULL ); + + if ( BER_BVISNULL( &ndn ) && op->ore_reqdata != NULL ) { + /* NOTE: most of this code is mutated + * from slap_passwd_parse(); + * But here we only need + * the first berval... */ + + ber_tag_t tag; + ber_len_t len = -1; + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + + struct berval tmpid = BER_BVNULL; + + if ( op->ore_reqdata->bv_len == 0 ) { + return LDAP_PROTOCOL_ERROR; + } + + /* ber_init2 uses reqdata directly, doesn't allocate new buffers */ + ber_init2( ber, op->ore_reqdata, 0 ); + + tag = ber_scanf( ber, "{" /*}*/ ); + + if ( tag == LBER_ERROR ) { + return LDAP_PROTOCOL_ERROR; + } + + tag = ber_peek_tag( ber, &len ); + if ( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_ID ) { + tag = ber_get_stringbv( ber, &tmpid, LBER_BV_NOTERM ); + + if ( tag == LBER_ERROR ) { + return LDAP_PROTOCOL_ERROR; + } + } + + if ( !BER_BVISEMPTY( &tmpid ) ) { + char idNull = tmpid.bv_val[tmpid.bv_len]; + tmpid.bv_val[tmpid.bv_len] = '\0'; + rs->sr_err = dnPrettyNormal( NULL, &tmpid, &dn, + &ndn, op->o_tmpmemctx ); + tmpid.bv_val[tmpid.bv_len] = idNull; + if ( rs->sr_err != LDAP_SUCCESS ) { + /* should have been successfully parsed earlier! */ + return rs->sr_err; + } + freedn = 1; + + } else { + dn = op->o_dn; + ndn = op->o_ndn; + } } - rc = slap_passwd_parse( op->oq_extended.rs_reqdata, &id, &old, &new, &rs->sr_text ); - if (rc != LDAP_SUCCESS) - return rc; - - if (id.bv_len) { - dn = id; - } else { - dn = op->o_dn; - } - -#ifdef NEW_LOGGING - LDAP_LOG ( ACL, DETAIL1, "ldap_back_exop_passwd: \"%s\"%s\"\n", - dn.bv_val, id.bv_len ? " (proxy)" : "", 0 ); -#else - Debug( LDAP_DEBUG_TRACE, "ldap_back_exop_passwd: \"%s\"%s\n", - dn.bv_val, id.bv_len ? " (proxy)" : "", 0 ); -#endif - - if (dn.bv_len == 0) { - rs->sr_text = "No password is associated with the Root DSE"; - return LDAP_UNWILLING_TO_PERFORM; - } - if (id.bv_len) { -#ifdef ENABLE_REWRITE - switch ( rewrite_session( li->rwinfo, "modifyPwd", dn.bv_val, op->o_conn, &mdn.bv_val ) ) { - case REWRITE_REGEXEC_OK: - if ( mdn.bv_val == NULL ) { - mdn.bv_val = dn.bv_val; + isproxy = ber_bvcmp( &ndn, &op->o_ndn ); + + Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_passwd(\"%s\")%s\n", + dn.bv_val, isproxy ? " (proxy)" : "", 0 ); + +retry: + rc = ldap_passwd( lc->lc_ld, isproxy ? &dn : NULL, + qpw->rs_old.bv_val ? &qpw->rs_old : NULL, + qpw->rs_new.bv_val ? &qpw->rs_new : NULL, + op->o_ctrls, NULL, &msgid ); + + if ( rc == LDAP_SUCCESS ) { + /* TODO: set timeout? */ + /* by now, make sure no timeout is used (ITS#6282) */ + struct timeval tv = { -1, 0 }; + if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) == -1 ) { + ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc ); + rs->sr_err = rc; + + } else { + /* only touch when activity actually took place... */ + if ( li->li_idle_timeout && lc ) { + lc->lc_time = op->o_time; } - mdn.bv_len = strlen(mdn.bv_val); -#ifdef NEW_LOGGING - LDAP_LOG( BACK_LDAP, DETAIL1, - "[rw] modifyPwd: \"%s\" -> \"%s\"\n", dn.bv_val, mdn.bv_val, 0 ); -#else /* !NEW_LOGGING */ - Debug( LDAP_DEBUG_ARGS, "rw> modifyPwd: \"%s\" -> \"%s\"\n%s", - dn.bv_val, mdn.bv_val, "" ); -#endif /* !NEW_LOGGING */ - break; - - case REWRITE_REGEXEC_UNWILLING: - send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, - "Operation not allowed" ); - return( -1 ); - - case REWRITE_REGEXEC_ERR: - send_ldap_error( op, rs, LDAP_OTHER, - "Rewrite error" ); - return( -1 ); + + /* sigh. parse twice, because parse_passwd + * doesn't give us the err / match / msg info. + */ + rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err, + (char **)&rs->sr_matched, + &text, + NULL, &rs->sr_ctrls, 0 ); + + if ( rc == LDAP_SUCCESS ) { + if ( rs->sr_err == LDAP_SUCCESS ) { + struct berval newpw; + + /* this never happens because + * the frontend is generating + * the new password, so when + * the passwd exop is proxied, + * it never delegates password + * generation to the remote server + */ + rc = ldap_parse_passwd( lc->lc_ld, res, + &newpw ); + if ( rc == LDAP_SUCCESS && + !BER_BVISNULL( &newpw ) ) + { + rs->sr_type = REP_EXTENDED; + rs->sr_rspdata = slap_passwd_return( &newpw ); + free( newpw.bv_val ); + } + + } else { + rc = rs->sr_err; + } + } + ldap_msgfree( res ); } -#else /* !ENABLE_REWRITE */ - ldap_back_dn_massage( li, &dn, &mdn, 0, 1 ); -#endif /* !ENABLE_REWRITE */ } - rc = ldap_passwd(lc->ld, id.bv_len ? &mdn : NULL, old.bv_len ? &old : NULL, - new.bv_len ? &new : NULL, op->o_ctrls, NULL, &msgid); + if ( rc != LDAP_SUCCESS ) { + rs->sr_err = slap_map_api2result( rs ); + if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) { + do_retry = 0; + if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) { + goto retry; + } + } - if (mdn.bv_val != dn.bv_val) { - free(mdn.bv_val); + if ( LDAP_BACK_QUARANTINE( li ) ) { + ldap_back_quarantine( op, rs ); + } + + if ( text ) rs->sr_text = text; + send_ldap_extended( op, rs ); + /* otherwise frontend resends result */ + rc = rs->sr_err = SLAPD_ABANDON; + + } else if ( LDAP_BACK_QUARANTINE( li ) ) { + ldap_back_quarantine( op, rs ); } - if (rc == LDAP_SUCCESS) { - if (ldap_result(lc->ld, msgid, 1, NULL, &res) == -1) { - ldap_get_option(lc->ld, LDAP_OPT_ERROR_NUMBER, &rc); + if ( freedn ) { + op->o_tmpfree( dn.bv_val, op->o_tmpmemctx ); + op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx ); + } + + /* these have to be freed anyway... */ + if ( rs->sr_matched ) { + free( (char *)rs->sr_matched ); + rs->sr_matched = NULL; + } + + if ( rs->sr_ctrls ) { + ldap_controls_free( rs->sr_ctrls ); + rs->sr_ctrls = NULL; + } + + if ( text ) { + free( text ); + rs->sr_text = NULL; + } + + /* in case, cleanup handler */ + if ( lc == NULL ) { + *lcp = NULL; + } + + return rc; +} + +static int +ldap_back_exop_generic( + Operation *op, + SlapReply *rs, + ldapconn_t **lcp ) +{ + ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; + + ldapconn_t *lc = *lcp; + LDAPMessage *res; + ber_int_t msgid; + int rc; + int do_retry = 1; + char *text = NULL; + + Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_generic(%s, \"%s\")\n", + op->ore_reqoid.bv_val, op->o_req_dn.bv_val, 0 ); + assert( lc != NULL ); + assert( rs->sr_ctrls == NULL ); + +retry: + rc = ldap_extended_operation( lc->lc_ld, + op->ore_reqoid.bv_val, op->ore_reqdata, + op->o_ctrls, NULL, &msgid ); + + if ( rc == LDAP_SUCCESS ) { + /* TODO: set timeout? */ + /* by now, make sure no timeout is used (ITS#6282) */ + struct timeval tv = { -1, 0 }; + if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) == -1 ) { + ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc ); + rs->sr_err = rc; + } else { - /* sigh. parse twice, because parse_passwd doesn't give - * us the err / match / msg info. + /* only touch when activity actually took place... */ + if ( li->li_idle_timeout && lc ) { + lc->lc_time = op->o_time; + } + + /* sigh. parse twice, because parse_passwd + * doesn't give us the err / match / msg info. */ - rc = ldap_parse_result(lc->ld, res, &rs->sr_err, (char **)&rs->sr_matched, (char **)&rs->sr_text, - NULL, NULL, 0); - if (rc == LDAP_SUCCESS) { - if (rs->sr_err == LDAP_SUCCESS) { - rc = ldap_parse_passwd(lc->ld, res, &newpw); - if (rc == LDAP_SUCCESS && newpw.bv_val) { + rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err, + (char **)&rs->sr_matched, + &text, + NULL, &rs->sr_ctrls, 0 ); + if ( rc == LDAP_SUCCESS ) { + if ( rs->sr_err == LDAP_SUCCESS ) { + rc = ldap_parse_extended_result( lc->lc_ld, res, + (char **)&rs->sr_rspoid, &rs->sr_rspdata, 0 ); + if ( rc == LDAP_SUCCESS ) { rs->sr_type = REP_EXTENDED; - rs->sr_rspdata = slap_passwd_return(&newpw); - free(newpw.bv_val); } + } else { rc = rs->sr_err; } } - ldap_msgfree(res); + ldap_msgfree( res ); } } - if (rc != LDAP_SUCCESS) { - rs->sr_err = ldap_back_map_result(rs); - send_ldap_result(op, rs); - if (rs->sr_matched) free((char *)rs->sr_matched); - if (rs->sr_text) free((char *)rs->sr_text); + + if ( rc != LDAP_SUCCESS ) { + rs->sr_err = slap_map_api2result( rs ); + if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) { + do_retry = 0; + if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) { + goto retry; + } + } + + if ( LDAP_BACK_QUARANTINE( li ) ) { + ldap_back_quarantine( op, rs ); + } + + if ( text ) rs->sr_text = text; + send_ldap_extended( op, rs ); + /* otherwise frontend resends result */ + rc = rs->sr_err = SLAPD_ABANDON; + + } else if ( LDAP_BACK_QUARANTINE( li ) ) { + ldap_back_quarantine( op, rs ); + } + + /* these have to be freed anyway... */ + if ( rs->sr_matched ) { + free( (char *)rs->sr_matched ); rs->sr_matched = NULL; + } + + if ( rs->sr_ctrls ) { + ldap_controls_free( rs->sr_ctrls ); + rs->sr_ctrls = NULL; + } + + if ( text ) { + free( text ); rs->sr_text = NULL; - rc = -1; } + + /* in case, cleanup handler */ + if ( lc == NULL ) { + *lcp = NULL; + } + return rc; }