]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/extended.c
fbe3716cae7b3cb4b3fb94f68407332aabc9cfa0
[openldap] / servers / slapd / back-ldap / extended.c
1 /* extended.c - ldap backend extended routines */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2003-2017 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
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>.
15  */
16 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by the Howard Chu for inclusion
18  * in OpenLDAP Software and subsequently enhanced by Pierangelo
19  * Masarati. 
20  */
21
22 #include "portable.h"
23
24 #include <stdio.h>
25 #include <ac/string.h>
26
27 #include "slap.h"
28 #include "back-ldap.h"
29 #include "lber_pvt.h"
30
31 typedef int (ldap_back_exop_f)( Operation *op, SlapReply *rs, ldapconn_t **lc );
32
33 static ldap_back_exop_f ldap_back_exop_passwd;
34 static ldap_back_exop_f ldap_back_exop_generic;
35
36 static struct exop {
37         struct berval           oid;
38         ldap_back_exop_f        *extended;
39 } exop_table[] = {
40         { BER_BVC(LDAP_EXOP_MODIFY_PASSWD),     ldap_back_exop_passwd },
41         { BER_BVNULL, NULL }
42 };
43
44 static int
45 ldap_back_extended_one( Operation *op, SlapReply *rs, ldap_back_exop_f exop )
46 {
47         ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
48
49         ldapconn_t      *lc = NULL;
50         LDAPControl     **ctrls = NULL, **oldctrls = NULL;
51         int             rc;
52
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()
56          * call ... */
57         if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
58                 return -1;
59         }
60
61         ctrls = op->o_ctrls;
62         if ( ldap_back_controls_add( op, rs, lc, &ctrls ) )
63         {
64                 op->o_ctrls = oldctrls;
65                 send_ldap_extended( op, rs );
66                 rs->sr_text = NULL;
67                 /* otherwise frontend resends result */
68                 rc = rs->sr_err = SLAPD_ABANDON;
69                 goto done;
70         }
71
72         op->o_ctrls = ctrls;
73         rc = exop( op, rs, &lc );
74
75         op->o_ctrls = oldctrls;
76         (void)ldap_back_controls_free( op, rs, &ctrls );
77
78 done:;
79         if ( lc != NULL ) {
80                 ldap_back_release_conn( li, lc );
81         }
82                         
83         return rc;
84 }
85
86 int
87 ldap_back_extended(
88                 Operation       *op,
89                 SlapReply       *rs )
90 {
91         int     i;
92
93         RS_ASSERT( !(rs->sr_flags & REP_ENTRY_MASK) );
94         rs->sr_flags &= ~REP_ENTRY_MASK;        /* paranoia */
95
96         for ( i = 0; exop_table[i].extended != NULL; i++ ) {
97                 if ( bvmatch( &exop_table[i].oid, &op->oq_extended.rs_reqoid ) )
98                 {
99                         return ldap_back_extended_one( op, rs, exop_table[i].extended );
100                 }
101         }
102
103         /* if we get here, the exop is known; the best that we can do
104          * is pass it thru as is */
105         /* FIXME: maybe a list of OIDs to pass thru would be safer */
106         return ldap_back_extended_one( op, rs, ldap_back_exop_generic );
107 }
108
109 static int
110 ldap_back_exop_passwd(
111         Operation       *op,
112         SlapReply       *rs,
113         ldapconn_t      **lcp )
114 {
115         ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
116
117         ldapconn_t      *lc = *lcp;
118         req_pwdexop_s   *qpw = &op->oq_pwdexop;
119         LDAPMessage     *res;
120         ber_int_t       msgid;
121         int             rc, isproxy, freedn = 0;
122         int             do_retry = 1;
123         char            *text = NULL;
124         struct berval   dn = op->o_req_dn,
125                         ndn = op->o_req_ndn;
126
127         assert( lc != NULL );
128         assert( rs->sr_ctrls == NULL );
129
130         if ( BER_BVISNULL( &ndn ) && op->ore_reqdata != NULL ) {
131                 /* NOTE: most of this code is mutated
132                  * from slap_passwd_parse();
133                  * But here we only need
134                  * the first berval... */
135
136                 ber_tag_t tag;
137                 ber_len_t len = -1;
138                 BerElementBuffer berbuf;
139                 BerElement *ber = (BerElement *)&berbuf;
140
141                 struct berval   tmpid = BER_BVNULL;
142
143                 if ( op->ore_reqdata->bv_len == 0 ) {
144                         return LDAP_PROTOCOL_ERROR;
145                 }
146
147                 /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
148                 ber_init2( ber, op->ore_reqdata, 0 );
149
150                 tag = ber_scanf( ber, "{" /*}*/ );
151
152                 if ( tag == LBER_ERROR ) {
153                         return LDAP_PROTOCOL_ERROR;
154                 }
155
156                 tag = ber_peek_tag( ber, &len );
157                 if ( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_ID ) {
158                         tag = ber_get_stringbv( ber, &tmpid, LBER_BV_NOTERM );
159
160                         if ( tag == LBER_ERROR ) {
161                                 return LDAP_PROTOCOL_ERROR;
162                         }
163                 }
164
165                 if ( !BER_BVISEMPTY( &tmpid ) ) {
166                         char idNull = tmpid.bv_val[tmpid.bv_len];
167                         tmpid.bv_val[tmpid.bv_len] = '\0';
168                         rs->sr_err = dnPrettyNormal( NULL, &tmpid, &dn,
169                                 &ndn, op->o_tmpmemctx );
170                         tmpid.bv_val[tmpid.bv_len] = idNull;
171                         if ( rs->sr_err != LDAP_SUCCESS ) {
172                                 /* should have been successfully parsed earlier! */
173                                 return rs->sr_err;
174                         }
175                         freedn = 1;
176
177                 } else {
178                         dn = op->o_dn;
179                         ndn = op->o_ndn;
180                 }
181         }
182
183         isproxy = ber_bvcmp( &ndn, &op->o_ndn );
184
185         Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_passwd(\"%s\")%s\n",
186                 dn.bv_val, isproxy ? " (proxy)" : "", 0 );
187
188 retry:
189         rc = ldap_passwd( lc->lc_ld,  &dn,
190                 qpw->rs_old.bv_val ? &qpw->rs_old : NULL,
191                 qpw->rs_new.bv_val ? &qpw->rs_new : NULL,
192                 op->o_ctrls, NULL, &msgid );
193
194         if ( rc == LDAP_SUCCESS ) {
195                 /* TODO: set timeout? */
196                 /* by now, make sure no timeout is used (ITS#6282) */
197                 struct timeval tv = { -1, 0 };
198                 if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) == -1 ) {
199                         ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc );
200                         rs->sr_err = rc;
201
202                 } else {
203                         /* only touch when activity actually took place... */
204                         if ( li->li_idle_timeout ) {
205                                 lc->lc_time = op->o_time;
206                         }
207
208                         /* sigh. parse twice, because parse_passwd
209                          * doesn't give us the err / match / msg info.
210                          */
211                         rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
212                                         (char **)&rs->sr_matched,
213                                         &text,
214                                         NULL, &rs->sr_ctrls, 0 );
215
216                         if ( rc == LDAP_SUCCESS ) {
217                                 if ( rs->sr_err == LDAP_SUCCESS ) {
218                                         struct berval   newpw;
219
220                                         /* this never happens because 
221                                          * the frontend is generating 
222                                          * the new password, so when
223                                          * the passwd exop is proxied,
224                                          * it never delegates password
225                                          * generation to the remote server
226                                          */
227                                         rc = ldap_parse_passwd( lc->lc_ld, res,
228                                                         &newpw );
229                                         if ( rc == LDAP_SUCCESS &&
230                                                         !BER_BVISNULL( &newpw ) )
231                                         {
232                                                 rs->sr_type = REP_EXTENDED;
233                                                 rs->sr_rspdata = slap_passwd_return( &newpw );
234                                                 free( newpw.bv_val );
235                                         }
236
237                                 } else {
238                                         rc = rs->sr_err;
239                                 }
240                         }
241                         ldap_msgfree( res );
242                 }
243         }
244
245         if ( rc != LDAP_SUCCESS ) {
246                 rs->sr_err = slap_map_api2result( rs );
247                 if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
248                         do_retry = 0;
249                         if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
250                                 goto retry;
251                         }
252                 }
253
254                 if ( LDAP_BACK_QUARANTINE( li ) ) {
255                         ldap_back_quarantine( op, rs );
256                 }
257
258                 if ( text ) rs->sr_text = text;
259                 send_ldap_extended( op, rs );
260                 /* otherwise frontend resends result */
261                 rc = rs->sr_err = SLAPD_ABANDON;
262
263         } else if ( LDAP_BACK_QUARANTINE( li ) ) {
264                 ldap_back_quarantine( op, rs );
265         }
266
267         ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
268         ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_EXTENDED ], 1 );
269         ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
270
271         if ( freedn ) {
272                 op->o_tmpfree( dn.bv_val, op->o_tmpmemctx );
273                 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
274         }
275
276         /* these have to be freed anyway... */
277         if ( rs->sr_matched ) {
278                 free( (char *)rs->sr_matched );
279                 rs->sr_matched = NULL;
280         }
281
282         if ( rs->sr_ctrls ) {
283                 ldap_controls_free( rs->sr_ctrls );
284                 rs->sr_ctrls = NULL;
285         }
286
287         if ( text ) {
288                 free( text );
289                 rs->sr_text = NULL;
290         }
291
292         /* in case, cleanup handler */
293         if ( lc == NULL ) {
294                 *lcp = NULL;
295         }
296
297         return rc;
298 }
299
300 static int
301 ldap_back_exop_generic(
302         Operation       *op,
303         SlapReply       *rs,
304         ldapconn_t      **lcp )
305 {
306         ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
307
308         ldapconn_t      *lc = *lcp;
309         LDAPMessage     *res;
310         ber_int_t       msgid;
311         int             rc;
312         int             do_retry = 1;
313         char            *text = NULL;
314
315         Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_generic(%s, \"%s\")\n",
316                 op->ore_reqoid.bv_val, op->o_req_dn.bv_val, 0 );
317         assert( lc != NULL );
318         assert( rs->sr_ctrls == NULL );
319
320 retry:
321         rc = ldap_extended_operation( lc->lc_ld,
322                 op->ore_reqoid.bv_val, op->ore_reqdata,
323                 op->o_ctrls, NULL, &msgid );
324
325         if ( rc == LDAP_SUCCESS ) {
326                 /* TODO: set timeout? */
327                 /* by now, make sure no timeout is used (ITS#6282) */
328                 struct timeval tv = { -1, 0 };
329                 if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) == -1 ) {
330                         ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc );
331                         rs->sr_err = rc;
332
333                 } else {
334                         /* only touch when activity actually took place... */
335                         if ( li->li_idle_timeout ) {
336                                 lc->lc_time = op->o_time;
337                         }
338
339                         /* sigh. parse twice, because parse_passwd
340                          * doesn't give us the err / match / msg info.
341                          */
342                         rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
343                                         (char **)&rs->sr_matched,
344                                         &text,
345                                         NULL, &rs->sr_ctrls, 0 );
346                         if ( rc == LDAP_SUCCESS ) {
347                                 if ( rs->sr_err == LDAP_SUCCESS ) {
348                                         rc = ldap_parse_extended_result( lc->lc_ld, res,
349                                                         (char **)&rs->sr_rspoid, &rs->sr_rspdata, 0 );
350                                         if ( rc == LDAP_SUCCESS ) {
351                                                 rs->sr_type = REP_EXTENDED;
352                                         }
353
354                                 } else {
355                                         rc = rs->sr_err;
356                                 }
357                         }
358                         ldap_msgfree( res );
359                 }
360         }
361
362         if ( rc != LDAP_SUCCESS ) {
363                 rs->sr_err = slap_map_api2result( rs );
364                 if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
365                         do_retry = 0;
366                         if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
367                                 goto retry;
368                         }
369                 }
370
371                 if ( LDAP_BACK_QUARANTINE( li ) ) {
372                         ldap_back_quarantine( op, rs );
373                 }
374
375                 if ( text ) rs->sr_text = text;
376                 send_ldap_extended( op, rs );
377                 /* otherwise frontend resends result */
378                 rc = rs->sr_err = SLAPD_ABANDON;
379
380         } else if ( LDAP_BACK_QUARANTINE( li ) ) {
381                 ldap_back_quarantine( op, rs );
382         }
383
384         ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
385         ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_EXTENDED ], 1 );
386         ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
387
388         /* these have to be freed anyway... */
389         if ( rs->sr_matched ) {
390                 free( (char *)rs->sr_matched );
391                 rs->sr_matched = NULL;
392         }
393
394         if ( rs->sr_ctrls ) {
395                 ldap_controls_free( rs->sr_ctrls );
396                 rs->sr_ctrls = NULL;
397         }
398
399         if ( text ) {
400                 free( text );
401                 rs->sr_text = NULL;
402         }
403
404         /* in case, cleanup handler */
405         if ( lc == NULL ) {
406                 *lcp = NULL;
407         }
408
409         return rc;
410 }