1/* $NetBSD: vc.c,v 1.2 2021/08/14 16:14:54 christos Exp $ */ 2 3/* vc.c - LDAP Verify Credentials extop (no spec yet) */ 4/* $OpenLDAP$ */ 5/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2010-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 Pierangelo Masarati for inclusion 20 * in OpenLDAP Software. 21 */ 22 23/* 24 * LDAP Verify Credentials: suggested by Kurt Zeilenga 25 * no spec yet 26 */ 27 28#include <sys/cdefs.h> 29__RCSID("$NetBSD: vc.c,v 1.2 2021/08/14 16:14:54 christos Exp $"); 30 31#include "portable.h" 32 33#include "slap.h" 34#include "ac/string.h" 35 36typedef struct vc_conn_t { 37 struct vc_conn_t *conn; 38 Connection connbuf; 39 OperationBuffer opbuf; 40 Operation *op; 41 int refcnt; 42} vc_conn_t; 43 44static const struct berval vc_exop_oid_bv = BER_BVC(LDAP_EXOP_VERIFY_CREDENTIALS); 45static ldap_pvt_thread_mutex_t vc_mutex; 46static Avlnode *vc_tree; 47 48static int 49vc_conn_cmp( const void *c1, const void *c2 ) 50{ 51 const vc_conn_t *vc1 = (const vc_conn_t *)c1; 52 const vc_conn_t *vc2 = (const vc_conn_t *)c2; 53 54 return SLAP_PTRCMP( vc1->conn, vc2->conn ); 55} 56 57static int 58vc_conn_dup( void *c1, void *c2 ) 59{ 60 vc_conn_t *vc1 = (vc_conn_t *)c1; 61 vc_conn_t *vc2 = (vc_conn_t *)c2; 62 63 if ( vc1->conn == vc2->conn ) { 64 return -1; 65 } 66 67 return 0; 68} 69 70static int 71vc_create_response( 72 void *conn, 73 int resultCode, 74 const char *diagnosticMessage, 75 struct berval *servercred, 76 struct berval *authzid, 77 LDAPControl **ctrls, 78 struct berval **val ) 79{ 80 BerElementBuffer berbuf; 81 BerElement *ber = (BerElement *)&berbuf; 82 struct berval bv; 83 int rc; 84 85 assert( val != NULL ); 86 87 *val = NULL; 88 89 ber_init2( ber, NULL, LBER_USE_DER ); 90 91 (void)ber_printf( ber, "{is" /*}*/ , resultCode, diagnosticMessage ? diagnosticMessage : "" ); 92 93 if ( conn ) { 94 struct berval cookie; 95 96 cookie.bv_len = sizeof( conn ); 97 cookie.bv_val = (char *)&conn; 98 (void)ber_printf( ber, "tO", 0, LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE, &cookie ); 99 } 100 101 if ( servercred ) { 102 ber_printf( ber, "tO", LDAP_TAG_EXOP_VERIFY_CREDENTIALS_SCREDS, servercred ); 103 } 104 105#if 0 106 if ( authzid ) { 107 ber_printf( ber, "tO", LDAP_TAG_EXOP_VERIFY_CREDENTIALS_AUTHZID, authzid ); 108 } 109#endif 110 111 if ( ctrls ) { 112 int c; 113 114 rc = ber_printf( ber, "t{"/*}*/, LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS ); 115 if ( rc == -1 ) goto done; 116 117 for ( c = 0; ctrls[c] != NULL; c++ ) { 118 rc = ber_printf( ber, "{s" /*}*/, ctrls[c]->ldctl_oid ); 119 120 if ( ctrls[c]->ldctl_iscritical ) { 121 rc = ber_printf( ber, "b", (ber_int_t)ctrls[c]->ldctl_iscritical ) ; 122 if ( rc == -1 ) goto done; 123 } 124 125 if ( ctrls[c]->ldctl_value.bv_val != NULL ) { 126 rc = ber_printf( ber, "O", &ctrls[c]->ldctl_value ); 127 if( rc == -1 ) goto done; 128 } 129 130 rc = ber_printf( ber, /*{*/"N}" ); 131 if ( rc == -1 ) goto done; 132 } 133 134 rc = ber_printf( ber, /*{*/"N}" ); 135 if ( rc == -1 ) goto done; 136 } 137 138 rc = ber_printf( ber, /*{*/ "}" ); 139 if ( rc == -1 ) goto done; 140 141 rc = ber_flatten2( ber, &bv, 0 ); 142 if ( rc == 0 ) { 143 *val = ber_bvdup( &bv ); 144 } 145 146done:; 147 ber_free_buf( ber ); 148 149 return rc; 150} 151 152typedef struct vc_cb_t { 153 struct berval sasldata; 154 LDAPControl **ctrls; 155} vc_cb_t; 156 157static int 158vc_cb( 159 Operation *op, 160 SlapReply *rs ) 161{ 162 vc_cb_t *vc = (vc_cb_t *)op->o_callback->sc_private; 163 164 if ( rs->sr_tag == LDAP_RES_BIND ) { 165 if ( rs->sr_sasldata != NULL ) { 166 ber_dupbv( &vc->sasldata, rs->sr_sasldata ); 167 } 168 169 if ( rs->sr_ctrls != NULL ) { 170 vc->ctrls = ldap_controls_dup( rs->sr_ctrls ); 171 } 172 } 173 174 return 0; 175} 176 177static int 178vc_exop( 179 Operation *op, 180 SlapReply *rs ) 181{ 182 int rc = LDAP_SUCCESS; 183 ber_tag_t tag; 184 ber_len_t len = -1; 185 BerElementBuffer berbuf; 186 BerElement *ber = (BerElement *)&berbuf; 187 struct berval reqdata = BER_BVNULL; 188 189 struct berval cookie = BER_BVNULL; 190 struct berval bdn = BER_BVNULL; 191 ber_tag_t authtag; 192 struct berval cred = BER_BVNULL; 193 struct berval ndn = BER_BVNULL; 194 struct berval mechanism = BER_BVNULL; 195 196 vc_conn_t *conn = NULL; 197 vc_cb_t vc = { 0 }; 198 slap_callback sc = { 0 }; 199 SlapReply rs2 = { 0 }; 200 201 if ( op->ore_reqdata == NULL || op->ore_reqdata->bv_len == 0 ) { 202 rs->sr_text = "empty request data field in VerifyCredentials exop"; 203 return LDAP_PROTOCOL_ERROR; 204 } 205 206 /* optimistic */ 207 rs->sr_err = LDAP_SUCCESS; 208 209 ber_dupbv_x( &reqdata, op->ore_reqdata, op->o_tmpmemctx ); 210 211 /* ber_init2 uses reqdata directly, doesn't allocate new buffers */ 212 ber_init2( ber, &reqdata, 0 ); 213 214 tag = ber_scanf( ber, "{" /*}*/ ); 215 if ( tag != LBER_SEQUENCE ) { 216 rs->sr_err = LDAP_PROTOCOL_ERROR; 217 goto done; 218 } 219 220 tag = ber_peek_tag( ber, &len ); 221 if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE ) { 222 /* 223 * cookie: the pointer to the connection 224 * of this operation 225 */ 226 227 ber_scanf( ber, "m", &cookie ); 228 if ( cookie.bv_len != sizeof(Connection *) ) { 229 rs->sr_err = LDAP_PROTOCOL_ERROR; 230 goto done; 231 } 232 } 233 234 /* DN, authtag */ 235 tag = ber_scanf( ber, "mt", &bdn, &authtag ); 236 if ( tag == LBER_ERROR ) { 237 rs->sr_err = LDAP_PROTOCOL_ERROR; 238 goto done; 239 } 240 241 rc = dnNormalize( 0, NULL, NULL, &bdn, &ndn, op->o_tmpmemctx ); 242 if ( rc != LDAP_SUCCESS ) { 243 rs->sr_err = LDAP_PROTOCOL_ERROR; 244 goto done; 245 } 246 247 switch ( authtag ) { 248 case LDAP_AUTH_SIMPLE: 249 /* cookie only makes sense for SASL bind (so far) */ 250 if ( !BER_BVISNULL( &cookie ) ) { 251 rs->sr_err = LDAP_PROTOCOL_ERROR; 252 goto done; 253 } 254 255 tag = ber_scanf( ber, "m", &cred ); 256 if ( tag == LBER_ERROR ) { 257 rs->sr_err = LDAP_PROTOCOL_ERROR; 258 goto done; 259 } 260 break; 261 262 case LDAP_AUTH_SASL: 263 tag = ber_scanf( ber, "{m" /*}*/ , &mechanism ); 264 if ( tag == LBER_ERROR || 265 BER_BVISNULL( &mechanism ) || BER_BVISEMPTY( &mechanism ) ) 266 { 267 rs->sr_err = LDAP_PROTOCOL_ERROR; 268 goto done; 269 } 270 271 tag = ber_peek_tag( ber, &len ); 272 if ( tag == LBER_OCTETSTRING ) { 273 ber_scanf( ber, "m", &cred ); 274 } 275 276 tag = ber_scanf( ber, /*{*/ "}" ); 277 break; 278 279 default: 280 rs->sr_err = LDAP_PROTOCOL_ERROR; 281 goto done; 282 } 283 284 if ( !BER_BVISNULL( &cookie ) ) { 285 vc_conn_t tmp = { 0 }; 286 287 AC_MEMCPY( (char *)&tmp.conn, (const char *)cookie.bv_val, cookie.bv_len ); 288 ldap_pvt_thread_mutex_lock( &vc_mutex ); 289 conn = (vc_conn_t *)ldap_avl_find( vc_tree, (caddr_t)&tmp, vc_conn_cmp ); 290 if ( conn == NULL || ( conn != NULL && conn->refcnt != 0 ) ) { 291 conn = NULL; 292 ldap_pvt_thread_mutex_unlock( &vc_mutex ); 293 rs->sr_err = LDAP_PROTOCOL_ERROR; 294 goto done; 295 } 296 conn->refcnt++; 297 ldap_pvt_thread_mutex_unlock( &vc_mutex ); 298 299 } else { 300 void *thrctx; 301 302 conn = (vc_conn_t *)SLAP_CALLOC( 1, sizeof( vc_conn_t ) ); 303 conn->refcnt = 1; 304 305 thrctx = ldap_pvt_thread_pool_context(); 306 connection_fake_init2( &conn->connbuf, &conn->opbuf, thrctx, 0 ); 307 conn->op = &conn->opbuf.ob_op; 308 snprintf( conn->op->o_log_prefix, sizeof( conn->op->o_log_prefix ), 309 "%s VERIFYCREDENTIALS", op->o_log_prefix ); 310 } 311 312 conn->op->o_tag = LDAP_REQ_BIND; 313 memset( &conn->op->oq_bind, 0, sizeof( conn->op->oq_bind ) ); 314 conn->op->o_req_dn = ndn; 315 conn->op->o_req_ndn = ndn; 316 conn->op->o_protocol = LDAP_VERSION3; 317 conn->op->orb_method = authtag; 318 conn->op->o_callback = ≻ 319 320 /* TODO: controls */ 321 tag = ber_peek_tag( ber, &len ); 322 if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS ) { 323 conn->op->o_ber = ber; 324 rc = get_ctrls2( conn->op, &rs2, 0, LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS ); 325 if ( rc != LDAP_SUCCESS ) { 326 rs->sr_err = LDAP_PROTOCOL_ERROR; 327 goto done; 328 } 329 } 330 331 tag = ber_skip_tag( ber, &len ); 332 if ( len || tag != LBER_DEFAULT ) { 333 rs->sr_err = LDAP_PROTOCOL_ERROR; 334 goto done; 335 } 336 337 switch ( authtag ) { 338 case LDAP_AUTH_SIMPLE: 339 break; 340 341 case LDAP_AUTH_SASL: 342 conn->op->orb_mech = mechanism; 343 break; 344 } 345 346 conn->op->orb_cred = cred; 347 sc.sc_response = vc_cb; 348 sc.sc_private = &vc; 349 350 conn->op->o_bd = frontendDB; 351 rs->sr_err = frontendDB->be_bind( conn->op, &rs2 ); 352 353 if ( conn->op->o_conn->c_sasl_bind_in_progress ) { 354 rc = vc_create_response( conn, rs2.sr_err, rs2.sr_text, 355 !BER_BVISEMPTY( &vc.sasldata ) ? &vc.sasldata : NULL, 356 NULL, 357 vc.ctrls, &rs->sr_rspdata ); 358 359 } else { 360 rc = vc_create_response( NULL, rs2.sr_err, rs2.sr_text, 361 NULL, 362 &conn->op->o_conn->c_dn, 363 vc.ctrls, &rs->sr_rspdata ); 364 } 365 366 if ( rc != 0 ) { 367 rs->sr_err = LDAP_OTHER; 368 goto done; 369 } 370 371 if ( !BER_BVISNULL( &conn->op->o_conn->c_dn ) && 372 conn->op->o_conn->c_dn.bv_val != conn->op->o_conn->c_ndn.bv_val ) 373 ber_memfree( conn->op->o_conn->c_dn.bv_val ); 374 if ( !BER_BVISNULL( &conn->op->o_conn->c_ndn ) ) 375 ber_memfree( conn->op->o_conn->c_ndn.bv_val ); 376 377done:; 378 if ( conn ) { 379 if ( conn->op->o_conn->c_sasl_bind_in_progress ) { 380 if ( conn->conn == NULL ) { 381 conn->conn = conn; 382 conn->refcnt--; 383 ldap_pvt_thread_mutex_lock( &vc_mutex ); 384 rc = ldap_avl_insert( &vc_tree, (caddr_t)conn, 385 vc_conn_cmp, vc_conn_dup ); 386 ldap_pvt_thread_mutex_unlock( &vc_mutex ); 387 assert( rc == 0 ); 388 389 } else { 390 ldap_pvt_thread_mutex_lock( &vc_mutex ); 391 conn->refcnt--; 392 ldap_pvt_thread_mutex_unlock( &vc_mutex ); 393 } 394 395 } else { 396 if ( conn->conn != NULL ) { 397 vc_conn_t *tmp; 398 399 ldap_pvt_thread_mutex_lock( &vc_mutex ); 400 tmp = ldap_avl_delete( &vc_tree, (caddr_t)conn, vc_conn_cmp ); 401 ldap_pvt_thread_mutex_unlock( &vc_mutex ); 402 } 403 SLAP_FREE( conn ); 404 } 405 } 406 407 if ( vc.ctrls ) { 408 ldap_controls_free( vc.ctrls ); 409 vc.ctrls = NULL; 410 } 411 412 if ( !BER_BVISNULL( &ndn ) ) { 413 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx ); 414 BER_BVZERO( &ndn ); 415 } 416 417 op->o_tmpfree( reqdata.bv_val, op->o_tmpmemctx ); 418 BER_BVZERO( &reqdata ); 419 420 return rs->sr_err; 421} 422 423static int 424vc_initialize( void ) 425{ 426 int rc; 427 428 rc = load_extop2( (struct berval *)&vc_exop_oid_bv, 429 SLAP_EXOP_HIDE, vc_exop, 0 ); 430 if ( rc != LDAP_SUCCESS ) { 431 Debug( LDAP_DEBUG_ANY, 432 "vc_initialize: unable to register VerifyCredentials exop: %d.\n", 433 rc ); 434 } 435 436 ldap_pvt_thread_mutex_init( &vc_mutex ); 437 438 return rc; 439} 440 441int 442init_module( int argc, char *argv[] ) 443{ 444 return vc_initialize(); 445} 446 447