1 /*	$NetBSD: extended.c,v 1.1.1.3 2010/12/12 15:23:05 adam Exp $	*/
2 
3 /* extended.c - ldap backend extended routines */
4 /* OpenLDAP: pkg/ldap/servers/slapd/back-ldap/extended.c,v 1.36.2.11 2010/04/13 20:23:28 kurt Exp */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2003-2010 The OpenLDAP Foundation.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  * This work was initially developed by the Howard Chu for inclusion
20  * in OpenLDAP Software and subsequently enhanced by Pierangelo
21  * Masarati.
22  */
23 
24 #include "portable.h"
25 
26 #include <stdio.h>
27 #include <ac/string.h>
28 
29 #include "slap.h"
30 #include "back-ldap.h"
31 #include "lber_pvt.h"
32 
33 typedef int (ldap_back_exop_f)( Operation *op, SlapReply *rs, ldapconn_t **lc );
34 
35 static ldap_back_exop_f ldap_back_exop_passwd;
36 static ldap_back_exop_f ldap_back_exop_generic;
37 
38 static struct exop {
39 	struct berval		oid;
40 	ldap_back_exop_f	*extended;
41 } exop_table[] = {
42 	{ BER_BVC(LDAP_EXOP_MODIFY_PASSWD),	ldap_back_exop_passwd },
43 	{ BER_BVNULL, NULL }
44 };
45 
46 static int
47 ldap_back_extended_one( Operation *op, SlapReply *rs, ldap_back_exop_f exop )
48 {
49 	ldapinfo_t	*li = (ldapinfo_t *) op->o_bd->be_private;
50 
51 	ldapconn_t	*lc = NULL;
52 	LDAPControl	**ctrls = NULL, **oldctrls = NULL;
53 	int		rc;
54 
55 	/* FIXME: this needs to be called here, so it is
56 	 * called twice; maybe we could avoid the
57 	 * ldap_back_dobind() call inside each extended()
58 	 * call ... */
59 	if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
60 		return -1;
61 	}
62 
63 	ctrls = op->o_ctrls;
64 	if ( ldap_back_controls_add( op, rs, lc, &ctrls ) )
65 	{
66 		op->o_ctrls = oldctrls;
67 		send_ldap_extended( op, rs );
68 		rs->sr_text = NULL;
69 		/* otherwise frontend resends result */
70 		rc = rs->sr_err = SLAPD_ABANDON;
71 		goto done;
72 	}
73 
74 	op->o_ctrls = ctrls;
75 	rc = exop( op, rs, &lc );
76 
77 	op->o_ctrls = oldctrls;
78 	(void)ldap_back_controls_free( op, rs, &ctrls );
79 
80 done:;
81 	if ( lc != NULL ) {
82 		ldap_back_release_conn( li, lc );
83 	}
84 
85 	return rc;
86 }
87 
88 int
89 ldap_back_extended(
90 		Operation	*op,
91 		SlapReply	*rs )
92 {
93 	int	i;
94 
95 	for ( i = 0; exop_table[i].extended != NULL; i++ ) {
96 		if ( bvmatch( &exop_table[i].oid, &op->oq_extended.rs_reqoid ) )
97 		{
98 			return ldap_back_extended_one( op, rs, exop_table[i].extended );
99 		}
100 	}
101 
102 	/* if we get here, the exop is known; the best that we can do
103 	 * is pass it thru as is */
104 	/* FIXME: maybe a list of OIDs to pass thru would be safer */
105 	return ldap_back_extended_one( op, rs, ldap_back_exop_generic );
106 }
107 
108 static int
109 ldap_back_exop_passwd(
110 	Operation	*op,
111 	SlapReply	*rs,
112 	ldapconn_t	**lcp )
113 {
114 	ldapinfo_t	*li = (ldapinfo_t *) op->o_bd->be_private;
115 
116 	ldapconn_t	*lc = *lcp;
117 	req_pwdexop_s	*qpw = &op->oq_pwdexop;
118 	LDAPMessage	*res;
119 	ber_int_t	msgid;
120 	int		rc, isproxy, freedn = 0;
121 	int		do_retry = 1;
122 	char		*text = NULL;
123 	struct berval	dn = op->o_req_dn,
124 			ndn = op->o_req_ndn;
125 
126 	assert( lc != NULL );
127 	assert( rs->sr_ctrls == NULL );
128 
129 	if ( BER_BVISNULL( &ndn ) && op->ore_reqdata != NULL ) {
130 		/* NOTE: most of this code is mutated
131 		 * from slap_passwd_parse();
132 		 * But here 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_get_stringbv( ber, &tmpid, LBER_BV_NOTERM );
158 
159 			if ( tag == LBER_ERROR ) {
160 				return LDAP_PROTOCOL_ERROR;
161 			}
162 		}
163 
164 		if ( !BER_BVISEMPTY( &tmpid ) ) {
165 			char idNull = tmpid.bv_val[tmpid.bv_len];
166 			tmpid.bv_val[tmpid.bv_len] = '\0';
167 			rs->sr_err = dnPrettyNormal( NULL, &tmpid, &dn,
168 				&ndn, op->o_tmpmemctx );
169 			tmpid.bv_val[tmpid.bv_len] = idNull;
170 			if ( rs->sr_err != LDAP_SUCCESS ) {
171 				/* should have been successfully parsed earlier! */
172 				return rs->sr_err;
173 			}
174 			freedn = 1;
175 
176 		} else {
177 			dn = op->o_dn;
178 			ndn = op->o_ndn;
179 		}
180 	}
181 
182 	isproxy = ber_bvcmp( &ndn, &op->o_ndn );
183 
184 	Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_passwd(\"%s\")%s\n",
185 		dn.bv_val, isproxy ? " (proxy)" : "", 0 );
186 
187 retry:
188 	rc = ldap_passwd( lc->lc_ld, isproxy ? &dn : NULL,
189 		qpw->rs_old.bv_val ? &qpw->rs_old : NULL,
190 		qpw->rs_new.bv_val ? &qpw->rs_new : NULL,
191 		op->o_ctrls, NULL, &msgid );
192 
193 	if ( rc == LDAP_SUCCESS ) {
194 		/* TODO: set timeout? */
195 		/* by now, make sure no timeout is used (ITS#6282) */
196 		struct timeval tv;
197 		tv.tv_sec = -1;
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 && lc ) {
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 	if ( freedn ) {
268 		op->o_tmpfree( dn.bv_val, op->o_tmpmemctx );
269 		op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
270 	}
271 
272 	/* these have to be freed anyway... */
273 	if ( rs->sr_matched ) {
274 		free( (char *)rs->sr_matched );
275 		rs->sr_matched = NULL;
276 	}
277 
278 	if ( rs->sr_ctrls ) {
279 		ldap_controls_free( rs->sr_ctrls );
280 		rs->sr_ctrls = 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 	assert( rs->sr_ctrls == NULL );
313 
314 	Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_generic(%s, \"%s\")\n",
315 		op->ore_reqoid.bv_val, op->o_req_dn.bv_val, 0 );
316 
317 retry:
318 	rc = ldap_extended_operation( lc->lc_ld,
319 		op->ore_reqoid.bv_val, op->ore_reqdata,
320 		op->o_ctrls, NULL, &msgid );
321 
322 	if ( rc == LDAP_SUCCESS ) {
323 		/* TODO: set timeout? */
324 		/* by now, make sure no timeout is used (ITS#6282) */
325 		struct timeval tv;
326 		tv.tv_sec = -1;
327 		if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) == -1 ) {
328 			ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc );
329 			rs->sr_err = rc;
330 
331 		} else {
332 			/* only touch when activity actually took place... */
333 			if ( li->li_idle_timeout && lc ) {
334 				lc->lc_time = op->o_time;
335 			}
336 
337 			/* sigh. parse twice, because parse_passwd
338 			 * doesn't give us the err / match / msg info.
339 			 */
340 			rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
341 					(char **)&rs->sr_matched,
342 					&text,
343 					NULL, &rs->sr_ctrls, 0 );
344 			if ( rc == LDAP_SUCCESS ) {
345 				if ( rs->sr_err == LDAP_SUCCESS ) {
346 					rc = ldap_parse_extended_result( lc->lc_ld, res,
347 							(char **)&rs->sr_rspoid, &rs->sr_rspdata, 0 );
348 					if ( rc == LDAP_SUCCESS ) {
349 						rs->sr_type = REP_EXTENDED;
350 					}
351 
352 				} else {
353 					rc = rs->sr_err;
354 				}
355 			}
356 			ldap_msgfree( res );
357 		}
358 	}
359 
360 	if ( rc != LDAP_SUCCESS ) {
361 		rs->sr_err = slap_map_api2result( rs );
362 		if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
363 			do_retry = 0;
364 			if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
365 				goto retry;
366 			}
367 		}
368 
369 		if ( LDAP_BACK_QUARANTINE( li ) ) {
370 			ldap_back_quarantine( op, rs );
371 		}
372 
373 		if ( text ) rs->sr_text = text;
374 		send_ldap_extended( op, rs );
375 		/* otherwise frontend resends result */
376 		rc = rs->sr_err = SLAPD_ABANDON;
377 
378 	} else if ( LDAP_BACK_QUARANTINE( li ) ) {
379 		ldap_back_quarantine( op, rs );
380 	}
381 
382 	/* these have to be freed anyway... */
383 	if ( rs->sr_matched ) {
384 		free( (char *)rs->sr_matched );
385 		rs->sr_matched = NULL;
386 	}
387 
388 	if ( rs->sr_ctrls ) {
389 		ldap_controls_free( rs->sr_ctrls );
390 		rs->sr_ctrls = 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 
406