Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit eb882a1

Browse files
committed
GSSAPI: Improve documentation and tests
The GSSAPI encryption patch neglected to update the protocol documentation to describe how to set up a GSSAPI encrypted connection from a client to the server, so fix that by adding the appropriate documentation to protocol.sgml. The tests added for encryption support were overly long and couldn't be run in parallel due to race conditions; this was largely because each test was setting up its own KDC to perform the tests. Instead, merge the authentication tests and the encryption tests into the original test, where we only create one KDC to run the tests with. Also, have the tests check what the server's opinion is of the connection and if it was GSS authenticated or encrypted using the pg_stat_gssapi view. In passing, fix the libpq label for GSSENC-Mode to be consistent with the "PGGSSENCMODE" environment variable. Missing protocol documentation pointed out by Michael Paquier. Issues with the tests pointed out by Tom Lane and Peter Eisentraut. Refactored tests and added documentation by me. Reviewed by Robbie Harwood (protocol documentation) and Michael Paquier (rework of the tests).
1 parent b8b94ea commit eb882a1

File tree

4 files changed

+216
-211
lines changed

4 files changed

+216
-211
lines changed

doc/src/sgml/protocol.sgml

+106
Original file line numberDiff line numberDiff line change
@@ -1479,6 +1479,75 @@ SELCT 1/0;
14791479
of authentication checking.
14801480
</para>
14811481
</sect2>
1482+
1483+
<sect2>
1484+
<title><acronym>GSSAPI</acronym> Session Encryption</title>
1485+
1486+
<para>
1487+
If <productname>PostgreSQL</productname> was built with
1488+
<acronym>GSSAPI</acronym> support, frontend/backend communications
1489+
can be encrypted using <acronym>GSSAPI</acronym>. This provides
1490+
communication security in environments where attackers might be
1491+
able to capture the session traffic. For more information on
1492+
encrypting <productname>PostgreSQL</productname> sessions with
1493+
<acronym>GSSAPI</acronym>, see <xref linkend="gssapi-enc"/>.
1494+
</para>
1495+
1496+
<para>
1497+
To initiate a <acronym>GSSAPI</acronym>-encrypted connection, the
1498+
frontend initially sends a GSSENCRequest message rather than a
1499+
StartupMessage. The server then responds with a single byte
1500+
containing <literal>G</literal> or <literal>N</literal>, indicating that it
1501+
is willing or unwilling to perform <acronym>GSSAPI</acronym> encryption,
1502+
respectively. The frontend might close the connection at this point
1503+
if it is dissatisfied with the response. To continue after
1504+
<literal>G</literal>, using the GSSAPI C bindings as discussed in RFC2744
1505+
or equivilant, perform a <acronym>GSSAPI</acronym> initialization by
1506+
calling <function>gss_init_sec_context()</function> in a loop and sending
1507+
the result to the server, starting with an empty input and then with each
1508+
result from the server, until it returns no output. When sending the
1509+
results of <function>gss_init_sec_context()</function> to the server,
1510+
prepend the length of the message as a four byte integer in network byte
1511+
order. If this is successful, then use <function>gss_wrap()</function> to
1512+
encrypt the usual StartupMessage and all subsequent data, prepending the
1513+
length of the result from <function>gss_wrap()</function> as a four byte
1514+
integer in network byte order to the actual encrypted payload. Note that
1515+
the server will only accept encrypted packets from the client which are less
1516+
than 16KB; <function>gss_wrap_size_limit()</function> should be used by the
1517+
client to determine the size of the unencrypted message which will fit
1518+
within this limit and larger messages should be broken up into multiple
1519+
<function>gss_wrap()</function> calls. Typical segments are 8KB of
1520+
unencrypted data, resulting in encrypted packets of slightly larger than 8KB
1521+
but well within the 16KB maximum. The server can be expected to not send
1522+
encrypted packets of larger than 16KB to the client. To continue after
1523+
<literal>N</literal>, send the usual StartupMessage and proceed without
1524+
encryption.
1525+
</para>
1526+
1527+
<para>
1528+
The frontend should also be prepared to handle an ErrorMessage
1529+
response to GSSENCRequest from the server. This would only occur if
1530+
the server predates the addition of <acronym>GSSAPI</acronym> encryption
1531+
support to <productname>PostgreSQL</productname>. In this case the
1532+
connection must be closed, but the frontend might choose to open a fresh
1533+
connection and proceed without requesting <acronym>GSSAPI</acronym>
1534+
encryption. Given the length limits specified above, the ErrorMessage can
1535+
not be confused with a proper response from the server with an appropriate
1536+
length.
1537+
</para>
1538+
1539+
<para>
1540+
An initial GSSENCRequest can also be used in a connection that is being
1541+
opened to send a CancelRequest message.
1542+
</para>
1543+
1544+
<para>
1545+
While the protocol itself does not provide a way for the server to
1546+
force <acronym>GSSAPI</acronym> encryption, the administrator can
1547+
configure the server to reject unencrypted sessions as a byproduct
1548+
of authentication checking.
1549+
</para>
1550+
</sect2>
14821551
</sect1>
14831552

