Skip to content

Commit 75ec5e7

Browse files
Add notBefore and notAfter to SSL cert info display
This adds the X509 attributes notBefore and notAfter to sslinfo as well as pg_stat_ssl to allow verifying and identifying the validity period of the current client certificate. Author: Cary Huang <cary.huang@highgo.ca> Discussion: https://postgr.es/m/182b8565486.10af1a86f158715.2387262617218380588@highgo.ca
1 parent 40fad96 commit 75ec5e7

File tree

18 files changed

+246
-33
lines changed

18 files changed

+246
-33
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
)

contrib/sslinfo/sslinfo--1.2--1.3.sql

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 timestamp
7+
AS 'MODULE_PATHNAME', 'ssl_client_get_notbefore'
8+
LANGUAGE C STRICT PARALLEL RESTRICTED;
9+
10+
CREATE FUNCTION ssl_client_get_notafter() RETURNS timestamp
11+
AS 'MODULE_PATHNAME', 'ssl_client_get_notafter'
12+
LANGUAGE C STRICT PARALLEL RESTRICTED;

contrib/sslinfo/sslinfo.c

Lines changed: 67 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
/*
2324
* On Windows, <wincrypt.h> includes a #define for X509_NAME, which breaks our
@@ -34,6 +35,7 @@ PG_MODULE_MAGIC;
3435

3536
static Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName);
3637
static Datum ASN1_STRING_to_text(ASN1_STRING *str);
38+
static Datum ASN1_TIME_to_timestamp(ASN1_TIME *time);
3739

3840
/*
3941
* Function context for data persisting over repeated calls.
@@ -225,6 +227,39 @@ X509_NAME_field_to_text(X509_NAME *name, text *fieldName)
225227
}
226228

227229

230+
/*
231+
* Converts OpenSSL ASN1_TIME structure into timestamp
232+
*
233+
* Parameter: time - OpenSSL ASN1_TIME structure.
234+
*
235+
* Returns Datum, which can be directly returned from a C language SQL
236+
* function.
237+
*/
238+
static Datum
239+
ASN1_TIME_to_timestamp(ASN1_TIME * time)
240+
{
241+
struct tm tm_time;
242+
struct pg_tm pgtm_time;
243+
Timestamp ts;
244+
245+
ASN1_TIME_to_tm(time, &tm_time);
246+
247+
pgtm_time.tm_sec = tm_time.tm_sec;
248+
pgtm_time.tm_min = tm_time.tm_min;
249+
pgtm_time.tm_hour = tm_time.tm_hour;
250+
pgtm_time.tm_mday = tm_time.tm_mday;
251+
pgtm_time.tm_mon = tm_time.tm_mon + 1;
252+
pgtm_time.tm_year = tm_time.tm_year + 1900;
253+
254+
if (tm2timestamp(&pgtm_time, 0, NULL, &ts))
255+
ereport(ERROR,
256+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
257+
errmsg("failed to convert tm to timestamp")));
258+
259+
PG_RETURN_TIMESTAMP(ts);
260+
}
261+
262+
228263
/*
229264
* Returns specified field of client certificate distinguished name
230265
*
@@ -482,3 +517,35 @@ ssl_extension_info(PG_FUNCTION_ARGS)
482517
/* All done */
483518
SRF_RETURN_DONE(funcctx);
484519
}
520+
521+
/*
522+
* Returns current client certificate notBefore timestamp in
523+
* timestamp data type
524+
*/
525+
PG_FUNCTION_INFO_V1(ssl_client_get_notbefore);
526+
Datum
527+
ssl_client_get_notbefore(PG_FUNCTION_ARGS)
528+
{
529+
X509 *cert = MyProcPort->peer;
530+
531+
if (!MyProcPort->ssl_in_use || !MyProcPort->peer_cert_valid)
532+
PG_RETURN_NULL();
533+
534+
return ASN1_TIME_to_timestamp(X509_get_notBefore(cert));
535+
}
536+
537+
/*
538+
* Returns current client certificate notAfter timestamp in
539+
* timestamp data type
540+
*/
541+
PG_FUNCTION_INFO_V1(ssl_client_get_notafter);
542+
Datum
543+
ssl_client_get_notafter(PG_FUNCTION_ARGS)
544+
{
545+
X509 *cert = MyProcPort->peer;
546+
547+
if (!MyProcPort->ssl_in_use || !MyProcPort->peer_cert_valid)
548+
PG_RETURN_NULL();
549+
550+
return ASN1_TIME_to_timestamp(X509_get_notAfter(cert));
551+
}

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
@@ -2257,6 +2257,26 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
22572257
This field is truncated like <structfield>client_dn</structfield>.
22582258
</para></entry>
22592259
</row>
2260+
2261+
<row>
2262+
<entry role="catalog_table_entry"><para role="column_definition">
2263+
<structfield>not_before</structfield> <type>text</type>
2264+
</para>
2265+
<para>
2266+
Not before UTC timestamp of the client certificate, or NULL if no client
2267+
certificate was supplied.
2268+
</para></entry>
2269+
</row>
2270+
2271+
<row>
2272+
<entry role="catalog_table_entry"><para role="column_definition">
2273+
<structfield>not_after</structfield> <type>text</type>
2274+
</para>
2275+
<para>
2276+
Not after UTC timestamp of the client certificate, or NULL if no client
2277+
certificate was supplied.
2278+
</para></entry>
2279+
</row>
22602280
</tbody>
22612281
</tgroup>
22622282
</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 text</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> UTC timestamp of the client
254+
certificate.
255+
</para>
256+
</listitem>
257+
</varlistentry>
258+
259+
<varlistentry>
260+
<term>
261+
<function>ssl_client_get_notafter() returns text</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> UTC 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
@@ -970,7 +970,9 @@ CREATE VIEW pg_stat_ssl AS
970970
S.sslbits AS bits,
971971
S.ssl_client_dn AS client_dn,
972972
S.ssl_client_serial AS client_serial,
973-
S.ssl_issuer_dn AS issuer_dn
973+
S.ssl_issuer_dn AS issuer_dn,
974+
S.ssl_not_before AS not_before,
975+
S.ssl_not_after AS not_after
974976
FROM pg_stat_get_activity(NULL) AS S
975977
WHERE S.client_port IS NOT NULL;
976978

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "tcop/tcopprot.h"
3737
#include "utils/builtins.h"
3838
#include "utils/memutils.h"
39+
#include "utils/timestamp.h"
3940

