1146773Ssam/* $NetBSD: vc.c,v 1.2 2021/08/14 16:14:54 christos Exp $ */ 2146773Ssam 3146773Ssam/* vc.c - LDAP Verify Credentials extop (no spec yet) */ 4146773Ssam/* $OpenLDAP$ */ 5251158Sdelphij/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6146773Ssam * 7146773Ssam * Copyright 2010-2021 The OpenLDAP Foundation. 8146773Ssam * All rights reserved. 9146773Ssam * 10146773Ssam * Redistribution and use in source and binary forms, with or without 11146773Ssam * modification, are permitted only as authorized by the OpenLDAP 12146773Ssam * Public License. 13146773Ssam * 14146773Ssam * A copy of this license is available in the file LICENSE in the 15146773Ssam * top-level directory of the distribution or, alternatively, at 16146773Ssam * <http://www.OpenLDAP.org/license.html>. 17146773Ssam */ 18146773Ssam/* ACKNOWLEDGEMENTS: 19146773Ssam * This work was initially developed by Pierangelo Masarati for inclusion 20146773Ssam * in OpenLDAP Software. 21146773Ssam */ 22146773Ssam 23146773Ssam/* 24214478Srpaulo * LDAP Verify Credentials: suggested by Kurt Zeilenga 25146773Ssam * no spec yet 26146773Ssam */ 27146773Ssam 28146773Ssam#include <sys/cdefs.h> 29146773Ssam__RCSID("$NetBSD: vc.c,v 1.2 2021/08/14 16:14:54 christos Exp $"); 30146773Ssam 31146773Ssam#include "portable.h" 32146773Ssam 33146773Ssam#include "slap.h" 34146773Ssam#include "ac/string.h" 35146773Ssam 36146773Ssamtypedef struct vc_conn_t { 37146773Ssam struct vc_conn_t *conn; 38146773Ssam Connection connbuf; 39146773Ssam OperationBuffer opbuf; 40146773Ssam Operation *op; 41146773Ssam int refcnt; 42146773Ssam} vc_conn_t; 43146773Ssam 44146773Ssamstatic const struct berval vc_exop_oid_bv = BER_BVC(LDAP_EXOP_VERIFY_CREDENTIALS); 45146773Ssamstatic ldap_pvt_thread_mutex_t vc_mutex; 46146773Ssamstatic Avlnode *vc_tree; 47146773Ssam 48146773Ssamstatic int 49146773Ssamvc_conn_cmp( const void *c1, const void *c2 ) 50146773Ssam{ 51146773Ssam const vc_conn_t *vc1 = (const vc_conn_t *)c1; 52146773Ssam const vc_conn_t *vc2 = (const vc_conn_t *)c2; 53146773Ssam 54146773Ssam return SLAP_PTRCMP( vc1->conn, vc2->conn ); 55146773Ssam} 56146773Ssam 57146773Ssamstatic int 58146773Ssamvc_conn_dup( void *c1, void *c2 ) 59146773Ssam{ 60146773Ssam vc_conn_t *vc1 = (vc_conn_t *)c1; 61146773Ssam vc_conn_t *vc2 = (vc_conn_t *)c2; 62146773Ssam 63146773Ssam if ( vc1->conn == vc2->conn ) { 64146773Ssam return -1; 65146773Ssam } 66146773Ssam 67146773Ssam return 0; 68146773Ssam} 69146773Ssam 70146773Ssamstatic int 71146773Ssamvc_create_response( 72146773Ssam void *conn, 73146773Ssam int resultCode, 74146773Ssam const char *diagnosticMessage, 75146773Ssam struct berval *servercred, 76146773Ssam struct berval *authzid, 77146773Ssam LDAPControl **ctrls, 78146773Ssam struct berval **val ) 79146773Ssam{ 80190207Srpaulo BerElementBuffer berbuf; 81146773Ssam BerElement *ber = (BerElement *)&berbuf; 82146773Ssam struct berval bv; 83146773Ssam int rc; 84146773Ssam 85146773Ssam assert( val != NULL ); 86146773Ssam 87214478Srpaulo *val = NULL; 88146773Ssam 89146773Ssam ber_init2( ber, NULL, LBER_USE_DER ); 90190207Srpaulo 91146773Ssam (void)ber_printf( ber, "{is" /*}*/ , resultCode, diagnosticMessage ? diagnosticMessage : "" ); 92146773Ssam 93146773Ssam if ( conn ) { 94146773Ssam struct berval cookie; 95146773Ssam 96146773Ssam cookie.bv_len = sizeof( conn ); 97146773Ssam cookie.bv_val = (char *)&conn; 98146773Ssam (void)ber_printf( ber, "tO", 0, LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE, &cookie ); 99146773Ssam } 100146773Ssam 101146773Ssam if ( servercred ) { 102146773Ssam ber_printf( ber, "tO", LDAP_TAG_EXOP_VERIFY_CREDENTIALS_SCREDS, servercred ); 103146773Ssam } 104146773Ssam 105146773Ssam#if 0 106190207Srpaulo if ( authzid ) { 107190207Srpaulo ber_printf( ber, "tO", LDAP_TAG_EXOP_VERIFY_CREDENTIALS_AUTHZID, authzid ); 108146773Ssam } 109146773Ssam#endif 110235530Sdelphij 111146773Ssam if ( ctrls ) { 112146773Ssam int c; 113251158Sdelphij 114146773Ssam rc = ber_printf( ber, "t{"/*}*/, LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS ); 115190207Srpaulo if ( rc == -1 ) goto done; 116190207Srpaulo 117190207Srpaulo for ( c = 0; ctrls[c] != NULL; c++ ) { 118146773Ssam rc = ber_printf( ber, "{s" /*}*/, ctrls[c]->ldctl_oid ); 119162017Ssam 120235530Sdelphij if ( ctrls[c]->ldctl_iscritical ) { 121162017Ssam rc = ber_printf( ber, "b", (ber_int_t)ctrls[c]->ldctl_iscritical ) ; 122146773Ssam if ( rc == -1 ) goto done; 123146773Ssam } 124146773Ssam 125146773Ssam if ( ctrls[c]->ldctl_value.bv_val != NULL ) { 126146773Ssam rc = ber_printf( ber, "O", &ctrls[c]->ldctl_value ); 127146773Ssam if( rc == -1 ) goto done; 128214478Srpaulo } 129146773Ssam 130146773Ssam rc = ber_printf( ber, /*{*/"N}" ); 131146773Ssam if ( rc == -1 ) goto done; 132146773Ssam } 133146773Ssam 134146773Ssam rc = ber_printf( ber, /*{*/"N}" ); 135146773Ssam if ( rc == -1 ) goto done; 136146773Ssam } 137146773Ssam 138146773Ssam rc = ber_printf( ber, /*{*/ "}" ); 139146773Ssam if ( rc == -1 ) goto done; 140146773Ssam 141146773Ssam rc = ber_flatten2( ber, &bv, 0 ); 142146773Ssam if ( rc == 0 ) { 143146773Ssam *val = ber_bvdup( &bv ); 144146773Ssam } 145146773Ssam 146146773Ssamdone:; 147146773Ssam ber_free_buf( ber ); 148146773Ssam 149146773Ssam return rc; 150146773Ssam} 151146773Ssam 152146773Ssamtypedef struct vc_cb_t { 153146773Ssam struct berval sasldata; 154214478Srpaulo LDAPControl **ctrls; 155214478Srpaulo} vc_cb_t; 156214478Srpaulo 157214478Srpaulostatic int 158214478Srpaulovc_cb( 159146773Ssam Operation *op, 160214478Srpaulo SlapReply *rs ) 161214478Srpaulo{ 162235530Sdelphij vc_cb_t *vc = (vc_cb_t *)op->o_callback->sc_private; 163214478Srpaulo 164214478Srpaulo if ( rs->sr_tag == LDAP_RES_BIND ) { 165146773Ssam if ( rs->sr_sasldata != NULL ) { 166214478Srpaulo ber_dupbv( &vc->sasldata, rs->sr_sasldata ); 167214478Srpaulo } 168214478Srpaulo 169214478Srpaulo if ( rs->sr_ctrls != NULL ) { 170214478Srpaulo vc->ctrls = ldap_controls_dup( rs->sr_ctrls ); 171146773Ssam } 172146773Ssam } 173146773Ssam 174146773Ssam return 0; 175146773Ssam} 176146773Ssam 177146773Ssamstatic int 178146773Ssamvc_exop( 179146773Ssam Operation *op, 180251158Sdelphij SlapReply *rs ) 181251158Sdelphij{ 182251158Sdelphij int rc = LDAP_SUCCESS; 183251158Sdelphij ber_tag_t tag; 184251158Sdelphij ber_len_t len = -1; 185251158Sdelphij BerElementBuffer berbuf; 186146773Ssam BerElement *ber = (BerElement *)&berbuf; 187146773Ssam struct berval reqdata = BER_BVNULL; 188146773Ssam 189146773Ssam struct berval cookie = BER_BVNULL; 190146773Ssam struct berval bdn = BER_BVNULL; 191146773Ssam ber_tag_t authtag; 192146773Ssam struct berval cred = BER_BVNULL; 193146773Ssam struct berval ndn = BER_BVNULL; 194146773Ssam struct berval mechanism = BER_BVNULL; 195214478Srpaulo 196214478Srpaulo vc_conn_t *conn = NULL; 197214478Srpaulo vc_cb_t vc = { 0 }; 198214478Srpaulo slap_callback sc = { 0 }; 199214478Srpaulo SlapReply rs2 = { 0 }; 200214478Srpaulo 201214478Srpaulo if ( op->ore_reqdata == NULL || op->ore_reqdata->bv_len == 0 ) { 202146773Ssam rs->sr_text = "empty request data field in VerifyCredentials exop"; 203214478Srpaulo return LDAP_PROTOCOL_ERROR; 204146773Ssam } 205214478Srpaulo 206214478Srpaulo /* optimistic */ 207214478Srpaulo rs->sr_err = LDAP_SUCCESS; 208214478Srpaulo 209146773Ssam ber_dupbv_x( &reqdata, op->ore_reqdata, op->o_tmpmemctx ); 210146773Ssam 211146773Ssam /* ber_init2 uses reqdata directly, doesn't allocate new buffers */ 212146773Ssam ber_init2( ber, &reqdata, 0 ); 213146773Ssam 214146773Ssam tag = ber_scanf( ber, "{" /*}*/ ); 215146773Ssam if ( tag != LBER_SEQUENCE ) { 216146773Ssam rs->sr_err = LDAP_PROTOCOL_ERROR; 217146773Ssam goto done; 218146773Ssam } 219146773Ssam 220146773Ssam tag = ber_peek_tag( ber, &len ); 221146773Ssam if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE ) { 222146773Ssam /* 223146773Ssam * cookie: the pointer to the connection 224146773Ssam * of this operation 225146773Ssam */ 226146773Ssam 227146773Ssam ber_scanf( ber, "m", &cookie ); 228146773Ssam if ( cookie.bv_len != sizeof(Connection *) ) { 229146773Ssam rs->sr_err = LDAP_PROTOCOL_ERROR; 230146773Ssam goto done; 231146773Ssam } 232146773Ssam } 233146773Ssam 234146773Ssam /* DN, authtag */ 235146773Ssam tag = ber_scanf( ber, "mt", &bdn, &authtag ); 236146773Ssam if ( tag == LBER_ERROR ) { 237146773Ssam rs->sr_err = LDAP_PROTOCOL_ERROR; 238146773Ssam goto done; 239146773Ssam } 240146773Ssam 241146773Ssam rc = dnNormalize( 0, NULL, NULL, &bdn, &ndn, op->o_tmpmemctx ); 242146773Ssam if ( rc != LDAP_SUCCESS ) { 243251158Sdelphij rs->sr_err = LDAP_PROTOCOL_ERROR; 244146773Ssam goto done; 245146773Ssam } 246146773Ssam 247146773Ssam switch ( authtag ) { 248146773Ssam case LDAP_AUTH_SIMPLE: 249146773Ssam /* cookie only makes sense for SASL bind (so far) */ 250146773Ssam if ( !BER_BVISNULL( &cookie ) ) { 251146773Ssam rs->sr_err = LDAP_PROTOCOL_ERROR; 252146773Ssam goto done; 253146773Ssam } 254146773Ssam 255146773Ssam tag = ber_scanf( ber, "m", &cred ); 256146773Ssam if ( tag == LBER_ERROR ) { 257146773Ssam rs->sr_err = LDAP_PROTOCOL_ERROR; 258146773Ssam goto done; 259146773Ssam } 260146773Ssam break; 261172683Smlaier 262146773Ssam case LDAP_AUTH_SASL: 263235530Sdelphij tag = ber_scanf( ber, "{m" /*}*/ , &mechanism ); 264235530Sdelphij if ( tag == LBER_ERROR || 265235530Sdelphij BER_BVISNULL( &mechanism ) || BER_BVISEMPTY( &mechanism ) ) 266146773Ssam { 267146773Ssam rs->sr_err = LDAP_PROTOCOL_ERROR; 268146773Ssam goto done; 269146773Ssam } 270146773Ssam 271146773Ssam tag = ber_peek_tag( ber, &len ); 272146773Ssam if ( tag == LBER_OCTETSTRING ) { 273146773Ssam ber_scanf( ber, "m", &cred ); 274146773Ssam } 275146773Ssam 276146773Ssam tag = ber_scanf( ber, /*{*/ "}" ); 277235530Sdelphij break; 278235530Sdelphij 279235530Sdelphij default: 280146773Ssam rs->sr_err = LDAP_PROTOCOL_ERROR; 281235530Sdelphij goto done; 282235530Sdelphij } 283235530Sdelphij 284190207Srpaulo if ( !BER_BVISNULL( &cookie ) ) { 285146773Ssam vc_conn_t tmp = { 0 }; 286146773Ssam 287146773Ssam AC_MEMCPY( (char *)&tmp.conn, (const char *)cookie.bv_val, cookie.bv_len ); 288146773Ssam ldap_pvt_thread_mutex_lock( &vc_mutex ); 289241235Sdelphij conn = (vc_conn_t *)ldap_avl_find( vc_tree, (caddr_t)&tmp, vc_conn_cmp ); 290251158Sdelphij if ( conn == NULL || ( conn != NULL && conn->refcnt != 0 ) ) { 291214478Srpaulo conn = NULL; 292214478Srpaulo ldap_pvt_thread_mutex_unlock( &vc_mutex ); 293146773Ssam rs->sr_err = LDAP_PROTOCOL_ERROR; 294146773Ssam goto done; 295146773Ssam } 296146773Ssam conn->refcnt++; 297146773Ssam ldap_pvt_thread_mutex_unlock( &vc_mutex ); 298146773Ssam 299146773Ssam } else { 300146773Ssam void *thrctx; 301190207Srpaulo 302146773Ssam conn = (vc_conn_t *)SLAP_CALLOC( 1, sizeof( vc_conn_t ) ); 303235530Sdelphij conn->refcnt = 1; 304235530Sdelphij 305235530Sdelphij thrctx = ldap_pvt_thread_pool_context(); 306235530Sdelphij connection_fake_init2( &conn->connbuf, &conn->opbuf, thrctx, 0 ); 307235530Sdelphij conn->op = &conn->opbuf.ob_op; 308251158Sdelphij snprintf( conn->op->o_log_prefix, sizeof( conn->op->o_log_prefix ), 309235530Sdelphij "%s VERIFYCREDENTIALS", op->o_log_prefix ); 310251158Sdelphij } 311235530Sdelphij 312251158Sdelphij conn->op->o_tag = LDAP_REQ_BIND; 313235530Sdelphij memset( &conn->op->oq_bind, 0, sizeof( conn->op->oq_bind ) ); 314235530Sdelphij conn->op->o_req_dn = ndn; 315235530Sdelphij conn->op->o_req_ndn = ndn; 316235530Sdelphij conn->op->o_protocol = LDAP_VERSION3; 317235530Sdelphij conn->op->orb_method = authtag; 318235530Sdelphij conn->op->o_callback = ≻ 319146773Ssam 320146773Ssam /* TODO: controls */ 321162017Ssam tag = ber_peek_tag( ber, &len ); 322162017Ssam if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS ) { 323162017Ssam conn->op->o_ber = ber; 324162017Ssam rc = get_ctrls2( conn->op, &rs2, 0, LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS ); 325146773Ssam if ( rc != LDAP_SUCCESS ) { 326162017Ssam rs->sr_err = LDAP_PROTOCOL_ERROR; 327162017Ssam goto done; 328162017Ssam } 329146773Ssam } 330146773Ssam 331146773Ssam tag = ber_skip_tag( ber, &len ); 332146773Ssam if ( len || tag != LBER_DEFAULT ) { 333146773Ssam rs->sr_err = LDAP_PROTOCOL_ERROR; 334146773Ssam goto done; 335146773Ssam } 336146773Ssam 337146773Ssam switch ( authtag ) { 338146773Ssam case LDAP_AUTH_SIMPLE: 339146773Ssam break; 340146773Ssam 341146773Ssam case LDAP_AUTH_SASL: 342146773Ssam conn->op->orb_mech = mechanism; 343146773Ssam break; 344146773Ssam } 345146773Ssam 346146773Ssam conn->op->orb_cred = cred; 347146773Ssam sc.sc_response = vc_cb; 348146773Ssam sc.sc_private = &vc; 349146773Ssam 350146773Ssam conn->op->o_bd = frontendDB; 351146773Ssam rs->sr_err = frontendDB->be_bind( conn->op, &rs2 ); 352146773Ssam 353146773Ssam if ( conn->op->o_conn->c_sasl_bind_in_progress ) { 354146773Ssam rc = vc_create_response( conn, rs2.sr_err, rs2.sr_text, 355146773Ssam !BER_BVISEMPTY( &vc.sasldata ) ? &vc.sasldata : NULL, 356146773Ssam NULL, 357146773Ssam vc.ctrls, &rs->sr_rspdata ); 358146773Ssam 359146773Ssam } else { 360146773Ssam rc = vc_create_response( NULL, rs2.sr_err, rs2.sr_text, 361146773Ssam NULL, 362146773Ssam &conn->op->o_conn->c_dn, 363146773Ssam vc.ctrls, &rs->sr_rspdata ); 364146773Ssam } 365146773Ssam 366146773Ssam if ( rc != 0 ) { 367146773Ssam rs->sr_err = LDAP_OTHER; 368235530Sdelphij goto done; 369146773Ssam } 370146773Ssam 371146773Ssam if ( !BER_BVISNULL( &conn->op->o_conn->c_dn ) && 372146773Ssam conn->op->o_conn->c_dn.bv_val != conn->op->o_conn->c_ndn.bv_val ) 373146773Ssam ber_memfree( conn->op->o_conn->c_dn.bv_val ); 374146773Ssam if ( !BER_BVISNULL( &conn->op->o_conn->c_ndn ) ) 375146773Ssam ber_memfree( conn->op->o_conn->c_ndn.bv_val ); 376146773Ssam 377146773Ssamdone:; 378146773Ssam if ( conn ) { 379146773Ssam if ( conn->op->o_conn->c_sasl_bind_in_progress ) { 380146773Ssam if ( conn->conn == NULL ) { 381146773Ssam conn->conn = conn; 382146773Ssam conn->refcnt--; 383146773Ssam ldap_pvt_thread_mutex_lock( &vc_mutex ); 384146773Ssam rc = ldap_avl_insert( &vc_tree, (caddr_t)conn, 385146773Ssam vc_conn_cmp, vc_conn_dup ); 386146773Ssam ldap_pvt_thread_mutex_unlock( &vc_mutex ); 387146773Ssam assert( rc == 0 ); 388146773Ssam 389146773Ssam } else { 390146773Ssam ldap_pvt_thread_mutex_lock( &vc_mutex ); 391146773Ssam conn->refcnt--; 392146773Ssam ldap_pvt_thread_mutex_unlock( &vc_mutex ); 393146773Ssam } 394146773Ssam 395214478Srpaulo } else { 396146773Ssam if ( conn->conn != NULL ) { 397146773Ssam vc_conn_t *tmp; 398146773Ssam 399146773Ssam ldap_pvt_thread_mutex_lock( &vc_mutex ); 400146773Ssam tmp = ldap_avl_delete( &vc_tree, (caddr_t)conn, vc_conn_cmp ); 401146773Ssam ldap_pvt_thread_mutex_unlock( &vc_mutex ); 402146773Ssam } 403146773Ssam SLAP_FREE( conn ); 404146773Ssam } 405146773Ssam } 406146773Ssam 407146773Ssam if ( vc.ctrls ) { 408146773Ssam ldap_controls_free( vc.ctrls ); 409146773Ssam vc.ctrls = NULL; 410146773Ssam } 411146773Ssam 412146773Ssam if ( !BER_BVISNULL( &ndn ) ) { 413146773Ssam op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx ); 414235530Sdelphij BER_BVZERO( &ndn ); 415146773Ssam } 416146773Ssam 417251158Sdelphij op->o_tmpfree( reqdata.bv_val, op->o_tmpmemctx ); 418146773Ssam BER_BVZERO( &reqdata ); 419146773Ssam 420146773Ssam return rs->sr_err; 421146773Ssam} 422146773Ssam 423146773Ssamstatic int 424146773Ssamvc_initialize( void ) 425146773Ssam{ 426146773Ssam int rc; 427146773Ssam 428146773Ssam rc = load_extop2( (struct berval *)&vc_exop_oid_bv, 429146773Ssam SLAP_EXOP_HIDE, vc_exop, 0 ); 430146773Ssam if ( rc != LDAP_SUCCESS ) { 431146773Ssam Debug( LDAP_DEBUG_ANY, 432146773Ssam "vc_initialize: unable to register VerifyCredentials exop: %d.\n", 433146773Ssam rc ); 434146773Ssam } 435146773Ssam 436146773Ssam ldap_pvt_thread_mutex_init( &vc_mutex ); 437146773Ssam 438146773Ssam return rc; 439146773Ssam} 440146773Ssam 441146773Ssamint 442146773Ssaminit_module( int argc, char *argv[] ) 443146773Ssam{ 444146773Ssam return vc_initialize(); 445146773Ssam} 446146773Ssam 447146773Ssam