*** a/doc/src/sgml/client-auth.sgml
--- b/doc/src/sgml/client-auth.sgml
***************
*** 393,398 **** hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>
--- 393,408 ----
         </varlistentry>
  
         <varlistentry>
+         <term><literal>radius</></term>
+         <listitem>
+          <para>
+           Authenticate using a RADIUS server. See <xref
+           linkend="auth-radius"> for detauls.
+          </para>
+         </listitem>
+        </varlistentry>
+ 
+        <varlistentry>
          <term><literal>cert</></term>
          <listitem>
           <para>
***************
*** 1329,1334 **** ldapserver=ldap.example.net ldapprefix="cn=" ldapsuffix=", dc=example, dc=net"
--- 1339,1424 ----
  
    </sect2>
  
+   <sect2 id="auth-radius">
+    <title>RADIUS authentication</title>
+ 
+    <indexterm zone="auth-radius">
+     <primary>RADIUS</primary>
+    </indexterm>
+ 
+    <para>
+     This authentication method operates similarly to
+     <literal>password</literal> except that it uses RADIUS
+     as the password verification method. RADIUS is used only to validate
+     the user name/password pairs. Therefore the user must already
+     exist in the database before LDAP can be used for
+     authentication.
+    </para>
+ 
+    <para>
+     When using RADIUS authentication, an Access Request message will be sent
+     to the configured RADIUS server. This request will be of type
+     <literal>Authenticate Only</literal>, and include parameters for
+     <literal>user name</>, <literal>password</>(encrypted) and
+     <literal>NAS Identifier</>. The request will be encrypted using
+     a secret shared with the server. The RADIUS server will respond to
+     this server with either <literal>Access Accept</> or
+     <literal>Access Reject</>. There is no support for RADIUS accounting.
+    </para>
+ 
+    <para>
+     The following configuration options are supported for RADIUS:
+      <variablelist>
+       <varlistentry>
+        <term><literal>radiusserver</literal></term>
+        <listitem>
+         <para>
+          The IP address of the RADIUS server to connect to. This must
+          be an IP address and not a hostname. This parameter is required.
+         </para>
+        </listitem>
+       </varlistentry>
+ 
+       <varlistentry>
+        <term><literal>radiussecret</literal></term>
+        <listitem>
+         <para>
+          The shared secret used when talking securely to the RAIDUS
+          server. This must have exactly the same value on the PostgreSQL
+          and RADIUS servers. It is recommended that this is a string of
+          at least 16 characters. This parameter is required.
+         </para>
+        </listitem>
+       </varlistentry>
+ 
+       <varlistentry>
+        <term><literal>radiusport</literal></term>
+        <listitem>
+         <para>
+          The port number on the RADIUS server to connect to. If no port
+          is specified, the default port <literal>1812</> will be used.
+         </para>
+        </listitem>
+       </varlistentry>
+ 
+       <varlistentry>
+        <term><literal>radiusidentifier</literal></term>
+        <listitem>
+         <para>
+          The string used as <literal>NAS Identifier</> in the RADIUS
+          requests. This parameter can be used as a second parameter
+          identifying for example which database the user is attempting
+          to authenticate as, which can be used for policy matching on
+          the RADIUS server. If no identifier is specified, the default
+          <literal>postgresql</> will be used.
+         </para>
+        </listitem>
+       </varlistentry>
+ 
+      </variablelist>
+    </para>
+   </sect2>
+ 
    <sect2 id="auth-cert">
     <title>Certificate authentication</title>
  
*** a/src/backend/libpq/auth.c
--- b/src/backend/libpq/auth.c
***************
*** 33,38 ****
--- 33,39 ----
  #include "libpq/ip.h"
  #include "libpq/libpq.h"
  #include "libpq/pqformat.h"
+ #include "libpq/md5.h"
  #include "miscadmin.h"
  #include "storage/ipc.h"
  
***************
*** 182,187 **** typedef SECURITY_STATUS
--- 183,194 ----
  static int	pg_SSPI_recvauth(Port *port);
  #endif
  
