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