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