1/* $NetBSD$ */ 2 3/* OpenLDAP: pkg/ldap/servers/slapd/extended.c,v 1.92.2.7 2010/04/13 20:23:14 kurt Exp */ 4/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1999-2010 The OpenLDAP Foundation. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted only as authorized by the OpenLDAP 11 * Public License. 12 * 13 * A copy of this license is available in the file LICENSE in the 14 * top-level directory of the distribution or, alternatively, at 15 * <http://www.OpenLDAP.org/license.html>. 16 */ 17 18/* 19 * LDAPv3 Extended Operation Request 20 * ExtendedRequest ::= [APPLICATION 23] SEQUENCE { 21 * requestName [0] LDAPOID, 22 * requestValue [1] OCTET STRING OPTIONAL 23 * } 24 * 25 * LDAPv3 Extended Operation Response 26 * ExtendedResponse ::= [APPLICATION 24] SEQUENCE { 27 * COMPONENTS OF LDAPResult, 28 * responseName [10] LDAPOID OPTIONAL, 29 * response [11] OCTET STRING OPTIONAL 30 * } 31 * 32 */ 33 34#include "portable.h" 35 36#include <stdio.h> 37 38#include <ac/socket.h> 39#include <ac/string.h> 40 41#include "slap.h" 42#include "lber_pvt.h" 43 44static struct extop_list { 45 struct extop_list *next; 46 struct berval oid; 47 slap_mask_t flags; 48 SLAP_EXTOP_MAIN_FN *ext_main; 49} *supp_ext_list = NULL; 50 51static SLAP_EXTOP_MAIN_FN whoami_extop; 52 53/* This list of built-in extops is for extops that are not part 54 * of backends or in external modules. Essentially, this is 55 * just a way to get built-in extops onto the extop list without 56 * having a separate init routine for each built-in extop. 57 */ 58static struct { 59 const struct berval *oid; 60 slap_mask_t flags; 61 SLAP_EXTOP_MAIN_FN *ext_main; 62} builtin_extops[] = { 63#ifdef LDAP_X_TXN 64 { &slap_EXOP_TXN_START, 0, txn_start_extop }, 65 { &slap_EXOP_TXN_END, 0, txn_end_extop }, 66#endif 67 { &slap_EXOP_CANCEL, 0, cancel_extop }, 68 { &slap_EXOP_WHOAMI, 0, whoami_extop }, 69 { &slap_EXOP_MODIFY_PASSWD, SLAP_EXOP_WRITES, passwd_extop }, 70 { NULL, 0, NULL } 71}; 72 73 74static struct extop_list *find_extop( 75 struct extop_list *list, struct berval *oid ); 76 77struct berval * 78get_supported_extop (int index) 79{ 80 struct extop_list *ext; 81 82 /* linear scan is slow, but this way doesn't force a 83 * big change on root_dse.c, where this routine is used. 84 */ 85 for (ext = supp_ext_list; ext != NULL && --index >= 0; ext = ext->next) { 86 ; /* empty */ 87 } 88 89 if (ext == NULL) return NULL; 90 91 return &ext->oid; 92} 93 94 95int exop_root_dse_info( Entry *e ) 96{ 97 AttributeDescription *ad_supportedExtension 98 = slap_schema.si_ad_supportedExtension; 99 struct berval vals[2]; 100 struct extop_list *ext; 101 102 vals[1].bv_val = NULL; 103 vals[1].bv_len = 0; 104 105 for (ext = supp_ext_list; ext != NULL; ext = ext->next) { 106 if( ext->flags & SLAP_EXOP_HIDE ) continue; 107 108 vals[0] = ext->oid; 109 110 if( attr_merge( e, ad_supportedExtension, vals, NULL ) ) { 111 return LDAP_OTHER; 112 } 113 } 114 115 return LDAP_SUCCESS; 116} 117 118int 119do_extended( 120 Operation *op, 121 SlapReply *rs 122) 123{ 124 struct berval reqdata = {0, NULL}; 125 ber_tag_t tag; 126 ber_len_t len; 127 128 Debug( LDAP_DEBUG_TRACE, "%s do_extended\n", 129 op->o_log_prefix, 0, 0 ); 130 131 if( op->o_protocol < LDAP_VERSION3 ) { 132 Debug( LDAP_DEBUG_ANY, "%s do_extended: protocol version (%d) too low\n", 133 op->o_log_prefix, op->o_protocol, 0 ); 134 send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "requires LDAPv3" ); 135 rs->sr_err = SLAPD_DISCONNECT; 136 goto done; 137 } 138 139 if ( ber_scanf( op->o_ber, "{m" /*}*/, &op->ore_reqoid ) == LBER_ERROR ) { 140 Debug( LDAP_DEBUG_ANY, "%s do_extended: ber_scanf failed\n", 141 op->o_log_prefix, 0, 0 ); 142 send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" ); 143 rs->sr_err = SLAPD_DISCONNECT; 144 goto done; 145 } 146 147 tag = ber_peek_tag( op->o_ber, &len ); 148 149 if( ber_peek_tag( op->o_ber, &len ) == LDAP_TAG_EXOP_REQ_VALUE ) { 150 if( ber_scanf( op->o_ber, "m", &reqdata ) == LBER_ERROR ) { 151 Debug( LDAP_DEBUG_ANY, "%s do_extended: ber_scanf failed\n", 152 op->o_log_prefix, 0, 0 ); 153 send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" ); 154 rs->sr_err = SLAPD_DISCONNECT; 155 goto done; 156 } 157 } 158 159 if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) { 160 Debug( LDAP_DEBUG_ANY, "%s do_extended: get_ctrls failed\n", 161 op->o_log_prefix, 0, 0 ); 162 return rs->sr_err; 163 } 164 165 Statslog( LDAP_DEBUG_STATS, "%s EXT oid=%s\n", 166 op->o_log_prefix, op->ore_reqoid.bv_val, 0, 0, 0 ); 167 168 /* check for controls inappropriate for all extended operations */ 169 if( get_manageDSAit( op ) == SLAP_CONTROL_CRITICAL ) { 170 send_ldap_error( op, rs, 171 LDAP_UNAVAILABLE_CRITICAL_EXTENSION, 172 "manageDSAit control inappropriate" ); 173 goto done; 174 } 175 176 /* FIXME: temporary? */ 177 if ( reqdata.bv_val ) { 178 op->ore_reqdata = &reqdata; 179 } 180 181 op->o_bd = frontendDB; 182 rs->sr_err = frontendDB->be_extended( op, rs ); 183 184 /* clean up in case some overlay set them? */ 185 if ( !BER_BVISNULL( &op->o_req_ndn ) ) { 186 if ( !BER_BVISNULL( &op->o_req_dn ) 187 && op->o_req_ndn.bv_val != op->o_req_dn.bv_val ) 188 { 189 op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx ); 190 } 191 op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx ); 192 BER_BVZERO( &op->o_req_dn ); 193 BER_BVZERO( &op->o_req_ndn ); 194 } 195 196done: 197 return rs->sr_err; 198} 199 200int 201fe_extended( Operation *op, SlapReply *rs ) 202{ 203 struct extop_list *ext = NULL; 204 struct berval reqdata = BER_BVNULL; 205 206 if (op->ore_reqdata) { 207 reqdata = *op->ore_reqdata; 208 } 209 210 ext = find_extop(supp_ext_list, &op->ore_reqoid ); 211 if ( ext == NULL ) { 212 Debug( LDAP_DEBUG_ANY, "%s do_extended: unsupported operation \"%s\"\n", 213 op->o_log_prefix, op->ore_reqoid.bv_val, 0 ); 214 send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, 215 "unsupported extended operation" ); 216 goto done; 217 } 218 219 op->ore_flags = ext->flags; 220 221 Debug( LDAP_DEBUG_ARGS, "do_extended: oid=%s\n", 222 op->ore_reqoid.bv_val, 0 ,0 ); 223 224 { /* start of OpenLDAP extended operation */ 225 BackendDB *bd = op->o_bd; 226 227 rs->sr_err = (ext->ext_main)( op, rs ); 228 229 if( rs->sr_err != SLAPD_ABANDON ) { 230 if ( rs->sr_err == LDAP_REFERRAL && rs->sr_ref == NULL ) { 231 rs->sr_ref = referral_rewrite( default_referral, 232 NULL, NULL, LDAP_SCOPE_DEFAULT ); 233 if ( !rs->sr_ref ) rs->sr_ref = default_referral; 234 if ( !rs->sr_ref ) { 235 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 236 rs->sr_text = "referral missing"; 237 } 238 } 239 240 if ( op->o_bd == NULL ) 241 op->o_bd = bd; 242 send_ldap_extended( op, rs ); 243 244 if ( rs->sr_ref != default_referral ) { 245 ber_bvarray_free( rs->sr_ref ); 246 rs->sr_ref = NULL; 247 } 248 } 249 250 if ( rs->sr_rspoid != NULL ) { 251 free( (char *)rs->sr_rspoid ); 252 } 253 254 if ( rs->sr_rspdata != NULL ) { 255 ber_bvfree( rs->sr_rspdata ); 256 } 257 } /* end of OpenLDAP extended operation */ 258 259done:; 260 return rs->sr_err; 261} 262 263int 264load_extop2( 265 const struct berval *ext_oid, 266 slap_mask_t ext_flags, 267 SLAP_EXTOP_MAIN_FN *ext_main, 268 unsigned flags ) 269{ 270 struct berval oidm = BER_BVNULL; 271 struct extop_list *ext; 272 int insertme = 0; 273 274 if ( !ext_main ) { 275 return -1; 276 } 277 278 if ( ext_oid == NULL || BER_BVISNULL( ext_oid ) || 279 BER_BVISEMPTY( ext_oid ) ) 280 { 281 return -1; 282 } 283 284 if ( numericoidValidate( NULL, (struct berval *)ext_oid ) != 285 LDAP_SUCCESS ) 286 { 287 oidm.bv_val = oidm_find( ext_oid->bv_val ); 288 if ( oidm.bv_val == NULL ) { 289 return -1; 290 } 291 oidm.bv_len = strlen( oidm.bv_val ); 292 ext_oid = &oidm; 293 } 294 295 for ( ext = supp_ext_list; ext; ext = ext->next ) { 296 if ( bvmatch( ext_oid, &ext->oid ) ) { 297 if ( flags == 1 ) { 298 break; 299 } 300 return -1; 301 } 302 } 303 304 if ( flags == 0 || ext == NULL ) { 305 ext = ch_calloc( 1, sizeof(struct extop_list) + ext_oid->bv_len + 1 ); 306 if ( ext == NULL ) { 307 return(-1); 308 } 309 310 ext->oid.bv_val = (char *)(ext + 1); 311 AC_MEMCPY( ext->oid.bv_val, ext_oid->bv_val, ext_oid->bv_len ); 312 ext->oid.bv_len = ext_oid->bv_len; 313 ext->oid.bv_val[ext->oid.bv_len] = '\0'; 314 315 insertme = 1; 316 } 317 318 ext->flags = ext_flags; 319 ext->ext_main = ext_main; 320 321 if ( insertme ) { 322 ext->next = supp_ext_list; 323 supp_ext_list = ext; 324 } 325 326 return(0); 327} 328 329int 330extops_init (void) 331{ 332 int i; 333 334 for ( i = 0; builtin_extops[i].oid != NULL; i++ ) { 335 load_extop( (struct berval *)builtin_extops[i].oid, 336 builtin_extops[i].flags, 337 builtin_extops[i].ext_main ); 338 } 339 return(0); 340} 341 342int 343extops_kill (void) 344{ 345 struct extop_list *ext; 346 347 /* we allocated the memory, so we have to free it, too. */ 348 while ((ext = supp_ext_list) != NULL) { 349 supp_ext_list = ext->next; 350 ch_free(ext); 351 } 352 return(0); 353} 354 355static struct extop_list * 356find_extop( struct extop_list *list, struct berval *oid ) 357{ 358 struct extop_list *ext; 359 360 for (ext = list; ext; ext = ext->next) { 361 if (bvmatch(&ext->oid, oid)) 362 return(ext); 363 } 364 return(NULL); 365} 366 367 368const struct berval slap_EXOP_WHOAMI = BER_BVC(LDAP_EXOP_WHO_AM_I); 369 370static int 371whoami_extop ( 372 Operation *op, 373 SlapReply *rs ) 374{ 375 struct berval *bv; 376 377 if ( op->ore_reqdata != NULL ) { 378 /* no request data should be provided */ 379 rs->sr_text = "no request data expected"; 380 return LDAP_PROTOCOL_ERROR; 381 } 382 383 Statslog( LDAP_DEBUG_STATS, "%s WHOAMI\n", 384 op->o_log_prefix, 0, 0, 0, 0 ); 385 386 op->o_bd = op->o_conn->c_authz_backend; 387 if( backend_check_restrictions( op, rs, 388 (struct berval *)&slap_EXOP_WHOAMI ) != LDAP_SUCCESS ) 389 { 390 return rs->sr_err; 391 } 392 393 bv = (struct berval *) ch_malloc( sizeof(struct berval) ); 394 if( op->o_dn.bv_len ) { 395 bv->bv_len = op->o_dn.bv_len + STRLENOF( "dn:" ); 396 bv->bv_val = ch_malloc( bv->bv_len + 1 ); 397 AC_MEMCPY( bv->bv_val, "dn:", STRLENOF( "dn:" ) ); 398 AC_MEMCPY( &bv->bv_val[STRLENOF( "dn:" )], op->o_dn.bv_val, 399 op->o_dn.bv_len ); 400 bv->bv_val[bv->bv_len] = '\0'; 401 402 } else { 403 bv->bv_len = 0; 404 bv->bv_val = NULL; 405 } 406 407 rs->sr_rspdata = bv; 408 return LDAP_SUCCESS; 409} 410