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