+ /*----------------------------------------------------------------
+  * RADIUS Authentication
+  *----------------------------------------------------------------
+  */
+ static int	CheckRADIUSAuth(Port *port);
+ 
  
  /*
   * Maximum accepted size of GSS and SSPI authentication tokens.
***************
*** 265,270 **** auth_failed(Port *port, int status)
--- 272,280 ----
  		case uaLDAP:
  			errstr = gettext_noop("LDAP authentication failed for user \"%s\"");
  			break;
+ 		case uaRADIUS:
+ 			errstr = gettext_noop("RADIUS authentication failed for user \"%s\"");
+ 			break;
  		default:
  			errstr = gettext_noop("authentication failed for user \"%s\": invalid authentication method");
  			break;
***************
*** 473,479 **** ClientAuthentication(Port *port)
  			Assert(false);
  #endif
  			break;
! 
  		case uaTrust:
  			status = STATUS_OK;
  			break;
--- 483,491 ----
  			Assert(false);
  #endif
  			break;
! 		case uaRADIUS:
! 			status = CheckRADIUSAuth(port);
! 			break;
  		case uaTrust:
  			status = STATUS_OK;
  			break;
***************
*** 2415,2417 **** CheckCertAuth(Port *port)
--- 2427,2764 ----
  }
  
  #endif
+ 
+ 
+ /*----------------------------------------------------------------
+  * RADIUS authentication
+  *----------------------------------------------------------------
+  */
+ 
+ /*
+  * RADIUS authentication is described in RFC2865 (and several
+  * others).
+  */
+ 
+ #define RADIUS_VECTOR_LENGTH 16
+ #define RADIUS_HEADER_LENGTH 20
+ 
+ typedef struct
+ {
+ 	unsigned char	attribute;
+ 	unsigned char	length;
+ 	unsigned char	data[1];
+ } radius_attribute;
+ 
+ typedef struct
+ {
+ 	unsigned char	code;
+ 	unsigned char	id;
+ 	unsigned short	length;
+ 	unsigned char	vector[RADIUS_VECTOR_LENGTH];
+ } radius_packet;
+ 
+ /* RADIUS packet types */
+ #define RADIUS_ACCESS_REQUEST	1
+ #define RADIUS_ACCESS_ACCEPT	2
+ #define RADIUS_ACCESS_REJECT	3
+ 
+ /* RAIDUS attributes */
+ #define RADIUS_USER_NAME		1
+ #define RADIUS_PASSWORD			2
+ #define RADIUS_SERVICE_TYPE		6
+ #define RADIUS_NAS_IDENTIFIER	32
+ 
+ /* RADIUS service types */
+ #define RADIUS_AUTHENTICATE_ONLY	8
+ 
+ /* Maximum size of a RADIUS packet we will create or accept */
+ #define RADIUS_BUFFER_SIZE 1024
+ 
+ /* Seconds to wait - XXX: should be in a config variable! */
+ #define RADIUS_TIMEOUT 3
+ 
+ static void
+ radius_add_attribute(radius_packet *packet, int type, const unsigned char *data, int len)
+ {
+ 	radius_attribute		*attr;
+ 
+ 	if (packet->length + len > RADIUS_BUFFER_SIZE)
+ 	{
+ 		/*
+ 		 * With remotely realistic data, this can never happen. But catch it just to make
+ 		 * sure we don't overrun a buffer. We'll just skip adding the broken attribute,
+ 		 * which will in the end cause authentication to fail.
+ 		 */
+ 		elog(WARNING,
+ 			 "Adding attribute code %i with length %i to radius packet would create oversize packet, ignoring",
+ 			 type, len);
+ 		return;
+ 
+ 	}
+ 
+ 	attr = (radius_attribute *) ((unsigned char *)packet + packet->length);
+ 	attr->attribute = type;
+ 	attr->length = len + 2; /* total size includes type and length */
+ 	memcpy(attr->data, data, len);
+ 	packet->length += attr->length;
+ }
+ 
+ static int
+ CheckRADIUSAuth(Port *port)
+ {
+ 	char			   *passwd;
+ 	char			   *identifier = "postgresql";
+ 	char				radius_buffer[RADIUS_BUFFER_SIZE];
+ 	char				receive_buffer[RADIUS_BUFFER_SIZE];
+ 	radius_packet	   *packet = (radius_packet *)radius_buffer;
+ 	radius_packet	   *receivepacket = (radius_packet *)receive_buffer;
+ 	int32				service = htonl(RADIUS_AUTHENTICATE_ONLY);
+ 	unsigned char	   *cryptvector;
+ 	unsigned char		encryptedpassword[RADIUS_VECTOR_LENGTH];
+ 	int					packetlength;
+ 	int					sock;
+ 	struct sockaddr_in	localaddr;
+ 	struct sockaddr_in	remoteaddr;
+ 	socklen_t			addrsize;
+ 	fd_set				fdset;
+ 	struct timeval		timeout;
+ 	int					i,r;
+ 
+ 	/* Verify parameters */
+ 	if (!port->hba->radiusserver || port->hba->radiusserver[0] == '\0')
+ 	{
+ 		ereport(LOG,
+ 				(errmsg("RADIUS server not specified")));
+ 		return STATUS_ERROR;
+ 	}
+ 
+ 	if (!port->hba->radiussecret || port->hba->radiussecret[0] == '\0')
+ 	{
+ 		ereport(LOG,
+ 				(errmsg("RADIUS secret not specified")));
+ 		return STATUS_ERROR;
+ 	}
+ 
+ 	if (port->hba->radiusport == 0)
+ 		port->hba->radiusport = 1812;
+ 
+ 	memset(&remoteaddr, 0, sizeof(remoteaddr));
+ 	remoteaddr.sin_family = AF_INET;
+ 	remoteaddr.sin_addr.s_addr = inet_addr(port->hba->radiusserver);
+ 	if (remoteaddr.sin_addr.s_addr == INADDR_NONE)
+ 	{
+ 		ereport(LOG,
+ 				(errmsg("RADIUS server '%s' is not a valid IP address",
+ 						port->hba->radiusserver)));
+ 		return STATUS_ERROR;
+ 	}
+ 	remoteaddr.sin_port = htons(port->hba->radiusport);
+ 
+ 	if (port->hba->radiusidentifier && port->hba->radiusidentifier[0])
+ 		identifier = port->hba->radiusidentifier;
+ 
+ 	/* Send regular password request to client, and get the response */
+ 	sendAuthRequest(port, AUTH_REQ_PASSWORD);
+ 
+ 	passwd = recv_password_packet(port);
+ 	if (passwd == NULL)
+ 		return STATUS_EOF;		/* client wouldn't send password */
+ 
+ 	if (strlen(passwd) == 0)
+ 	{
+ 		ereport(LOG,
+ 				(errmsg("empty password returned by client")));
+ 		return STATUS_ERROR;
+ 	}
+ 
+ 	if (strlen(passwd) > RADIUS_VECTOR_LENGTH)
+ 	{
+ 		ereport(LOG,
+ 				(errmsg("RADIUS authentication does not support passwords longer than 16 characters")));
+ 		return STATUS_ERROR;
+ 	}
+ 
+ 	/* Construct RADIUS packet */
+ 	packet->code = RADIUS_ACCESS_REQUEST;
+ 	packet->length = RADIUS_HEADER_LENGTH;
+ 	for (i = 0; i < RADIUS_VECTOR_LENGTH; i++)
+ 		/* XXX: Generate a more secure random string? */
+ 		packet->vector[i] = random() % 255;
+ 	packet->id = packet->vector[0];
+ 	radius_add_attribute(packet, RADIUS_SERVICE_TYPE, (unsigned char *) &service, sizeof(service));
+ 	radius_add_attribute(packet, RADIUS_USER_NAME, (unsigned char *) port->user_name, strlen(port->user_name));
+ 	radius_add_attribute(packet, RADIUS_NAS_IDENTIFIER, (unsigned char *) identifier, strlen(identifier));
+ 
+ 	/*
+ 	 * RADIUS password attributes are calculated as:
+ 	 * e[0] = p[0] XOR MD5(secret + vector)
+ 	 */
+ 	cryptvector = palloc(RADIUS_VECTOR_LENGTH + strlen(port->hba->radiussecret));
+ 	memcpy(cryptvector, port->hba->radiussecret, strlen(port->hba->radiussecret));
+ 	memcpy(cryptvector + strlen(port->hba->radiussecret), packet->vector, RADIUS_VECTOR_LENGTH);
+ 	if (!pg_md5_binary(cryptvector, RADIUS_VECTOR_LENGTH + strlen(port->hba->radiussecret), encryptedpassword))
+ 	{
+ 		ereport(LOG,
+ 				(errmsg("could not perform md5 encryption of password")));
+ 		pfree(cryptvector);
+ 		return STATUS_ERROR;
+ 	}
+ 	pfree(cryptvector);
+ 	for (i = 0; i < RADIUS_VECTOR_LENGTH; i++)
+ 	{
+ 		if (i < strlen(passwd))
+ 			encryptedpassword[i] = passwd[i] ^ encryptedpassword[i];
+ 		else
+ 			encryptedpassword[i] = '\0' ^ encryptedpassword[i];
+ 	}
+ 	radius_add_attribute(packet, RADIUS_PASSWORD, encryptedpassword, RADIUS_VECTOR_LENGTH);
+ 
+ 	/* Length need to be in network order on the wire */
+ 	packetlength = packet->length;
+ 	packet->length = htons(packet->length);
+ 
+ 	sock = socket(AF_INET, SOCK_DGRAM, 0);
+ 	if (sock < 0)
+ 	{
+ 		ereport(LOG,
+ 				(errmsg("could not create RADIUS socket: %m")));
+ 		return STATUS_ERROR;
+ 	}
+ 
+ 	memset(&localaddr, 0, sizeof(localaddr));
+ 	localaddr.sin_family = AF_INET;
+ 	localaddr.sin_addr.s_addr = INADDR_ANY;
+ 	if (bind(sock, (struct sockaddr *) &localaddr, sizeof(localaddr)))
+ 	{
+ 		ereport(LOG,
+ 				(errmsg("could not bind local RADIUS socket: %m")));
+ 		closesocket(sock);
+ 		return STATUS_ERROR;
+ 	}
+ 
+ 	if (sendto(sock, radius_buffer, packetlength, 0,
+ 			   (struct sockaddr *) &remoteaddr, sizeof(remoteaddr)) < 0)
+ 	{
+ 		ereport(LOG,
+ 				(errmsg("could not send RADIUS packet: %m")));
+ 		closesocket(sock);
+ 		return STATUS_ERROR;
+ 	}
+ 
+ 	timeout.tv_sec = RADIUS_TIMEOUT;
+ 	timeout.tv_usec = 0;
+ 	FD_ZERO(&fdset);
+ 	FD_SET(sock, &fdset);
+ 	while (true)
+ 	{
+ 		r = select(sock + 1, &fdset, NULL, NULL, &timeout);
+ 		if (r < 0)
+ 		{
+ 			if (errno == EINTR)
+ 				continue;
+ 
+ 			/* Anything else is an actual error */
+ 			ereport(LOG,
+ 					(errmsg("could not check status on RADIUS socket: %m")));
+ 			closesocket(sock);
+ 			return STATUS_ERROR;
+ 		}
+ 		if (r == 0)
+ 		{
+ 			ereport(LOG,
+ 					(errmsg("timeout waiting for RADIUS response")));
+ 			closesocket(sock);
+ 			return STATUS_ERROR;
+ 		}
+ 
+ 		/* else we actually have a packet ready to read */
+ 		break;
+ 	}
+ 
+ 	/* Read the response packet */
+ 	addrsize = sizeof(remoteaddr);
+ 	packetlength = recvfrom(sock, receive_buffer, RADIUS_BUFFER_SIZE, 0,
+ 							(struct sockaddr *) &remoteaddr, &addrsize);
+ 	if (packetlength < 0)
+ 	{
+ 		ereport(LOG,
+ 				(errmsg("could not read RADIUS response: %m")));
+ 		closesocket(sock);
+ 		return STATUS_ERROR;
+ 	}
+ 
+ 	closesocket(sock);
+ 
+ 	if (remoteaddr.sin_port != htons(port->hba->radiusport))
+ 	{
+ 		ereport(LOG,
+ 				(errmsg("RADIUS response was sent from incorrect port: %i",
+ 						ntohs(remoteaddr.sin_port))));
+ 		return STATUS_ERROR;
+ 	}
+ 
+ 	if (packetlength < RADIUS_HEADER_LENGTH)
+ 	{
+ 		ereport(LOG,
+ 				(errmsg("RADIUS response too short: %i", packetlength)));
+ 		return STATUS_ERROR;
+ 	}
+ 
+ 	if (packetlength != ntohs(receivepacket->length))
+ 	{
+ 		ereport(LOG,
+ 				(errmsg("RADIUS response has corrupt length: %i (actual length %i)",
+ 						ntohs(receivepacket->length), packetlength)));
+ 		return STATUS_ERROR;
+ 	}
+ 
+ 	if (packet->id != receivepacket->id)
+ 	{
+ 		ereport(LOG,
+ 				(errmsg("RADIUS response is to a different request: %i (should be %i)",
+ 						receivepacket->id, packet->id)));
+ 		return STATUS_ERROR;
+ 	}
+ 
+ 	/*
+ 	 * Verify the response authenticator, which is calculated as
+ 	 * MD5(Code+ID+Length+RequestAuthenticator+Attributes+Secret)
+ 	 */
+ 	cryptvector = palloc(packetlength + strlen(port->hba->radiussecret));
+ 
+ 	memcpy(cryptvector, receivepacket, 4);		/* code+id+length */
+ 	memcpy(cryptvector+4, packet->vector, RADIUS_VECTOR_LENGTH);	/* request authenticator, from original packet */
+ 	if (packetlength > RADIUS_HEADER_LENGTH)	/* there may be no attributes at all */
+ 		memcpy(cryptvector+RADIUS_HEADER_LENGTH, receive_buffer + RADIUS_HEADER_LENGTH, packetlength-RADIUS_HEADER_LENGTH);
+ 	memcpy(cryptvector+packetlength, port->hba->radiussecret, strlen(port->hba->radiussecret));
+ 
+ 	if (!pg_md5_binary(cryptvector,
+ 					   packetlength + strlen(port->hba->radiussecret),
+ 					   encryptedpassword))
+ 	{
+ 		ereport(LOG,
+ 				(errmsg("could not perform md5 encryption of received packet")));
+ 		pfree(cryptvector);
+ 		return STATUS_ERROR;
+ 	}
+ 	pfree(cryptvector);
+ 
+ 	if (memcmp(receivepacket->vector, encryptedpassword, RADIUS_VECTOR_LENGTH)  != 0)
+ 	{
+ 		ereport(LOG,
+ 				(errmsg("RADIUS response has incorrect MD5 signature")));
+ 		return STATUS_ERROR;
+ 	}
+ 
+ 	if (receivepacket->code == RADIUS_ACCESS_ACCEPT)
+ 		return STATUS_OK;
+ 	else if (receivepacket->code == RADIUS_ACCESS_REJECT)
+ 		return STATUS_ERROR;
+ 	else
+ 	{
+ 		ereport(LOG,
+ 				(errmsg("RADIUS response has invalid code (%i) for user '%s'",
+ 						receivepacket->code, port->user_name)));
+ 		return STATUS_ERROR;
+ 	}
+ }
*** a/src/backend/libpq/hba.c
--- b/src/backend/libpq/hba.c
***************
*** 947,952 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
--- 947,954 ----
  #else
  		unsupauth = "cert";
  #endif
