X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=servers%2Fslapd%2Fback-ldap%2Fextended.c;h=b0cc19fb48d1dee590bc7a0a82a575c5016df261;hb=473e2c997f6b1f226d35da186db8033c922001f3;hp=dde4c1ab2bee9f13e77a6fdf1bdd5a61a18fd1e1;hpb=3c598e89fb34a892d369a138daa8c3314294493c;p=openldap diff --git a/servers/slapd/back-ldap/extended.c b/servers/slapd/back-ldap/extended.c index dde4c1ab2b..b0cc19fb48 100644 --- a/servers/slapd/back-ldap/extended.c +++ b/servers/slapd/back-ldap/extended.c @@ -2,7 +2,7 @@ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * - * Copyright 2003-2004 The OpenLDAP Foundation. + * Copyright 2003-2011 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,164 +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; - - for( i=0; exop_table[i].extended != NULL; i++ ) { - if( ber_bvcmp( exop_table[i].oid, &op->oq_extended.rs_reqoid ) == 0 ) { -#ifdef LDAP_BACK_PROXY_AUTHZ - struct ldapconn *lc; - LDAPControl **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 ... */ - lc = ldap_back_getconn(op, rs); - if (!lc || !ldap_back_dobind(lc, op, rs) ) { - return -1; + int i; + + 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 ); + } + } + + /* 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 ); +} + +static int +ldap_back_exop_passwd( + Operation *op, + SlapReply *rs, + ldapconn_t **lcp ) +{ + 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; } + } - oldctrls = op->o_ctrls; - if ( ldap_back_proxy_authz_ctrl( lc, op, rs, &op->o_ctrls ) ) { - op->o_ctrls = oldctrls; - send_ldap_result( op, rs ); - rs->sr_text = NULL; + 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; - rc = (exop_table[i].extended)( op, rs ); + } else { + dn = op->o_dn; + ndn = op->o_ndn; + } + } + + 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; - if ( op->o_ctrls && op->o_ctrls != oldctrls ) { - free( op->o_ctrls[ 0 ] ); - free( op->o_ctrls ); + } else { + /* only touch when activity actually took place... */ + if ( li->li_idle_timeout && lc ) { + lc->lc_time = op->o_time; } - op->o_ctrls = oldctrls; - return rc; -#else /* ! LDAP_BACK_PROXY_AUTHZ */ - return (exop_table[i].extended)( op, rs ); -#endif /* ! LDAP_BACK_PROXY_AUTHZ */ + /* 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 ); } } - rs->sr_text = "not supported within naming context"; - return LDAP_UNWILLING_TO_PERFORM; -} + 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; + } + } -int -ldap_back_exop_passwd( - Operation *op, - SlapReply *rs ) -{ - 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; - dncookie dc; - - lc = ldap_back_getconn(op, rs); - if (!lc || !ldap_back_dobind(lc, op, rs) ) { - return -1; + 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 ); } - 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) { - dc.rwmap = &li->rwmap; -#ifdef ENABLE_REWRITE - dc.conn = op->o_conn; - dc.rs = rs; - dc.ctx = "modifyPwd"; -#else - dc.tofrom = 1; - dc.normalized = 0; -#endif - if ( ldap_back_dn_massage( &dc, &dn, &mdn ) ) { - send_ldap_result( op, rs ); - return -1; - } + if ( freedn ) { + op->o_tmpfree( dn.bv_val, op->o_tmpmemctx ); + op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx ); } - 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); + /* these have to be freed anyway... */ + if ( rs->sr_matched ) { + free( (char *)rs->sr_matched ); + rs->sr_matched = NULL; + } - if (mdn.bv_val != dn.bv_val) { - free(mdn.bv_val); + if ( rs->sr_ctrls ) { + ldap_controls_free( rs->sr_ctrls ); + rs->sr_ctrls = NULL; } - 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 ( 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; }