1 /* $NetBSD: remoteauth.c,v 1.3 2025/09/05 21:16:32 christos Exp $ */ 2 3 /* $OpenLDAP$ */ 4 /* remoteauth.c - Overlay to delegate bind processing to a remote server */ 5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2004-2024 The OpenLDAP Foundation. 8 * Portions Copyright 2017-2021 Ondej Kuznk, Symas Corporation. 9 * Portions Copyright 2004-2017 Howard Chu, Symas Corporation. 10 * Portions Copyright 2004 Hewlett-Packard Company. 11 * All rights reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted only as authorized by the OpenLDAP 15 * Public License. 16 * 17 * A copy of this license is available in the file LICENSE in the 18 * top-level directory of the distribution or, alternatively, at 19 * <http://www.OpenLDAP.org/license.html>. 20 */ 21 22 #include <sys/cdefs.h> 23 __RCSID("$NetBSD: remoteauth.c,v 1.3 2025/09/05 21:16:32 christos Exp $"); 24 25 #include "portable.h" 26 27 #include <ldap.h> 28 #if SLAPD_MODULES 29 #define LIBLTDL_DLL_IMPORT /* Win32: don't re-export libltdl's symbols */ 30 #include <ltdl.h> 31 #endif 32 #include <ac/errno.h> 33 #include <ac/time.h> 34 #include <ac/string.h> 35 #include <ac/ctype.h> 36 #include "lutil.h" 37 #include "slap.h" 38 #include "slap-config.h" 39 40 #ifndef UP_STR 41 #define UP_STR "userPassword" 42 #endif /* UP_STR */ 43 44 #ifndef LDAP_PREFIX 45 #define LDAP_PREFIX "ldap://" 46 #endif /* LDAP_PREFIX */ 47 48 #ifndef FILE_PREFIX 49 #define FILE_PREFIX "file://" 50 #endif /* LDAP_PREFIX */ 51 52 typedef struct _ad_info { 53 struct _ad_info *next; 54 char *domain; 55 char *realm; 56 } ad_info; 57 58 typedef struct _ad_pin { 59 struct _ad_pin *next; 60 char *hostname; 61 char *pin; 62 } ad_pin; 63 64 typedef struct _ad_private { 65 char *dn; 66 AttributeDescription *dn_ad; 67 char *domain_attr; 68 AttributeDescription *domain_ad; 69 70 AttributeDescription *up_ad; 71 ad_info *mappings; 72 73 char *default_realm; 74 char *default_domain; 75 76 int up_set; 77 int retry_count; 78 int store_on_success; 79 80 ad_pin *pins; 81 slap_bindconf ad_tls; 82 } ad_private; 83 84 enum { 85 REMOTE_AUTH_MAPPING = 1, 86 REMOTE_AUTH_DN_ATTRIBUTE, 87 REMOTE_AUTH_DOMAIN_ATTRIBUTE, 88 REMOTE_AUTH_DEFAULT_DOMAIN, 89 REMOTE_AUTH_DEFAULT_REALM, 90 REMOTE_AUTH_CACERT_DIR, 91 REMOTE_AUTH_CACERT_FILE, 92 REMOTE_AUTH_VALIDATE_CERTS, 93 REMOTE_AUTH_RETRY_COUNT, 94 REMOTE_AUTH_TLS, 95 REMOTE_AUTH_TLS_PIN, 96 REMOTE_AUTH_STORE_ON_SUCCESS, 97 }; 98 99 static ConfigDriver remoteauth_cf_gen; 100 101 static ConfigTable remoteauthcfg[] = { 102 { "remoteauth_mapping", "mapping between domain and realm", 2, 3, 0, 103 ARG_MAGIC|REMOTE_AUTH_MAPPING, 104 remoteauth_cf_gen, 105 "( OLcfgOvAt:24.1 NAME 'olcRemoteAuthMapping' " 106 "DESC 'Mapping from domain name to server' " 107 "SYNTAX OMsDirectoryString )", 108 NULL, NULL 109 }, 110 { "remoteauth_dn_attribute", "Attribute to use as AD bind DN", 2, 2, 0, 111 ARG_MAGIC|REMOTE_AUTH_DN_ATTRIBUTE, 112 remoteauth_cf_gen, 113 "( OLcfgOvAt:24.2 NAME 'olcRemoteAuthDNAttribute' " 114 "DESC 'Attribute in entry to use as bind DN for AD' " 115 "SYNTAX OMsDirectoryString SINGLE-VALUE )", 116 NULL, NULL 117 }, 118 { "remoteauth_domain_attribute", "Attribute to use as domain determinant", 2, 2, 0, 119 ARG_MAGIC|REMOTE_AUTH_DOMAIN_ATTRIBUTE, 120 remoteauth_cf_gen, 121 "( OLcfgOvAt:24.3 NAME 'olcRemoteAuthDomainAttribute' " 122 "DESC 'Attribute in entry to determine windows domain' " 123 "SYNTAX OMsDirectoryString SINGLE-VALUE )", 124 NULL, NULL 125 }, 126 { "remoteauth_default_domain", "Default Windows domain", 2, 2, 0, 127 ARG_MAGIC|REMOTE_AUTH_DEFAULT_DOMAIN, 128 remoteauth_cf_gen, 129 "( OLcfgOvAt:24.4 NAME 'olcRemoteAuthDefaultDomain' " 130 "DESC 'Default Windows domain to use' " 131 "SYNTAX OMsDirectoryString SINGLE-VALUE )", 132 NULL, NULL 133 }, 134 { "remoteauth_default_realm", "Default AD realm", 2, 2, 0, 135 ARG_MAGIC|REMOTE_AUTH_DEFAULT_REALM, 136 remoteauth_cf_gen, 137 "( OLcfgOvAt:24.5 NAME 'olcRemoteAuthDefaultRealm' " 138 "DESC 'Default AD realm to use' " 139 "SYNTAX OMsDirectoryString SINGLE-VALUE )", 140 NULL, NULL 141 }, 142 { "remoteauth_store", "on|off", 1, 2, 0, 143 ARG_OFFSET|ARG_ON_OFF|REMOTE_AUTH_STORE_ON_SUCCESS, 144 (void *)offsetof(ad_private, store_on_success), 145 "( OLcfgOvAt:24.6 NAME 'olcRemoteAuthStore' " 146 "DESC 'Store password locally on success' " 147 "SYNTAX OMsBoolean SINGLE-VALUE )", 148 NULL, NULL 149 }, 150 { "remoteauth_retry_count", "integer", 2, 2, 0, 151 ARG_OFFSET|ARG_UINT|REMOTE_AUTH_RETRY_COUNT, 152 (void *)offsetof(ad_private, retry_count), 153 "( OLcfgOvAt:24.7 NAME 'olcRemoteAuthRetryCount' " 154 "DESC 'Number of retries attempted' " 155 "SYNTAX OMsInteger SINGLE-VALUE )", 156 NULL, { .v_uint = 3 } 157 }, 158 { "remoteauth_tls", "tls settings", 2, 0, 0, 159 ARG_MAGIC|REMOTE_AUTH_TLS, 160 remoteauth_cf_gen, 161 "( OLcfgOvAt:24.8 NAME 'olcRemoteAuthTLS' " 162 "DESC 'StartTLS settings' " 163 "SYNTAX OMsDirectoryString SINGLE-VALUE )", 164 NULL, NULL 165 }, 166 { "remoteauth_tls_peerkey_hash", "mapping between hostnames and their public key hash", 3, 3, 0, 167 ARG_MAGIC|REMOTE_AUTH_TLS_PIN, 168 remoteauth_cf_gen, 169 "( OLcfgOvAt:24.9 NAME 'olcRemoteAuthTLSPeerkeyHash' " 170 "DESC 'StartTLS hostname to public key pin mapping file' " 171 "SYNTAX OMsDirectoryString )", 172 NULL, NULL 173 }, 174 175 { NULL, NULL, 0, 0, 0, ARG_IGNORED, NULL } 176 }; 177 178 static ConfigOCs remoteauthocs[] = { 179 { "( OLcfgOvOc:24.1 " 180 "NAME 'olcRemoteAuthCfg' " 181 "DESC 'Remote Directory passthough authentication configuration' " 182 "SUP olcOverlayConfig " 183 "MUST olcRemoteAuthTLS " 184 "MAY ( olcRemoteAuthMapping $ olcRemoteAuthDNAttribute $ " 185 " olcRemoteAuthDomainAttribute $ olcRemoteAuthDefaultDomain $ " 186 " olcRemoteAuthDefaultRealm $ olcRemoteAuthStore $ " 187 " olcRemoteAuthRetryCount $ olcRemoteAuthTLSPeerkeyHash ) )", 188 Cft_Overlay, remoteauthcfg }, 189 { NULL, 0, NULL } 190 }; 191 192 static int 193 remoteauth_cf_gen( ConfigArgs *c ) 194 { 195 slap_overinst *on = (slap_overinst *)c->bi; 196 ad_private *ad = (ad_private *)on->on_bi.bi_private; 197 struct berval bv; 198 int i, rc = 0; 199 ad_info *map; 200 const char *text = NULL; 201 202 switch ( c->op ) { 203 case SLAP_CONFIG_EMIT: 204 switch ( c->type ) { 205 case REMOTE_AUTH_MAPPING: 206 for ( map = ad->mappings; map; map = map->next ) { 207 char *str; 208 209 str = ch_malloc( strlen( map->domain ) + 210 strlen( map->realm ) + 2 ); 211 sprintf( str, "%s %s", map->domain, map->realm ); 212 ber_str2bv( str, 0, 0, &bv ); 213 rc = value_add_one( &c->rvalue_vals, &bv ); 214 if ( !rc ) 215 rc = value_add_one( &c->rvalue_nvals, &bv ); 216 ch_free( str ); 217 if ( rc ) break; 218 } 219 break; 220 case REMOTE_AUTH_DN_ATTRIBUTE: 221 if ( ad->dn ) 222 value_add_one( &c->rvalue_vals, &ad->dn_ad->ad_cname ); 223 break; 224 case REMOTE_AUTH_DOMAIN_ATTRIBUTE: 225 if ( ad->domain_attr ) 226 value_add_one( 227 &c->rvalue_vals, &ad->domain_ad->ad_cname ); 228 break; 229 case REMOTE_AUTH_DEFAULT_DOMAIN: 230 if ( ad->default_domain ) { 231 ber_str2bv( ad->default_domain, 0, 0, &bv ); 232 value_add_one( &c->rvalue_vals, &bv ); 233 } 234 break; 235 case REMOTE_AUTH_DEFAULT_REALM: 236 if ( ad->default_realm ) { 237 ber_str2bv( ad->default_realm, 0, 0, &bv ); 238 value_add_one( &c->rvalue_vals, &bv ); 239 } 240 break; 241 case REMOTE_AUTH_TLS: 242 bindconf_tls_unparse( &ad->ad_tls, &bv ); 243 244 for ( i = 0; isspace( (unsigned char) bv.bv_val[ i ] ); i++ ) 245 /* count spaces */ ; 246 247 if ( i ) { 248 bv.bv_len -= i; 249 AC_MEMCPY( bv.bv_val, &bv.bv_val[ i ], 250 bv.bv_len + 1 ); 251 } 252 253 value_add_one( &c->rvalue_vals, &bv ); 254 ch_free( bv.bv_val ); 255 break; 256 case REMOTE_AUTH_TLS_PIN: { 257 ad_pin *pin = ad->pins; 258 for ( pin = ad->pins; pin; pin = pin->next ) { 259 bv.bv_val = ch_malloc( strlen( pin->hostname ) + 260 strlen( pin->pin ) + 2 ); 261 bv.bv_len = sprintf( 262 bv.bv_val, "%s %s", pin->hostname, pin->pin ); 263 rc = value_add_one( &c->rvalue_vals, &bv ); 264 if ( rc ) return rc; 265 rc = value_add_one( &c->rvalue_nvals, &bv ); 266 if ( rc ) return rc; 267 } 268 } break; 269 270 default: 271 abort(); 272 } 273 break; 274 case LDAP_MOD_DELETE: 275 switch ( c->type ) { 276 case REMOTE_AUTH_MAPPING: 277 if ( c->valx < 0 ) { 278 /* delete all mappings */ 279 while ( ad->mappings ) { 280 map = ad->mappings; 281 ad->mappings = ad->mappings->next; 282 ch_free( map->domain ); 283 ch_free( map->realm ); 284 ch_free( map ); 285 } 286 } else { 287 /* delete a specific mapping indicated by 'valx'*/ 288 ad_info *pmap = NULL; 289 290 for ( map = ad->mappings, i = 0; 291 ( map ) && ( i < c->valx ); 292 pmap = map, map = map->next, i++ ) 293 ; 294 295 if ( pmap ) { 296 pmap->next = map->next; 297 map->next = NULL; 298 299 ch_free( map->domain ); 300 ch_free( map->realm ); 301 ch_free( map ); 302 } else if ( ad->mappings ) { 303 /* delete the first item in the list */ 304 map = ad->mappings; 305 ad->mappings = map->next; 306 ch_free( map->domain ); 307 ch_free( map->realm ); 308 ch_free( map ); 309 } 310 } 311 break; 312 case REMOTE_AUTH_DN_ATTRIBUTE: 313 if ( ad->dn ) { 314 ch_free( ad->dn ); 315 ad->dn = NULL; /* Don't free AttributeDescription */ 316 } 317 break; 318 case REMOTE_AUTH_DOMAIN_ATTRIBUTE: 319 if ( ad->domain_attr ) { 320 ch_free( ad->domain_attr ); 321 /* Don't free AttributeDescription */ 322 ad->domain_attr = NULL; 323 } 324 break; 325 case REMOTE_AUTH_DEFAULT_DOMAIN: 326 if ( ad->default_domain ) { 327 ch_free( ad->default_domain ); 328 ad->default_domain = NULL; 329 } 330 break; 331 case REMOTE_AUTH_DEFAULT_REALM: 332 if ( ad->default_realm ) { 333 ch_free( ad->default_realm ); 334 ad->default_realm = NULL; 335 } 336 break; 337 case REMOTE_AUTH_TLS: 338 /* MUST + SINGLE-VALUE -> this is a replace */ 339 bindconf_free( &ad->ad_tls ); 340 break; 341 case REMOTE_AUTH_TLS_PIN: 342 while ( ad->pins ) { 343 ad_pin *pin = ad->pins; 344 ad->pins = ad->pins->next; 345 ch_free( pin->hostname ); 346 ch_free( pin->pin ); 347 ch_free( pin ); 348 } 349 break; 350 /* ARG_OFFSET */ 351 case REMOTE_AUTH_STORE_ON_SUCCESS: 352 case REMOTE_AUTH_RETRY_COUNT: 353 abort(); 354 break; 355 default: 356 abort(); 357 } 358 break; 359 case SLAP_CONFIG_ADD: 360 case LDAP_MOD_ADD: 361 switch ( c->type ) { 362 case REMOTE_AUTH_MAPPING: 363 /* add mapping to head of list */ 364 map = ch_malloc( sizeof(ad_info) ); 365 map->domain = ber_strdup( c->argv[1] ); 366 map->realm = ber_strdup( c->argv[2] ); 367 map->next = ad->mappings; 368 ad->mappings = map; 369 370 break; 371 case REMOTE_AUTH_DN_ATTRIBUTE: 372 if ( slap_str2ad( c->argv[1], &ad->dn_ad, &text ) == 373 LDAP_SUCCESS ) { 374 ad->dn = ber_strdup( ad->dn_ad->ad_cname.bv_val ); 375 } else { 376 strncpy( c->cr_msg, text, sizeof(c->cr_msg) ); 377 c->cr_msg[sizeof(c->cr_msg) - 1] = '\0'; 378 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg ); 379 rc = ARG_BAD_CONF; 380 } 381 break; 382 case REMOTE_AUTH_DOMAIN_ATTRIBUTE: 383 if ( slap_str2ad( c->argv[1], &ad->domain_ad, &text ) == 384 LDAP_SUCCESS ) { 385 ad->domain_attr = 386 ber_strdup( ad->domain_ad->ad_cname.bv_val ); 387 } else { 388 strncpy( c->cr_msg, text, sizeof(c->cr_msg) ); 389 c->cr_msg[sizeof(c->cr_msg) - 1] = '\0'; 390 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg ); 391 rc = ARG_BAD_CONF; 392 } 393 break; 394 case REMOTE_AUTH_DEFAULT_DOMAIN: 395 if ( ad->default_domain ) { 396 ch_free( ad->default_domain ); 397 ad->default_domain = NULL; 398 } 399 ad->default_domain = ber_strdup( c->argv[1] ); 400 break; 401 case REMOTE_AUTH_DEFAULT_REALM: 402 if ( ad->default_realm ) { 403 ch_free( ad->default_realm ); 404 ad->default_realm = NULL; 405 } 406 ad->default_realm = ber_strdup( c->argv[1] ); 407 break; 408 case REMOTE_AUTH_TLS: 409 for ( i=1; i < c->argc; i++ ) { 410 if ( bindconf_tls_parse( c->argv[i], &ad->ad_tls ) ) { 411 rc = 1; 412 break; 413 } 414 } 415 bindconf_tls_defaults( &ad->ad_tls ); 416 break; 417 case REMOTE_AUTH_TLS_PIN: { 418 ad_pin *pin = ch_calloc( 1, sizeof(ad_pin) ); 419 420 pin->hostname = ber_strdup( c->argv[1] ); 421 pin->pin = ber_strdup( c->argv[2] ); 422 pin->next = ad->pins; 423 ad->pins = pin; 424 } break; 425 /* ARG_OFFSET */ 426 case REMOTE_AUTH_STORE_ON_SUCCESS: 427 case REMOTE_AUTH_RETRY_COUNT: 428 abort(); 429 break; 430 default: 431 abort(); 432 } 433 break; 434 default: 435 abort(); 436 } 437 438 return rc; 439 } 440 441 static char * 442 get_realm( 443 const char *domain, 444 ad_info *mappings, 445 const char *default_realm, 446 int *isfile ) 447 { 448 ad_info *ai; 449 char *dom = NULL, *ch, *ret = NULL; 450 451 if ( isfile ) *isfile = 0; 452 453 if ( !domain ) { 454 ret = default_realm ? ch_strdup( default_realm ) : NULL; 455 goto exit; 456 } 457 458 /* munge any DOMAIN\user or DOMAIN:user values into just DOMAIN */ 459 460 ch = strchr( domain, '\\' ); 461 if ( !ch ) ch = strchr( domain, ':' ); 462 463 if ( ch ) { 464 dom = ch_malloc( ch - domain + 1 ); 465 strncpy( dom, domain, ch - domain ); 466 dom[ch - domain] = '\0'; 467 } else { 468 dom = ch_strdup( domain ); 469 } 470 471 for ( ai = mappings; ai; ai = ai->next ) 472 if ( strcasecmp( ai->domain, dom ) == 0 ) { 473 ret = ch_strdup( ai->realm ); 474 break; 475 } 476 477 if ( !ai ) 478 ret = default_realm ? ch_strdup( default_realm ) : 479 NULL; /* no mapping found */ 480 exit: 481 if ( dom ) ch_free( dom ); 482 if ( ret && 483 ( strncasecmp( ret, FILE_PREFIX, strlen( FILE_PREFIX ) ) == 0 ) ) { 484 char *p; 485 486 p = ret; 487 ret = ch_strdup( p + strlen( FILE_PREFIX ) ); 488 ch_free( p ); 489 if ( isfile ) *isfile = 1; 490 } 491 492 return ret; 493 } 494 495 static char * 496 get_ldap_url( const char *realm, int isfile ) 497 { 498 char *ldap_url = NULL; 499 FILE *fp; 500 501 if ( !realm ) return NULL; 502 503 if ( !isfile ) { 504 if ( strstr( realm, "://" ) ) { 505 return ch_strdup( realm ); 506 } 507 508 ldap_url = ch_malloc( 1 + strlen( LDAP_PREFIX ) + strlen( realm ) ); 509 sprintf( ldap_url, "%s%s", LDAP_PREFIX, realm ); 510 return ldap_url; 511 } 512 513 fp = fopen( realm, "r" ); 514 if ( !fp ) { 515 char ebuf[128]; 516 int saved_errno = errno; 517 Debug( LDAP_DEBUG_TRACE, "remoteauth: " 518 "Unable to open realm file (%s)\n", 519 sock_errstr( saved_errno, ebuf, sizeof(ebuf) ) ); 520 return NULL; 521 } 522 /* 523 * Read each line in the file and return a URL of the form 524 * "ldap://<line1> ldap://<line2> ... ldap://<lineN>" 525 * which can be passed to ldap_initialize. 526 */ 527 while ( !feof( fp ) ) { 528 char line[512], *p; 529 530 p = fgets( line, sizeof(line), fp ); 531 if ( !p ) continue; 532 533 /* terminate line at first whitespace */ 534 for ( p = line; *p; p++ ) 535 if ( isspace( *p ) ) { 536 *p = '\0'; 537 break; 538 } 539 540 if ( ldap_url ) { 541 char *nu; 542 543 nu = ch_malloc( strlen( ldap_url ) + 2 + strlen( LDAP_PREFIX ) + 544 strlen( line ) ); 545 546 if ( strstr( line, "://" ) ) { 547 sprintf( nu, "%s %s", ldap_url, line ); 548 } else { 549 sprintf( nu, "%s %s%s", ldap_url, LDAP_PREFIX, line ); 550 } 551 ch_free( ldap_url ); 552 ldap_url = nu; 553 } else { 554 ldap_url = ch_malloc( 1 + strlen( line ) + strlen( LDAP_PREFIX ) ); 555 if ( strstr( line, "://" ) ) { 556 strcpy( ldap_url, line ); 557 } else { 558 sprintf( ldap_url, "%s%s", LDAP_PREFIX, line ); 559 } 560 } 561 } 562 563 fclose( fp ); 564 565 return ldap_url; 566 } 567 568 static void 569 trace_remoteauth_parameters( ad_private *ap ) 570 { 571 ad_info *pad_info; 572 struct berval bv; 573 574 if ( !ap ) return; 575 576 Debug( LDAP_DEBUG_TRACE, "remoteauth_dn_attribute: %s\n", 577 ap->dn ? ap->dn : "NULL" ); 578 579 Debug( LDAP_DEBUG_TRACE, "remoteauth_domain_attribute: %s\n", 580 ap->domain_attr ? ap->domain_attr : "NULL" ); 581 582 Debug( LDAP_DEBUG_TRACE, "remoteauth_default_realm: %s\n", 583 ap->default_realm ? ap->default_realm : "NULL" ); 584 585 Debug( LDAP_DEBUG_TRACE, "remoteauth_default_domain: %s\n", 586 ap->default_domain ? ap->default_domain : "NULL" ); 587 588 Debug( LDAP_DEBUG_TRACE, "remoteauth_retry_count: %d\n", ap->retry_count ); 589 590 bindconf_tls_unparse( &ap->ad_tls, &bv ); 591 Debug( LDAP_DEBUG_TRACE, "remoteauth_tls:%s\n", bv.bv_val ); 592 ch_free( bv.bv_val ); 593 594 pad_info = ap->mappings; 595 while ( pad_info ) { 596 Debug( LDAP_DEBUG_TRACE, "remoteauth_mappings(%s,%s)\n", 597 pad_info->domain ? pad_info->domain : "NULL", 598 pad_info->realm ? pad_info->realm : "NULL" ); 599 pad_info = pad_info->next; 600 } 601 602 return; 603 } 604 605 static int 606 remoteauth_conn_cb( 607 LDAP *ld, 608 Sockbuf *sb, 609 LDAPURLDesc *srv, 610 struct sockaddr *addr, 611 struct ldap_conncb *ctx ) 612 { 613 ad_private *ap = ctx->lc_arg; 614 ad_pin *pin = NULL; 615 char *host; 616 617 host = srv->lud_host; 618 if ( !host || !*host ) { 619 host = "localhost"; 620 } 621 622 for ( pin = ap->pins; pin; pin = pin->next ) { 623 if ( !strcasecmp( host, pin->hostname ) ) break; 624 } 625 626 if ( pin ) { 627 int rc = ldap_set_option( ld, LDAP_OPT_X_TLS_PEERKEY_HASH, pin->pin ); 628 if ( rc == LDAP_SUCCESS ) { 629 return 0; 630 } 631 632 Debug( LDAP_DEBUG_TRACE, "remoteauth_conn_cb: " 633 "TLS Peerkey hash could not be set to '%s': %d\n", 634 pin->pin, rc ); 635 } else { 636 Debug( LDAP_DEBUG_TRACE, "remoteauth_conn_cb: " 637 "No TLS Peerkey hash found for host '%s'\n", 638 host ); 639 } 640 641 return -1; 642 } 643 644 static void 645 remoteauth_conn_delcb( LDAP *ld, Sockbuf *sb, struct ldap_conncb *ctx ) 646 { 647 return; 648 } 649 650 static int 651 remoteauth_bind( Operation *op, SlapReply *rs ) 652 { 653 Entry *e; 654 int rc; 655 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 656 ad_private *ap = (ad_private *)on->on_bi.bi_private; 657 Attribute *a_dom, *a_dn; 658 struct ldap_conncb ad_conncb = { .lc_add = remoteauth_conn_cb, 659 .lc_del = remoteauth_conn_delcb, 660 .lc_arg = ap }; 661 struct berval dn = { 0 }; 662 char *dom_val, *realm = NULL; 663 char *ldap_url = NULL; 664 LDAP *ld = NULL; 665 int protocol = LDAP_VERSION3, isfile = 0; 666 int tries = 0; 667 668 if ( LogTest( LDAP_DEBUG_TRACE ) ) { 669 trace_remoteauth_parameters( ap ); 670 } 671 672 if ( op->orb_method != LDAP_AUTH_SIMPLE ) 673 return SLAP_CB_CONTINUE; /* only do password auth */ 674 675 /* Can't handle root via this mechanism */ 676 if ( be_isroot_dn( op->o_bd, &op->o_req_ndn ) ) return SLAP_CB_CONTINUE; 677 678 if ( !ap->up_set ) { 679 const char *txt = NULL; 680 681 if ( slap_str2ad( UP_STR, &ap->up_ad, &txt ) ) 682 Debug( LDAP_DEBUG_TRACE, "remoteauth_bind: " 683 "userPassword attr undefined: %s\n", 684 txt ? txt : "" ); 685 ap->up_set = 1; 686 } 687 688 if ( !ap->up_ad ) { 689 Debug( LDAP_DEBUG_TRACE, "remoteauth_bind: " 690 "password attribute not configured\n" ); 691 return SLAP_CB_CONTINUE; /* userPassword not defined */ 692 } 693 694 if ( !ap->dn ) { 695 Debug( LDAP_DEBUG_TRACE, "remoteauth_bind: " 696 "remote DN attribute not configured\n" ); 697 return SLAP_CB_CONTINUE; /* no mapped DN attribute */ 698 } 699 700 if ( !ap->domain_attr ) { 701 Debug( LDAP_DEBUG_TRACE, "remoteauth_bind: " 702 "domain attribute not configured\n" ); 703 return SLAP_CB_CONTINUE; /* no way to know domain */ 704 } 705 706 op->o_bd->bd_info = (BackendInfo *)on->on_info; 707 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); 708 if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE; 709 710 rc = SLAP_CB_CONTINUE; 711 /* if userPassword is defined in entry, skip to the end */ 712 if ( attr_find( e->e_attrs, ap->up_ad ) ) { 713 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 714 "user has a password, skipping\n", 715 op->o_log_prefix ); 716 goto exit; 717 } 718 719 a_dom = attr_find( e->e_attrs, ap->domain_ad ); 720 if ( !a_dom ) 721 dom_val = ap->default_domain; 722 else { 723 dom_val = a_dom->a_vals[0].bv_val; 724 } 725 726 if ( !dom_val ) { 727 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 728 "user has no domain nor do we have a default, skipping\n", 729 op->o_log_prefix ); 730 goto exit; /* user has no domain */ 731 } 732 733 realm = get_realm( dom_val, ap->mappings, ap->default_realm, &isfile ); 734 if ( !realm ) goto exit; 735 736 a_dn = attr_find( e->e_attrs, ap->dn_ad ); 737 if ( !a_dn ) { 738 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 739 "no remote DN found on user\n", 740 op->o_log_prefix ); 741 goto exit; /* user has no DN for the other directory */ 742 } 743 744 ber_dupbv_x( &dn, a_dn->a_vals, op->o_tmpmemctx ); 745 be_entry_release_r( op, e ); 746 e = NULL; 747 748 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 749 "(realm, dn) = (%s, %s)\n", 750 op->o_log_prefix, realm, dn.bv_val ); 751 752 ldap_url = get_ldap_url( realm, isfile ); 753 if ( !ldap_url ) { 754 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 755 "No LDAP URL obtained\n", 756 op->o_log_prefix ); 757 goto exit; 758 } 759 760 retry: 761 rc = ldap_initialize( &ld, ldap_url ); 762 if ( rc ) { 763 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 764 "Cannot initialize %s: %s\n", 765 op->o_log_prefix, ldap_url, ldap_err2string( rc ) ); 766 goto exit; /* user has no DN for the other directory */ 767 } 768 769 ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &protocol ); 770 771 #ifdef HAVE_TLS 772 rc = bindconf_tls_set( &ap->ad_tls, ld ); 773 if ( rc ) { 774 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 775 "bindconf_tls_set failed\n", 776 op->o_log_prefix ); 777 goto exit; 778 } 779 780 if ( ap->pins ) { 781 if ( (rc = ldap_set_option( ld, LDAP_OPT_CONNECT_CB, &ad_conncb )) != 782 LDAP_SUCCESS ) { 783 goto exit; 784 } 785 } 786 787 if ( (rc = ldap_connect( ld )) != LDAP_SUCCESS ) { 788 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 789 "Cannot connect to %s: %s\n", 790 op->o_log_prefix, ldap_url, ldap_err2string( rc ) ); 791 goto exit; 792 } 793 794 if ( ap->ad_tls.sb_tls && !ldap_tls_inplace( ld ) ) { 795 if ( (rc = ldap_start_tls_s( ld, NULL, NULL )) != LDAP_SUCCESS ) { 796 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 797 "LDAP TLS failed %s: %s\n", 798 op->o_log_prefix, ldap_url, ldap_err2string( rc ) ); 799 goto exit; 800 } 801 } 802 803 #endif /* HAVE_TLS */ 804 805 rc = ldap_sasl_bind_s( ld, dn.bv_val, LDAP_SASL_SIMPLE, 806 &op->oq_bind.rb_cred, NULL, NULL, NULL ); 807 if ( rc == LDAP_SUCCESS ) { 808 if ( ap->store_on_success ) { 809 const char *txt; 810 811 Operation op2 = *op; 812 SlapReply r2 = { REP_RESULT }; 813 slap_callback cb = { NULL, slap_null_cb, NULL, NULL }; 814 Modifications m = {}; 815 816 op2.o_tag = LDAP_REQ_MODIFY; 817 op2.o_callback = &cb; 818 op2.orm_modlist = &m; 819 op2.orm_no_opattrs = 0; 820 op2.o_dn = op->o_bd->be_rootdn; 821 op2.o_ndn = op->o_bd->be_rootndn; 822 823 m.sml_op = LDAP_MOD_ADD; 824 m.sml_flags = 0; 825 m.sml_next = NULL; 826 m.sml_type = ap->up_ad->ad_cname; 827 m.sml_desc = ap->up_ad; 828 m.sml_numvals = 1; 829 m.sml_values = op->o_tmpcalloc( 830 sizeof(struct berval), 2, op->o_tmpmemctx ); 831 832 slap_passwd_hash( &op->oq_bind.rb_cred, &m.sml_values[0], &txt ); 833 if ( m.sml_values[0].bv_val == NULL ) { 834 Debug( LDAP_DEBUG_ANY, "%s remoteauth_bind: " 835 "password hashing for '%s' failed, storing password in " 836 "plain text\n", 837 op->o_log_prefix, op->o_req_dn.bv_val ); 838 ber_dupbv( &m.sml_values[0], &op->oq_bind.rb_cred ); 839 } 840 841 /* 842 * If this server is a shadow use the frontend to perform this 843 * modify. That will trigger the update referral, which can then be 844 * forwarded by the chain overlay. Obviously the updateref and 845 * chain overlay must be configured appropriately for this to be 846 * useful. 847 */ 848 if ( SLAP_SHADOW(op->o_bd) ) { 849 op2.o_bd = frontendDB; 850 } else { 851 op2.o_bd->bd_info = (BackendInfo *)on->on_info; 852 } 853 854 if ( op2.o_bd->be_modify( &op2, &r2 ) != LDAP_SUCCESS ) { 855 Debug( LDAP_DEBUG_ANY, "%s remoteauth_bind: " 856 "attempt to store password in entry '%s' failed, " 857 "ignoring\n", 858 op->o_log_prefix, op->o_req_dn.bv_val ); 859 } 860 ch_free( m.sml_values[0].bv_val ); 861 } 862 goto exit; 863 } 864 865 if ( rc == LDAP_INVALID_CREDENTIALS ) { 866 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 867 "ldap_sasl_bind_s (%s) failed: invalid credentials\n", 868 op->o_log_prefix, ldap_url ); 869 goto exit; 870 } 871 872 if ( tries < ap->retry_count ) { 873 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 874 "ldap_sasl_bind_s failed %s: %s (try #%d)\n", 875 op->o_log_prefix, ldap_url, ldap_err2string( rc ), tries ); 876 if ( ld ) ldap_unbind_ext_s( ld, NULL, NULL ); 877 tries++; 878 goto retry; 879 } else 880 goto exit; 881 882 exit: 883 if ( dn.bv_val ) { 884 op->o_tmpfree( dn.bv_val, op->o_tmpmemctx ); 885 } 886 if ( e ) { 887 be_entry_release_r( op, e ); 888 } 889 if ( ld ) ldap_unbind_ext_s( ld, NULL, NULL ); 890 if ( ldap_url ) ch_free( ldap_url ); 891 if ( realm ) ch_free( realm ); 892 if ( rc == SLAP_CB_CONTINUE ) { 893 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 894 "continue\n", op->o_log_prefix ); 895 return rc; 896 } else { 897 /* for rc == 0, frontend sends result */ 898 if ( rc ) { 899 if ( rc > 0 ) { 900 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 901 "failed\n", op->o_log_prefix ); 902 send_ldap_error( op, rs, rc, "remoteauth_bind failed" ); 903 } else { 904 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 905 "operations error\n", op->o_log_prefix ); 906 send_ldap_error( op, rs, LDAP_OPERATIONS_ERROR, 907 "remoteauth_bind operations error" ); 908 } 909 } 910 911 return rs->sr_err; 912 } 913 } 914 915 static int 916 remoteauth_db_init( BackendDB *be, ConfigReply *cr ) 917 { 918 slap_overinst *on = (slap_overinst *)be->bd_info; 919 ad_private *ap; 920 921 if ( SLAP_ISGLOBALOVERLAY(be) ) { 922 Debug( LDAP_DEBUG_ANY, "remoteauth_db_init: " 923 "remoteauth overlay must be instantiated within a " 924 "database.\n" ); 925 return 1; 926 } 927 928 ap = ch_calloc( 1, sizeof(ad_private) ); 929 930 ap->dn = NULL; 931 ap->dn_ad = NULL; 932 ap->domain_attr = NULL; 933 ap->domain_ad = NULL; 934 935 ap->up_ad = NULL; 936 ap->mappings = NULL; 937 938 ap->default_realm = NULL; 939 ap->default_domain = NULL; 940 941 ap->pins = NULL; 942 943 ap->up_set = 0; 944 ap->retry_count = 3; 945 946 on->on_bi.bi_private = ap; 947 948 return LDAP_SUCCESS; 949 } 950 951 static int 952 remoteauth_db_destroy( BackendDB *be, ConfigReply *cr ) 953 { 954 slap_overinst *on = (slap_overinst *)be->bd_info; 955 ad_private *ap = (ad_private *)on->on_bi.bi_private; 956 ad_info *ai = ap->mappings; 957 958 while ( ai ) { 959 ad_info *next = ai->next; 960 961 if ( ai->domain ) ch_free( ai->domain ); 962 if ( ai->realm ) ch_free( ai->realm ); 963 964 ch_free( ai ); 965 ai = next; 966 } 967 968 if ( ap->dn ) ch_free( ap->dn ); 969 if ( ap->default_domain ) ch_free( ap->default_domain ); 970 if ( ap->default_realm ) ch_free( ap->default_realm ); 971 if ( ap->domain_attr ) ch_free( ap->domain_attr ); 972 973 bindconf_free( &ap->ad_tls ); 974 975 ch_free( ap ); 976 977 return 0; 978 } 979 980 static slap_overinst remoteauth; 981 982 int 983 remoteauth_initialize( void ) 984 { 985 int rc; 986 987 remoteauth.on_bi.bi_type = "remoteauth"; 988 remoteauth.on_bi.bi_flags = SLAPO_BFLAG_SINGLE; 989 990 remoteauth.on_bi.bi_cf_ocs = remoteauthocs; 991 rc = config_register_schema( remoteauthcfg, remoteauthocs ); 992 if ( rc ) return rc; 993 994 remoteauth.on_bi.bi_db_init = remoteauth_db_init; 995 remoteauth.on_bi.bi_db_destroy = remoteauth_db_destroy; 996 remoteauth.on_bi.bi_op_bind = remoteauth_bind; 997 998 return overlay_register( &remoteauth ); 999 } 1000 1001 #if SLAPD_OVER_ACCESSLOG == SLAPD_MOD_DYNAMIC 1002 int 1003 init_module( int argc, char *argv[] ) 1004 { 1005 return remoteauth_initialize(); 1006 } 1007 #endif 1008