</listitem>
</varlistentry>
- <varlistentry>
- <term><literal>radius</literal></term>
- <listitem>
- <para>
- Authenticate using a RADIUS server. See <xref
- linkend="auth-radius"/> for details.
- </para>
- </listitem>
- </varlistentry>
-
<varlistentry>
<term><literal>cert</literal></term>
<listitem>
relies on an LDAP authentication server.
</para>
</listitem>
- <listitem>
- <para>
- <link linkend="auth-radius">RADIUS authentication</link>, which
- relies on a RADIUS authentication server.
- </para>
- </listitem>
<listitem>
<para>
<link linkend="auth-cert">Certificate authentication</link>, which
</sect1>
- <sect1 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 RADIUS 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>, <literal>password</literal> (encrypted) and
- <literal>NAS Identifier</literal>. The request will be encrypted using
- a secret shared with the server. The RADIUS server will respond to
- this request with either <literal>Access Accept</literal> or
- <literal>Access Reject</literal>. There is no support for RADIUS accounting.
- </para>
-
- <para>
- Multiple RADIUS servers can be specified, in which case they will
- be tried sequentially. If a negative response is received from
- a server, the authentication will fail. If no response is received,
- the next server in the list will be tried. To specify multiple
- servers, separate the server names with commas and surround the list
- with double quotes. If multiple servers are specified, the other
- RADIUS options can also be given as comma-separated lists, to provide
- individual values for each server. They can also be specified as
- a single value, in which case that value will apply to all servers.
- </para>
-
- <para>
- The following configuration options are supported for RADIUS:
- <variablelist>
- <varlistentry>
- <term><literal>radiusservers</literal></term>
- <listitem>
- <para>
- The DNS names or IP addresses of the RADIUS servers to connect to.
- This parameter is required.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><literal>radiussecrets</literal></term>
- <listitem>
- <para>
- The shared secrets used when talking securely to the RADIUS
- servers. This must have exactly the same value on the PostgreSQL
- and RADIUS servers. It is recommended that this be a string of
- at least 16 characters. This parameter is required.
- <note>
- <para>
- The encryption vector used will only be cryptographically
- strong if <productname>PostgreSQL</productname> is built with support for
- <productname>OpenSSL</productname>. In other cases, the transmission to the
- RADIUS server should only be considered obfuscated, not secured, and
- external security measures should be applied if necessary.
- </para>
- </note>
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><literal>radiusports</literal></term>
- <listitem>
- <para>
- The port numbers to connect to on the RADIUS servers. If no port
- is specified, the default RADIUS port (<literal>1812</literal>)
- will be used.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><literal>radiusidentifiers</literal></term>
- <listitem>
- <para>
- The strings to be used as <literal>NAS Identifier</literal> in the
- RADIUS requests. This parameter can be used, for example, to
- identify which database cluster the user is attempting to connect
- to, which can be useful for policy matching on
- the RADIUS server. If no identifier is specified, the default
- <literal>postgresql</literal> will be used.
- </para>
- </listitem>
- </varlistentry>
-
- </variablelist>
- </para>
-
- <para>
- If it is necessary to have a comma or whitespace in a RADIUS parameter
- value, that can be done by putting double quotes around the value, but
- it is tedious because two layers of double-quoting are now required.
- An example of putting whitespace into RADIUS secret strings is:
-<programlisting>
-host ... radius radiusservers="server1,server2" radiussecrets="""secret one"",""secret two"""
-</programlisting>
- </para>
- </sect1>
-
<sect1 id="auth-cert">
<title>Certificate Authentication</title>
bool update_accountname);
#endif
-/*----------------------------------------------------------------
- * RADIUS Authentication
- *----------------------------------------------------------------
- */
-static int CheckRADIUSAuth(Port *port);
-static int PerformRadiusTransaction(const char *server, const char *secret, const char *portstr, const char *identifier, const char *user_name, const char *passwd);
-
/*----------------------------------------------------------------
* Global authentication functions
case uaCert:
errstr = gettext_noop("certificate authentication failed for user \"%s\"");
break;
- case uaRADIUS:
- errstr = gettext_noop("RADIUS authentication failed for user \"%s\"");
- break;
case uaOAuth:
errstr = gettext_noop("OAuth bearer authentication failed for user \"%s\"");
break;
Assert(false);
#endif
break;
- case uaRADIUS:
- status = CheckRADIUSAuth(port);
- break;
case uaCert:
/* uaCert will be treated as if clientcert=verify-full (uaTrust) */
case uaTrust:
* We rely on that for MD5 and SCRAM authentication, but we still need
* this check here, to prevent an empty password from being used with
* authentication methods that check the password against an external
- * system, like PAM, LDAP and RADIUS.
+ * system, like PAM and LDAP.
*/
if (buf.len == 1)
ereport(ERROR,
return status_check_usermap;
}
#endif
-
-
-/*----------------------------------------------------------------
- * RADIUS authentication
- *----------------------------------------------------------------
- */
-
-/*
- * RADIUS authentication is described in RFC2865 (and several others).
- */
-
-#define RADIUS_VECTOR_LENGTH 16
-#define RADIUS_HEADER_LENGTH 20
-#define RADIUS_MAX_PASSWORD_LENGTH 128
-
-/* Maximum size of a RADIUS packet we will create or accept */
-#define RADIUS_BUFFER_SIZE 1024
-
-typedef struct
-{
- uint8 attribute;
- uint8 length;
- uint8 data[FLEXIBLE_ARRAY_MEMBER];
-} radius_attribute;
-
-typedef struct
-{
- uint8 code;
- uint8 id;
- uint16 length;
- uint8 vector[RADIUS_VECTOR_LENGTH];
- /* this is a bit longer than strictly necessary: */
- char pad[RADIUS_BUFFER_SIZE - RADIUS_VECTOR_LENGTH];
-} radius_packet;
-
-/* RADIUS packet types */
-#define RADIUS_ACCESS_REQUEST 1
-#define RADIUS_ACCESS_ACCEPT 2
-#define RADIUS_ACCESS_REJECT 3
-
-/* RADIUS 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
-
-/* Seconds to wait - XXX: should be in a config variable! */
-#define RADIUS_TIMEOUT 3
-
-static void
-radius_add_attribute(radius_packet *packet, uint8 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 %d with length %d 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;
- ListCell *server,
- *secrets,
- *radiusports,
- *identifiers;
-
- /* Make sure struct alignment is correct */
- Assert(offsetof(radius_packet, vector) == 4);
-
- /* Verify parameters */
- if (port->hba->radiusservers == NIL)
- {
- ereport(LOG,
- (errmsg("RADIUS server not specified")));
- return STATUS_ERROR;
- }
-
- if (port->hba->radiussecrets == NIL)
- {
- ereport(LOG,
- (errmsg("RADIUS secret not specified")));
- return STATUS_ERROR;
- }
-
- /* Send regular password request to client, and get the response */
- sendAuthRequest(port, AUTH_REQ_PASSWORD, NULL, 0);
-
- passwd = recv_password_packet(port);
- if (passwd == NULL)
- return STATUS_EOF; /* client wouldn't send password */
-
- if (strlen(passwd) > RADIUS_MAX_PASSWORD_LENGTH)
- {
- ereport(LOG,
- (errmsg("RADIUS authentication does not support passwords longer than %d characters", RADIUS_MAX_PASSWORD_LENGTH)));
- pfree(passwd);
- return STATUS_ERROR;
- }
-
- /*
- * Loop over and try each server in order.
- */
- secrets = list_head(port->hba->radiussecrets);
- radiusports = list_head(port->hba->radiusports);
- identifiers = list_head(port->hba->radiusidentifiers);
- foreach(server, port->hba->radiusservers)
- {
- int ret = PerformRadiusTransaction(lfirst(server),
- lfirst(secrets),
- radiusports ? lfirst(radiusports) : NULL,
- identifiers ? lfirst(identifiers) : NULL,
- port->user_name,
- passwd);
-
- /*------
- * STATUS_OK = Login OK
- * STATUS_ERROR = Login not OK, but try next server
- * STATUS_EOF = Login not OK, and don't try next server
- *------
- */
- if (ret == STATUS_OK)
- {
- set_authn_id(port, port->user_name);
-
- pfree(passwd);
- return STATUS_OK;
- }
- else if (ret == STATUS_EOF)
- {
- pfree(passwd);
- return STATUS_ERROR;
- }
-
- /*
- * secret, port and identifiers either have length 0 (use default),
- * length 1 (use the same everywhere) or the same length as servers.
- * So if the length is >1, we advance one step. In other cases, we
- * don't and will then reuse the correct value.
- */
- if (list_length(port->hba->radiussecrets) > 1)
- secrets = lnext(port->hba->radiussecrets, secrets);
- if (list_length(port->hba->radiusports) > 1)
- radiusports = lnext(port->hba->radiusports, radiusports);
- if (list_length(port->hba->radiusidentifiers) > 1)
- identifiers = lnext(port->hba->radiusidentifiers, identifiers);
- }
-
- /* No servers left to try, so give up */
- pfree(passwd);
- return STATUS_ERROR;
-}
-
-static int
-PerformRadiusTransaction(const char *server, const char *secret, const char *portstr, const char *identifier, const char *user_name, const char *passwd)
-{
- radius_packet radius_send_pack;
- radius_packet radius_recv_pack;
- radius_packet *packet = &radius_send_pack;
- radius_packet *receivepacket = &radius_recv_pack;
- void *radius_buffer = &radius_send_pack;
- void *receive_buffer = &radius_recv_pack;
- int32 service = pg_hton32(RADIUS_AUTHENTICATE_ONLY);
- uint8 *cryptvector;
- int encryptedpasswordlen;
- uint8 encryptedpassword[RADIUS_MAX_PASSWORD_LENGTH];
- uint8 *md5trailer;
- int packetlength;
- pgsocket sock;
-
- struct sockaddr_in6 localaddr;
- struct sockaddr_in6 remoteaddr;
- struct addrinfo hint;
- struct addrinfo *serveraddrs;
- int port;
- socklen_t addrsize;
- fd_set fdset;
- struct timeval endtime;
- int i,
- j,
- r;
-
- /* Assign default values */
- if (portstr == NULL)
- portstr = "1812";
- if (identifier == NULL)
- identifier = "postgresql";
-
- MemSet(&hint, 0, sizeof(hint));
- hint.ai_socktype = SOCK_DGRAM;
- hint.ai_family = AF_UNSPEC;
- port = atoi(portstr);
-
- r = pg_getaddrinfo_all(server, portstr, &hint, &serveraddrs);
- if (r || !serveraddrs)
- {
- ereport(LOG,
- (errmsg("could not translate RADIUS server name \"%s\" to address: %s",
- server, gai_strerror(r))));
- if (serveraddrs)
- pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
- return STATUS_ERROR;
- }
- /* XXX: add support for multiple returned addresses? */
-
- /* Construct RADIUS packet */
- packet->code = RADIUS_ACCESS_REQUEST;
- packet->length = RADIUS_HEADER_LENGTH;
- if (!pg_strong_random(packet->vector, RADIUS_VECTOR_LENGTH))
- {
- ereport(LOG,
- (errmsg("could not generate random encryption vector")));
- pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
- return STATUS_ERROR;
- }
- packet->id = packet->vector[0];
- radius_add_attribute(packet, RADIUS_SERVICE_TYPE, (const unsigned char *) &service, sizeof(service));
- radius_add_attribute(packet, RADIUS_USER_NAME, (const unsigned char *) user_name, strlen(user_name));
- radius_add_attribute(packet, RADIUS_NAS_IDENTIFIER, (const unsigned char *) identifier, strlen(identifier));
-
- /*
- * RADIUS password attributes are calculated as: e[0] = p[0] XOR
- * MD5(secret + Request Authenticator) for the first group of 16 octets,
- * and then: e[i] = p[i] XOR MD5(secret + e[i-1]) for the following ones
- * (if necessary)
- */
- encryptedpasswordlen = ((strlen(passwd) + RADIUS_VECTOR_LENGTH - 1) / RADIUS_VECTOR_LENGTH) * RADIUS_VECTOR_LENGTH;
- cryptvector = palloc(strlen(secret) + RADIUS_VECTOR_LENGTH);
- memcpy(cryptvector, secret, strlen(secret));
-
- /* for the first iteration, we use the Request Authenticator vector */
- md5trailer = packet->vector;
- for (i = 0; i < encryptedpasswordlen; i += RADIUS_VECTOR_LENGTH)
- {
- const char *errstr = NULL;
-
- memcpy(cryptvector + strlen(secret), md5trailer, RADIUS_VECTOR_LENGTH);
-
- /*
- * .. and for subsequent iterations the result of the previous XOR
- * (calculated below)
- */
- md5trailer = encryptedpassword + i;
-
- if (!pg_md5_binary(cryptvector, strlen(secret) + RADIUS_VECTOR_LENGTH,
- encryptedpassword + i, &errstr))
- {
- ereport(LOG,
- (errmsg("could not perform MD5 encryption of password: %s",
- errstr)));
- pfree(cryptvector);
- pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
- return STATUS_ERROR;
- }
-
- for (j = i; j < i + RADIUS_VECTOR_LENGTH; j++)
- {
- if (j < strlen(passwd))
- encryptedpassword[j] = passwd[j] ^ encryptedpassword[j];
- else
- encryptedpassword[j] = '\0' ^ encryptedpassword[j];
- }
- }
- pfree(cryptvector);
-
- radius_add_attribute(packet, RADIUS_PASSWORD, encryptedpassword, encryptedpasswordlen);
-
- /* Length needs to be in network order on the wire */
- packetlength = packet->length;
- packet->length = pg_hton16(packet->length);
-
- sock = socket(serveraddrs[0].ai_family, SOCK_DGRAM, 0);
- if (sock == PGINVALID_SOCKET)
- {
- ereport(LOG,
- (errmsg("could not create RADIUS socket: %m")));
- pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
- return STATUS_ERROR;
- }
-
- memset(&localaddr, 0, sizeof(localaddr));
- localaddr.sin6_family = serveraddrs[0].ai_family;
- localaddr.sin6_addr = in6addr_any;
- if (localaddr.sin6_family == AF_INET6)
- addrsize = sizeof(struct sockaddr_in6);
- else
- addrsize = sizeof(struct sockaddr_in);
-
- if (bind(sock, (struct sockaddr *) &localaddr, addrsize))
- {
- ereport(LOG,
- (errmsg("could not bind local RADIUS socket: %m")));
- closesocket(sock);
- pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
- return STATUS_ERROR;
- }
-
- if (sendto(sock, radius_buffer, packetlength, 0,
- serveraddrs[0].ai_addr, serveraddrs[0].ai_addrlen) < 0)
- {
- ereport(LOG,
- (errmsg("could not send RADIUS packet: %m")));
- closesocket(sock);
- pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
- return STATUS_ERROR;
- }
-
- /* Don't need the server address anymore */
- pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
-
- /*
- * Figure out at what time we should time out. We can't just use a single
- * call to select() with a timeout, since somebody can be sending invalid
- * packets to our port thus causing us to retry in a loop and never time
- * out.
- *
- * XXX: Using WaitLatchOrSocket() and doing a CHECK_FOR_INTERRUPTS() if
- * the latch was set would improve the responsiveness to
- * timeouts/cancellations.
- */
- gettimeofday(&endtime, NULL);
- endtime.tv_sec += RADIUS_TIMEOUT;
-
- while (true)
- {
- struct timeval timeout;
- struct timeval now;
- int64 timeoutval;
- const char *errstr = NULL;
-
- gettimeofday(&now, NULL);
- timeoutval = (endtime.tv_sec * 1000000 + endtime.tv_usec) - (now.tv_sec * 1000000 + now.tv_usec);
- if (timeoutval <= 0)
- {
- ereport(LOG,
- (errmsg("timeout waiting for RADIUS response from %s",
- server)));
- closesocket(sock);
- return STATUS_ERROR;
- }
- timeout.tv_sec = timeoutval / 1000000;
- timeout.tv_usec = timeoutval % 1000000;
-
- FD_ZERO(&fdset);
- FD_SET(sock, &fdset);
-
- 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 from %s",
- server)));
- closesocket(sock);
- return STATUS_ERROR;
- }
-
- /*
- * Attempt to read the response packet, and verify the contents.
- *
- * Any packet that's not actually a RADIUS packet, or otherwise does
- * not validate as an explicit reject, is just ignored and we retry
- * for another packet (until we reach the timeout). This is to avoid
- * the possibility to denial-of-service the login by flooding the
- * server with invalid packets on the port that we're expecting the
- * RADIUS response on.
- */
-
- 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;
- }
-
- if (remoteaddr.sin6_port != pg_hton16(port))
- {
- ereport(LOG,
- (errmsg("RADIUS response from %s was sent from incorrect port: %d",
- server, pg_ntoh16(remoteaddr.sin6_port))));
- continue;
- }
-
- if (packetlength < RADIUS_HEADER_LENGTH)
- {
- ereport(LOG,
- (errmsg("RADIUS response from %s too short: %d", server, packetlength)));
- continue;
- }
-
- if (packetlength != pg_ntoh16(receivepacket->length))
- {
- ereport(LOG,
- (errmsg("RADIUS response from %s has corrupt length: %d (actual length %d)",
- server, pg_ntoh16(receivepacket->length), packetlength)));
- continue;
- }
-
- if (packet->id != receivepacket->id)
- {
- ereport(LOG,
- (errmsg("RADIUS response from %s is to a different request: %d (should be %d)",
- server, receivepacket->id, packet->id)));
- continue;
- }
-
- /*
- * Verify the response authenticator, which is calculated as
- * MD5(Code+ID+Length+RequestAuthenticator+Attributes+Secret)
- */
- cryptvector = palloc(packetlength + strlen(secret));
-
- 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,
- (char *) receive_buffer + RADIUS_HEADER_LENGTH,
- packetlength - RADIUS_HEADER_LENGTH);
- memcpy(cryptvector + packetlength, secret, strlen(secret));
-
- if (!pg_md5_binary(cryptvector,
- packetlength + strlen(secret),
- encryptedpassword, &errstr))
- {
- ereport(LOG,
- (errmsg("could not perform MD5 encryption of received packet: %s",
- errstr)));
- pfree(cryptvector);
- continue;
- }
- pfree(cryptvector);
-
- if (memcmp(receivepacket->vector, encryptedpassword, RADIUS_VECTOR_LENGTH) != 0)
- {
- ereport(LOG,
- (errmsg("RADIUS response from %s has incorrect MD5 signature",
- server)));
- continue;
- }
-
- if (receivepacket->code == RADIUS_ACCESS_ACCEPT)
- {
- closesocket(sock);
- return STATUS_OK;
- }
- else if (receivepacket->code == RADIUS_ACCESS_REJECT)
- {
- closesocket(sock);
- return STATUS_EOF;
- }
- else
- {
- ereport(LOG,
- (errmsg("RADIUS response from %s has invalid code (%d) for user \"%s\"",
- server, receivepacket->code, user_name)));
- continue;
- }
- } /* while (true) */
-}
"bsd",
"ldap",
"cert",
- "radius",
"peer",
"oauth",
};
#else
unsupauth = "cert";
#endif
- else if (strcmp(token->string, "radius") == 0)
- parsedline->auth_method = uaRADIUS;
else if (strcmp(token->string, "oauth") == 0)
parsedline->auth_method = uaOAuth;
else
}
}
- if (parsedline->auth_method == uaRADIUS)
- {
- MANDATORY_AUTH_ARG(parsedline->radiusservers, "radiusservers", "radius");
- MANDATORY_AUTH_ARG(parsedline->radiussecrets, "radiussecrets", "radius");
-
- if (parsedline->radiusservers == NIL)
- {
- ereport(elevel,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("list of RADIUS servers cannot be empty"),
- errcontext("line %d of configuration file \"%s\"",
- line_num, file_name)));
- *err_msg = "list of RADIUS servers cannot be empty";
- return NULL;
- }
-
- if (parsedline->radiussecrets == NIL)
- {
- ereport(elevel,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("list of RADIUS secrets cannot be empty"),
- errcontext("line %d of configuration file \"%s\"",
- line_num, file_name)));
- *err_msg = "list of RADIUS secrets cannot be empty";
- return NULL;
- }
-
- /*
- * Verify length of option lists - each can be 0 (except for secrets,
- * but that's already checked above), 1 (use the same value
- * everywhere) or the same as the number of servers.
- */
- if (!(list_length(parsedline->radiussecrets) == 1 ||
- list_length(parsedline->radiussecrets) == list_length(parsedline->radiusservers)))
- {
- ereport(elevel,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("the number of RADIUS secrets (%d) must be 1 or the same as the number of RADIUS servers (%d)",
- list_length(parsedline->radiussecrets),
- list_length(parsedline->radiusservers)),
- errcontext("line %d of configuration file \"%s\"",
- line_num, file_name)));
- *err_msg = psprintf("the number of RADIUS secrets (%d) must be 1 or the same as the number of RADIUS servers (%d)",
- list_length(parsedline->radiussecrets),
- list_length(parsedline->radiusservers));
- return NULL;
- }
- if (!(list_length(parsedline->radiusports) == 0 ||
- list_length(parsedline->radiusports) == 1 ||
- list_length(parsedline->radiusports) == list_length(parsedline->radiusservers)))
- {
- ereport(elevel,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("the number of RADIUS ports (%d) must be 1 or the same as the number of RADIUS servers (%d)",
- list_length(parsedline->radiusports),
- list_length(parsedline->radiusservers)),
- errcontext("line %d of configuration file \"%s\"",
- line_num, file_name)));
- *err_msg = psprintf("the number of RADIUS ports (%d) must be 1 or the same as the number of RADIUS servers (%d)",
- list_length(parsedline->radiusports),
- list_length(parsedline->radiusservers));
- return NULL;
- }
- if (!(list_length(parsedline->radiusidentifiers) == 0 ||
- list_length(parsedline->radiusidentifiers) == 1 ||
- list_length(parsedline->radiusidentifiers) == list_length(parsedline->radiusservers)))
- {
- ereport(elevel,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("the number of RADIUS identifiers (%d) must be 1 or the same as the number of RADIUS servers (%d)",
- list_length(parsedline->radiusidentifiers),
- list_length(parsedline->radiusservers)),
- errcontext("line %d of configuration file \"%s\"",
- line_num, file_name)));
- *err_msg = psprintf("the number of RADIUS identifiers (%d) must be 1 or the same as the number of RADIUS servers (%d)",
- list_length(parsedline->radiusidentifiers),
- list_length(parsedline->radiusservers));
- return NULL;
- }
- }
-
/*
* Enforce any parameters implied by other settings.
*/
else
hbaline->upn_username = false;
}
- else if (strcmp(name, "radiusservers") == 0)
- {
- struct addrinfo *gai_result;
- struct addrinfo hints;
- int ret;
- List *parsed_servers;
- ListCell *l;
- char *dupval = pstrdup(val);
-
- REQUIRE_AUTH_OPTION(uaRADIUS, "radiusservers", "radius");
-
- if (!SplitGUCList(dupval, ',', &parsed_servers))
- {
- /* syntax error in list */
- ereport(elevel,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("could not parse RADIUS server list \"%s\"",
- val),
- errcontext("line %d of configuration file \"%s\"",
- line_num, file_name)));
- return false;
- }
-
- /* For each entry in the list, translate it */
- foreach(l, parsed_servers)
- {
- MemSet(&hints, 0, sizeof(hints));
- hints.ai_socktype = SOCK_DGRAM;
- hints.ai_family = AF_UNSPEC;
-
- ret = pg_getaddrinfo_all((char *) lfirst(l), NULL, &hints, &gai_result);
- if (ret || !gai_result)
- {
- ereport(elevel,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("could not translate RADIUS server name \"%s\" to address: %s",
- (char *) lfirst(l), gai_strerror(ret)),
- errcontext("line %d of configuration file \"%s\"",
- line_num, file_name)));
- if (gai_result)
- pg_freeaddrinfo_all(hints.ai_family, gai_result);
-
- list_free(parsed_servers);
- return false;
- }
- pg_freeaddrinfo_all(hints.ai_family, gai_result);
- }
-
- /* All entries are OK, so store them */
- hbaline->radiusservers = parsed_servers;
- hbaline->radiusservers_s = pstrdup(val);
- }
- else if (strcmp(name, "radiusports") == 0)
- {
- List *parsed_ports;
- ListCell *l;
- char *dupval = pstrdup(val);
-
- REQUIRE_AUTH_OPTION(uaRADIUS, "radiusports", "radius");
-
- if (!SplitGUCList(dupval, ',', &parsed_ports))
- {
- ereport(elevel,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("could not parse RADIUS port list \"%s\"",
- val),
- errcontext("line %d of configuration file \"%s\"",
- line_num, file_name)));
- *err_msg = psprintf("invalid RADIUS port number: \"%s\"", val);
- return false;
- }
-
- foreach(l, parsed_ports)
- {
- if (atoi(lfirst(l)) == 0)
- {
- ereport(elevel,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("invalid RADIUS port number: \"%s\"", val),
- errcontext("line %d of configuration file \"%s\"",
- line_num, file_name)));
-
- return false;
- }
- }
- hbaline->radiusports = parsed_ports;
- hbaline->radiusports_s = pstrdup(val);
- }
- else if (strcmp(name, "radiussecrets") == 0)
- {
- List *parsed_secrets;
- char *dupval = pstrdup(val);
-
- REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecrets", "radius");
-
- if (!SplitGUCList(dupval, ',', &parsed_secrets))
- {
- /* syntax error in list */
- ereport(elevel,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("could not parse RADIUS secret list \"%s\"",
- val),
- errcontext("line %d of configuration file \"%s\"",
- line_num, file_name)));
- return false;
- }
-
- hbaline->radiussecrets = parsed_secrets;
- hbaline->radiussecrets_s = pstrdup(val);
- }
- else if (strcmp(name, "radiusidentifiers") == 0)
- {
- List *parsed_identifiers;
- char *dupval = pstrdup(val);
-
- REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifiers", "radius");
-
- if (!SplitGUCList(dupval, ',', &parsed_identifiers))
- {
- /* syntax error in list */
- ereport(elevel,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("could not parse RADIUS identifiers list \"%s\"",
- val),
- errcontext("line %d of configuration file \"%s\"",
- line_num, file_name)));
- return false;
- }
-
- hbaline->radiusidentifiers = parsed_identifiers;
- hbaline->radiusidentifiers_s = pstrdup(val);
- }
else if (strcmp(name, "issuer") == 0)
{
REQUIRE_AUTH_OPTION(uaOAuth, "issuer", "oauth");