extended.c 9.27 KB
Newer Older
1
2
/* extended.c - ldap backend extended routines */
/* $OpenLDAP$ */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
3
4
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
5
 * Copyright 2003-2009 The OpenLDAP Foundation.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
6
7
8
9
10
11
12
13
14
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
15
 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
16
17
18
19
20
/* ACKNOWLEDGEMENTS:
 * This work was initially developed by the Howard Chu for inclusion
 * in OpenLDAP Software and subsequently enhanced by Pierangelo
 * Masarati. 
 */
21
22
23
24
25
26
27
28
29
30

#include "portable.h"

#include <stdio.h>
#include <ac/string.h>

#include "slap.h"
#include "back-ldap.h"
#include "lber_pvt.h"

31
32
33
34
typedef int (ldap_back_exop_f)( Operation *op, SlapReply *rs, ldapconn_t **lc );

static ldap_back_exop_f ldap_back_exop_passwd;
static ldap_back_exop_f ldap_back_exop_generic;
35
36

static struct exop {
37
38
	struct berval		oid;
	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
48
	ldapinfo_t	*li = (ldapinfo_t *) op->o_bd->be_private;

49
	ldapconn_t	*lc = NULL;
50
	LDAPControl	**ctrls = NULL, **oldctrls = NULL;
51
52
53
54
55
56
	int		rc;

	/* FIXME: this needs to be called here, so it is
	 * called twice; maybe we could avoid the 
	 * ldap_back_dobind() call inside each extended()
	 * call ... */
57
	if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
58
59
60
		return -1;
	}

61
62
	ctrls = op->o_ctrls;
	if ( ldap_back_controls_add( op, rs, lc, &ctrls ) )
63
	{
64
65
66
67
68
69
70
71
		op->o_ctrls = oldctrls;
		send_ldap_extended( op, rs );
		rs->sr_text = NULL;
		/* otherwise frontend resends result */
		rc = rs->sr_err = SLAPD_ABANDON;
		goto done;
	}

72
73
	op->o_ctrls = ctrls;
	rc = exop( op, rs, &lc );
74
75

	op->o_ctrls = oldctrls;
76
	(void)ldap_back_controls_free( op, rs, &ctrls );
77
78
79

done:;
	if ( lc != NULL ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
80
		ldap_back_release_conn( li, lc );
81
82
83
84
85
	}
			
	return rc;
}

86
87
int
ldap_back_extended(
88
89
		Operation	*op,
		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
101
102
103
	/* if we get here, the exop is known; the best that we can do
	 * is pass it thru as is */
	/* FIXME: maybe a list of OIDs to pass thru would be safer */
	return ldap_back_extended_one( op, rs, ldap_back_exop_generic );
104
105
}

106
static int
107
ldap_back_exop_passwd(
108
109
110
	Operation	*op,
	SlapReply	*rs,
	ldapconn_t	**lcp )
