@@ -2368,45 +2368,95 @@ InitializeLDAPConnection(Port *port, LDAP **ldap)
2368
2368
}
2369
2369
#else
2370
2370
#ifdef HAVE_LDAP_INITIALIZE
2371
+
2372
+ /*
2373
+ * OpenLDAP provides a non-standard extension ldap_initialize() that takes
2374
+ * a list of URIs, allowing us to request "ldaps" instead of "ldap". It
2375
+ * also provides ldap_domain2hostlist() to find LDAP servers automatically
2376
+ * using DNS SRV. They were introduced in the same version, so for now we
2377
+ * don't have an extra configure check for the latter.
2378
+ */
2371
2379
{
2372
- const char * hostnames = port -> hba -> ldapserver ;
2373
- char * uris = NULL ;
2380
+ StringInfoData uris ;
2381
+ char * hostlist = NULL ;
2382
+ char * p ;
2383
+ bool append_port ;
2384
+
2385
+ /* We'll build a space-separated scheme://hostname:port list here */
2386
+ initStringInfo (& uris );
2374
2387
2375
2388
/*
2376
- * We have a space-separated list of hostnames. Convert it
2377
- * to a space-separated list of URIs.
2389
+ * If pg_hba.conf provided no hostnames, we can ask OpenLDAP to try to
2390
+ * find some by extracting a domain name from the base DN and looking
2391
+ * up DSN SRV records for _ldap._tcp.<domain>.
2378
2392
*/
2393
+ if (!port -> hba -> ldapserver || port -> hba -> ldapserver [0 ] == '\0' )
2394
+ {
2395
+ char * domain ;
2396
+
2397
+ /* ou=blah,dc=foo,dc=bar -> foo.bar */
2398
+ if (ldap_dn2domain (port -> hba -> ldapbasedn , & domain ))
2399
+ {
2400
+ ereport (LOG ,
2401
+ (errmsg ("could not extract domain name from ldapbasedn" )));
2402
+ return STATUS_ERROR ;
2403
+ }
2404
+
2405
+ /* Look up a list of LDAP server hosts and port numbers */
2406
+ if (ldap_domain2hostlist (domain , & hostlist ))
2407
+ {
2408
+ ereport (LOG ,
2409
+ (errmsg ("LDAP authentication could not find DNS SRV records for \"%s\"" ,
2410
+ domain ),
2411
+ (errhint ("Set an LDAP server name explicitly." ))));
2412
+ ldap_memfree (domain );
2413
+ return STATUS_ERROR ;
2414
+ }
2415
+ ldap_memfree (domain );
2416
+
2417
+ /* We have a space-separated list of host:port entries */
2418
+ p = hostlist ;
2419
+ append_port = false;
2420
+ }
2421
+ else
2422
+ {
2423
+ /* We have a space-separated list of hosts from pg_hba.conf */
2424
+ p = port -> hba -> ldapserver ;
2425
+ append_port = true;
2426
+ }
2427
+
2428
+ /* Convert the list of host[:port] entries to full URIs */
2379
2429
do
2380
2430
{
2381
- char * hostname ;
2382
- size_t hostname_size ;
2383
- char * new_uris ;
2384
-
2385
- /* Find the leading hostname. */
2386
- hostname_size = strcspn ( hostnames , " " );
2387
- hostname = pnstrdup ( hostnames , hostname_size );
2388
-
2389
- /* Append a URI for this hostname. */
2390
- new_uris = psprintf ( "%s%s%s ://%s:%d" ,
2391
- uris ? uris : "" ,
2392
- uris ? " " : "" ,
2393
- scheme ,
2394
- hostname ,
2395
- port -> hba -> ldapport );
2396
-
2397
- pfree ( hostname );
2398
- if ( uris )
2399
- pfree ( uris );
2400
- uris = new_uris ;
2401
-
2402
- /* Step over this hostname and any spaces. */
2403
- hostnames += hostname_size ;
2404
- while ( * hostnames == ' ' )
2405
- ++ hostnames ;
2406
- } while ( * hostnames );
2407
-
2408
- r = ldap_initialize (ldap , uris );
2409
- pfree (uris );
2431
+ size_t size ;
2432
+
2433
+ /* Find the span of the next entry */
2434
+ size = strcspn ( p , " " );
2435
+
2436
+ /* Append a space separator if this isn't the first URI */
2437
+ if ( uris . len > 0 )
2438
+ appendStringInfoChar ( & uris , ' ' );
2439
+
2440
+ /* Append scheme ://host:port */
2441
+ appendStringInfoString ( & uris , scheme );
2442
+ appendStringInfoString ( & uris , "://" );
2443
+ appendBinaryStringInfo ( & uris , p , size );
2444
+ if ( append_port )
2445
+ appendStringInfo ( & uris , ":%d" , port -> hba -> ldapport );
2446
+
2447
+ /* Step over this entry and any number of trailing spaces */
2448
+ p += size ;
2449
+ while ( * p == ' ' )
2450
+ ++ p ;
2451
+ } while ( * p );
2452
+
2453
+ /* Free memory from OpenLDAP if we looked up SRV records */
2454
+ if ( hostlist )
2455
+ ldap_memfree ( hostlist ) ;
2456
+
2457
+ /* Finally, try to connect using the URI list */
2458
+ r = ldap_initialize (ldap , uris . data );
2459
+ pfree (uris . data );
2410
2460
if (r != LDAP_SUCCESS )
2411
2461
{
2412
2462
ereport (LOG ,
@@ -2552,13 +2602,35 @@ CheckLDAPAuth(Port *port)
2552
2602
LDAP * ldap ;
2553
2603
int r ;
2554
2604
char * fulluser ;
2605
+ const char * server_name ;
2555
2606
2607
+ #ifdef HAVE_LDAP_INITIALIZE
2608
+
2609
+ /*
2610
+ * For OpenLDAP, allow empty hostname if we have a basedn. We'll look for
2611
+ * servers with DNS SRV records via OpenLDAP library facilities.
2612
+ */
2613
+ if ((!port -> hba -> ldapserver || port -> hba -> ldapserver [0 ] == '\0' ) &&
2614
+ (!port -> hba -> ldapbasedn || port -> hba -> ldapbasedn [0 ] == '\0' ))
2615
+ {
2616
+ ereport (LOG ,
2617
+ (errmsg ("LDAP server not specified, and no ldapbasedn" )));
2618
+ return STATUS_ERROR ;
2619
+ }
2620
+ #else
2556
2621
if (!port -> hba -> ldapserver || port -> hba -> ldapserver [0 ] == '\0' )
2557
2622
{
2558
2623
ereport (LOG ,
2559
2624
(errmsg ("LDAP server not specified" )));
2560
2625
return STATUS_ERROR ;
2561
2626
}
2627
+ #endif
2628
+
2629
+ /*
2630
+ * If we're using SRV records, we don't have a server name so we'll just
2631
+ * show an empty string in error messages.
2632
+ */
2633
+ server_name = port -> hba -> ldapserver ? port -> hba -> ldapserver : "" ;
2562
2634
2563
2635
if (port -> hba -> ldapport == 0 )
2564
2636
{
@@ -2630,7 +2702,7 @@ CheckLDAPAuth(Port *port)
2630
2702
ereport (LOG ,
2631
2703
(errmsg ("could not perform initial LDAP bind for ldapbinddn \"%s\" on server \"%s\": %s" ,
2632
2704
port -> hba -> ldapbinddn ? port -> hba -> ldapbinddn : "" ,
2633
- port -> hba -> ldapserver ,
2705
+ server_name ,
2634
2706
ldap_err2string (r )),
2635
2707
errdetail_for_ldap (ldap )));
2636
2708
ldap_unbind (ldap );
@@ -2658,7 +2730,7 @@ CheckLDAPAuth(Port *port)
2658
2730
{
2659
2731
ereport (LOG ,
2660
2732
(errmsg ("could not search LDAP for filter \"%s\" on server \"%s\": %s" ,
2661
- filter , port -> hba -> ldapserver , ldap_err2string (r )),
2733
+ filter , server_name , ldap_err2string (r )),
2662
2734
errdetail_for_ldap (ldap )));
2663
2735
ldap_unbind (ldap );
2664
2736
pfree (passwd );
@@ -2673,14 +2745,14 @@ CheckLDAPAuth(Port *port)
2673
2745
ereport (LOG ,
2674
2746
(errmsg ("LDAP user \"%s\" does not exist" , port -> user_name ),
2675
2747
errdetail ("LDAP search for filter \"%s\" on server \"%s\" returned no entries." ,
2676
- filter , port -> hba -> ldapserver )));
2748
+ filter , server_name )));
2677
2749
else
2678
2750
ereport (LOG ,
2679
2751
(errmsg ("LDAP user \"%s\" is not unique" , port -> user_name ),
2680
2752
errdetail_plural ("LDAP search for filter \"%s\" on server \"%s\" returned %d entry." ,
2681
2753
"LDAP search for filter \"%s\" on server \"%s\" returned %d entries." ,
2682
2754
count ,
2683
- filter , port -> hba -> ldapserver , count )));
2755
+ filter , server_name , count )));
2684
2756
2685
2757
ldap_unbind (ldap );
2686
2758
pfree (passwd );
@@ -2698,7 +2770,7 @@ CheckLDAPAuth(Port *port)
2698
2770
(void ) ldap_get_option (ldap , LDAP_OPT_ERROR_NUMBER , & error );
2699
2771
ereport (LOG ,
2700
2772
(errmsg ("could not get dn for the first entry matching \"%s\" on server \"%s\": %s" ,
2701
- filter , port -> hba -> ldapserver ,
2773
+ filter , server_name ,
2702
2774
ldap_err2string (error )),
2703
2775
errdetail_for_ldap (ldap )));
2704
2776
ldap_unbind (ldap );
@@ -2719,7 +2791,7 @@ CheckLDAPAuth(Port *port)
2719
2791
{
2720
2792
ereport (LOG ,
2721
2793
(errmsg ("could not unbind after searching for user \"%s\" on server \"%s\"" ,
2722
- fulluser , port -> hba -> ldapserver )));
2794
+ fulluser , server_name )));
2723
2795
pfree (passwd );
2724
2796
pfree (fulluser );
2725
2797
return STATUS_ERROR ;
@@ -2750,7 +2822,7 @@ CheckLDAPAuth(Port *port)
2750
2822
{
2751
2823
ereport (LOG ,
2752
2824
(errmsg ("LDAP login failed for user \"%s\" on server \"%s\": %s" ,
2753
- fulluser , port -> hba -> ldapserver , ldap_err2string (r )),
2825
+ fulluser , server_name , ldap_err2string (r )),
2754
2826
errdetail_for_ldap (ldap )));
2755
2827
ldap_unbind (ldap );
2756
2828
pfree (passwd );
0 commit comments