14841553
<sect1 id="sasl-authentication">
@@ -5714,6 +5783,43 @@ SSLRequest (F)
57145783
</listitem>
57155784
</varlistentry>
57165785

5786+
<varlistentry>
5787+
<term>
5788+
GSSENCRequest (F)
5789+
</term>
5790+
<listitem>
5791+
<para>
5792+
5793+
<variablelist>
5794+
<varlistentry>
5795+
<term>
5796+
Int32(8)
5797+
</term>
5798+
<listitem>
5799+
<para>
5800+
Length of message contents in bytes, including self.
5801+
</para>
5802+
</listitem>
5803+
</varlistentry>
5804+
<varlistentry>
5805+
<term>
5806+
Int32(80877104)
5807+
</term>
5808+
<listitem>
5809+
<para>
5810+
The <acronym>GSSAPI</acronym> Encryption request code. The value is chosen to contain
5811+
<literal>1234</literal> in the most significant 16 bits, and <literal>5680</literal> in the
5812+
least significant 16 bits. (To avoid confusion, this code
5813+
must not be the same as any protocol version number.)
5814+
</para>
5815+
</listitem>
5816+
</varlistentry>
5817+
</variablelist>
5818+
5819+
</para>
5820+
</listitem>
5821+
</varlistentry>
5822+
57175823

57185824
<varlistentry>
57195825
<term>

src/interfaces/libpq/fe-connect.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
313313
* and "prefer".
314314
*/
315315
{"gssencmode", "PGGSSENCMODE", DefaultGSSMode, NULL,
316-
"GSS-Mode", "", 7, /* sizeof("disable") == 7 */
316+
"GSSENC-Mode", "", 7, /* sizeof("disable") == 7 */
317317
offsetof(struct pg_conn, gssencmode)},
318318

319319
#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)

src/test/kerberos/t/001_auth.pl

+109-13
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
# Sets up a KDC and then runs a variety of tests to make sure that the
2+
# GSSAPI/Kerberos authentication and encryption are working properly,
3+
# that the options in pg_hba.conf and pg_ident.conf are handled correctly,
4+
# and that the server-side pg_stat_gssapi view reports what we expect to
5+
# see for each test.
6+
#
7+
# Since this requires setting up a full KDC, it doesn't make much sense
8+
# to have multiple test scripts (since they'd have to also create their
9+
# own KDC and that could cause race conditions or other problems)- so
10+
# just add whatever other tests are needed to here.
11+
#
12+
# See the README for additional information.
13+
114
use strict;
215
use warnings;
316
use TestLib;
@@ -6,7 +19,7 @@
619