111
{
112
113
	ldapinfo_t	*li = (ldapinfo_t *) op->o_bd->be_private;

114
	ldapconn_t	*lc = *lcp;
115
116
117
	req_pwdexop_s	*qpw = &op->oq_pwdexop;
	LDAPMessage	*res;
	ber_int_t	msgid;
118
	int		rc, isproxy, freedn = 0;
119
	int		do_retry = 1;
120
121
122
123
124
125
126
127
	char		*text = NULL;
	struct berval	dn = op->o_req_dn,
			ndn = op->o_req_ndn;

	assert( lc != NULL );
	assert( rs->sr_ctrls == NULL );

	if ( BER_BVISNULL( &ndn ) && op->ore_reqdata != NULL ) {
128
129
130
		/* NOTE: most of this code is mutated
		 * from slap_passwd_parse();
		 * But here we only need
131
132
133
134
135
136
137
138
139
140
141
142
		 * the first berval... */

		ber_tag_t tag;
		ber_len_t len = -1;
		BerElementBuffer berbuf;
		BerElement *ber = (BerElement *)&berbuf;

		struct berval	tmpid = BER_BVNULL;

		if ( op->ore_reqdata->bv_len == 0 ) {
			return LDAP_PROTOCOL_ERROR;
		}
143

144
145
146
147
148
149
150
151
152
153
154
		/* ber_init2 uses reqdata directly, doesn't allocate new buffers */
		ber_init2( ber, op->ore_reqdata, 0 );

		tag = ber_scanf( ber, "{" /*}*/ );

		if ( tag == LBER_ERROR ) {
			return LDAP_PROTOCOL_ERROR;
		}

		tag = ber_peek_tag( ber, &len );
		if ( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_ID ) {
155
			tag = ber_get_stringbv( ber, &tmpid, LBER_BV_NOTERM );
156
157
158
159
160
161
162

			if ( tag == LBER_ERROR ) {
				return LDAP_PROTOCOL_ERROR;
			}
		}

		if ( !BER_BVISEMPTY( &tmpid ) ) {
163
164
			char idNull = tmpid.bv_val[tmpid.bv_len];
			tmpid.bv_val[tmpid.bv_len] = '\0';
165
166
			rs->sr_err = dnPrettyNormal( NULL, &tmpid, &dn,
				&ndn, op->o_tmpmemctx );
167
			tmpid.bv_val[tmpid.bv_len] = idNull;
168
169
170
171
172
173
174
175
176
177
			if ( rs->sr_err != LDAP_SUCCESS ) {
				/* should have been successfully parsed earlier! */
				return rs->sr_err;
			}
			freedn = 1;

		} else {
			dn = op->o_dn;
			ndn = op->o_ndn;
		}
178
179
	}

180
	isproxy = ber_bvcmp( &ndn, &op->o_ndn );
181

182
	Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_passwd(\"%s\")%s\n",
183
		dn.bv_val, isproxy ? " (proxy)" : "", 0 );
184

185
retry:
186
	rc = ldap_passwd( lc->lc_ld, isproxy ? &dn : NULL,
187
188
		qpw->rs_old.bv_val ? &qpw->rs_old : NULL,
		qpw->rs_new.bv_val ? &qpw->rs_new : NULL,
189
		op->o_ctrls, NULL, &msgid );
190

191
	if ( rc == LDAP_SUCCESS ) {
192
		/* TODO: set timeout? */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
193
194
195
196
		/* by now, make sure no timeout is used (ITS#6282) */
		struct timeval tv;
		tv.tv_sec = -1;
		if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) == -1 ) {
197
			ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
198
			rs->sr_err = rc;
199

200
		} else {
201
202
203
204
205
			/* only touch when activity actually took place... */
			if ( li->li_idle_timeout && lc ) {
				lc->lc_time = op->o_time;
			}

206
207
			/* sigh. parse twice, because parse_passwd
			 * doesn't give us the err / match / msg info.
208
			 */
209
210
			rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
					(char **)&rs->sr_matched,
211
					&text,
212
					NULL, &rs->sr_ctrls, 0 );
213

214
215
216
			if ( rc == LDAP_SUCCESS ) {
				if ( rs->sr_err == LDAP_SUCCESS ) {
					struct berval	newpw;
217
218
219
220
221
222
223
224

					/* this never happens because 
					 * the frontend	is generating 
					 * the new password, so when
					 * the passwd exop is proxied,
					 * it never delegates password
					 * generation to the remote server
					 */
225
					rc = ldap_parse_passwd( lc->lc_ld, res,
226
							&newpw );
227
228
229
					if ( rc == LDAP_SUCCESS &&
							!BER_BVISNULL( &newpw ) )
					{
230
						rs->sr_type = REP_EXTENDED;
231
232
						rs->sr_rspdata = slap_passwd_return( &newpw );
						free( newpw.bv_val );
233
					}
234

235
				} else {
236
					rc = rs->sr_err;
237
238
				}
			}
239
			ldap_msgfree( res );
240
241
		}
	}
Pierangelo Masarati's avatar
Pierangelo Masarati committed
242

243
	if ( rc != LDAP_SUCCESS ) {
244
		rs->sr_err = slap_map_api2result( rs );
245
246
		if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
			do_retry = 0;
247
			if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
248
249
250
				goto retry;
			}
		}
251
252

		if ( LDAP_BACK_QUARANTINE( li ) ) {
253
			ldap_back_quarantine( op, rs );
254
255
		}

256
		if ( text ) rs->sr_text = text;
257
258
259
		send_ldap_extended( op, rs );
		/* otherwise frontend resends result */
		rc = rs->sr_err = SLAPD_ABANDON;
260
261

	} else if ( LDAP_BACK_QUARANTINE( li ) ) {
262
		ldap_back_quarantine( op, rs );
263
	}
264

265
266
267
268
269
	if ( freedn ) {
		op->o_tmpfree( dn.bv_val, op->o_tmpmemctx );
		op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
	}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
