1 /* $NetBSD: remoteauth.c,v 1.2 2021/08/14 16:15:02 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-2021 The OpenLDAP Foundation. 8 * Portions Copyright 2017-2021 Ondřej Kuzník, 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.2 2021/08/14 16:15:02 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, strlen( str ), 1, &bv ); 213 ch_free( str ); 214 rc = value_add_one( &c->rvalue_vals, &bv ); 215 if ( rc ) return rc; 216 rc = value_add_one( &c->rvalue_nvals, &bv ); 217 if ( rc ) return rc; 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, 1, &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, 1, &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 break; 255 case REMOTE_AUTH_TLS_PIN: { 256 ad_pin *pin = ad->pins; 257 for ( pin = ad->pins; pin; pin = pin->next ) { 258 bv.bv_val = ch_malloc( strlen( pin->hostname ) + 259 strlen( pin->pin ) + 2 ); 260 bv.bv_len = sprintf( 261 bv.bv_val, "%s %s", pin->hostname, pin->pin ); 262 rc = value_add_one( &c->rvalue_vals, &bv ); 263 if ( rc ) return rc; 264 rc = value_add_one( &c->rvalue_nvals, &bv ); 265 if ( rc ) return rc; 266 } 267 } break; 268 269 default: 270 abort(); 271 } 272 break; 273 case LDAP_MOD_DELETE: 274 switch ( c->type ) { 275 case REMOTE_AUTH_MAPPING: 276 if ( c->valx < 0 ) { 277 /* delete all mappings */ 278 while ( ad->mappings ) { 279 map = ad->mappings; 280 ad->mappings = ad->mappings->next; 281 ch_free( map->domain ); 282 ch_free( map->realm ); 283 ch_free( map ); 284 } 285 } else { 286 /* delete a specific mapping indicated by 'valx'*/ 287 ad_info *pmap = NULL; 288 289 for ( map = ad->mappings, i = 0; 290 ( map ) && ( i < c->valx ); 291 pmap = map, map = map->next, i++ ) 292 ; 293 294 if ( pmap ) { 295 pmap->next = map->next; 296 map->next = NULL; 297 298 ch_free( map->domain ); 299 ch_free( map->realm ); 300 ch_free( map ); 301 } else if ( ad->mappings ) { 302 /* delete the first item in the list */ 303 map = ad->mappings; 304 ad->mappings = map->next; 305 ch_free( map->domain ); 306 ch_free( map->realm ); 307 ch_free( map ); 308 } 309 } 310 break; 311 case REMOTE_AUTH_DN_ATTRIBUTE: 312 if ( ad->dn ) { 313 ch_free( ad->dn ); 314 ad->dn = NULL; /* Don't free AttributeDescription */ 315 } 316 break; 317 case REMOTE_AUTH_DOMAIN_ATTRIBUTE: 318 if ( ad->domain_attr ) { 319 ch_free( ad->domain_attr ); 320 /* Don't free AttributeDescription */ 321 ad->domain_attr = NULL; 322 } 323 break; 324 case REMOTE_AUTH_DEFAULT_DOMAIN: 325 if ( ad->default_domain ) { 326 ch_free( ad->default_domain ); 327 ad->default_domain = NULL; 328 } 329 break; 330 case REMOTE_AUTH_DEFAULT_REALM: 331 if ( ad->default_realm ) { 332 ch_free( ad->default_realm ); 333 ad->default_realm = NULL; 334 } 335 break; 336 case REMOTE_AUTH_TLS: 337 /* MUST + SINGLE-VALUE -> this is a replace */ 338 bindconf_free( &ad->ad_tls ); 339 break; 340 case REMOTE_AUTH_TLS_PIN: 341 while ( ad->pins ) { 342 ad_pin *pin = ad->pins; 343 ad->pins = ad->pins->next; 344 ch_free( pin->hostname ); 345 ch_free( pin->pin ); 346 ch_free( pin ); 347 } 348 break; 349 /* ARG_OFFSET */ 350 case REMOTE_AUTH_STORE_ON_SUCCESS: 351 case REMOTE_AUTH_RETRY_COUNT: 352 abort(); 353 break; 354 default: 355 abort(); 356 } 357 break; 358 case SLAP_CONFIG_ADD: 359 case LDAP_MOD_ADD: 360 switch ( c->type ) { 361 case REMOTE_AUTH_MAPPING: 362 /* add mapping to head of list */ 363 map = ch_malloc( sizeof(ad_info) ); 364 map->domain = ber_strdup( c->argv[1] ); 365 map->realm = ber_strdup( c->argv[2] ); 366 map->next = ad->mappings; 367 ad->mappings = map; 368 369 break; 370 case REMOTE_AUTH_DN_ATTRIBUTE: 371 if ( slap_str2ad( c->argv[1], &ad->dn_ad, &text ) == 372 LDAP_SUCCESS ) { 373 ad->dn = ber_strdup( ad->dn_ad->ad_cname.bv_val ); 374 } else { 375 strncpy( c->cr_msg, text, sizeof(c->cr_msg) ); 376 c->cr_msg[sizeof(c->cr_msg) - 1] = '\0'; 377 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg ); 378 rc = ARG_BAD_CONF; 379 } 380 break; 381 case REMOTE_AUTH_DOMAIN_ATTRIBUTE: 382 if ( slap_str2ad( c->argv[1], &ad->domain_ad, &text ) == 383 LDAP_SUCCESS ) { 384 ad->domain_attr = 385 ber_strdup( ad->domain_ad->ad_cname.bv_val ); 386 } else { 387 strncpy( c->cr_msg, text, sizeof(c->cr_msg) ); 388 c->cr_msg[sizeof(c->cr_msg) - 1] = '\0'; 389 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg ); 390 rc = ARG_BAD_CONF; 391 } 392 break; 393 case REMOTE_AUTH_DEFAULT_DOMAIN: 394 if ( ad->default_domain ) { 395 ch_free( ad->default_domain ); 396 ad->default_domain = NULL; 397 } 398 ad->default_domain = ber_strdup( c->argv[1] ); 399 break; 400 case REMOTE_AUTH_DEFAULT_REALM: 401 if ( ad->default_realm ) { 402 ch_free( ad->default_realm ); 403 ad->default_realm = NULL; 404 } 405 ad->default_realm = ber_strdup( c->argv[1] ); 406 break; 407 case REMOTE_AUTH_TLS: 408 for ( i=1; i < c->argc; i++ ) { 409 if ( bindconf_tls_parse( c->argv[i], &ad->ad_tls ) ) { 410 rc = 1; 411 break; 412 } 413 } 414 bindconf_tls_defaults( &ad->ad_tls ); 415 break; 416 case REMOTE_AUTH_TLS_PIN: { 417 ad_pin *pin = ch_calloc( 1, sizeof(ad_pin) ); 418 419 pin->hostname = ber_strdup( c->argv[1] ); 420 pin->pin = ber_strdup( c->argv[2] ); 421 pin->next = ad->pins; 422 ad->pins = pin; 423 } break; 424 /* ARG_OFFSET */ 425 case REMOTE_AUTH_STORE_ON_SUCCESS: 426 case REMOTE_AUTH_RETRY_COUNT: 427 abort(); 428 break; 429 default: 430 abort(); 431 } 432 break; 433 default: 434 abort(); 435 } 436 437 return rc; 438 } 439 440 static char * 441 get_realm( 442 const char *domain, 443 ad_info *mappings, 444 const char *default_realm, 445 int *isfile ) 446 { 447 ad_info *ai; 448 char *dom = NULL, *ch, *ret = NULL; 449 450 if ( isfile ) *isfile = 0; 451 452 if ( !domain ) { 453 ret = default_realm ? ch_strdup( default_realm ) : NULL; 454 goto exit; 455 } 456 457 /* munge any DOMAIN\user or DOMAIN:user values into just DOMAIN */ 458 459 ch = strchr( domain, '\\' ); 460 if ( !ch ) ch = strchr( domain, ':' ); 461 462 if ( ch ) { 463 dom = ch_malloc( ch - domain + 1 ); 464 strncpy( dom, domain, ch - domain ); 465 dom[ch - domain] = '\0'; 466 } else { 467 dom = ch_strdup( domain ); 468 } 469 470 for ( ai = mappings; ai; ai = ai->next ) 471 if ( strcasecmp( ai->domain, dom ) == 0 ) { 472 ret = ch_strdup( ai->realm ); 473 break; 474 } 475 476 if ( !ai ) 477 ret = default_realm ? ch_strdup( default_realm ) : 478 NULL; /* no mapping found */ 479 exit: 480 if ( dom ) ch_free( dom ); 481 if ( ret && 482 ( strncasecmp( ret, FILE_PREFIX, strlen( FILE_PREFIX ) ) == 0 ) ) { 483 char *p; 484 485 p = ret; 486 ret = ch_strdup( p + strlen( FILE_PREFIX ) ); 487 ch_free( p ); 488 if ( isfile ) *isfile = 1; 489 } 490 491 return ret; 492 } 493 494 static char * 495 get_ldap_url( const char *realm, int isfile ) 496 { 497 char *ldap_url = NULL; 498 FILE *fp; 499 500 if ( !realm ) return NULL; 501 502 if ( !isfile ) { 503 if ( strstr( realm, "://" ) ) { 504 return ch_strdup( realm ); 505 } 506 507 ldap_url = ch_malloc( 1 + strlen( LDAP_PREFIX ) + strlen( realm ) ); 508 sprintf( ldap_url, "%s%s", LDAP_PREFIX, realm ); 509 return ldap_url; 510 } 511 512 fp = fopen( realm, "r" ); 513 if ( !fp ) { 514 char ebuf[128]; 515 int saved_errno = errno; 516 Debug( LDAP_DEBUG_TRACE, "remoteauth: " 517 "Unable to open realm file (%s)\n", 518 sock_errstr( saved_errno, ebuf, sizeof(ebuf) ) ); 519 return NULL; 520 } 521 /* 522 * Read each line in the file and return a URL of the form 523 * "ldap://<line1> ldap://<line2> ... ldap://<lineN>" 524 * which can be passed to ldap_initialize. 525 */ 526 while ( !feof( fp ) ) { 527 char line[512], *p; 528 529 p = fgets( line, sizeof(line), fp ); 530 if ( !p ) continue; 531 532 /* terminate line at first whitespace */ 533 for ( p = line; *p; p++ ) 534 if ( isspace( *p ) ) { 535 *p = '\0'; 536 break; 537 } 538 539 if ( ldap_url ) { 540 char *nu; 541 542 nu = ch_malloc( strlen( ldap_url ) + 2 + strlen( LDAP_PREFIX ) + 543 strlen( line ) ); 544 545 if ( strstr( line, "://" ) ) { 546 sprintf( nu, "%s %s", ldap_url, line ); 547 } else { 548 sprintf( nu, "%s %s%s", ldap_url, LDAP_PREFIX, line ); 549 } 550 ch_free( ldap_url ); 551 ldap_url = nu; 552 } else { 553 ldap_url = ch_malloc( 1 + strlen( line ) + strlen( LDAP_PREFIX ) ); 554 if ( strstr( line, "://" ) ) { 555 strcpy( ldap_url, line ); 556 } else { 557 sprintf( ldap_url, "%s%s", LDAP_PREFIX, line ); 558 } 559 } 560 } 561 562 fclose( fp ); 563 564 return ldap_url; 565 } 566 567 static void 568 trace_remoteauth_parameters( ad_private *ap ) 569 { 570 ad_info *pad_info; 571 struct berval bv; 572 573 if ( !ap ) return; 574 575 Debug( LDAP_DEBUG_TRACE, "remoteauth_dn_attribute: %s\n", 576 ap->dn ? ap->dn : "NULL" ); 577 578 Debug( LDAP_DEBUG_TRACE, "remoteauth_domain_attribute: %s\n", 579 ap->domain_attr ? ap->domain_attr : "NULL" ); 580 581 Debug( LDAP_DEBUG_TRACE, "remoteauth_default_realm: %s\n", 582 ap->default_realm ? ap->default_realm : "NULL" ); 583 584 Debug( LDAP_DEBUG_TRACE, "remoteauth_default_domain: %s\n", 585 ap->default_domain ? ap->default_domain : "NULL" ); 586 587 Debug( LDAP_DEBUG_TRACE, "remoteauth_retry_count: %d\n", ap->retry_count ); 588 589 bindconf_tls_unparse( &ap->ad_tls, &bv ); 590 Debug( LDAP_DEBUG_TRACE, "remoteauth_tls:%s\n", bv.bv_val ); 591 ch_free( bv.bv_val ); 592 593 pad_info = ap->mappings; 594 while ( pad_info ) { 595 Debug( LDAP_DEBUG_TRACE, "remoteauth_mappings(%s,%s)\n", 596 pad_info->domain ? pad_info->domain : "NULL", 597 pad_info->realm ? pad_info->realm : "NULL" ); 598 pad_info = pad_info->next; 599 } 600 601 return; 602 } 603 604 static int 605 remoteauth_conn_cb( 606 LDAP *ld, 607 Sockbuf *sb, 608 LDAPURLDesc *srv, 609 struct sockaddr *addr, 610 struct ldap_conncb *ctx ) 611 { 612 ad_private *ap = ctx->lc_arg; 613 ad_pin *pin = NULL; 614 char *host; 615 616 host = srv->lud_host; 617 if ( !host || !*host ) { 618 host = "localhost"; 619 } 620 621 for ( pin = ap->pins; pin; pin = pin->next ) { 622 if ( !strcasecmp( host, pin->hostname ) ) break; 623 } 624 625 if ( pin ) { 626 int rc = ldap_set_option( ld, LDAP_OPT_X_TLS_PEERKEY_HASH, pin->pin ); 627 if ( rc == LDAP_SUCCESS ) { 628 return 0; 629 } 630 631 Debug( LDAP_DEBUG_TRACE, "remoteauth_conn_cb: " 632 "TLS Peerkey hash could not be set to '%s': %d\n", 633 pin->pin, rc ); 634 } else { 635 Debug( LDAP_DEBUG_TRACE, "remoteauth_conn_cb: " 636 "No TLS Peerkey hash found for host '%s'\n", 637 host ); 638 } 639 640 return -1; 641 } 642 643 static void 644 remoteauth_conn_delcb( LDAP *ld, Sockbuf *sb, struct ldap_conncb *ctx ) 645 { 646 return; 647 } 648 649 static int 650 remoteauth_bind( Operation *op, SlapReply *rs ) 651 { 652 Entry *e; 653 int rc; 654 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 655 ad_private *ap = (ad_private *)on->on_bi.bi_private; 656 Attribute *a_dom, *a_dn; 657 struct ldap_conncb ad_conncb = { .lc_add = remoteauth_conn_cb, 658 .lc_del = remoteauth_conn_delcb, 659 .lc_arg = ap }; 660 struct berval dn = { 0 }; 661 char *dom_val, *realm = NULL; 662 char *ldap_url = NULL; 663 LDAP *ld = NULL; 664 int protocol = LDAP_VERSION3, isfile = 0; 665 int tries = 0; 666 667 if ( LogTest( LDAP_DEBUG_TRACE ) ) { 668 trace_remoteauth_parameters( ap ); 669 } 670 671 if ( op->orb_method != LDAP_AUTH_SIMPLE ) 672 return SLAP_CB_CONTINUE; /* only do password auth */ 673 674 /* Can't handle root via this mechanism */ 675 if ( be_isroot_dn( op->o_bd, &op->o_req_ndn ) ) return SLAP_CB_CONTINUE; 676 677 if ( !ap->up_set ) { 678 const char *txt = NULL; 679 680 if ( slap_str2ad( UP_STR, &ap->up_ad, &txt ) ) 681 Debug( LDAP_DEBUG_TRACE, "remoteauth_bind: " 682 "userPassword attr undefined: %s\n", 683 txt ? txt : "" ); 684 ap->up_set = 1; 685 } 686 687 if ( !ap->up_ad ) { 688 Debug( LDAP_DEBUG_TRACE, "remoteauth_bind: " 689 "password attribute not configured\n" ); 690 return SLAP_CB_CONTINUE; /* userPassword not defined */ 691 } 692 693 if ( !ap->dn ) { 694 Debug( LDAP_DEBUG_TRACE, "remoteauth_bind: " 695 "remote DN attribute not configured\n" ); 696 return SLAP_CB_CONTINUE; /* no mapped DN attribute */ 697 } 698 699 if ( !ap->domain_attr ) { 700 Debug( LDAP_DEBUG_TRACE, "remoteauth_bind: " 701 "domain attribute not configured\n" ); 702 return SLAP_CB_CONTINUE; /* no way to know domain */ 703 } 704 705 op->o_bd->bd_info = (BackendInfo *)on->on_info; 706 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e ); 707 if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE; 708 709 rc = SLAP_CB_CONTINUE; 710 /* if userPassword is defined in entry, skip to the end */ 711 if ( attr_find( e->e_attrs, ap->up_ad ) ) { 712 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 713 "user has a password, skipping\n", 714 op->o_log_prefix ); 715 goto exit; 716 } 717 718 a_dom = attr_find( e->e_attrs, ap->domain_ad ); 719 if ( !a_dom ) 720 dom_val = ap->default_domain; 721 else { 722 dom_val = a_dom->a_vals[0].bv_val; 723 } 724 725 if ( !dom_val ) { 726 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 727 "user has no domain nor do we have a default, skipping\n", 728 op->o_log_prefix ); 729 goto exit; /* user has no domain */ 730 } 731 732 realm = get_realm( dom_val, ap->mappings, ap->default_realm, &isfile ); 733 if ( !realm ) goto exit; 734 735 a_dn = attr_find( e->e_attrs, ap->dn_ad ); 736 if ( !a_dn ) { 737 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 738 "no remote DN found on user\n", 739 op->o_log_prefix ); 740 goto exit; /* user has no DN for the other directory */ 741 } 742 743 ber_dupbv_x( &dn, a_dn->a_vals, op->o_tmpmemctx ); 744 be_entry_release_r( op, e ); 745 e = NULL; 746 747 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 748 "(realm, dn) = (%s, %s)\n", 749 op->o_log_prefix, realm, dn.bv_val ); 750 751 ldap_url = get_ldap_url( realm, isfile ); 752 if ( !ldap_url ) { 753 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 754 "No LDAP URL obtained\n", 755 op->o_log_prefix ); 756 goto exit; 757 } 758 759 retry: 760 rc = ldap_initialize( &ld, ldap_url ); 761 if ( rc ) { 762 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 763 "Cannot initialize %s: %s\n", 764 op->o_log_prefix, ldap_url, ldap_err2string( rc ) ); 765 goto exit; /* user has no DN for the other directory */ 766 } 767 768 ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &protocol ); 769 770 #ifdef HAVE_TLS 771 rc = bindconf_tls_set( &ap->ad_tls, ld ); 772 if ( rc ) { 773 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 774 "bindconf_tls_set failed\n", 775 op->o_log_prefix ); 776 goto exit; 777 } 778 779 if ( ap->pins ) { 780 if ( (rc = ldap_set_option( ld, LDAP_OPT_CONNECT_CB, &ad_conncb )) != 781 LDAP_SUCCESS ) { 782 goto exit; 783 } 784 } 785 786 if ( (rc = ldap_connect( ld )) != LDAP_SUCCESS ) { 787 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 788 "Cannot connect to %s: %s\n", 789 op->o_log_prefix, ldap_url, ldap_err2string( rc ) ); 790 goto exit; 791 } 792 793 if ( ap->ad_tls.sb_tls && !ldap_tls_inplace( ld ) ) { 794 if ( (rc = ldap_start_tls_s( ld, NULL, NULL )) != LDAP_SUCCESS ) { 795 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 796 "LDAP TLS failed %s: %s\n", 797 op->o_log_prefix, ldap_url, ldap_err2string( rc ) ); 798 goto exit; 799 } 800 } 801 802 #endif /* HAVE_TLS */ 803 804 rc = ldap_sasl_bind_s( ld, dn.bv_val, LDAP_SASL_SIMPLE, 805 &op->oq_bind.rb_cred, NULL, NULL, NULL ); 806 if ( rc == LDAP_SUCCESS ) { 807 if ( ap->store_on_success ) { 808 const char *txt; 809 810 Operation op2 = *op; 811 SlapReply r2 = { REP_RESULT }; 812 slap_callback cb = { NULL, slap_null_cb, NULL, NULL }; 813 Modifications m = {}; 814 815 op2.o_tag = LDAP_REQ_MODIFY; 816 op2.o_callback = &cb; 817 op2.orm_modlist = &m; 818 op2.orm_no_opattrs = 0; 819 op2.o_dn = op->o_bd->be_rootdn; 820 op2.o_ndn = op->o_bd->be_rootndn; 821 822 m.sml_op = LDAP_MOD_ADD; 823 m.sml_flags = 0; 824 m.sml_next = NULL; 825 m.sml_type = ap->up_ad->ad_cname; 826 m.sml_desc = ap->up_ad; 827 m.sml_numvals = 1; 828 m.sml_values = op->o_tmpcalloc( 829 sizeof(struct berval), 2, op->o_tmpmemctx ); 830 831 slap_passwd_hash( &op->oq_bind.rb_cred, &m.sml_values[0], &txt ); 832 if ( m.sml_values[0].bv_val == NULL ) { 833 Debug( LDAP_DEBUG_ANY, "%s remoteauth_bind: " 834 "password hashing for '%s' failed, storing password in " 835 "plain text\n", 836 op->o_log_prefix, op->o_req_dn.bv_val ); 837 ber_dupbv( &m.sml_values[0], &op->oq_bind.rb_cred ); 838 } 839 840 /* 841 * If this server is a shadow use the frontend to perform this 842 * modify. That will trigger the update referral, which can then be 843 * forwarded by the chain overlay. Obviously the updateref and 844 * chain overlay must be configured appropriately for this to be 845 * useful. 846 */ 847 if ( SLAP_SHADOW(op->o_bd) ) { 848 op2.o_bd = frontendDB; 849 } else { 850 op2.o_bd->bd_info = (BackendInfo *)on->on_info; 851 } 852 853 if ( op2.o_bd->be_modify( &op2, &r2 ) != LDAP_SUCCESS ) { 854 Debug( LDAP_DEBUG_ANY, "%s remoteauth_bind: " 855 "attempt to store password in entry '%s' failed, " 856 "ignoring\n", 857 op->o_log_prefix, op->o_req_dn.bv_val ); 858 } 859 ch_free( m.sml_values[0].bv_val ); 860 } 861 goto exit; 862 } 863 864 if ( rc == LDAP_INVALID_CREDENTIALS ) { 865 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 866 "ldap_sasl_bind_s (%s) failed: invalid credentials\n", 867 op->o_log_prefix, ldap_url ); 868 goto exit; 869 } 870 871 if ( tries < ap->retry_count ) { 872 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 873 "ldap_sasl_bind_s failed %s: %s (try #%d)\n", 874 op->o_log_prefix, ldap_url, ldap_err2string( rc ), tries ); 875 if ( ld ) ldap_unbind_ext_s( ld, NULL, NULL ); 876 tries++; 877 goto retry; 878 } else 879 goto exit; 880 881 exit: 882 if ( dn.bv_val ) { 883 op->o_tmpfree( dn.bv_val, op->o_tmpmemctx ); 884 } 885 if ( e ) { 886 be_entry_release_r( op, e ); 887 } 888 if ( ld ) ldap_unbind_ext_s( ld, NULL, NULL ); 889 if ( ldap_url ) ch_free( ldap_url ); 890 if ( realm ) ch_free( realm ); 891 if ( rc == SLAP_CB_CONTINUE ) { 892 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 893 "continue\n", op->o_log_prefix ); 894 return rc; 895 } else { 896 /* for rc == 0, frontend sends result */ 897 if ( rc ) { 898 if ( rc > 0 ) { 899 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 900 "failed\n", op->o_log_prefix ); 901 send_ldap_error( op, rs, rc, "remoteauth_bind failed" ); 902 } else { 903 Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: " 904 "operations error\n", op->o_log_prefix ); 905 send_ldap_error( op, rs, LDAP_OPERATIONS_ERROR, 906 "remoteauth_bind operations error" ); 907 } 908 } 909 910 return rs->sr_err; 911 } 912 } 913 914 static int 915 remoteauth_db_init( BackendDB *be, ConfigReply *cr ) 916 { 917 slap_overinst *on = (slap_overinst *)be->bd_info; 918 ad_private *ap; 919 920 if ( SLAP_ISGLOBALOVERLAY(be) ) { 921 Debug( LDAP_DEBUG_ANY, "remoteauth_db_init: " 922 "remoteauth overlay must be instantiated within a " 923 "database.\n" ); 924 return 1; 925 } 926 927 ap = ch_calloc( 1, sizeof(ad_private) ); 928 929 ap->dn = NULL; 930 ap->dn_ad = NULL; 931 ap->domain_attr = NULL; 932 ap->domain_ad = NULL; 933 934 ap->up_ad = NULL; 935 ap->mappings = NULL; 936 937 ap->default_realm = NULL; 938 ap->default_domain = NULL; 939 940 ap->pins = NULL; 941 942 ap->up_set = 0; 943 ap->retry_count = 3; 944 945 on->on_bi.bi_private = ap; 946 947 return LDAP_SUCCESS; 948 } 949 950 static int 951 remoteauth_db_destroy( BackendDB *be, ConfigReply *cr ) 952 { 953 slap_overinst *on = (slap_overinst *)be->bd_info; 954 ad_private *ap = (ad_private *)on->on_bi.bi_private; 955 ad_info *ai = ap->mappings; 956 957 while ( ai ) { 958 if ( ai->domain ) ch_free( ai->domain ); 959 if ( ai->realm ) ch_free( ai->realm ); 960 ai = ai->next; 961 } 962 963 if ( ap->dn ) ch_free( ap->dn ); 964 if ( ap->default_domain ) ch_free( ap->default_domain ); 965 if ( ap->default_realm ) ch_free( ap->default_realm ); 966 967 bindconf_free( &ap->ad_tls ); 968 969 ch_free( ap ); 970 971 return 0; 972 } 973 974 static slap_overinst remoteauth; 975 976 int 977 remoteauth_initialize( void ) 978 { 979 int rc; 980 981 remoteauth.on_bi.bi_type = "remoteauth"; 982 remoteauth.on_bi.bi_flags = SLAPO_BFLAG_SINGLE; 983 984 remoteauth.on_bi.bi_cf_ocs = remoteauthocs; 985 rc = config_register_schema( remoteauthcfg, remoteauthocs ); 986 if ( rc ) return rc; 987 988 remoteauth.on_bi.bi_db_init = remoteauth_db_init; 989 remoteauth.on_bi.bi_db_destroy = remoteauth_db_destroy; 990 remoteauth.on_bi.bi_op_bind = remoteauth_bind; 991 992 return overlay_register( &remoteauth ); 993 } 994 995 #if SLAPD_OVER_ACCESSLOG == SLAPD_MOD_DYNAMIC 996 int 997 init_module( int argc, char *argv[] ) 998 { 999 return remoteauth_initialize(); 1000 } 1001 #endif 1002