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