+ 	else if (strcmp(token, "radius")== 0)
+ 		parsedline->auth_method = uaRADIUS;
  	else
  	{
  		ereport(LOG,
***************
*** 1157,1162 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
--- 1159,1203 ----
  				else
  					parsedline->include_realm = false;
  			}
+ 			else if (strcmp(token, "radiusserver") == 0)
+ 			{
+ 				REQUIRE_AUTH_OPTION(uaRADIUS, "radiusserver", "radius");
+ 				if (inet_addr(c) == INADDR_NONE)
+ 				{
+ 					ereport(LOG,
+ 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 							 errmsg("invalid RADIUS server IP address: \"%s\"", c),
+ 						   errcontext("line %d of configuration file \"%s\"",
+ 									  line_num, HbaFileName)));
+ 					return false;
+ 
+ 				}
+ 				parsedline->radiusserver = pstrdup(c);
+ 			}
+ 			else if (strcmp(token, "radiusport") == 0)
+ 			{
+ 				REQUIRE_AUTH_OPTION(uaRADIUS, "radiusport", "radius");
+ 				parsedline->radiusport = atoi(c);
+ 				if (parsedline->radiusport == 0)
+ 				{
+ 					ereport(LOG,
+ 							(errcode(ERRCODE_CONFIG_FILE_ERROR),
+ 							 errmsg("invalid RADIUS port number: \"%s\"", c),
+ 						   errcontext("line %d of configuration file \"%s\"",
+ 									  line_num, HbaFileName)));
+ 					return false;
+ 				}
+ 			}
+ 			else if (strcmp(token, "radiussecret") == 0)
+ 			{
+ 				REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius");
+ 				parsedline->radiussecret = pstrdup(c);
+ 			}
+ 			else if (strcmp(token, "radiusidentifier") == 0)
+ 			{
+ 				REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius");
+ 				parsedline->radiusidentifier = pstrdup(c);
+ 			}
  			else
  			{
  				ereport(LOG,
***************
*** 1209,1214 **** parse_hba_line(List *line, int line_num, HbaLine *parsedline)
--- 1250,1261 ----
  		}
  	}
  
