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