4041
/*
4142
* These SSL-related #includes must come after all system-provided headers.
@@ -72,6 +73,7 @@ static bool initialize_ecdh(SSL_CTX *context, bool isServerStart);
7273
static const char *SSLerrmessage(unsigned long ecode);
7374

7475
static char *X509_NAME_to_cstring(X509_NAME *name);
76+
static Timestamp ASN1_TIME_to_timestamp(ASN1_TIME *time);
7577

7678
static SSL_CTX *SSL_context = NULL;
7779
static bool SSL_initialized = false;
@@ -1406,6 +1408,24 @@ be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len)
14061408
ptr[0] = '\0';
14071409
}
14081410

1411+
void
1412+
be_tls_get_peer_not_before(Port *port, Timestamp *ptr)
1413+
{
1414+
if (port->peer)
1415+
*ptr = ASN1_TIME_to_timestamp(X509_get_notBefore(port->peer));
1416+
else
1417+
*ptr = 0;
1418+
}
1419+
1420+
void
1421+
be_tls_get_peer_not_after(Port *port, Timestamp *ptr)
1422+
{
1423+
if (port->peer)
1424+
*ptr = ASN1_TIME_to_timestamp(X509_get_notAfter(port->peer));
1425+
else
1426+
*ptr = 0;
1427+
}
1428+
14091429
void
14101430
be_tls_get_peer_serial(Port *port, char *ptr, size_t len)
14111431
{
@@ -1549,6 +1569,33 @@ X509_NAME_to_cstring(X509_NAME *name)
15491569
return result;
15501570
}
15511571

1572+
/*
1573+
* Convert an ASN1_TIME to a Timestamp
1574+
*/
1575+
static Timestamp
1576+
ASN1_TIME_to_timestamp(ASN1_TIME * time)
1577+
{
1578+
struct tm tm_time;
1579+
struct pg_tm pgtm_time;
1580+
Timestamp ts;
1581+
1582+
ASN1_TIME_to_tm(time, &tm_time);
1583+
1584+
pgtm_time.tm_sec = tm_time.tm_sec;
1585+
pgtm_time.tm_min = tm_time.tm_min;
1586+
pgtm_time.tm_hour = tm_time.tm_hour;
1587+
pgtm_time.tm_mday = tm_time.tm_mday;
1588+
pgtm_time.tm_mon = tm_time.tm_mon + 1;
1589+
pgtm_time.tm_year = tm_time.tm_year + 1900;
1590+
1591+
if (tm2timestamp(&pgtm_time, 0, NULL, &ts))
1592+
ereport(ERROR,
1593+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1594+
errmsg("timestamp out of range")));
1595+
1596+
return ts;
1597+
}
1598+
15521599
/*
15531600
* Convert TLS protocol version GUC enum to OpenSSL values
15541601
*

src/backend/utils/activity/backend_status.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,8 @@ pgstat_bestart(void)
367367
be_tls_get_peer_subject_name(MyProcPort, lsslstatus.ssl_client_dn, NAMEDATALEN);
368368
be_tls_get_peer_serial(MyProcPort, lsslstatus.ssl_client_serial, NAMEDATALEN);
369369
be_tls_get_peer_issuer_name(MyProcPort, lsslstatus.ssl_issuer_dn, NAMEDATALEN);
370+
be_tls_get_peer_not_before(MyProcPort, &lsslstatus.ssl_not_before);
371+
be_tls_get_peer_not_after(MyProcPort, &lsslstatus.ssl_not_after);
370372
}
371373
else
372374
{

src/backend/utils/adt/pgstatfuncs.c

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
303303
Datum
304304
pg_stat_get_activity(PG_FUNCTION_ARGS)
305305
{
306-
#define PG_STAT_GET_ACTIVITY_COLS 31
306+
#define PG_STAT_GET_ACTIVITY_COLS 33
307307
int num_backends = pgstat_fetch_stat_numbackends();
308308
int curr_backend;
309309
int pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
@@ -395,7 +395,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
395395
pfree(clipped_activity);
396396

397397
/* leader_pid */
398-
nulls[29] = true;
398+
nulls[31] = true;
399399

