]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/extended.c
Sync with HEAD for OL 2.4.5
[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-2007 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         for ( i = 0; exop_table[i].extended != NULL; i++ ) {
94                 if ( bvmatch( &exop_table[i].oid, &op->oq_extended.rs_reqoid ) )
95                 {
96                         return ldap_back_extended_one( op, rs, exop_table[i].extended );
97                 }
98         }
99
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 );
104 }
105
106 static int
107 ldap_back_exop_passwd(
108         Operation       *op,
109         SlapReply       *rs,
110         ldapconn_t      **lcp )
111 {
112         ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
113
114         ldapconn_t      *lc = *lcp;
115         req_pwdexop_s   *qpw = &op->oq_pwdexop;
116         LDAPMessage     *res;
117         ber_int_t       msgid;
118         int             rc, isproxy, freedn = 0;
119         int             do_retry = 1;
120         char            *text = NULL;
121         struct berval   dn = op->o_req_dn,
122                         ndn = op->o_req_ndn;
123
124         assert( lc != NULL );
125         assert( rs->sr_ctrls == NULL );
126
127         if ( BER_BVISNULL( &ndn ) && op->ore_reqdata != NULL ) {
128                 /* NOTE: most of this code is mutuated
129                  * from slap_passwd_parse(); we can't call
130                  * that function since now the request data
131                  * has been destroyed by NULL-terminating
132                  * the bervals.  Luckily enough, we only need
133                  * the first berval... */
134
135                 ber_tag_t tag;
136                 ber_len_t len = -1;
137                 BerElementBuffer berbuf;
138                 BerElement *ber = (BerElement *)&berbuf;
139
140                 struct berval   tmpid = BER_BVNULL;
141
142                 if ( op->ore_reqdata->bv_len == 0 ) {
143                         return LDAP_PROTOCOL_ERROR;
144                 }
145
146                 /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
147                 ber_init2( ber, op->ore_reqdata, 0 );
148
149                 tag = ber_scanf( ber, "{" /*}*/ );
150
151                 if ( tag == LBER_ERROR ) {
152                         return LDAP_PROTOCOL_ERROR;
153                 }
154
155                 tag = ber_peek_tag( ber, &len );
156                 if ( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_ID ) {
157                         tag = ber_scanf( ber, "m", &tmpid );
158
159                         if ( tag == LBER_ERROR ) {
160                                 return LDAP_PROTOCOL_ERROR;
161                         }
162                 }
163
164                 if ( !BER_BVISEMPTY( &tmpid ) ) {
165                         rs->sr_err = dnPrettyNormal( NULL, &tmpid, &dn,
166                                 &ndn, op->o_tmpmemctx );
167                         if ( rs->sr_err != LDAP_SUCCESS ) {
168                                 /* should have been successfully parsed earlier! */
169                                 return rs->sr_err;
170                         }
171                         freedn = 1;
172
173                 } else {
174                         dn = op->o_dn;
175                         ndn = op->o_ndn;
176                 }
177         }
178
179         isproxy = ber_bvcmp( &ndn, &op->o_ndn );
180
181         Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_passwd(\"%s\")%s\n",
182                 dn.bv_val, isproxy ? " (proxy)" : "", 0 );
183
184 retry:
185         rc = ldap_passwd( lc->lc_ld, isproxy ? &dn : NULL,
186                 qpw->rs_old.bv_val ? &qpw->rs_old : NULL,
187                 qpw->rs_new.bv_val ? &qpw->rs_new : NULL,
188                 op->o_ctrls, NULL, &msgid );
189
190         if ( rc == LDAP_SUCCESS ) {
191                 /* TODO: set timeout? */
192                 if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, NULL, &res ) == -1 ) {
193                         ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc );
194                         rs->sr_err = rc;
195
196                 } else {
197                         /* only touch when activity actually took place... */
198                         if ( li->li_idle_timeout && lc ) {
199                                 lc->lc_time = op->o_time;
200                         }
201
202                         /* sigh. parse twice, because parse_passwd
203                          * doesn't give us the err / match / msg info.
204                          */
205                         rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
206                                         (char **)&rs->sr_matched,
207                                         &text,
208                                         NULL, &rs->sr_ctrls, 0 );
209
210                         if ( rc == LDAP_SUCCESS ) {
211                                 if ( rs->sr_err == LDAP_SUCCESS ) {
212                                         struct berval   newpw;
213
214                                         /* this never happens because 
215                                          * the frontend is generating 
216                                          * the new password, so when
217                                          * the passwd exop is proxied,
218                                          * it never delegates password
219                                          * generation to the remote server
220                                          */
221                                         rc = ldap_parse_passwd( lc->lc_ld, res,
222                                                         &newpw );
223                                         if ( rc == LDAP_SUCCESS &&
224                                                         !BER_BVISNULL( &newpw ) )
225                                         {
226                                                 rs->sr_type = REP_EXTENDED;
227                                                 rs->sr_rspdata = slap_passwd_return( &newpw );
228                                                 free( newpw.bv_val );
229                                         }
230
231                                 } else {
232                                         rc = rs->sr_err;
233                                 }
234                         }
235                         ldap_msgfree( res );
236                 }
237         }
238
239         if ( rc != LDAP_SUCCESS ) {
240                 rs->sr_err = slap_map_api2result( rs );
241                 if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
242                         do_retry = 0;
243                         if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
244                                 goto retry;
245                         }
246                 }
247
248                 if ( LDAP_BACK_QUARANTINE( li ) ) {
249                         ldap_back_quarantine( op, rs );
250                 }
251
252                 if ( text ) rs->sr_text = text;
253                 send_ldap_extended( op, rs );
254                 /* otherwise frontend resends result */
255                 rc = rs->sr_err = SLAPD_ABANDON;
256
257         } else if ( LDAP_BACK_QUARANTINE( li ) ) {
258                 ldap_back_quarantine( op, rs );
259         }
260
261         if ( freedn ) {
262                 op->o_tmpfree( dn.bv_val, op->o_tmpmemctx );
263                 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
264         }
265
266         /* these have to be freed anyway... */
267         if ( rs->sr_matched ) {
268                 free( (char *)rs->sr_matched );
269                 rs->sr_matched = NULL;
270         }
271
272         if ( rs->sr_ctrls ) {
273                 ldap_controls_free( rs->sr_ctrls );
274                 rs->sr_ctrls = NULL;
275         }
276
277         if ( text ) {
278                 free( text );
279                 rs->sr_text = NULL;
280         }
281
282         /* in case, cleanup handler */
283         if ( lc == NULL ) {
284                 *lcp = NULL;
285         }
286
287         return rc;
288 }
289
290 static int
291 ldap_back_exop_generic(
292         Operation       *op,
293         SlapReply       *rs,
294         ldapconn_t      **lcp )
295 {
296         ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
297
298         ldapconn_t      *lc = *lcp;
299         LDAPMessage     *res;
300         ber_int_t       msgid;
301         int             rc;
302         int             do_retry = 1;
303         char            *text = NULL;
304
305         assert( lc != NULL );
306         assert( rs->sr_ctrls == NULL );
307
308         Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_generic(%s, \"%s\")\n",
309                 op->ore_reqoid.bv_val, op->o_req_dn.bv_val, 0 );
310
311 retry:
312         rc = ldap_extended_operation( lc->lc_ld,
313                 op->ore_reqoid.bv_val, op->ore_reqdata,
314                 op->o_ctrls, NULL, &msgid );
315
316         if ( rc == LDAP_SUCCESS ) {
317                 /* TODO: set timeout? */
318                 if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, NULL, &res ) == -1 ) {
319                         ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc );
320                         rs->sr_err = rc;
321
322                 } else {
323                         /* only touch when activity actually took place... */
324                         if ( li->li_idle_timeout && lc ) {
325                                 lc->lc_time = op->o_time;
326                         }
327
328                         /* sigh. parse twice, because parse_passwd
329                          * doesn't give us the err / match / msg info.
330                          */
331                         rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
332                                         (char **)&rs->sr_matched,
333                                         &text,
334                                         NULL, &rs->sr_ctrls, 0 );
335                         if ( rc == LDAP_SUCCESS ) {
336                                 if ( rs->sr_err == LDAP_SUCCESS ) {
337                                         rc = ldap_parse_extended_result( lc->lc_ld, res,
338                                                         (char **)&rs->sr_rspoid, &rs->sr_rspdata, 0 );
339                                         if ( rc == LDAP_SUCCESS ) {
340                                                 rs->sr_type = REP_EXTENDED;
341                                         }
342
343                                 } else {
344                                         rc = rs->sr_err;
345                                 }
346                         }
347                         ldap_msgfree( res );
348                 }
349         }
350
351         if ( rc != LDAP_SUCCESS ) {
352                 rs->sr_err = slap_map_api2result( rs );
353                 if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
354                         do_retry = 0;
355                         if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
356                                 goto retry;
357                         }
358                 }
359
360                 if ( LDAP_BACK_QUARANTINE( li ) ) {
361                         ldap_back_quarantine( op, rs );
362                 }
363
364                 if ( text ) rs->sr_text = text;
365                 send_ldap_extended( op, rs );
366                 /* otherwise frontend resends result */
367                 rc = rs->sr_err = SLAPD_ABANDON;
368
369         } else if ( LDAP_BACK_QUARANTINE( li ) ) {
370                 ldap_back_quarantine( op, rs );
371         }
372
373         /* these have to be freed anyway... */
374         if ( rs->sr_matched ) {
375                 free( (char *)rs->sr_matched );
376                 rs->sr_matched = NULL;
377         }
378
379         if ( rs->sr_ctrls ) {
380                 ldap_controls_free( rs->sr_ctrls );
381                 rs->sr_ctrls = NULL;
382         }
383
384         if ( text ) {
385                 free( text );
386                 rs->sr_text = NULL;
387         }
388
389         /* in case, cleanup handler */
390         if ( lc == NULL ) {
391                 *lcp = NULL;
392         }
393
394         return rc;
395 }
396