Skip to content

Commit befd67d

Browse files
author
Commitfest Bot
committed
[CF 4390] v15 - add not_before and not_after timestamps to sslinfo extension and pg_stat_ssl
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/4390 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/[email protected] Author(s): Cary Huang
2 parents 9ea3b6f + 559466a commit befd67d

File tree

17 files changed

+255
-32
lines changed

17 files changed

+255
-32
lines changed

contrib/sslinfo/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ OBJS = \
66
sslinfo.o
77

88
EXTENSION = sslinfo
9-
DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
9+
DATA = sslinfo--1.2--1.3.sql sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
1010
PGFILEDESC = "sslinfo - information about client SSL certificate"
1111

1212
ifdef USE_PGXS

contrib/sslinfo/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ install_data(
2626
'sslinfo--1.0--1.1.sql',
2727
'sslinfo--1.1--1.2.sql',
2828
'sslinfo--1.2.sql',
29+
'sslinfo--1.2--1.3.sql',
2930
'sslinfo.control',
3031
kwargs: contrib_data_args,
3132
)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
2+
3+
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
4+
\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
5+
6+
CREATE FUNCTION ssl_client_get_notbefore() RETURNS timestamptz
7+
AS 'MODULE_PATHNAME', 'ssl_client_get_notbefore'
8+
LANGUAGE C STRICT PARALLEL RESTRICTED;
9+
10+
CREATE FUNCTION ssl_client_get_notafter() RETURNS timestamptz
11+
AS 'MODULE_PATHNAME', 'ssl_client_get_notafter'
12+
LANGUAGE C STRICT PARALLEL RESTRICTED;

contrib/sslinfo/sslinfo.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "libpq/libpq-be.h"
1919
#include "miscadmin.h"
2020
#include "utils/builtins.h"
21+
#include "utils/timestamp.h"
2122

2223
PG_MODULE_MAGIC_EXT(
2324
.name = "sslinfo",
@@ -26,6 +27,7 @@ PG_MODULE_MAGIC_EXT(
2627

2728
static Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName);
2829
static Datum ASN1_STRING_to_text(ASN1_STRING *str);
30+
static Datum ASN1_TIME_to_timestamptz(const ASN1_TIME *time);
2931

3032
/*
3133
* Function context for data persisting over repeated calls.
@@ -217,6 +219,43 @@ X509_NAME_field_to_text(X509_NAME *name, text *fieldName)
217219
}
218220

219221

222+
/*
223+
* Converts OpenSSL ASN1_TIME structure into timestamptz
224+
*
225+
* Parameter: ASN1_TIME timestamp from certificate
226+
*
227+
* Returns the ASN1_TIME timestamp as a timestamptz datum.
228+
*/
229+
static Datum
230+
ASN1_TIME_to_timestamptz(const ASN1_TIME *ASN1_cert_ts)
231+
{
232+
struct pg_tm pgtm;
233+
struct tm tm;
234+
int ret;
235+
Timestamp ts;
236+
237+
ret = ASN1_TIME_to_tm(ASN1_cert_ts, &tm);
238+
if (!ret)
239+
ereport(ERROR,
240+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
241+
errmsg("failed to parse certificate validity")));
242+
243+
pgtm.tm_sec = tm.tm_sec;
244+
pgtm.tm_min = tm.tm_min;
245+
pgtm.tm_hour = tm.tm_hour;
246+
pgtm.tm_mday = tm.tm_mday;
247+
pgtm.tm_mon = tm.tm_mon + 1;
248+
pgtm.tm_year = tm.tm_year + 1900;
249+
250+
if (unlikely(tm2timestamp(&pgtm, 0, NULL, &ts) != 0))
251+
ereport(ERROR,
252+
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
253+
errmsg("timestamp out of range"));
254+
255+
PG_RETURN_TIMESTAMP(ts);
256+
}
257+
258+
220259
/*
221260
* Returns specified field of client certificate distinguished name
222261
*
@@ -474,3 +513,35 @@ ssl_extension_info(PG_FUNCTION_ARGS)
474513
/* All done */
475514
SRF_RETURN_DONE(funcctx);
476515
}
516+
517+
/*
518+
* Returns current client certificate notBefore timestamp in
519+
* timestamptz data type
520+
*/
521+
PG_FUNCTION_INFO_V1(ssl_client_get_notbefore);
522+
Datum
523+
ssl_client_get_notbefore(PG_FUNCTION_ARGS)
524+
{
525+
X509 *cert = MyProcPort->peer;
526+
527+
if (!MyProcPort->ssl_in_use || !MyProcPort->peer_cert_valid)
528+
PG_RETURN_NULL();
529+
530+
return ASN1_TIME_to_timestamptz(X509_get0_notBefore(cert));
531+
}
532+
533+
/*
534+
* Returns current client certificate notAfter timestamp in
535+
* timestamptz data type
536+
*/
537+
PG_FUNCTION_INFO_V1(ssl_client_get_notafter);
538+
Datum
539+
ssl_client_get_notafter(PG_FUNCTION_ARGS)
540+
{
541+
X509 *cert = MyProcPort->peer;
542+
543+
if (!MyProcPort->ssl_in_use || !MyProcPort->peer_cert_valid)
544+
PG_RETURN_NULL();
545+
546+
return ASN1_TIME_to_timestamptz(X509_get0_notAfter(cert));
547+
}

