diff -r -c pgsql.orig/configure.in pgsql/configure.in *** pgsql.orig/configure.in 2006-03-23 16:49:17.000000000 +0100 --- pgsql/configure.in 2006-03-23 16:51:14.000000000 +0100 *************** *** 1088,1093 **** --- 1088,1101 ---- PGAC_FUNC_GETPWUID_R_5ARG PGAC_FUNC_STRERROR_R_INT + # this will link libpq against libldap_r + if test "$with_ldap" = yes ; then + if test "$PORTNAME" != "win32"; then + AC_CHECK_LIB(ldap_r, ldap_simple_bind, [], [AC_MSG_ERROR([library 'ldap_r' is required for LDAP])]) + PTHREAD_LIBS="$PTHREAD_LIBS -lldap_r" + fi + fi + CFLAGS="$_CFLAGS" LIBS="$_LIBS" diff -r -c pgsql.orig/src/interfaces/libpq/fe-connect.c pgsql/src/interfaces/libpq/fe-connect.c *** pgsql.orig/src/interfaces/libpq/fe-connect.c 2006-03-23 16:49:23.000000000 +0100 --- pgsql/src/interfaces/libpq/fe-connect.c 2006-03-23 16:51:14.000000000 +0100 *************** *** 62,67 **** --- 62,79 ---- #endif #endif + #ifdef USE_LDAP + # ifdef WIN32 + # include + # else + /* OpenLDAP deprecates RFC 1823, but we want standard conformance */ + # define LDAP_DEPRECATED 1 + # include + typedef struct timeval LDAP_TIMEVAL; + # endif + static int ldapServiceLookup(const char *, PQconninfoOption *, PQExpBuffer); + #endif + #include "libpq/ip.h" #include "mb/pg_wchar.h" *************** *** 2344,2350 **** --- 2356,2697 ---- return STATUS_OK; } + #ifdef USE_LDAP + + #define PGLDAP_TIMEOUT 2 + #define ld_isspace(x) ((' ' == x) || ('\t' == x)) + #define ld_islf(x) (('\r' == x) || ('\n' == x)) + + /* + * ldapServiceLookup + * + * Search the LDAP URL passed as first argument, treat the result as a + * string of connection options that are parsed and added to the array of + * options passed as second argument. + * + * LDAP URLs must conform to RFC 1959 without escape sequences. + * + * Returns + * 0 if the lookup was successful, + * 1 if the connection to the LDAP server could be established but + * the search was unsuccessful, + * 2 if a connection could not be established, and + * 3 if a fatal error occurred. + * + * An error message is returned in the third argument for return codes 1 and 3. + */ + static int + ldapServiceLookup(const char *purl, PQconninfoOption *options, PQExpBuffer errorMessage) { + int port=389, scope, rc, msgid, size, state, oldstate, i; + long lport; + bool found_keyword; + char *url, *hostname, *portstr, *endptr, *dn, *scopestr, *filter, *result, *p, *p1 = NULL, *optname = NULL, *optval = NULL; + char *attrs[2] = {NULL, NULL}; + LDAP *ld = NULL; + LDAPMessage *res, *entry; + struct berval **values; + LDAP_TIMEVAL time; + time.tv_sec = PGLDAP_TIMEOUT; + time.tv_usec = 0; + + /* make working copy of URL */ + if (NULL == (url = malloc(strlen(purl) + 1))) { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + return 3; + } + strcpy(url, purl); + + /* parse URL components, check for correctness */ + + if (0 != strncasecmp(url, "ldap://", 7)) { + printfPQExpBuffer(errorMessage, + libpq_gettext("bad LDAP URL \"%s\": scheme must be ldap://\n"), purl); + free(url); + return 3; + } + hostname = url + 7; + if ('/' == *hostname) + hostname = "localhost"; + p = strchr(url + 7, ':'); + p1 = strchr(url + 7, '/'); + if ((NULL == p1) || ('\0' == *(p1 + 1)) || ('?' == *(p1 + 1))) { + printfPQExpBuffer(errorMessage, + libpq_gettext("bad LDAP URL \"%s\": missing distinguished name\n"), purl); + free(url); + return 3; + } + dn = p1 + 1; + *p1 = '\0'; + if ((NULL != p) && (p < p1)) { + portstr = p + 1; + *p = '\0'; + lport = strtol(portstr, &endptr, 10); + errno = 0; + if (('\0' == *portstr) || ('\0' != *endptr) || (errno) || (lport < 0l) || (lport > 65535l)) { + printfPQExpBuffer(errorMessage, + libpq_gettext("bad LDAP URL \"%s\": invalid port number\n"), purl); + free(url); + return 3; + } + port = (int)lport; + } + if ((NULL == (p = strchr(dn, '?'))) || ('\0' == *(p + 1)) || ('?' == *(p + 1))) { + printfPQExpBuffer(errorMessage, + libpq_gettext("bad LDAP URL \"%s\": must have exactly one attribute\n"), purl); + free(url); + return 3; + } + attrs[0] = p + 1; + *p = '\0'; + if ((NULL == (p = strchr(attrs[0], '?'))) || ('\0' == *(p + 1)) || ('?' == *(p + 1))) { + printfPQExpBuffer(errorMessage, + libpq_gettext("bad LDAP URL \"%s\": must have search scope (base/one/sub)\n"), purl); + free(url); + return 3; + } + scopestr = p + 1; + *p = '\0'; + if (NULL != strchr(attrs[0], ',')) { + printfPQExpBuffer(errorMessage, + libpq_gettext("bad LDAP URL \"%s\": must have exactly one attribute\n"), purl); + free(url); + return 3; + } + if ((NULL == (p = strchr(scopestr, '?'))) || ('\0' == *(p + 1)) || ('?' == *(p + 1))) { + printfPQExpBuffer(errorMessage, + libpq_gettext("bad LDAP URL \"%s\": no filter\n"), purl); + free(url); + return 3; + } + filter = p + 1; + *p = '\0'; + if (0 == strcasecmp(scopestr, "base")) + scope = LDAP_SCOPE_BASE; + else if (0 == strcasecmp(scopestr, "one")) + scope = LDAP_SCOPE_ONELEVEL; + else if (0 == strcasecmp(scopestr, "sub")) + scope = LDAP_SCOPE_SUBTREE; + else { + printfPQExpBuffer(errorMessage, + libpq_gettext("bad LDAP URL \"%s\": must have search scope (base/one/sub)\n"), purl); + free(url); + return 3; + } + if (NULL != (p = strchr(filter, '?'))) + *p = '\0'; + + /* initialize LDAP structure */ + if (NULL == (ld = ldap_init(hostname, port))) { + printfPQExpBuffer(errorMessage, + libpq_gettext("error creating LDAP structure\n")); + free(url); + return 3; + } + + /* initialize connection to the server */ + /* we do an explicit bind because we want to return 2 if the bind fails */ + msgid = ldap_simple_bind(ld, NULL, NULL); + if (-1 == msgid) { + /* error in ldap_simple_bind() */ + free(url); + ldap_unbind(ld); + return 2; + } + + /* wait some time for the connection to succeed */ + res = NULL; + rc = ldap_result(ld, msgid, LDAP_MSG_ALL, &time, &res); + if ((-1 == rc) || (NULL == res)) { + if (NULL != res) { + /* timeout */ + ldap_msgfree(res); + } + /* error in ldap_result() */ + free(url); + ldap_unbind(ld); + return 2; + } + ldap_msgfree(res); + + /* search */ + res = NULL; + rc = ldap_search_st(ld, dn, scope, filter, attrs, 0, &time, &res); + if (LDAP_SUCCESS != rc) { + if (NULL != res) + ldap_msgfree(res); + printfPQExpBuffer(errorMessage, + libpq_gettext("lookup on LDAP server failed: %s\n"), + ldap_err2string(rc)); + ldap_unbind(ld); + free(url); + return 1; + } + + /* complain if there was not exactly one result */ + rc = ldap_count_entries(ld, res); + if (1 != rc) { + printfPQExpBuffer(errorMessage, + rc ? libpq_gettext("more than one entry found on LDAP lookup\n") + : libpq_gettext("no entry found on LDAP lookup\n")); + ldap_msgfree(res); + ldap_unbind(ld); + free(url); + return 1; + } + + /* get entry */ + if (NULL == (entry = ldap_first_entry(ld, res))) { + /* should never happen */ + printfPQExpBuffer(errorMessage, + libpq_gettext("no entry found on LDAP lookup\n")); + ldap_msgfree(res); + ldap_unbind(ld); + free(url); + return 1; + } + + /* get values */ + if (NULL == (values = ldap_get_values_len(ld, entry, attrs[0]))) { + printfPQExpBuffer(errorMessage, + libpq_gettext("attribute has no values on LDAP lookup\n")); + ldap_msgfree(res); + ldap_unbind(ld); + free(url); + return 1; + } + ldap_msgfree(res); + free(url); + if (NULL == values[0]) { + printfPQExpBuffer(errorMessage, + libpq_gettext("attribute has no values on LDAP lookup\n")); + ldap_value_free_len(values); + ldap_unbind(ld); + return 1; + } + + /* concatenate values to a single string */ + size = 0; + for (i = 0; NULL != values[i]; ++i) + size += values[i]->bv_len + 1; + if (NULL == (result = malloc(size + 1))) { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + ldap_value_free_len(values); + ldap_unbind(ld); + return 3; + } + p = result; + for (i = 0; NULL != values[i]; ++i) { + strncpy(p, values[i]->bv_val, values[i]->bv_len); + p += values[i]->bv_len; + *(p++) = '\n'; + if (NULL == values[i+1]) + *(p+1) = '\0'; + } + ldap_value_free_len(values); + + ldap_unbind(ld); + + /* parse result string */ + oldstate = state = 0; + for (p = result; '\0' != *p; ++p) { + switch (state) { + case 0: /* between entries */ + if (! (ld_isspace(*p) || ld_islf(*p))) { + optname = p; + state = 1; + } + break; + case 1: /* in option name */ + if (ld_isspace(*p)) { + *p = '\0'; + state = 2; + } else if (ld_islf(*p)) { + printfPQExpBuffer(errorMessage, + libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"), + optname); + return 3; + } else if ('=' == *p) { + *p = '\0'; + state = 3; + } + break; + case 2: /* after option name */ + if ('=' == *p) { + state = 3; + } else if (! ld_isspace(*p)) { + printfPQExpBuffer(errorMessage, + libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"), + optname); + return 3; + } + break; + case 3: /* before option value */ + if ('\'' == *p) { + optval = p+1; + p1 = p+1; + state = 5; + } else if (ld_islf(*p)) { + optval = optname + strlen(optname); /* empty */ + state = 0; + } else if (! ld_isspace(*p)) { + optval = p; + state = 4; + } + break; + case 4: /* in unquoted option value */ + if (ld_isspace(*p) || ld_islf(*p)) { + *p = '\0'; + state = 0; + } + break; + case 5: /* in quoted option value */ + if ('\'' == *p) { + *p1 = '\0'; + state = 0; + } else if ('\\' == *p) { + state = 6; + } else + *(p1++) = *p; + break; + case 6: /* in quoted option value after escape */ + *(p1++) = *p; + state = 5; + break; + } + + if ((0 == state) && (0 != oldstate)) { + found_keyword = false; + for (i = 0; options[i].keyword; i++) { + if (strcmp(options[i].keyword, optname) == 0) { + if (options[i].val == NULL) + options[i].val = strdup(optval); + found_keyword = true; + break; + } + } + if (!found_keyword) { + printfPQExpBuffer(errorMessage, + libpq_gettext("invalid connection option \"%s\"\n"), + optname); + return 1; + } + optname = NULL; + optval = NULL; + } + oldstate = state; + } + if ((5 == state) || (6 == state)) { + printfPQExpBuffer(errorMessage, + libpq_gettext("unterminated quoted string in connection info string\n")); + return 3; + } + + return 0; + } + #endif #define MAXBUFSIZE 256 *************** *** 2440,2445 **** --- 2787,2811 ---- *val; bool found_keyword; + #ifdef USE_LDAP + int rc; + if (0 == strncmp(line, "ldap", 4)) { + rc = ldapServiceLookup(line, options, errorMessage); + /* if rc = 2, go on reading for fallback */ + switch (rc) { + case 0: + fclose(f); + return 0; + case 1: + case 3: + fclose(f); + return 3; + case 2: + continue; + } + } + #endif + key = line; val = strchr(line, '='); if (val == NULL) diff -r -c pgsql.orig/src/interfaces/libpq/Makefile pgsql/src/interfaces/libpq/Makefile *** pgsql.orig/src/interfaces/libpq/Makefile 2006-03-23 16:49:23.000000000 +0100 --- pgsql/src/interfaces/libpq/Makefile 2006-03-23 16:51:14.000000000 +0100 *************** *** 59,65 **** SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lssl -lsocket -lnsl -lresolv -lintl $(PTHREAD_LIBS), $(LIBS)) endif ifeq ($(PORTNAME), win32) ! SHLIB_LINK += -lshfolder -lwsock32 -lws2_32 $(filter -leay32 -lssleay32 -lcomerr32 -lkrb5_32, $(LIBS)) endif --- 59,65 ---- SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lssl -lsocket -lnsl -lresolv -lintl $(PTHREAD_LIBS), $(LIBS)) endif ifeq ($(PORTNAME), win32) ! SHLIB_LINK += -lshfolder -lwsock32 -lws2_32 $(filter -leay32 -lssleay32 -lcomerr32 -lkrb5_32 -lwldap32, $(LIBS)) endif diff -r -c pgsql.orig/src/interfaces/libpq/po/de.po pgsql/src/interfaces/libpq/po/de.po *** pgsql.orig/src/interfaces/libpq/po/de.po 2006-03-23 16:49:23.000000000 +0100 --- pgsql/src/interfaces/libpq/po/de.po 2006-03-23 17:05:00.000000000 +0100 *************** *** 652,654 **** --- 652,709 ---- #, c-format msgid "certificate could not be obtained: %s\n" msgstr "Zertifikat konnte nicht ermittelt werden: %s\n" + + #: fe-connect.c:2409 + #, c-format + msgid "bad LDAP URL \"%s\": scheme must be ldap://\n" + msgstr "ungültige LDAP URL »%s«: Schema ist nicht ldap://\n" + + #: fe-connect.c:2420 + #, c-format + msgid "bad LDAP URL \"%s\": missing distinguished name\n" + msgstr "ungültige LDAP URL »%s«: distinguished name fehlt\n" + + #: fe-connect.c:2433 + #, c-format + msgid "bad LDAP URL \"%s\": invalid port number\n" + msgstr "ungültige LDAP URL »%s«: ungültige Portnummer\n" + + #: fe-connect.c:2441 fe-connect.c:2457 + #, c-format + msgid "bad LDAP URL \"%s\": must have exactly one attribute\n" + msgstr "ungültige LDAP URL »%s«: muss genau ein Attribut haben\n" + + #: fe-connect.c:2449 fe-connect.c:2477 + #, c-format + msgid "bad LDAP URL \"%s\": must have search scope (base/one/sub)\n" + msgstr "ungültige LDAP URL »%s«: fehlender Suchbereich (base/one/sub)\n" + + #: fe-connect.c:2463 + #, c-format + msgid "bad LDAP URL \"%s\": no filter\n" + msgstr "ungültige LDAP URL »%s«: Filter fehlt\n" + + #: fe-connect.c:2487 + #, c-format + msgid "error creating LDAP structure\n" + msgstr "Fehler beim Erzeugen der LDAP-Struktur\n" + + #: fe-connect.c:2524 + #, c-format + msgid "lookup on LDAP server failed: %s\n" + msgstr "Suche am LDAP-Server schlug fehl: %s\n" + + #: fe-connect.c:2535 + #, c-format + msgid "more than one entry found on LDAP lookup\n" + msgstr "bei der LDAP-Suche wurde mehr als ein Eintrag gefunden\n" + + #: fe-connect.c:2536 fe-connect.c:2547 + #, c-format + msgid "no entry found on LDAP lookup\n" + msgstr "bei der LDAP-Suche wurde kein Eintrag gefunden\n" + + #: fe-connect.c:2557 fe-connect.c:2567 + #, c-format + msgid "attribute has no values on LDAP lookup\n" + msgstr "bei der LDAP-Suche wurde ein Attribut ohne Werte gefunden\n"