Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd6e612f

Browse files
committed
Add libpq parameter 'channel_binding'.
Allow clients to require channel binding to enhance security againstuntrusted servers.Author: Jeff DavisReviewed-by: Michael PaquierDiscussion:https://postgr.es/m/227015d8417f2b4fef03f8966dbfa5cbcc4f44da.camel%40j-davis.com
1 parent13cd97e commitd6e612f

File tree

9 files changed

+233
-20
lines changed

9 files changed

+233
-20
lines changed

‎doc/src/sgml/libpq.sgml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,28 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
11221122
</listitem>
11231123
</varlistentry>
11241124

1125+
<varlistentry id="libpq-connect-channel-binding" xreflabel="channel_binding">
1126+
<term><literal>channel_binding</literal></term>
1127+
<listitem>
1128+
<para>
1129+
This option controls the client's use of channel binding. A setting
1130+
of <literal>require</literal> means that the connection must employ
1131+
channel binding, <literal>prefer</literal> means that the client will
1132+
choose channel binding if available, and <literal>disable</literal>
1133+
prevents the use of channel binding. The default
1134+
is <literal>prefer</literal> if
1135+
<productname>PostgreSQL</productname> is compiled with SSL support;
1136+
otherwise the default is <literal>disable</literal>.
1137+
</para>
1138+
<para>
1139+
Channel binding is a method for the server to authenticate itself to
1140+
the client. It is only supported over SSL connections
1141+
with <productname>PostgreSQL</productname> 11 or later servers using
1142+
the <literal>SCRAM</literal> authentication method.
1143+
</para>
1144+
</listitem>
1145+
</varlistentry>
1146+
11251147
<varlistentry id="libpq-connect-connect-timeout" xreflabel="connect_timeout">
11261148
<term><literal>connect_timeout</literal></term>
11271149
<listitem>
@@ -6864,6 +6886,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
68646886
</para>
68656887
</listitem>
68666888

6889+
<listitem>
6890+
<para>
6891+
<indexterm>
6892+
<primary><envar>PGCHANNELBINDING</envar></primary>
6893+
</indexterm>
6894+
<envar>PGCHANNELBINDING</envar> behaves the same as the <xref
6895+
linkend="libpq-connect-channel-binding"/> connection parameter.
6896+
</para>
6897+
</listitem>
6898+
68676899
<listitem>
68686900
<para>
68696901
<indexterm>

‎src/interfaces/libpq/fe-auth-scram.c

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,35 @@ pg_fe_scram_init(PGconn *conn,
119119
returnstate;
120120
}
121121

122+
/*
123+
* Return true if channel binding was employed and the SCRAM exchange
124+
* completed. This should be used after a successful exchange to determine
125+
* whether the server authenticated itself to the client.
126+
*
127+
* Note that the caller must also ensure that the exchange was actually
128+
* successful.
129+
*/
130+
bool
131+
pg_fe_scram_channel_bound(void*opaq)
132+
{
133+
fe_scram_state*state= (fe_scram_state*)opaq;
134+
135+
/* no SCRAM exchange done */
136+
if (state==NULL)
137+
return false;
138+
139+
/* SCRAM exchange not completed */
140+
if (state->state!=FE_SCRAM_FINISHED)
141+
return false;
142+
143+
/* channel binding mechanism not used */
144+
if (strcmp(state->sasl_mechanism,SCRAM_SHA_256_PLUS_NAME)!=0)
145+
return false;
146+
147+
/* all clear! */
148+
return true;
149+
}
150+
122151
/*
123152
* Free SCRAM exchange status
124153
*/
@@ -225,9 +254,7 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
225254

226255
/*
227256
* Verify server signature, to make sure we're talking to the
228-
* genuine server. XXX: A fake server could simply not require
229-
* authentication, though. There is currently no option in libpq
230-
* to reject a connection, if SCRAM authentication did not happen.
257+
* genuine server.
231258
*/
232259
if (verify_server_signature(state))
233260
*success= true;
@@ -358,7 +385,8 @@ build_client_first_message(fe_scram_state *state)
358385
appendPQExpBufferStr(&buf,"p=tls-server-end-point");
359386
}
360387
#ifdefHAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
361-
elseif (conn->ssl_in_use)
388+
elseif (conn->channel_binding[0]!= 'd'&&/* disable */
389+
conn->ssl_in_use)
362390
{
363391
/*
364392
* Client supports channel binding, but thinks the server does not.
@@ -369,7 +397,7 @@ build_client_first_message(fe_scram_state *state)
369397
else
370398
{
371399
/*
372-
* Client does not support channel binding.
400+
* Client does not support channel binding, or has disabled it.
373401
*/
374402
appendPQExpBufferChar(&buf,'n');
375403
}
@@ -498,7 +526,8 @@ build_client_final_message(fe_scram_state *state)
498526
#endif/* HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH */
499527
}
500528
#ifdefHAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
501-
elseif (conn->ssl_in_use)
529+
elseif (conn->channel_binding[0]!= 'd'&&/* disable */
530+
conn->ssl_in_use)
502531
appendPQExpBufferStr(&buf, "c=eSws");/* base64 of "y,,"*/
503532
#endif
504533
else

