]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/extended.c
RFC 4511 calls for unavailableCriticalExtension to returned when
[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-2006 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 static BI_op_extended ldap_back_exop_passwd;
32 static BI_op_extended ldap_back_exop_generic;
33
34 static struct exop {
35         struct berval   oid;
36         BI_op_extended  *extended;
37 } exop_table[] = {
38         { BER_BVC(LDAP_EXOP_MODIFY_PASSWD),     ldap_back_exop_passwd },
39         { BER_BVNULL, NULL }
40 };
41
42 static int
43 ldap_back_extended_one( Operation *op, SlapReply *rs, BI_op_extended exop )
44 {
45         ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
46
47         ldapconn_t      *lc = NULL;
48         LDAPControl     **oldctrls = NULL;
49         int             rc;
50
51         /* FIXME: this needs to be called here, so it is
52          * called twice; maybe we could avoid the 
53          * ldap_back_dobind() call inside each extended()
54          * call ... */
55         if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
56                 return -1;
57         }
58
59         oldctrls = op->o_ctrls;
60         if ( ldap_back_proxy_authz_ctrl( &lc->lc_bound_ndn,
61                 li->li_version, &li->li_idassert, op, rs, &op->o_ctrls ) )
62         {
63                 op->o_ctrls = oldctrls;
64                 send_ldap_extended( op, rs );
65                 rs->sr_text = NULL;
66                 /* otherwise frontend resends result */
67                 rc = rs->sr_err = SLAPD_ABANDON;
68                 goto done;
69         }
70
71         rc = exop( op, rs );
72
73         if ( op->o_ctrls && op->o_ctrls != oldctrls ) {
74                 free( op->o_ctrls[ 0 ] );
75                 free( op->o_ctrls );
76         }
77         op->o_ctrls = oldctrls;
78
79 done:;
80         if ( lc != NULL ) {
81                 ldap_back_release_conn( op, rs, lc );
82         }
83                         
84         return rc;
85 }
86
87 int
88 ldap_back_extended(
89                 Operation       *op,
90                 SlapReply       *rs )
91 {
92         int     i;
93
94         for ( i = 0; exop_table[i].extended != NULL; i++ ) {
95                 if ( bvmatch( &exop_table[i].oid, &op->oq_extended.rs_reqoid ) )
96                 {
97                         return ldap_back_extended_one( op, rs, exop_table[i].extended );
98                 }
99         }
100
101         /* if we get here, the exop is known; the best that we can do
102          * is pass it thru as is */
103         /* FIXME: maybe a list of OIDs to pass thru would be safer */
104         return ldap_back_extended_one( op, rs, ldap_back_exop_generic );
105 }
106
107 static int
108 ldap_back_exop_passwd(
109                 Operation       *op,
110                 SlapReply       *rs )
111 {
112         ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
113
114         ldapconn_t      *lc = NULL;
115         req_pwdexop_s   *qpw = &op->oq_pwdexop;
116         LDAPMessage     *res;
117         ber_int_t       msgid;
118         int             rc, isproxy;
119         int             do_retry = 1;
120         char *text = NULL;
121
122         if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
123                 return -1;
124         }
125
126         isproxy = ber_bvcmp( &op->o_req_ndn, &op->o_ndn );
127
128         Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_passwd(\"%s\")%s\n",
129                 op->o_req_dn.bv_val, isproxy ? " (proxy)" : "", 0 );
130
131 retry:
132         rc = ldap_passwd( lc->lc_ld, isproxy ? &op->o_req_dn : NULL,
133                 qpw->rs_old.bv_val ? &qpw->rs_old : NULL,
134                 qpw->rs_new.bv_val ? &qpw->rs_new : NULL,
135                 op->o_ctrls, NULL, &msgid );
136
137         if ( rc == LDAP_SUCCESS ) {
138                 if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, NULL, &res ) == -1 ) {
139                         ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc );
140                         rs->sr_err = rc;
141
142                 } else {
143                         /* only touch when activity actually took place... */
144                         if ( li->li_idle_timeout && lc ) {
145                                 lc->lc_time = op->o_time;
146                         }
147
148                         /* sigh. parse twice, because parse_passwd
149                          * doesn't give us the err / match / msg info.
150                          */
151                         rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
152                                         (char **)&rs->sr_matched,
153                                         &text,
154                                         NULL, NULL, 0 );
155
156                         if ( rc == LDAP_SUCCESS ) {
157                                 if ( rs->sr_err == LDAP_SUCCESS ) {
158                                         struct berval   newpw;
159
160                                         /* this never happens because 
161                                          * the frontend is generating 
162                                          * the new password, so when
163                                          * the passwd exop is proxied,
164                                          * it never delegates password
165                                          * generation to the remote server
166                                          */
167                                         rc = ldap_parse_passwd( lc->lc_ld, res,
168                                                         &newpw );
169                                         if ( rc == LDAP_SUCCESS &&
170                                                         !BER_BVISNULL( &newpw ) )
171                                         {
172                                                 rs->sr_type = REP_EXTENDED;
173                                                 rs->sr_rspdata = slap_passwd_return( &newpw );
174                                                 free( newpw.bv_val );
175                                         }
176
177                                 } else {
178                                         rc = rs->sr_err;
179                                 }
180                         }
181                         ldap_msgfree( res );
182                 }
183         }
184
185         if ( rc != LDAP_SUCCESS ) {
186                 rs->sr_err = slap_map_api2result( rs );
187                 if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
188                         do_retry = 0;
189                         if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
190                                 goto retry;
191                         }
192                 }
193
194                 if ( LDAP_BACK_QUARANTINE( li ) ) {
195                         ldap_back_quarantine( op, rs );
196                 }
197
198                 if ( text ) rs->sr_text = text;
199                 send_ldap_extended( op, rs );
200                 /* otherwise frontend resends result */
201                 rc = rs->sr_err = SLAPD_ABANDON;
202
203         } else if ( LDAP_BACK_QUARANTINE( li ) ) {
204                 ldap_back_quarantine( op, rs );
205         }
206
207         /* these have to be freed anyway... */
208         if ( rs->sr_matched ) {
209                 free( (char *)rs->sr_matched );
210                 rs->sr_matched = NULL;
211         }
212
213         if ( text ) {
214                 free( text );
215                 rs->sr_text = NULL;
216         }
217
218         if ( lc != NULL ) {
219                 ldap_back_release_conn( op, rs, lc );
220         }
221
222         return rc;
223 }
224
225 static int
226 ldap_back_exop_generic(
227         Operation       *op,
228         SlapReply       *rs )
229 {
230         ldapinfo_t      *li = (ldapinfo_t *) op->o_bd->be_private;
231
232         ldapconn_t      *lc = NULL;
233         LDAPMessage     *res;
234         ber_int_t       msgid;
235         int             rc;
236         int             do_retry = 1;
237         char *text = NULL;
238
239         if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
240                 return -1;
241         }
242
243         Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_generic(%s, \"%s\")\n",
244                 op->ore_reqoid.bv_val, op->o_req_dn.bv_val, 0 );
245
246 retry:
247         rc = ldap_extended_operation( lc->lc_ld,
248                 op->ore_reqoid.bv_val, op->ore_reqdata,
249                 op->o_ctrls, NULL, &msgid );
250
251         if ( rc == LDAP_SUCCESS ) {
252                 if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, NULL, &res ) == -1 ) {
253                         ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc );
254                         rs->sr_err = rc;
255
256                 } else {
257                         /* only touch when activity actually took place... */
258                         if ( li->li_idle_timeout && lc ) {
259                                 lc->lc_time = op->o_time;
260                         }
261
262                         /* sigh. parse twice, because parse_passwd
263                          * doesn't give us the err / match / msg info.
264                          */
265                         rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
266                                         (char **)&rs->sr_matched,
267                                         &text,
268                                         NULL, NULL, 0 );
269                         if ( rc == LDAP_SUCCESS ) {
270                                 if ( rs->sr_err == LDAP_SUCCESS ) {
271                                         rc = ldap_parse_extended_result( lc->lc_ld, res,
272                                                         (char **)&rs->sr_rspoid, &rs->sr_rspdata, 0 );
273                                         if ( rc == LDAP_SUCCESS ) {
274                                                 rs->sr_type = REP_EXTENDED;
275                                         }
276
277                                 } else {
278                                         rc = rs->sr_err;
279                                 }
280                         }
281                         ldap_msgfree( res );
282                 }
283         }
284
285         if ( rc != LDAP_SUCCESS ) {
286                 rs->sr_err = slap_map_api2result( rs );
287                 if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
288                         do_retry = 0;
289                         if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
290                                 goto retry;
291                         }
292                 }
293
294                 if ( LDAP_BACK_QUARANTINE( li ) ) {
295                         ldap_back_quarantine( op, rs );
296                 }
297
298                 if ( text ) rs->sr_text = text;
299                 send_ldap_extended( op, rs );
300                 /* otherwise frontend resends result */
301                 rc = rs->sr_err = SLAPD_ABANDON;
302
303         } else if ( LDAP_BACK_QUARANTINE( li ) ) {
304                 ldap_back_quarantine( op, rs );
305         }
306
307         /* these have to be freed anyway... */
308         if ( rs->sr_matched ) {
309                 free( (char *)rs->sr_matched );
310                 rs->sr_matched = NULL;
311         }
312
313         if ( text ) {
314                 free( text );
315                 rs->sr_text = NULL;
316         }
317
318         if ( lc != NULL ) {
319                 ldap_back_release_conn( op, rs, lc );
320         }
321
322         return rc;
323 }
324