+ 	if (parsedline->auth_method == uaRADIUS)
+ 	{
+ 		MANDATORY_AUTH_ARG(parsedline->radiusserver, "radiusserver", "radius");
+ 		MANDATORY_AUTH_ARG(parsedline->radiussecret, "radiussecret", "radius");
+ 	}
+ 
  	/*
  	 * Enforce any parameters implied by other settings.
  	 */
*** a/src/backend/libpq/md5.c
--- b/src/backend/libpq/md5.c
***************
*** 298,303 **** pg_md5_hash(const void *buff, size_t len, char *hexsum)
--- 298,309 ----
  	return true;
  }
  
+ bool pg_md5_binary(const void *buff, size_t len, void *outbuf)
+ {
+ 	if (!calculateDigestFromBuffer((uint8 *) buff, len, outbuf))
+ 		return false;
+ 	return true;
+ }
  
  
  /*
*** a/src/backend/libpq/pg_hba.conf.sample
--- b/src/backend/libpq/pg_hba.conf.sample
***************
*** 38,45 ****
  # that the server is directly connected to.
  #
  # METHOD can be "trust", "reject", "md5", "password", "gss", "sspi", "krb5",
! # "ident", "pam", "ldap" or "cert".  Note that "password" sends passwords
! # in clear text; "md5" is preferred since it sends encrypted passwords.
  #
  # OPTIONS are a set of options for the authentication in the format
  # NAME=VALUE. The available options depend on the different authentication
--- 38,46 ----
  # that the server is directly connected to.
  #
  # METHOD can be "trust", "reject", "md5", "password", "gss", "sspi", "krb5",
! # "ident", "pam", "ldap", "radius" or "cert".  Note that "password" sends
! # passwords in clear text; "md5" is preferred since it sends encrypted
! # passwords.
  #
  # OPTIONS are a set of options for the authentication in the format
  # NAME=VALUE. The available options depend on the different authentication
*** a/src/include/libpq/hba.h
--- b/src/include/libpq/hba.h
***************
*** 27,33 **** typedef enum UserAuth
  	uaSSPI,
  	uaPAM,
  	uaLDAP,
! 	uaCert
  } UserAuth;
  
  typedef enum IPCompareMethod
--- 27,34 ----
  	uaSSPI,
  	uaPAM,
  	uaLDAP,
! 	uaCert,
! 	uaRADIUS
  } UserAuth;
  
  typedef enum IPCompareMethod
***************
*** 71,76 **** typedef struct
--- 72,81 ----
  	char	   *krb_server_hostname;
  	char	   *krb_realm;
  	bool		include_realm;
+ 	char	   *radiusserver;
+ 	char	   *radiussecret;
+ 	char	   *radiusidentifier;
+ 	int			radiusport;
  } HbaLine;
  
  /* kluge to avoid including libpq/libpq-be.h here */
*** a/src/include/libpq/md5.h
--- b/src/include/libpq/md5.h
***************
*** 23,28 ****
--- 23,29 ----
  
  
  extern bool pg_md5_hash(const void *buff, size_t len, char *hexsum);
+ extern bool pg_md5_binary(const void *buff, size_t len, void *outbuf);
  extern bool pg_md5_encrypt(const char *passwd, const char *salt,
  			   size_t salt_len, char *buf);
  