‎src/interfaces/libpq/fe-auth.c

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,14 @@ pg_SASL_init(PGconn *conn, int payloadlen)
423423

424424
initPQExpBuffer(&mechanism_buf);
425425

426+
if (conn->channel_binding[0]=='r'&&/* require */
427+
!conn->ssl_in_use)
428+
{
429+
printfPQExpBuffer(&conn->errorMessage,
430+
libpq_gettext("Channel binding required, but SSL not in use\n"));
431+
gotoerror;
432+
}
433+
426434
if (conn->sasl_state)
427435
{
428436
printfPQExpBuffer(&conn->errorMessage,
@@ -454,10 +462,10 @@ pg_SASL_init(PGconn *conn, int payloadlen)
454462

455463
/*
456464
* Select the mechanism to use. Pick SCRAM-SHA-256-PLUS over anything
457-
* else if a channel binding type is set and if the client supports
458-
*it. Pick SCRAM-SHA-256 if nothing else has already been picked. If
459-
* we add more mechanisms, a more refined priority mechanism might
460-
* become necessary.
465+
* else if a channel binding type is set and if the client supports it
466+
*(and did not set channel_binding=disable). Pick SCRAM-SHA-256 if
467+
*nothing else has already been picked. Ifwe add more mechanisms, a
468+
*more refined priority mechanism mightbecome necessary.
461469
*/
462470
if (strcmp(mechanism_buf.data,SCRAM_SHA_256_PLUS_NAME)==0)
463471
{
@@ -466,10 +474,11 @@ pg_SASL_init(PGconn *conn, int payloadlen)
466474
/*
467475
* The server has offered SCRAM-SHA-256-PLUS, which is only
468476
* supported by the client if a hash of the peer certificate
469-
* can be created.
477+
* can be created, and if channel_binding is not disabled.
470478
*/
471479
#ifdefHAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
472-
selected_mechanism=SCRAM_SHA_256_PLUS_NAME;
480+
if (conn->channel_binding[0]!='d')/* disable */
481+
selected_mechanism=SCRAM_SHA_256_PLUS_NAME;
473482
#endif
474483
}
475484
else
@@ -493,6 +502,14 @@ pg_SASL_init(PGconn *conn, int payloadlen)
493502
selected_mechanism=SCRAM_SHA_256_NAME;
494503
}
495504

505+
if (conn->channel_binding[0]=='r'&&/* require */
506+
strcmp(selected_mechanism,SCRAM_SHA_256_PLUS_NAME)!=0)
507+
{
508+
printfPQExpBuffer(&conn->errorMessage,
509+
libpq_gettext("channel binding is required, but server did not offer an authentication method that supports channel binding\n"));
510+
gotoerror;
511+
}
512+
496513
if (!selected_mechanism)
497514
{
498515
printfPQExpBuffer(&conn->errorMessage,
@@ -774,6 +791,50 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
774791
returnret;
775792
}
776793

794+
/*
795+
* Verify that the authentication request is expected, given the connection
796+
* parameters. This is especially important when the client wishes to
797+
* authenticate the server before any sensitive information is exchanged.
798+
*/
799+
staticbool
800+
check_expected_areq(AuthRequestareq,PGconn*conn)
801+
{
802+
boolresult= true;
803+
804+
/*
805+
* When channel_binding=require, we must protect against two cases: (1) we
806+
* must not respond to non-SASL authentication requests, which might leak
807+
* information such as the client's password; and (2) even if we receive
808+
* AUTH_REQ_OK, we still must ensure that channel binding has happened in
809+
* order to authenticate the server.
810+
*/
811+
if (conn->channel_binding[0]=='r'/* require */ )
812+
{
813+
switch (areq)
814+
{
815+
caseAUTH_REQ_SASL:
816+
caseAUTH_REQ_SASL_CONT:
817+
caseAUTH_REQ_SASL_FIN:
818+
break;
819+
caseAUTH_REQ_OK:
820+
if (!pg_fe_scram_channel_bound(conn->sasl_state))
821+
{
822+
printfPQExpBuffer(&conn->errorMessage,
823+
libpq_gettext("Channel binding required, but server authenticated client without channel binding\n"));
824+
result= false;
825+
}
826+
break;
827+
default:
828+
printfPQExpBuffer(&conn->errorMessage,
829+
libpq_gettext("Channel binding required but not supported by server's authentication request\n"));
830+
result= false;
831+
break;
832+
}
833+
}
834+
835+
returnresult;
836+
}
837+
777838
/*
778839
* pg_fe_sendauth
779840
*client demux routine for processing an authentication request
@@ -788,6 +849,9 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
788849
int
789850
pg_fe_sendauth(AuthRequestareq,intpayloadlen,PGconn*conn)
790851
{
852+
if (!check_expected_areq(areq,conn))
853+
returnSTATUS_ERROR;
854+
791855
switch (areq)
792856
{
793857
caseAUTH_REQ_OK:

‎src/interfaces/libpq/fe-auth.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ extern char *pg_fe_getauthname(PQExpBuffer errorMessage);
2626
externvoid*pg_fe_scram_init(PGconn*conn,
2727
constchar*password,
2828
constchar*sasl_mechanism);
29+
externboolpg_fe_scram_channel_bound(void*opaq);
2930
externvoidpg_fe_scram_free(void*opaq);
3031
externvoidpg_fe_scram_exchange(void*opaq,char*input,intinputlen,
3132
char**output,int*outputlen,

‎src/interfaces/libpq/fe-connect.c

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ static intldapServiceLookup(const char *purl, PQconninfoOption *options,
124124
#defineDefaultTty""
125125
#defineDefaultOption""
126126
#defineDefaultAuthtype ""
127+
#ifdefUSE_SSL
128+
#defineDefaultChannelBinding"prefer"
129+
#else
130+
#defineDefaultChannelBinding"disable"
131+
#endif
127132
#defineDefaultTargetSessionAttrs"any"
128133
#ifdefUSE_SSL
129134
#defineDefaultSSLMode "prefer"
@@ -211,6 +216,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
211216
"Database-Password-File","",64,
212217
offsetof(structpg_conn,pgpassfile)},
213218

219+
{"channel_binding","PGCHANNELBINDING",NULL,NULL,
220+
"Channel-Binding","",7,/* sizeof("require") */
221+
offsetof(structpg_conn,channel_binding)},
222+
214223
{"connect_timeout","PGCONNECT_TIMEOUT",NULL,NULL,
215224
"Connect-timeout","",10,/* strlen(INT32_MAX) == 10 */
216225
offsetof(structpg_conn,connect_timeout)},
@@ -1197,6 +1206,29 @@ connectOptions2(PGconn *conn)
11971206
}
11981207
}
11991208