270
271
272
	/* these have to be freed anyway... */
	if ( rs->sr_matched ) {
		free( (char *)rs->sr_matched );
273
		rs->sr_matched = NULL;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
274
	}
Pierangelo Masarati's avatar
Pierangelo Masarati committed
275

276
277
278
279
280
	if ( rs->sr_ctrls ) {
		ldap_controls_free( rs->sr_ctrls );
		rs->sr_ctrls = NULL;
	}

281
282
	if ( text ) {
		free( text );
283
		rs->sr_text = NULL;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
284
285
	}

286
287
288
	/* in case, cleanup handler */
	if ( lc == NULL ) {
		*lcp = NULL;
289
290
	}

291
292
	return rc;
}
293
294
295
296

static int
ldap_back_exop_generic(
	Operation	*op,
297
298
	SlapReply	*rs,
	ldapconn_t	**lcp )
299
{
300
301
	ldapinfo_t	*li = (ldapinfo_t *) op->o_bd->be_private;

302
	ldapconn_t	*lc = *lcp;
303
304
305
306
	LDAPMessage	*res;
	ber_int_t	msgid;
	int		rc;
	int		do_retry = 1;
307
	char		*text = NULL;
308

309
310
	assert( lc != NULL );
	assert( rs->sr_ctrls == NULL );
311
312
313
314
315
316
317
318
319
320

	Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_generic(%s, \"%s\")\n",
		op->ore_reqoid.bv_val, op->o_req_dn.bv_val, 0 );

retry:
	rc = ldap_extended_operation( lc->lc_ld,
		op->ore_reqoid.bv_val, op->ore_reqdata,
		op->o_ctrls, NULL, &msgid );

	if ( rc == LDAP_SUCCESS ) {
321
		/* TODO: set timeout? */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
322
323
324
325
		/* by now, make sure no timeout is used (ITS#6282) */
		struct timeval tv;
		tv.tv_sec = -1;
		if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) == -1 ) {
326
			ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
327
			rs->sr_err = rc;
328
329

		} else {
330
331
332
333
334
			/* only touch when activity actually took place... */
			if ( li->li_idle_timeout && lc ) {
				lc->lc_time = op->o_time;
			}

335
336
337
338
339
			/* sigh. parse twice, because parse_passwd
			 * doesn't give us the err / match / msg info.
			 */
			rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
					(char **)&rs->sr_matched,
Pierangelo Masarati's avatar
Pierangelo Masarati committed
340
					&text,
341
					NULL, &rs->sr_ctrls, 0 );
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
			if ( rc == LDAP_SUCCESS ) {
				if ( rs->sr_err == LDAP_SUCCESS ) {
					rc = ldap_parse_extended_result( lc->lc_ld, res,
							(char **)&rs->sr_rspoid, &rs->sr_rspdata, 0 );
					if ( rc == LDAP_SUCCESS ) {
						rs->sr_type = REP_EXTENDED;
					}

				} else {
					rc = rs->sr_err;
				}
			}
			ldap_msgfree( res );
		}
	}
Pierangelo Masarati's avatar
Pierangelo Masarati committed
357

358
359
360
361
362
363
364
365
	if ( rc != LDAP_SUCCESS ) {
		rs->sr_err = slap_map_api2result( rs );
		if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
			do_retry = 0;
			if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
				goto retry;
			}
		}
366
367

		if ( LDAP_BACK_QUARANTINE( li ) ) {
368
			ldap_back_quarantine( op, rs );
369
370
		}

371
		if ( text ) rs->sr_text = text;
372
373
374
		send_ldap_extended( op, rs );
		/* otherwise frontend resends result */
		rc = rs->sr_err = SLAPD_ABANDON;
375
376

	} else if ( LDAP_BACK_QUARANTINE( li ) ) {
377
		ldap_back_quarantine( op, rs );
378
379
380
381
382
383
384
	}

	/* these have to be freed anyway... */
	if ( rs->sr_matched ) {
		free( (char *)rs->sr_matched );
		rs->sr_matched = NULL;
	}
Pierangelo Masarati's avatar
Pierangelo Masarati committed
385

386
387
388
389
390
	if ( rs->sr_ctrls ) {
		ldap_controls_free( rs->sr_ctrls );
		rs->sr_ctrls = NULL;
	}

391
392
	if ( text ) {
		free( text );
393
394
395
		rs->sr_text = NULL;
	}

396
397
398
	/* in case, cleanup handler */
	if ( lc == NULL ) {
		*lcp = NULL;
399
400
401
402
403
	}

	return rc;
}