720
if ($ENV{with_gssapi} eq 'yes')
821
{
9-
plan tests => 4;
22+
plan tests => 12;
1023
}
1124
else
1225
{
@@ -50,7 +63,7 @@
5063

5164
my $host = 'auth-test-localhost.postgresql.example.com';
5265
my $hostaddr = '127.0.0.1';
53-
my $realm = 'EXAMPLE.COM';
66+
my $realm = 'EXAMPLE.COM';
5467

5568
my $krb5_conf = "${TestLib::tmp_check}/krb5.conf";
5669
my $kdc_conf = "${TestLib::tmp_check}/kdc.conf";
@@ -155,18 +168,30 @@ END
155168

156169
sub test_access
157170
{
158-
my ($node, $role, $expected_res, $test_name) = @_;
171+
my ($node, $role, $server_check, $expected_res, $gssencmode, $test_name)
172+
= @_;
159173

160174
# need to connect over TCP/IP for Kerberos
161-
my $res = $node->psql(
175+
my ($res, $stdoutres, $stderrres) = $node->psql(
162176
'postgres',
163-
'SELECT 1',
177+
"$server_check",
164178
extra_params => [
165-
'-d',
166-
$node->connstr('postgres') . " host=$host hostaddr=$hostaddr",
167-
'-U', $role
179+
'-XAtd',
180+
$node->connstr('postgres')
181+
. " host=$host hostaddr=$hostaddr $gssencmode",
182+
'-U',
183+
$role
168184
]);
169-
is($res, $expected_res, $test_name);
185+
186+
# If we get a query result back, it should be true.
187+
if ($res == $expected_res and $res eq 0)
188+
{
189+
is($stdoutres, "t", $test_name);
190+
}
191+
else
192+
{
193+
is($res, $expected_res, $test_name);
194+
}
170195
return;
171196
}
172197

@@ -175,21 +200,92 @@ sub test_access
175200
qq{host all all $hostaddr/32 gss map=mymap});
176201
$node->restart;
177202

178-
test_access($node, 'test1', 2, 'fails without ticket');
203+
test_access($node, 'test1', 'SELECT true', 2, '', 'fails without ticket');
179204

180205
run_log [ $kinit, 'test1' ], \$test1_password or BAIL_OUT($?);
181206

182-
test_access($node, 'test1', 2, 'fails without mapping');
207+
test_access($node, 'test1', 'SELECT true', 2, '', 'fails without mapping');
183208

184209
$node->append_conf('pg_ident.conf', qq{mymap /^(.*)\@$realm\$ \\1});
185210
$node->restart;
186211

187-
test_access($node, 'test1', 0, 'succeeds with mapping');
212+
test_access(
213+
$node,
214+
'test1',
215+
'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
216+
0,
217+
'',
218+
'succeeds with mapping with default gssencmode and host hba');
219+
test_access(
220+
$node,
221+
"test1",
222+
'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
223+
0,
224+
"gssencmode=prefer",
225+
"succeeds with GSS-encrypted access preferred with host hba");
226+
test_access(
227+
$node,
228+
"test1",
229+
'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
230+
0,
231+
"gssencmode=require",
232+
"succeeds with GSS-encrypted access required with host hba");
233+
234+
unlink($node->data_dir . '/pg_hba.conf');
235+
$node->append_conf('pg_hba.conf',
236+
qq{hostgssenc all all $hostaddr/32 gss map=mymap});
237+
$node->restart;
238+
239+
test_access(
240+
$node,
241+
"test1",
242+
'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
243+
0,
244+
"gssencmode=prefer",
245+
"succeeds with GSS-encrypted access preferred and hostgssenc hba");
246+
test_access(
247+
$node,
248+
"test1",
249+
'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
250+
0,
251+
"gssencmode=require",
252+
"succeeds with GSS-encrypted access required and hostgssenc hba");
253+
test_access($node, "test1", 'SELECT true', 2, "gssencmode=disable",
254+
"fails with GSS encryption disabled and hostgssenc hba");
255+
256+
unlink($node->data_dir . '/pg_hba.conf');
257+
$node->append_conf('pg_hba.conf',
258+
qq{hostnogssenc all all $hostaddr/32 gss map=mymap});
259+
$node->restart;
260+
261+
test_access(
262+
$node,
263+
"test1",
264+
'SELECT gss_authenticated and not encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
265+
0,
266+
"gssencmode=prefer",
267+
"succeeds with GSS-encrypted access preferred and hostnogssenc hba, but no encryption"
268+
);
269+
test_access($node, "test1", 'SELECT true', 2, "gssencmode=require",
270+
"fails with GSS-encrypted access required and hostnogssenc hba");
271+
test_access(
272+
$node,
273+
"test1",
274+
'SELECT gss_authenticated and not encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
275+
0,
276+
"gssencmode=disable",
277+
"succeeds with GSS encryption disabled and hostnogssenc hba");
188278

189279
truncate($node->data_dir . '/pg_ident.conf', 0);
190280
unlink($node->data_dir . '/pg_hba.conf');
191281
$node->append_conf('pg_hba.conf',
192282
qq{host all all $hostaddr/32 gss include_realm=0});
193283
$node->restart;
194284

195-
test_access($node, 'test1', 0, 'succeeds with include_realm=0');
285+
test_access(
286+
$node,
287+
'test1',
288+
'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
289+
0,
290+
'',
291+
'succeeds with include_realm=0 and defaults');

0 commit comments

Comments
 (0)