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