contrib/sslinfo/sslinfo.control

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# sslinfo extension
22
comment = 'information about SSL certificates'
3-
default_version = '1.2'
3+
default_version = '1.3'
44
module_pathname = '$libdir/sslinfo'
55
relocatable = true

doc/src/sgml/monitoring.sgml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2409,6 +2409,26 @@ description | Waiting for a newly initialized WAL file to reach durable storage
24092409
This field is truncated like <structfield>client_dn</structfield>.
24102410
</para></entry>
24112411
</row>
2412+
2413+
<row>
2414+
<entry role="catalog_table_entry"><para role="column_definition">
2415+
<structfield>not_before</structfield> <type>text</type>
2416+
</para>
2417+
<para>
2418+
Not before timestamp of the client certificate, or NULL if no client
2419+
certificate was supplied.
2420+
</para></entry>
2421+
</row>
2422+
2423+
<row>
2424+
<entry role="catalog_table_entry"><para role="column_definition">
2425+
<structfield>not_after</structfield> <type>text</type>
2426+
</para>
2427+
<para>
2428+
Not after timestamp of the client certificate, or NULL if no client
2429+
certificate was supplied.
2430+
</para></entry>
2431+
</row>
24122432
</tbody>
24132433
</tgroup>
24142434
</table>

doc/src/sgml/sslinfo.sgml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,36 @@ emailAddress
240240
</para>
241241
</listitem>
242242
</varlistentry>
243+
244+
<varlistentry>
245+
<term>
246+
<function>ssl_client_get_notbefore() returns timestamptz</function>
247+
<indexterm>
248+
<primary>ssl_client_get_notbefore</primary>
249+
</indexterm>
250+
</term>
251+
<listitem>
252+
<para>
253+
Return the <structfield>not before</structfield> timestamp of the client
254+
certificate.
255+
</para>
256+
</listitem>
257+
</varlistentry>
258+
259+
<varlistentry>
260+
<term>
261+
<function>ssl_client_get_notafter() returns timestamptz</function>
262+
<indexterm>
263+
<primary>ssl_client_get_notafter</primary>
264+
</indexterm>
265+
</term>
266+
<listitem>
267+
<para>
268+
Return the <structfield>not after</structfield> timestamp of the client
269+
certificate.
270+
</para>
271+
</listitem>
272+
</varlistentry>
243273
</variablelist>
244274
</sect2>
245275

src/backend/catalog/system_views.sql

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1010,7 +1010,9 @@ CREATE VIEW pg_stat_ssl AS
10101010
S.sslbits AS bits,
10111011
S.ssl_client_dn AS client_dn,
10121012
S.ssl_client_serial AS client_serial,
1013-
S.ssl_issuer_dn AS issuer_dn
1013+
S.ssl_issuer_dn AS issuer_dn,
1014+
S.ssl_not_before AS not_before,
1015+
S.ssl_not_after AS not_after
10141016
FROM pg_stat_get_activity(NULL) AS S
10151017
WHERE S.client_port IS NOT NULL;
10161018

