Home | History | Annotate | Line # | Download | only in overlays
      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