diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index 53577d2..b16839e 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -1408,6 +1408,18 @@ ldapserver=ldap.example.net ldapprefix="cn=" ldapsuffix=", dc=example, dc=net" + radiussecretfile + + + Read the shared secret from the file specified. This file will be + read every time authentication is attempted, and should contain the + secret without any quotes. This parameter cannot be used together + with radiussecret. + + + + + radiusport diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 99d97b4..d32803c 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -37,6 +37,7 @@ #include "libpq/md5.h" #include "miscadmin.h" #include "storage/ipc.h" +#include "storage/fd.h" /*---------------------------------------------------------------- @@ -2511,6 +2512,8 @@ static int CheckRADIUSAuth(Port *port) { char *passwd; + char *secret; + char local_secret[RADIUS_BUFFER_SIZE]; char *identifier = "postgresql"; char radius_buffer[RADIUS_BUFFER_SIZE]; char receive_buffer[RADIUS_BUFFER_SIZE]; @@ -2547,13 +2550,50 @@ CheckRADIUSAuth(Port *port) return STATUS_ERROR; } - if (!port->hba->radiussecret || port->hba->radiussecret[0] == '\0') + if ((!port->hba->radiussecret || port->hba->radiussecret[0] == '\0') && + (!port->hba->radiussecretfile || port->hba->radiussecretfile[0] == '\0')) { ereport(LOG, (errmsg("RADIUS secret not specified"))); return STATUS_ERROR; } + if (port->hba->radiussecretfile) + { + FILE *f; + /* + * RADIUS secret is in a file. Read it into a temporary buffer and use + * that. + */ + f = AllocateFile(port->hba->radiussecretfile, "r"); + if (!f) + { + ereport(LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("could not open RADIUS secret file \"%s\": %m", + port->hba->radiussecretfile))); + return STATUS_ERROR; + } + if (!fgets(local_secret, sizeof(local_secret), f)) + { + ereport(LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("could not read from RADIUS secret file \"%s\": %m", + port->hba->radiussecretfile))); + FreeFile(f); + return STATUS_ERROR; + } + FreeFile(f); + while (strlen(local_secret) && + (local_secret[strlen(local_secret)-1] == '\n' || + local_secret[strlen(local_secret)-1] == '\r')) + local_secret[strlen(local_secret)-1] = '\0'; + + secret = local_secret; + } + else + secret = port->hba->radiussecret; + if (port->hba->radiusport == 0) port->hba->radiusport = 1812; @@ -2622,10 +2662,10 @@ CheckRADIUSAuth(Port *port) * 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)) + cryptvector = palloc(RADIUS_VECTOR_LENGTH + strlen(secret)); + memcpy(cryptvector, secret, strlen(secret)); + memcpy(cryptvector + strlen(secret), packet->vector, RADIUS_VECTOR_LENGTH); + if (!pg_md5_binary(cryptvector, RADIUS_VECTOR_LENGTH + strlen(secret), encryptedpassword)) { ereport(LOG, (errmsg("could not perform md5 encryption of password"))); @@ -2780,16 +2820,16 @@ CheckRADIUSAuth(Port *port) * Verify the response authenticator, which is calculated as * MD5(Code+ID+Length+RequestAuthenticator+Attributes+Secret) */ - cryptvector = palloc(packetlength + strlen(port->hba->radiussecret)); + 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, receive_buffer + RADIUS_HEADER_LENGTH, packetlength-RADIUS_HEADER_LENGTH); - memcpy(cryptvector+packetlength, port->hba->radiussecret, strlen(port->hba->radiussecret)); + memcpy(cryptvector+packetlength, secret, strlen(secret)); if (!pg_md5_binary(cryptvector, - packetlength + strlen(port->hba->radiussecret), + packetlength + strlen(secret), encryptedpassword)) { ereport(LOG, diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index de56683..352898d 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -1207,6 +1207,11 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius"); parsedline->radiussecret = pstrdup(c); } + else if (strcmp(token, "radiussecretfile") == 0) + { + REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecretfile", "radius"); + parsedline->radiussecretfile = pstrdup(c); + } else if (strcmp(token, "radiusidentifier") == 0) { REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius"); @@ -1267,7 +1272,24 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline) if (parsedline->auth_method == uaRADIUS) { MANDATORY_AUTH_ARG(parsedline->radiusserver, "radiusserver", "radius"); - MANDATORY_AUTH_ARG(parsedline->radiussecret, "radiussecret", "radius"); + if (!(parsedline->radiussecret || parsedline->radiussecretfile)) + { + ereport(LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("authentication method \"radius\" requires argument \"radiussecret\" or \"radiussecretfile\" to be set"), + errcontext("line %d of configuration file \"%s\"", + line_num, HbaFileName))); + return false; + } + if (parsedline->radiussecret && parsedline->radiussecretfile) + { + ereport(LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("cannot use \"radiussecretfile\" together with \"radiussecret\""), + errcontext("line %d of configuration file \"%s\"", + line_num, HbaFileName))); + return false; + } } /* diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h index 76fefcb..4e5d3b1 100644 --- a/src/include/libpq/hba.h +++ b/src/include/libpq/hba.h @@ -74,6 +74,7 @@ typedef struct bool include_realm; char *radiusserver; char *radiussecret; + char *radiussecretfile; char *radiusidentifier; int radiusport; } HbaLine;