src/backend/libpq/be-secure-openssl.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "storage/latch.h"
3636
#include "utils/guc.h"
3737
#include "utils/memutils.h"
38+
#include "utils/timestamp.h"
3839

3940
/*
4041
* These SSL-related #includes must come after all system-provided headers.
@@ -79,6 +80,7 @@ static const char *SSLerrmessageExt(unsigned long ecode, const char *replacement
7980
static const char *SSLerrmessage(unsigned long ecode);
8081

8182
static char *X509_NAME_to_cstring(X509_NAME *name);
83+
static TimestampTz ASN1_TIME_to_timestamptz(const ASN1_TIME *time);
8284

8385
static SSL_CTX *SSL_context = NULL;
8486
static bool dummy_ssl_passwd_cb_called = false;
@@ -1557,6 +1559,24 @@ be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len)
15571559
ptr[0] = '\0';
15581560
}
15591561

1562+
void
1563+
be_tls_get_peer_not_before(Port *port, TimestampTz *ptr)
1564+
{
1565+
if (port->peer)
1566+
*ptr = ASN1_TIME_to_timestamptz(X509_get0_notBefore(port->peer));
1567+
else
1568+
*ptr = 0;
1569+
}
1570+
1571+
void
1572+
be_tls_get_peer_not_after(Port *port, TimestampTz *ptr)
1573+
{
1574+
if (port->peer)
1575+
*ptr = ASN1_TIME_to_timestamptz(X509_get0_notAfter(port->peer));
1576+
else
1577+
*ptr = 0;
1578+
}
1579+
15601580
void
15611581
be_tls_get_peer_serial(Port *port, char *ptr, size_t len)
15621582
{
@@ -1700,6 +1720,38 @@ X509_NAME_to_cstring(X509_NAME *name)
17001720
return result;
17011721
}
17021722

1723+
/*
1724+
* Convert an ASN1_TIME to a PostgreSQL Timestamp datum.
1725+
*/
1726+
static TimestampTz
1727+
ASN1_TIME_to_timestamptz(const ASN1_TIME *ASN1_cert_ts)
1728+
{
1729+
struct pg_tm pgtm;
1730+
struct tm tm;
1731+
int ret;
1732+
TimestampTz ts;
1733+
1734+
ret = ASN1_TIME_to_tm(ASN1_cert_ts, &tm);
1735+
if (!ret)
1736+
ereport(ERROR,
1737+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1738+
errmsg("failed to parse certificate validity")));
1739+
1740+
pgtm.tm_sec = tm.tm_sec;
1741+
pgtm.tm_min = tm.tm_min;
1742+
pgtm.tm_hour = tm.tm_hour;
1743+
pgtm.tm_mday = tm.tm_mday;
1744+
pgtm.tm_mon = tm.tm_mon + 1;
1745+
pgtm.tm_year = tm.tm_year + 1900;
1746+
1747+
if (unlikely(tm2timestamp(&pgtm, 0, NULL, &ts) != 0))
1748+
ereport(ERROR,
1749+
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1750+
errmsg("timestamp out of range"));
1751+
1752+
return ts;
1753+
}
1754+
17031755
/*
17041756
* Convert TLS protocol version GUC enum to OpenSSL values
17051757
*

src/backend/utils/activity/backend_status.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,8 @@ pgstat_bestart_security(void)
413413
be_tls_get_peer_subject_name(MyProcPort, lsslstatus.ssl_client_dn, NAMEDATALEN);
414414
be_tls_get_peer_serial(MyProcPort, lsslstatus.ssl_client_serial, NAMEDATALEN);
415415
be_tls_get_peer_issuer_name(MyProcPort, lsslstatus.ssl_issuer_dn, NAMEDATALEN);
416+
be_tls_get_peer_not_before(MyProcPort, &lsslstatus.ssl_not_before);
417+
be_tls_get_peer_not_after(MyProcPort, &lsslstatus.ssl_not_after);
416418
}
417419
#endif
418420

0 commit comments

Comments
 (0)