X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=servers%2Fslapd%2Fback-ldap%2Fextended.c;h=b0cc19fb48d1dee590bc7a0a82a575c5016df261;hb=473e2c997f6b1f226d35da186db8033c922001f3;hp=879c0ce7ca0cda8696dd01bbf65f4d56f213690d;hpb=f176935a586fb46ebeed6d4d79b77f8ca020ccb9;p=openldap diff --git a/servers/slapd/back-ldap/extended.c b/servers/slapd/back-ldap/extended.c index 879c0ce7ca..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,16 +28,61 @@ #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, @@ -45,100 +90,142 @@ ldap_back_extended( { 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 ) ) + if ( bvmatch( &exop_table[i].oid, &op->oq_extended.rs_reqoid ) ) { -#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; - } - - 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; - return rs->sr_err; - } - - rc = ( *exop_table[i].extended )( op, rs ); - - if ( op->o_ctrls && op->o_ctrls != oldctrls ) { - free( op->o_ctrls[ 0 ] ); - free( op->o_ctrls ); - } - op->o_ctrls = oldctrls; - - return rc; -#else /* ! LDAP_BACK_PROXY_AUTHZ */ - return ( *exop_table[i].extended )( op, rs ); -#endif /* ! LDAP_BACK_PROXY_AUTHZ */ + 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 ldapconn *lc; + 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; + int rc, isproxy, freedn = 0; int do_retry = 1; + char *text = NULL; + struct berval dn = op->o_req_dn, + ndn = op->o_req_ndn; - lc = ldap_back_getconn( op, rs ); - if ( !lc || !ldap_back_dobind( lc, op, rs ) ) { - return -1; + 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; + } } - isproxy = ber_bvcmp( &op->o_req_ndn, &op->o_ndn ); + isproxy = ber_bvcmp( &ndn, &op->o_ndn ); Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_passwd(\"%s\")%s\n", - op->o_req_dn.bv_val, isproxy ? " (proxy)" : "", 0 ); + dn.bv_val, isproxy ? " (proxy)" : "", 0 ); retry: - rc = ldap_passwd( lc->lc_ld, isproxy ? &op->o_req_dn : NULL, + 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 ) { - if ( ldap_result( lc->lc_ld, msgid, 1, NULL, &res ) == -1 ) { + /* 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 ); - ldap_back_freeconn( op, lc ); - lc = NULL; + rs->sr_err = rc; } else { + /* 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->lc_ld, res, &rs->sr_err, (char **)&rs->sr_matched, - (char **)&rs->sr_text, - NULL, NULL, 0 ); + &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); + &newpw ); if ( rc == LDAP_SUCCESS && !BER_BVISNULL( &newpw ) ) { @@ -154,24 +241,161 @@ retry: ldap_msgfree( res ); } } + 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 ) ) { + if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) { goto retry; } } - send_ldap_result( op, rs ); - if ( rs->sr_matched ) { - free( (char *)rs->sr_matched ); + + 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 ( 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 { + /* 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->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; + } + + } else { + rc = rs->sr_err; + } + } + ldap_msgfree( res ); + } + } + + 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 ( rs->sr_text ) { - free( (char *)rs->sr_text ); + + 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;