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