Home | History | Annotate | Line # | Download | only in overlays
remoteauth.c revision 1.1
      1 /*	$NetBSD: remoteauth.c,v 1.1 2021/08/14 16:05:24 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 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.1 2021/08/14 16:05:24 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