Back to index

openldap  2.4.31
extended.c
Go to the documentation of this file.
00001 /* extended.c - ldap backend extended routines */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 2003-2012 The OpenLDAP Foundation.
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted only as authorized by the OpenLDAP
00010  * Public License.
00011  *
00012  * A copy of this license is available in the file LICENSE in the
00013  * top-level directory of the distribution or, alternatively, at
00014  * <http://www.OpenLDAP.org/license.html>.
00015  */
00016 /* ACKNOWLEDGEMENTS:
00017  * This work was initially developed by the Howard Chu for inclusion
00018  * in OpenLDAP Software and subsequently enhanced by Pierangelo
00019  * Masarati. 
00020  */
00021 
00022 #include "portable.h"
00023 
00024 #include <stdio.h>
00025 #include <ac/string.h>
00026 
00027 #include "slap.h"
00028 #include "back-ldap.h"
00029 #include "lber_pvt.h"
00030 
00031 typedef int (ldap_back_exop_f)( Operation *op, SlapReply *rs, ldapconn_t **lc );
00032 
00033 static ldap_back_exop_f ldap_back_exop_passwd;
00034 static ldap_back_exop_f ldap_back_exop_generic;
00035 
00036 static struct exop {
00037        struct berval        oid;
00038        ldap_back_exop_f     *extended;
00039 } exop_table[] = {
00040        { BER_BVC(LDAP_EXOP_MODIFY_PASSWD),       ldap_back_exop_passwd },
00041        { BER_BVNULL, NULL }
00042 };
00043 
00044 static int
00045 ldap_back_extended_one( Operation *op, SlapReply *rs, ldap_back_exop_f exop )
00046 {
00047        ldapinfo_t    *li = (ldapinfo_t *) op->o_bd->be_private;
00048 
00049        ldapconn_t    *lc = NULL;
00050        LDAPControl   **ctrls = NULL, **oldctrls = NULL;
00051        int           rc;
00052 
00053        /* FIXME: this needs to be called here, so it is
00054         * called twice; maybe we could avoid the 
00055         * ldap_back_dobind() call inside each extended()
00056         * call ... */
00057        if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
00058               return -1;
00059        }
00060 
00061        ctrls = op->o_ctrls;
00062        if ( ldap_back_controls_add( op, rs, lc, &ctrls ) )
00063        {
00064               op->o_ctrls = oldctrls;
00065               send_ldap_extended( op, rs );
00066               rs->sr_text = NULL;
00067               /* otherwise frontend resends result */
00068               rc = rs->sr_err = SLAPD_ABANDON;
00069               goto done;
00070        }
00071 
00072        op->o_ctrls = ctrls;
00073        rc = exop( op, rs, &lc );
00074 
00075        op->o_ctrls = oldctrls;
00076        (void)ldap_back_controls_free( op, rs, &ctrls );
00077 
00078 done:;
00079        if ( lc != NULL ) {
00080               ldap_back_release_conn( li, lc );
00081        }
00082                      
00083        return rc;
00084 }
00085 
00086 int
00087 ldap_back_extended(
00088               Operation     *op,
00089               SlapReply     *rs )
00090 {
00091        int    i;
00092 
00093        RS_ASSERT( !(rs->sr_flags & REP_ENTRY_MASK) );
00094        rs->sr_flags &= ~REP_ENTRY_MASK;   /* paranoia */
00095 
00096        for ( i = 0; exop_table[i].extended != NULL; i++ ) {
00097               if ( bvmatch( &exop_table[i].oid, &op->oq_extended.rs_reqoid ) )
00098               {
00099                      return ldap_back_extended_one( op, rs, exop_table[i].extended );
00100               }
00101        }
00102 
00103        /* if we get here, the exop is known; the best that we can do
00104         * is pass it thru as is */
00105        /* FIXME: maybe a list of OIDs to pass thru would be safer */
00106        return ldap_back_extended_one( op, rs, ldap_back_exop_generic );
00107 }
00108 
00109 static int
00110 ldap_back_exop_passwd(
00111        Operation     *op,
00112        SlapReply     *rs,
00113        ldapconn_t    **lcp )
00114 {
00115        ldapinfo_t    *li = (ldapinfo_t *) op->o_bd->be_private;
00116 
00117        ldapconn_t    *lc = *lcp;
00118        req_pwdexop_s *qpw = &op->oq_pwdexop;
00119        LDAPMessage   *res;
00120        ber_int_t     msgid;
00121        int           rc, isproxy, freedn = 0;
00122        int           do_retry = 1;
00123        char          *text = NULL;
00124        struct berval dn = op->o_req_dn,
00125                      ndn = op->o_req_ndn;
00126 
00127        assert( lc != NULL );
00128        assert( rs->sr_ctrls == NULL );
00129 
00130        if ( BER_BVISNULL( &ndn ) && op->ore_reqdata != NULL ) {
00131               /* NOTE: most of this code is mutated
00132                * from slap_passwd_parse();
00133                * But here we only need
00134                * the first berval... */
00135 
00136               ber_tag_t tag;
00137               ber_len_t len = -1;
00138               BerElementBuffer berbuf;
00139               BerElement *ber = (BerElement *)&berbuf;
00140 
00141               struct berval tmpid = BER_BVNULL;
00142 
00143               if ( op->ore_reqdata->bv_len == 0 ) {
00144                      return LDAP_PROTOCOL_ERROR;
00145               }
00146 
00147               /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
00148               ber_init2( ber, op->ore_reqdata, 0 );
00149 
00150               tag = ber_scanf( ber, "{" /*}*/ );
00151 
00152               if ( tag == LBER_ERROR ) {
00153                      return LDAP_PROTOCOL_ERROR;
00154               }
00155 
00156               tag = ber_peek_tag( ber, &len );
00157               if ( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_ID ) {
00158                      tag = ber_get_stringbv( ber, &tmpid, LBER_BV_NOTERM );
00159 
00160                      if ( tag == LBER_ERROR ) {
00161                             return LDAP_PROTOCOL_ERROR;
00162                      }
00163               }
00164 
00165               if ( !BER_BVISEMPTY( &tmpid ) ) {
00166                      char idNull = tmpid.bv_val[tmpid.bv_len];
00167                      tmpid.bv_val[tmpid.bv_len] = '\0';
00168                      rs->sr_err = dnPrettyNormal( NULL, &tmpid, &dn,
00169                             &ndn, op->o_tmpmemctx );
00170                      tmpid.bv_val[tmpid.bv_len] = idNull;
00171                      if ( rs->sr_err != LDAP_SUCCESS ) {
00172                             /* should have been successfully parsed earlier! */
00173                             return rs->sr_err;
00174                      }
00175                      freedn = 1;
00176 
00177               } else {
00178                      dn = op->o_dn;
00179                      ndn = op->o_ndn;
00180               }
00181        }
00182 
00183        isproxy = ber_bvcmp( &ndn, &op->o_ndn );
00184 
00185        Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_passwd(\"%s\")%s\n",
00186               dn.bv_val, isproxy ? " (proxy)" : "", 0 );
00187 
00188 retry:
00189        rc = ldap_passwd( lc->lc_ld, isproxy ? &dn : NULL,
00190               qpw->rs_old.bv_val ? &qpw->rs_old : NULL,
00191               qpw->rs_new.bv_val ? &qpw->rs_new : NULL,
00192               op->o_ctrls, NULL, &msgid );
00193 
00194        if ( rc == LDAP_SUCCESS ) {
00195               /* TODO: set timeout? */
00196               /* by now, make sure no timeout is used (ITS#6282) */
00197               struct timeval tv = { -1, 0 };
00198               if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) == -1 ) {
00199                      ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc );
00200                      rs->sr_err = rc;
00201 
00202               } else {
00203                      /* only touch when activity actually took place... */
00204                      if ( li->li_idle_timeout && lc ) {
00205                             lc->lc_time = op->o_time;
00206                      }
00207 
00208                      /* sigh. parse twice, because parse_passwd
00209                       * doesn't give us the err / match / msg info.
00210                       */
00211                      rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
00212                                    (char **)&rs->sr_matched,
00213                                    &text,
00214                                    NULL, &rs->sr_ctrls, 0 );
00215 
00216                      if ( rc == LDAP_SUCCESS ) {
00217                             if ( rs->sr_err == LDAP_SUCCESS ) {
00218                                    struct berval newpw;
00219 
00220                                    /* this never happens because 
00221                                     * the frontend      is generating 
00222                                     * the new password, so when
00223                                     * the passwd exop is proxied,
00224                                     * it never delegates password
00225                                     * generation to the remote server
00226                                     */
00227                                    rc = ldap_parse_passwd( lc->lc_ld, res,
00228                                                  &newpw );
00229                                    if ( rc == LDAP_SUCCESS &&
00230                                                  !BER_BVISNULL( &newpw ) )
00231                                    {
00232                                           rs->sr_type = REP_EXTENDED;
00233                                           rs->sr_rspdata = slap_passwd_return( &newpw );
00234                                           free( newpw.bv_val );
00235                                    }
00236 
00237                             } else {
00238                                    rc = rs->sr_err;
00239                             }
00240                      }
00241                      ldap_msgfree( res );
00242               }
00243        }
00244 
00245        if ( rc != LDAP_SUCCESS ) {
00246               rs->sr_err = slap_map_api2result( rs );
00247               if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
00248                      do_retry = 0;
00249                      if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
00250                             goto retry;
00251                      }
00252               }
00253 
00254               if ( LDAP_BACK_QUARANTINE( li ) ) {
00255                      ldap_back_quarantine( op, rs );
00256               }
00257 
00258               if ( text ) rs->sr_text = text;
00259               send_ldap_extended( op, rs );
00260               /* otherwise frontend resends result */
00261               rc = rs->sr_err = SLAPD_ABANDON;
00262 
00263        } else if ( LDAP_BACK_QUARANTINE( li ) ) {
00264               ldap_back_quarantine( op, rs );
00265        }
00266 
00267        if ( freedn ) {
00268               op->o_tmpfree( dn.bv_val, op->o_tmpmemctx );
00269               op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
00270        }
00271 
00272        /* these have to be freed anyway... */
00273        if ( rs->sr_matched ) {
00274               free( (char *)rs->sr_matched );
00275               rs->sr_matched = NULL;
00276        }
00277 
00278        if ( rs->sr_ctrls ) {
00279               ldap_controls_free( rs->sr_ctrls );
00280               rs->sr_ctrls = NULL;
00281        }
00282 
00283        if ( text ) {
00284               free( text );
00285               rs->sr_text = NULL;
00286        }
00287 
00288        /* in case, cleanup handler */
00289        if ( lc == NULL ) {
00290               *lcp = NULL;
00291        }
00292 
00293        return rc;
00294 }
00295 
00296 static int
00297 ldap_back_exop_generic(
00298        Operation     *op,
00299        SlapReply     *rs,
00300        ldapconn_t    **lcp )
00301 {
00302        ldapinfo_t    *li = (ldapinfo_t *) op->o_bd->be_private;
00303 
00304        ldapconn_t    *lc = *lcp;
00305        LDAPMessage   *res;
00306        ber_int_t     msgid;
00307        int           rc;
00308        int           do_retry = 1;
00309        char          *text = NULL;
00310 
00311        Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_generic(%s, \"%s\")\n",
00312               op->ore_reqoid.bv_val, op->o_req_dn.bv_val, 0 );
00313        assert( lc != NULL );
00314        assert( rs->sr_ctrls == NULL );
00315 
00316 retry:
00317        rc = ldap_extended_operation( lc->lc_ld,
00318               op->ore_reqoid.bv_val, op->ore_reqdata,
00319               op->o_ctrls, NULL, &msgid );
00320 
00321        if ( rc == LDAP_SUCCESS ) {
00322               /* TODO: set timeout? */
00323               /* by now, make sure no timeout is used (ITS#6282) */
00324               struct timeval tv = { -1, 0 };
00325               if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) == -1 ) {
00326                      ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc );
00327                      rs->sr_err = rc;
00328 
00329               } else {
00330                      /* only touch when activity actually took place... */
00331                      if ( li->li_idle_timeout && lc ) {
00332                             lc->lc_time = op->o_time;
00333                      }
00334 
00335                      /* sigh. parse twice, because parse_passwd
00336                       * doesn't give us the err / match / msg info.
00337                       */
00338                      rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
00339                                    (char **)&rs->sr_matched,
00340                                    &text,
00341                                    NULL, &rs->sr_ctrls, 0 );
00342                      if ( rc == LDAP_SUCCESS ) {
00343                             if ( rs->sr_err == LDAP_SUCCESS ) {
00344                                    rc = ldap_parse_extended_result( lc->lc_ld, res,
00345                                                  (char **)&rs->sr_rspoid, &rs->sr_rspdata, 0 );
00346                                    if ( rc == LDAP_SUCCESS ) {
00347                                           rs->sr_type = REP_EXTENDED;
00348                                    }
00349 
00350                             } else {
00351                                    rc = rs->sr_err;
00352                             }
00353                      }
00354                      ldap_msgfree( res );
00355               }
00356        }
00357 
00358        if ( rc != LDAP_SUCCESS ) {
00359               rs->sr_err = slap_map_api2result( rs );
00360               if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
00361                      do_retry = 0;
00362                      if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
00363                             goto retry;
00364                      }
00365               }
00366 
00367               if ( LDAP_BACK_QUARANTINE( li ) ) {
00368                      ldap_back_quarantine( op, rs );
00369               }
00370 
00371               if ( text ) rs->sr_text = text;
00372               send_ldap_extended( op, rs );
00373               /* otherwise frontend resends result */
00374               rc = rs->sr_err = SLAPD_ABANDON;
00375 
00376        } else if ( LDAP_BACK_QUARANTINE( li ) ) {
00377               ldap_back_quarantine( op, rs );
00378        }
00379 
00380        /* these have to be freed anyway... */
00381        if ( rs->sr_matched ) {
00382               free( (char *)rs->sr_matched );
00383               rs->sr_matched = NULL;
00384        }
00385 
00386        if ( rs->sr_ctrls ) {
00387               ldap_controls_free( rs->sr_ctrls );
00388               rs->sr_ctrls = NULL;
00389        }
00390 
00391        if ( text ) {
00392               free( text );
00393               rs->sr_text = NULL;
00394        }
00395 
00396        /* in case, cleanup handler */
00397        if ( lc == NULL ) {
00398               *lcp = NULL;
00399        }
00400 
00401        return rc;
00402 }