1 /* extended.c - ldap backend extended routines */
2 /* $OpenLDAP: pkg/ldap/servers/slapd/back-ldap/extended.c,v 1.36.2.8 2008/02/11 23:26:46 kurt Exp $ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2003-2008 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
17 * This work was initially developed by the Howard Chu for inclusion
18 * in OpenLDAP Software and subsequently enhanced by Pierangelo
25 #include <ac/string.h>
28 #include "back-ldap.h"
31 typedef int (ldap_back_exop_f
)( Operation
*op
, SlapReply
*rs
, ldapconn_t
**lc
);
33 static ldap_back_exop_f ldap_back_exop_passwd
;
34 static ldap_back_exop_f ldap_back_exop_generic
;
38 ldap_back_exop_f
*extended
;
40 { BER_BVC(LDAP_EXOP_MODIFY_PASSWD
), ldap_back_exop_passwd
},
45 ldap_back_extended_one( Operation
*op
, SlapReply
*rs
, ldap_back_exop_f exop
)
47 ldapinfo_t
*li
= (ldapinfo_t
*) op
->o_bd
->be_private
;
49 ldapconn_t
*lc
= NULL
;
50 LDAPControl
**ctrls
= NULL
, **oldctrls
= NULL
;
53 /* FIXME: this needs to be called here, so it is
54 * called twice; maybe we could avoid the
55 * ldap_back_dobind() call inside each extended()
57 if ( !ldap_back_dobind( &lc
, op
, rs
, LDAP_BACK_SENDERR
) ) {
62 if ( ldap_back_controls_add( op
, rs
, lc
, &ctrls
) )
64 op
->o_ctrls
= oldctrls
;
65 send_ldap_extended( op
, rs
);
67 /* otherwise frontend resends result */
68 rc
= rs
->sr_err
= SLAPD_ABANDON
;
73 rc
= exop( op
, rs
, &lc
);
75 op
->o_ctrls
= oldctrls
;
76 (void)ldap_back_controls_free( op
, rs
, &ctrls
);
80 ldap_back_release_conn( li
, lc
);
93 for ( i
= 0; exop_table
[i
].extended
!= NULL
; i
++ ) {
94 if ( bvmatch( &exop_table
[i
].oid
, &op
->oq_extended
.rs_reqoid
) )
96 return ldap_back_extended_one( op
, rs
, exop_table
[i
].extended
);
100 /* if we get here, the exop is known; the best that we can do
101 * is pass it thru as is */
102 /* FIXME: maybe a list of OIDs to pass thru would be safer */
103 return ldap_back_extended_one( op
, rs
, ldap_back_exop_generic
);
107 ldap_back_exop_passwd(
112 ldapinfo_t
*li
= (ldapinfo_t
*) op
->o_bd
->be_private
;
114 ldapconn_t
*lc
= *lcp
;
115 req_pwdexop_s
*qpw
= &op
->oq_pwdexop
;
118 int rc
, isproxy
, freedn
= 0;
121 struct berval dn
= op
->o_req_dn
,
124 assert( lc
!= NULL
);
125 assert( rs
->sr_ctrls
== NULL
);
127 if ( BER_BVISNULL( &ndn
) && op
->ore_reqdata
!= NULL
) {
128 /* NOTE: most of this code is mutated
129 * from slap_passwd_parse();
130 * But here we only need
131 * the first berval... */
135 BerElementBuffer berbuf
;
136 BerElement
*ber
= (BerElement
*)&berbuf
;
138 struct berval tmpid
= BER_BVNULL
;
140 if ( op
->ore_reqdata
->bv_len
== 0 ) {
141 return LDAP_PROTOCOL_ERROR
;
144 /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
145 ber_init2( ber
, op
->ore_reqdata
, 0 );
147 tag
= ber_scanf( ber
, "{" /*}*/ );
149 if ( tag
== LBER_ERROR
) {
150 return LDAP_PROTOCOL_ERROR
;
153 tag
= ber_peek_tag( ber
, &len
);
154 if ( tag
== LDAP_TAG_EXOP_MODIFY_PASSWD_ID
) {
155 tag
= ber_get_stringbv( ber
, &tmpid
, LBER_BV_NOTERM
);
157 if ( tag
== LBER_ERROR
) {
158 return LDAP_PROTOCOL_ERROR
;
162 if ( !BER_BVISEMPTY( &tmpid
) ) {
163 char idNull
= tmpid
.bv_val
[tmpid
.bv_len
];
164 tmpid
.bv_val
[tmpid
.bv_len
] = '\0';
165 rs
->sr_err
= dnPrettyNormal( NULL
, &tmpid
, &dn
,
166 &ndn
, op
->o_tmpmemctx
);
167 tmpid
.bv_val
[tmpid
.bv_len
] = idNull
;
168 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
169 /* should have been successfully parsed earlier! */
180 isproxy
= ber_bvcmp( &ndn
, &op
->o_ndn
);
182 Debug( LDAP_DEBUG_ARGS
, "==> ldap_back_exop_passwd(\"%s\")%s\n",
183 dn
.bv_val
, isproxy
? " (proxy)" : "", 0 );
186 rc
= ldap_passwd( lc
->lc_ld
, isproxy
? &dn
: NULL
,
187 qpw
->rs_old
.bv_val
? &qpw
->rs_old
: NULL
,
188 qpw
->rs_new
.bv_val
? &qpw
->rs_new
: NULL
,
189 op
->o_ctrls
, NULL
, &msgid
);
191 if ( rc
== LDAP_SUCCESS
) {
192 /* TODO: set timeout? */
193 if ( ldap_result( lc
->lc_ld
, msgid
, LDAP_MSG_ALL
, NULL
, &res
) == -1 ) {
194 ldap_get_option( lc
->lc_ld
, LDAP_OPT_ERROR_NUMBER
, &rc
);
198 /* only touch when activity actually took place... */
199 if ( li
->li_idle_timeout
&& lc
) {
200 lc
->lc_time
= op
->o_time
;
203 /* sigh. parse twice, because parse_passwd
204 * doesn't give us the err / match / msg info.
206 rc
= ldap_parse_result( lc
->lc_ld
, res
, &rs
->sr_err
,
207 (char **)&rs
->sr_matched
,
209 NULL
, &rs
->sr_ctrls
, 0 );
211 if ( rc
== LDAP_SUCCESS
) {
212 if ( rs
->sr_err
== LDAP_SUCCESS
) {
215 /* this never happens because
216 * the frontend is generating
217 * the new password, so when
218 * the passwd exop is proxied,
219 * it never delegates password
220 * generation to the remote server
222 rc
= ldap_parse_passwd( lc
->lc_ld
, res
,
224 if ( rc
== LDAP_SUCCESS
&&
225 !BER_BVISNULL( &newpw
) )
227 rs
->sr_type
= REP_EXTENDED
;
228 rs
->sr_rspdata
= slap_passwd_return( &newpw
);
229 free( newpw
.bv_val
);
240 if ( rc
!= LDAP_SUCCESS
) {
241 rs
->sr_err
= slap_map_api2result( rs
);
242 if ( rs
->sr_err
== LDAP_UNAVAILABLE
&& do_retry
) {
244 if ( ldap_back_retry( &lc
, op
, rs
, LDAP_BACK_SENDERR
) ) {
249 if ( LDAP_BACK_QUARANTINE( li
) ) {
250 ldap_back_quarantine( op
, rs
);
253 if ( text
) rs
->sr_text
= text
;
254 send_ldap_extended( op
, rs
);
255 /* otherwise frontend resends result */
256 rc
= rs
->sr_err
= SLAPD_ABANDON
;
258 } else if ( LDAP_BACK_QUARANTINE( li
) ) {
259 ldap_back_quarantine( op
, rs
);
263 op
->o_tmpfree( dn
.bv_val
, op
->o_tmpmemctx
);
264 op
->o_tmpfree( ndn
.bv_val
, op
->o_tmpmemctx
);
267 /* these have to be freed anyway... */
268 if ( rs
->sr_matched
) {
269 free( (char *)rs
->sr_matched
);
270 rs
->sr_matched
= NULL
;
273 if ( rs
->sr_ctrls
) {
274 ldap_controls_free( rs
->sr_ctrls
);
283 /* in case, cleanup handler */
292 ldap_back_exop_generic(
297 ldapinfo_t
*li
= (ldapinfo_t
*) op
->o_bd
->be_private
;
299 ldapconn_t
*lc
= *lcp
;
306 assert( lc
!= NULL
);
307 assert( rs
->sr_ctrls
== NULL
);
309 Debug( LDAP_DEBUG_ARGS
, "==> ldap_back_exop_generic(%s, \"%s\")\n",
310 op
->ore_reqoid
.bv_val
, op
->o_req_dn
.bv_val
, 0 );
313 rc
= ldap_extended_operation( lc
->lc_ld
,
314 op
->ore_reqoid
.bv_val
, op
->ore_reqdata
,
315 op
->o_ctrls
, NULL
, &msgid
);
317 if ( rc
== LDAP_SUCCESS
) {
318 /* TODO: set timeout? */
319 if ( ldap_result( lc
->lc_ld
, msgid
, LDAP_MSG_ALL
, NULL
, &res
) == -1 ) {
320 ldap_get_option( lc
->lc_ld
, LDAP_OPT_ERROR_NUMBER
, &rc
);
324 /* only touch when activity actually took place... */
325 if ( li
->li_idle_timeout
&& lc
) {
326 lc
->lc_time
= op
->o_time
;
329 /* sigh. parse twice, because parse_passwd
330 * doesn't give us the err / match / msg info.
332 rc
= ldap_parse_result( lc
->lc_ld
, res
, &rs
->sr_err
,
333 (char **)&rs
->sr_matched
,
335 NULL
, &rs
->sr_ctrls
, 0 );
336 if ( rc
== LDAP_SUCCESS
) {
337 if ( rs
->sr_err
== LDAP_SUCCESS
) {
338 rc
= ldap_parse_extended_result( lc
->lc_ld
, res
,
339 (char **)&rs
->sr_rspoid
, &rs
->sr_rspdata
, 0 );
340 if ( rc
== LDAP_SUCCESS
) {
341 rs
->sr_type
= REP_EXTENDED
;
352 if ( rc
!= LDAP_SUCCESS
) {
353 rs
->sr_err
= slap_map_api2result( rs
);
354 if ( rs
->sr_err
== LDAP_UNAVAILABLE
&& do_retry
) {
356 if ( ldap_back_retry( &lc
, op
, rs
, LDAP_BACK_SENDERR
) ) {
361 if ( LDAP_BACK_QUARANTINE( li
) ) {
362 ldap_back_quarantine( op
, rs
);
365 if ( text
) rs
->sr_text
= text
;
366 send_ldap_extended( op
, rs
);
367 /* otherwise frontend resends result */
368 rc
= rs
->sr_err
= SLAPD_ABANDON
;
370 } else if ( LDAP_BACK_QUARANTINE( li
) ) {
371 ldap_back_quarantine( op
, rs
);
374 /* these have to be freed anyway... */
375 if ( rs
->sr_matched
) {
376 free( (char *)rs
->sr_matched
);
377 rs
->sr_matched
= NULL
;
380 if ( rs
->sr_ctrls
) {
381 ldap_controls_free( rs
->sr_ctrls
);
390 /* in case, cleanup handler */