400400
proc = BackendPidGetProc(beentry->st_procpid);
401401

@@ -432,17 +432,17 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
432432
*/
433433
if (leader && leader->pid != beentry->st_procpid)
434434
{
435-
values[29] = Int32GetDatum(leader->pid);
436-
nulls[29] = false;
435+
values[31] = Int32GetDatum(leader->pid);
436+
nulls[31] = false;
437437
}
438438
else if (beentry->st_backendType == B_BG_WORKER)
439439
{
440440
int leader_pid = GetLeaderApplyWorkerPid(beentry->st_procpid);
441441

442442
if (leader_pid != InvalidPid)
443443
{
444-
values[29] = Int32GetDatum(leader_pid);
445-
nulls[29] = false;
444+
values[31] = Int32GetDatum(leader_pid);
445+
nulls[31] = false;
446446
}
447447
}
448448
}
@@ -587,35 +587,45 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
587587
values[24] = CStringGetTextDatum(beentry->st_sslstatus->ssl_issuer_dn);
588588
else
589589
nulls[24] = true;
590+
591+
if (beentry->st_sslstatus->ssl_not_before != 0)
592+
values[25] = TimestampGetDatum(beentry->st_sslstatus->ssl_not_before);
593+
else
594+
nulls[25] = true;
595+
596+
if (beentry->st_sslstatus->ssl_not_after != 0)
597+
values[26] = TimestampGetDatum(beentry->st_sslstatus->ssl_not_after);
598+
else
599+
nulls[26] = true;
590600
}
591601
else
592602
{
593603
values[18] = BoolGetDatum(false); /* ssl */
594-
nulls[19] = nulls[20] = nulls[21] = nulls[22] = nulls[23] = nulls[24] = true;
604+
nulls[19] = nulls[20] = nulls[21] = nulls[22] = nulls[23] = nulls[24] = nulls[25] = nulls[26] = true;
595605
}
596606

597607
/* GSSAPI information */
598608
if (beentry->st_gss)
599609
{
600-
values[25] = BoolGetDatum(beentry->st_gssstatus->gss_auth); /* gss_auth */
601-
values[26] = CStringGetTextDatum(beentry->st_gssstatus->gss_princ);
602-
values[27] = BoolGetDatum(beentry->st_gssstatus->gss_enc); /* GSS Encryption in use */
603-
values[28] = BoolGetDatum(beentry->st_gssstatus->gss_delegation); /* GSS credentials
610+
values[27] = BoolGetDatum(beentry->st_gssstatus->gss_auth); /* gss_auth */
611+
values[28] = CStringGetTextDatum(beentry->st_gssstatus->gss_princ);
612+
values[29] = BoolGetDatum(beentry->st_gssstatus->gss_enc); /* GSS Encryption in use */
613+
values[30] = BoolGetDatum(beentry->st_gssstatus->gss_delegation); /* GSS credentials
604614
* delegated */
605615
}
606616
else
607617
{
608-
values[25] = BoolGetDatum(false); /* gss_auth */
609-
nulls[26] = true; /* No GSS principal */
610-
values[27] = BoolGetDatum(false); /* GSS Encryption not in
618+
values[27] = BoolGetDatum(false); /* gss_auth */
619+
nulls[28] = true; /* No GSS principal */
620+
values[29] = BoolGetDatum(false); /* GSS Encryption not in
611621
* use */
612-
values[28] = BoolGetDatum(false); /* GSS credentials not
622+
values[30] = BoolGetDatum(false); /* GSS credentials not
613623
* delegated */
614624
}
615625
if (beentry->st_query_id == 0)
616-
nulls[30] = true;
626+
nulls[32] = true;
617627
else
618-
values[30] = UInt64GetDatum(beentry->st_query_id);
628+
values[32] = UInt64GetDatum(beentry->st_query_id);
619629
}
620630
else
621631
{
@@ -645,6 +655,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
645655
nulls[28] = true;
646656
nulls[29] = true;
647657
nulls[30] = true;
658+
nulls[31] = true;
659+
nulls[32] = true;
648660
}
649661

650662
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy