1 /* extended.c - ldap backend extended routines */ 2 /* $OpenLDAP: pkg/ldap/servers/slapd/back-ldap/extended.c,v 1.36.2.8 2008/02/11 23:26:46 kurt Exp $ */ 3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 4 * 5 * Copyright 2003-2008 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 **ctrls = NULL, **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 ctrls = op->o_ctrls; 62 if ( ldap_back_controls_add( op, rs, lc, &ctrls ) ) 63 { 64 op->o_ctrls = oldctrls; 65 send_ldap_extended( op, rs ); 66 rs->sr_text = NULL; 67 /* otherwise frontend resends result */ 68 rc = rs->sr_err = SLAPD_ABANDON; 69 goto done; 70 } 71 72 op->o_ctrls = ctrls; 73 rc = exop( op, rs, &lc ); 74 75 op->o_ctrls = oldctrls; 76 (void)ldap_back_controls_free( op, rs, &ctrls ); 77 78 done:; 79 if ( lc != NULL ) { 80 ldap_back_release_conn( li, lc ); 81 } 82 83 return rc; 84 } 85 86 int 87 ldap_back_extended( 88 Operation *op, 89 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 /* if we get here, the exop is known; the best that we can do 101 * is pass it thru as is */ 102 /* FIXME: maybe a list of OIDs to pass thru would be safer */ 103 return ldap_back_extended_one( op, rs, ldap_back_exop_generic ); 104 } 105 106 static int 107 ldap_back_exop_passwd( 108 Operation *op, 109 SlapReply *rs, 110 ldapconn_t **lcp ) 111 { 112 ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; 113 114 ldapconn_t *lc = *lcp; 115 req_pwdexop_s *qpw = &op->oq_pwdexop; 116 LDAPMessage *res; 117 ber_int_t msgid; 118 int rc, isproxy, freedn = 0; 119 int do_retry = 1; 120 char *text = NULL; 121 struct berval dn = op->o_req_dn, 122 ndn = op->o_req_ndn; 123 124 assert( lc != NULL ); 125 assert( rs->sr_ctrls == NULL ); 126 127 if ( BER_BVISNULL( &ndn ) && op->ore_reqdata != NULL ) { 128 /* NOTE: most of this code is mutated 129 * from slap_passwd_parse(); 130 * But here we only need 131 * the first berval... */ 132 133 ber_tag_t tag; 134 ber_len_t len = -1; 135 BerElementBuffer berbuf; 136 BerElement *ber = (BerElement *)&berbuf; 137 138 struct berval tmpid = BER_BVNULL; 139 140 if ( op->ore_reqdata->bv_len == 0 ) { 141 return LDAP_PROTOCOL_ERROR; 142 } 143 144 /* ber_init2 uses reqdata directly, doesn't allocate new buffers */ 145 ber_init2( ber, op->ore_reqdata, 0 ); 146 147 tag = ber_scanf( ber, "{" /*}*/ ); 148 149 if ( tag == LBER_ERROR ) { 150 return LDAP_PROTOCOL_ERROR; 151 } 152 153 tag = ber_peek_tag( ber, &len ); 154 if ( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_ID ) { 155 tag = ber_get_stringbv( ber, &tmpid, LBER_BV_NOTERM ); 156 157 if ( tag == LBER_ERROR ) { 158 return LDAP_PROTOCOL_ERROR; 159 } 160 } 161 162 if ( !BER_BVISEMPTY( &tmpid ) ) { 163 char idNull = tmpid.bv_val[tmpid.bv_len]; 164 tmpid.bv_val[tmpid.bv_len] = '\0'; 165 rs->sr_err = dnPrettyNormal( NULL, &tmpid, &dn, 166 &ndn, op->o_tmpmemctx ); 167 tmpid.bv_val[tmpid.bv_len] = idNull; 168 if ( rs->sr_err != LDAP_SUCCESS ) { 169 /* should have been successfully parsed earlier! */ 170 return rs->sr_err; 171 } 172 freedn = 1; 173 174 } else { 175 dn = op->o_dn; 176 ndn = op->o_ndn; 177 } 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 qpw->rs_old.bv_val ? &qpw->rs_old : NULL, 188 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? */ 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, &rs->sr_ctrls, 0 ); 210 211 if ( rc == LDAP_SUCCESS ) { 212 if ( rs->sr_err == LDAP_SUCCESS ) { 213 struct berval newpw; 214 215 /* this never happens because 216 * the frontend is generating 217 * the new password, so when 218 * the passwd exop is proxied, 219 * it never delegates password 220 * generation to the remote server 221 */ 222 rc = ldap_parse_passwd( lc->lc_ld, res, 223 &newpw ); 224 if ( rc == LDAP_SUCCESS && 225 !BER_BVISNULL( &newpw ) ) 226 { 227 rs->sr_type = REP_EXTENDED; 228 rs->sr_rspdata = slap_passwd_return( &newpw ); 229 free( newpw.bv_val ); 230 } 231 232 } else { 233 rc = rs->sr_err; 234 } 235 } 236 ldap_msgfree( res ); 237 } 238 } 239 240 if ( rc != LDAP_SUCCESS ) { 241 rs->sr_err = slap_map_api2result( rs ); 242 if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) { 243 do_retry = 0; 244 if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) { 245 goto retry; 246 } 247 } 248 249 if ( LDAP_BACK_QUARANTINE( li ) ) { 250 ldap_back_quarantine( op, rs ); 251 } 252 253 if ( text ) rs->sr_text = text; 254 send_ldap_extended( op, rs ); 255 /* otherwise frontend resends result */ 256 rc = rs->sr_err = SLAPD_ABANDON; 257 258 } else if ( LDAP_BACK_QUARANTINE( li ) ) { 259 ldap_back_quarantine( op, rs ); 260 } 261 262 if ( freedn ) { 263 op->o_tmpfree( dn.bv_val, op->o_tmpmemctx ); 264 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx ); 265 } 266 267 /* these have to be freed anyway... */ 268 if ( rs->sr_matched ) { 269 free( (char *)rs->sr_matched ); 270 rs->sr_matched = NULL; 271 } 272 273 if ( rs->sr_ctrls ) { 274 ldap_controls_free( rs->sr_ctrls ); 275 rs->sr_ctrls = NULL; 276 } 277 278 if ( text ) { 279 free( text ); 280 rs->sr_text = NULL; 281 } 282 283 /* in case, cleanup handler */ 284 if ( lc == NULL ) { 285 *lcp = NULL; 286 } 287 288 return rc; 289 } 290 291 static int 292 ldap_back_exop_generic( 293 Operation *op, 294 SlapReply *rs, 295 ldapconn_t **lcp ) 296 { 297 ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private; 298 299 ldapconn_t *lc = *lcp; 300 LDAPMessage *res; 301 ber_int_t msgid; 302 int rc; 303 int do_retry = 1; 304 char *text = NULL; 305 306 assert( lc != NULL ); 307 assert( rs->sr_ctrls == NULL ); 308 309 Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_generic(%s, \"%s\")\n", 310 op->ore_reqoid.bv_val, op->o_req_dn.bv_val, 0 ); 311 312 retry: 313 rc = ldap_extended_operation( lc->lc_ld, 314 op->ore_reqoid.bv_val, op->ore_reqdata, 315 op->o_ctrls, NULL, &msgid ); 316 317 if ( rc == LDAP_SUCCESS ) { 318 /* TODO: set timeout? */ 319 if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, NULL, &res ) == -1 ) { 320 ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc ); 321 rs->sr_err = rc; 322 323 } else { 324 /* only touch when activity actually took place... */ 325 if ( li->li_idle_timeout && lc ) { 326 lc->lc_time = op->o_time; 327 } 328 329 /* sigh. parse twice, because parse_passwd 330 * doesn't give us the err / match / msg info. 331 */ 332 rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err, 333 (char **)&rs->sr_matched, 334 &text, 335 NULL, &rs->sr_ctrls, 0 ); 336 if ( rc == LDAP_SUCCESS ) { 337 if ( rs->sr_err == LDAP_SUCCESS ) { 338 rc = ldap_parse_extended_result( lc->lc_ld, res, 339 (char **)&rs->sr_rspoid, &rs->sr_rspdata, 0 ); 340 if ( rc == LDAP_SUCCESS ) { 341 rs->sr_type = REP_EXTENDED; 342 } 343 344 } else { 345 rc = rs->sr_err; 346 } 347 } 348 ldap_msgfree( res ); 349 } 350 } 351 352 if ( rc != LDAP_SUCCESS ) { 353 rs->sr_err = slap_map_api2result( rs ); 354 if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) { 355 do_retry = 0; 356 if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) { 357 goto retry; 358 } 359 } 360 361 if ( LDAP_BACK_QUARANTINE( li ) ) { 362 ldap_back_quarantine( op, rs ); 363 } 364 365 if ( text ) rs->sr_text = text; 366 send_ldap_extended( op, rs ); 367 /* otherwise frontend resends result */ 368 rc = rs->sr_err = SLAPD_ABANDON; 369 370 } else if ( LDAP_BACK_QUARANTINE( li ) ) { 371 ldap_back_quarantine( op, rs ); 372 } 373 374 /* these have to be freed anyway... */ 375 if ( rs->sr_matched ) { 376 free( (char *)rs->sr_matched ); 377 rs->sr_matched = NULL; 378 } 379 380 if ( rs->sr_ctrls ) { 381 ldap_controls_free( rs->sr_ctrls ); 382 rs->sr_ctrls = NULL; 383 } 384 385 if ( text ) { 386 free( text ); 387 rs->sr_text = NULL; 388 } 389 390 /* in case, cleanup handler */ 391 if ( lc == NULL ) { 392 *lcp = NULL; 393 } 394 395 return rc; 396 } 397 398