xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/overlays/remoteauth.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
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
remoteauth_cf_gen(ConfigArgs * c)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 *
get_realm(const char * domain,ad_info * mappings,const char * default_realm,int * isfile)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 *
get_ldap_url(const char * realm,int isfile)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
trace_remoteauth_parameters(ad_private * ap)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
remoteauth_conn_cb(LDAP * ld,Sockbuf * sb,LDAPURLDesc * srv,struct sockaddr * addr,struct ldap_conncb * ctx)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
remoteauth_conn_delcb(LDAP * ld,Sockbuf * sb,struct ldap_conncb * ctx)644 remoteauth_conn_delcb( LDAP *ld, Sockbuf *sb, struct ldap_conncb *ctx )
645 {
646 	return;
647 }
648 
649 static int
remoteauth_bind(Operation * op,SlapReply * rs)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
remoteauth_db_init(BackendDB * be,ConfigReply * cr)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
remoteauth_db_destroy(BackendDB * be,ConfigReply * cr)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
remoteauth_initialize(void)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
init_module(int argc,char * argv[])997 init_module( int argc, char *argv[] )
998 {
999 	return remoteauth_initialize();
1000 }
1001 #endif
1002