1209+
/*
1210+
* validate channel_binding option
1211+
*/
1212+
if (conn->channel_binding)
1213+
{
1214+
if (strcmp(conn->channel_binding,"disable")!=0
1215+
&&strcmp(conn->channel_binding,"prefer")!=0
1216+
&&strcmp(conn->channel_binding,"require")!=0)
1217+
{
1218+
conn->status=CONNECTION_BAD;
1219+
printfPQExpBuffer(&conn->errorMessage,
1220+
libpq_gettext("invalid channel_binding value: \"%s\"\n"),
1221+
conn->channel_binding);
1222+
return false;
1223+
}
1224+
}
1225+
else
1226+
{
1227+
conn->channel_binding=strdup(DefaultChannelBinding);
1228+
if (!conn->channel_binding)
1229+
gotooom_error;
1230+
}
1231+
12001232
/*
12011233
* validate sslmode option
12021234
*/
@@ -3485,10 +3517,11 @@ PQconnectPoll(PGconn *conn)
34853517
caseCONNECTION_SETENV:
34863518
{
34873519
/*
3488-
* Do post-connection housekeeping (only needed in protocol 2.0).
3520+
* Do post-connection housekeeping (only needed in protocol
3521+
* 2.0).
34893522
*
3490-
* We pretend that the connection is OK for the duration of these
3491-
* queries.
3523+
* We pretend that the connection is OK for the duration of
3524+
*thesequeries.
34923525
*/
34933526
conn->status=CONNECTION_OK;
34943527

@@ -3905,6 +3938,8 @@ freePGconn(PGconn *conn)
39053938
}
39063939
if (conn->pgpassfile)
39073940
free(conn->pgpassfile);
3941+
if (conn->channel_binding)
3942+
free(conn->channel_binding);
39083943
if (conn->keepalives)
39093944
free(conn->keepalives);
39103945
if (conn->keepalives_idle)

‎src/interfaces/libpq/libpq-int.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,8 @@ struct pg_conn
347347
char*pguser;/* Postgres username and password, if any */
348348
char*pgpass;
349349
char*pgpassfile;/* path to a file containing password(s) */
350+
char*channel_binding;/* channel binding mode
351+
* (require,prefer,disable) */
350352
char*keepalives;/* use TCP keepalives? */
351353
char*keepalives_idle;/* time between TCP keepalives */
352354
char*keepalives_interval;/* time between TCP keepalive

‎src/test/authentication/t/001_password.pl

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
}
1818
else
1919
{
20-
plantests=>8;
20+
plantests=>10;
2121
}
2222

2323

@@ -86,3 +86,13 @@ sub test_role
8686
reset_pg_hba($node,'md5');
8787
test_role($node,'scram_role','md5', 0);
8888
test_role($node,'md5_role','md5', 0);
89+
90+
# Tests for channel binding without SSL.
91+
# Using the password authentication method; channel binding can't work
92+
reset_pg_hba($node,'password');
93+
$ENV{"PGCHANNELBINDING"} ='require';
94+
test_role($node,'scram_role','scram-sha-256', 2);
95+
# SSL not in use; channel binding still can't work
96+
reset_pg_hba($node,'scram-sha-256');
97+
$ENV{"PGCHANNELBINDING"} ='require';
98+
test_role($node,'scram_role','scram-sha-256', 2);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp