On Mon, Jul 21, 2014 at 5:24 PM, Bernd Helmle <maili...@oopsware.de> wrote: > > > --On 12. Juli 2014 15:08:01 +0200 Magnus Hagander <mag...@hagander.net> > wrote: > >> Before doing that, however, I'd like to ask for opinions :) The hack >> currently exposes a separate view that you can join to >> pg_stat_activity (or pg_stat_replication) on the pid -- this is sort >> of the same way that pg_stat_replication works in the first place. Do >> we want something similar to that for a builtin SSL view as well, or >> do we want to include the fields directly in pg_stat_activity and >> pg_stat_replication? > > > I've heard more than once the wish to get this information without > contrib..especially for the SSL version used (client and server likewise). > So ++1 for this feature. > > I'd vote for a special view, that will keep the information into a single > place and someone can easily join extra information together.
Here's a patch that implements that. Docs are currently ont included because I'm waiting for the restructuring of tha section to be done (started by me in a separate thread) first, but the code is there for review. Right now it just truncates the dn at NAMEDATALEN - so treating it the same as we do with hostnames. My guess is this is not a big problem because in the case of long DNs, most of the time the important stuff is at the beginning anyway... (And it's not like it's actually used for authentication, in which case it would of course be a problem). -- Magnus Hagander Me: http://www.hagander.net/ Work: http://www.redpill-linpro.com/
*** a/src/backend/catalog/system_views.sql --- b/src/backend/catalog/system_views.sql *************** *** 648,653 **** CREATE VIEW pg_stat_replication AS --- 648,664 ---- WHERE S.usesysid = U.oid AND S.pid = W.pid; + CREATE VIEW pg_stat_ssl AS + SELECT + I.pid, + I.ssl, + I.bits, + I.compression, + I.version, + I.cipher, + I.clientdn + FROM pg_stat_get_sslstatus() AS I; + CREATE VIEW pg_replication_slots AS SELECT L.slot_name, *** a/src/backend/libpq/be-secure-openssl.c --- b/src/backend/libpq/be-secure-openssl.c *************** *** 88,93 **** static void info_cb(const SSL *ssl, int type, int args); --- 88,95 ---- static void initialize_ecdh(void); static const char *SSLerrmessage(void); + static char *X509_NAME_to_cstring(X509_NAME *name); + /* are we in the middle of a renegotiation? */ static bool in_ssl_renegotiation = false; *************** *** 1053,1055 **** SSLerrmessage(void) --- 1055,1159 ---- snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode); return errbuf; } + + /* + * Return information about the SSL connection + */ + int + be_tls_get_cipher_bits(Port *port) + { + int bits; + + if (port->ssl) + { + SSL_get_cipher_bits(port->ssl, &bits); + return bits; + } + else + return 0; + } + + bool + be_tls_get_compression(Port *port) + { + if (port->ssl) + return (SSL_get_current_compression(port->ssl) != NULL); + else + return false; + } + + void + be_tls_get_version(Port *port, char *ptr, size_t len) + { + if (port->ssl) + strlcpy(ptr, SSL_get_version(port->ssl), len); + else + ptr[0] = '\0'; + } + + void + be_tls_get_cipher(Port *port, char *ptr, size_t len) + { + if (port->ssl) + strlcpy(ptr, SSL_get_cipher(port->ssl), NAMEDATALEN); + else + ptr[0] = '\0'; + } + + void + be_tls_get_peerdn_name(Port *port, char *ptr, size_t len) + { + if (port->peer) + strlcpy(ptr, X509_NAME_to_cstring(X509_get_subject_name(port->peer)), NAMEDATALEN); + else + ptr[0] = '\0'; + } + + /* + * Convert an X509 subject name to a cstring. + * + */ + static char * + X509_NAME_to_cstring(X509_NAME *name) + { + BIO *membuf = BIO_new(BIO_s_mem()); + int i, + nid, + count = X509_NAME_entry_count(name); + X509_NAME_ENTRY *e; + ASN1_STRING *v; + const char *field_name; + size_t size; + char nullterm; + char *sp; + char *dp; + char *result; + + (void) BIO_set_close(membuf, BIO_CLOSE); + for (i = 0; i < count; i++) + { + e = X509_NAME_get_entry(name, i); + nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(e)); + v = X509_NAME_ENTRY_get_data(e); + field_name = OBJ_nid2sn(nid); + if (!field_name) + field_name = OBJ_nid2ln(nid); + BIO_printf(membuf, "/%s=", field_name); + ASN1_STRING_print_ex(membuf, v, + ((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB) + | ASN1_STRFLGS_UTF8_CONVERT)); + } + + /* ensure null termination of the BIO's content */ + nullterm = '\0'; + BIO_write(membuf, &nullterm, 1); + size = BIO_get_mem_data(membuf, &sp); + dp = pg_any_to_server(sp, size - 1, PG_UTF8); + + result = pstrdup(dp); + if (dp != sp) + pfree(dp); + BIO_free(membuf); + + return result; + } *** a/src/backend/postmaster/pgstat.c --- b/src/backend/postmaster/pgstat.c *************** *** 2386,2391 **** pgstat_fetch_global(void) --- 2386,2394 ---- static PgBackendStatus *BackendStatusArray = NULL; static PgBackendStatus *MyBEEntry = NULL; static char *BackendClientHostnameBuffer = NULL; + static char *BackendSslVersionBuffer = NULL; + static char *BackendSslCipherBuffer = NULL; + static char *BackendSslClientDNBuffer = NULL; static char *BackendAppnameBuffer = NULL; static char *BackendActivityBuffer = NULL; static Size BackendActivityBufferSize = 0; *************** *** 2470,2475 **** CreateSharedBackendStatus(void) --- 2473,2531 ---- } } + /* Create or attach to the shared SSL status buffers */ + size = mul_size(NAMEDATALEN, MaxBackends); + BackendSslVersionBuffer = (char *) + ShmemInitStruct("Backend SSL Version Buffer", size, &found); + + if (!found) + { + MemSet(BackendSslVersionBuffer, 0, size); + + /* Initialize st_ssl_version pointers. */ + buffer = BackendSslVersionBuffer; + for (i = 0; i < MaxBackends; i++) + { + BackendStatusArray[i].st_ssl_version = buffer; + buffer += NAMEDATALEN; + } + } + + size = mul_size(NAMEDATALEN, MaxBackends); + BackendSslCipherBuffer = (char *) + ShmemInitStruct("Backend SSL Cipher Buffer", size, &found); + + if (!found) + { + MemSet(BackendSslCipherBuffer, 0, size); + + /* Initialize st_ssl_cipher pointers. */ + buffer = BackendSslCipherBuffer; + for (i = 0; i < MaxBackends; i++) + { + BackendStatusArray[i].st_ssl_cipher = buffer; + buffer += NAMEDATALEN; + } + } + + size = mul_size(NAMEDATALEN, MaxBackends); + BackendSslClientDNBuffer = (char *) + ShmemInitStruct("Backend SSL Client DN Buffer", size, &found); + + if (!found) + { + MemSet(BackendSslClientDNBuffer, 0, size); + + /* Initialize st_ssl_clientdn pointers. */ + buffer = BackendSslClientDNBuffer; + for (i = 0; i < MaxBackends; i++) + { + BackendStatusArray[i].st_ssl_clientdn = buffer; + buffer += NAMEDATALEN; + } + } + + /* Create or attach to the shared activity buffer */ BackendActivityBufferSize = mul_size(pgstat_track_activity_query_size, MaxBackends); *************** *** 2579,2584 **** pgstat_bestart(void) --- 2635,2653 ---- NAMEDATALEN); else beentry->st_clienthostname[0] = '\0'; + #ifdef USE_SSL + beentry->st_ssl = (MyProcPort && MyProcPort->ssl != NULL); + if (beentry->st_ssl) + { + beentry->st_ssl_bits = be_tls_get_cipher_bits(MyProcPort); + beentry->st_ssl_compression = be_tls_get_compression(MyProcPort); + be_tls_get_version(MyProcPort, beentry->st_ssl_version, NAMEDATALEN); + be_tls_get_cipher(MyProcPort, beentry->st_ssl_cipher, NAMEDATALEN); + be_tls_get_peerdn_name(MyProcPort, beentry->st_ssl_clientdn, NAMEDATALEN); + } + #else + beentry->st_ssl = false; + #endif beentry->st_waiting = false; beentry->st_state = STATE_UNDEFINED; beentry->st_appname[0] = '\0'; *************** *** 2806,2811 **** pgstat_read_current_status(void) --- 2875,2883 ---- LocalPgBackendStatus *localtable; LocalPgBackendStatus *localentry; char *localappname, + *localsslversion, + *localsslcipher, + *localsslclientdn, *localactivity; int i; *************** *** 2821,2826 **** pgstat_read_current_status(void) --- 2893,2907 ---- localappname = (char *) MemoryContextAlloc(pgStatLocalContext, NAMEDATALEN * MaxBackends); + localsslversion = (char *) + MemoryContextAlloc(pgStatLocalContext, + NAMEDATALEN * MaxBackends); + localsslcipher = (char *) + MemoryContextAlloc(pgStatLocalContext, + NAMEDATALEN * MaxBackends); + localsslclientdn = (char *) + MemoryContextAlloc(pgStatLocalContext, + NAMEDATALEN * MaxBackends); localactivity = (char *) MemoryContextAlloc(pgStatLocalContext, pgstat_track_activity_query_size * MaxBackends); *************** *** 2854,2859 **** pgstat_read_current_status(void) --- 2935,2946 ---- localentry->backendStatus.st_appname = localappname; strcpy(localactivity, (char *) beentry->st_activity); localentry->backendStatus.st_activity = localactivity; + strcpy(localsslversion, (char *) beentry->st_ssl_version); + localentry->backendStatus.st_ssl_version = localsslversion; + strcpy(localsslcipher, (char *) beentry->st_ssl_cipher); + localentry->backendStatus.st_ssl_cipher = localsslcipher; + strcpy(localsslclientdn, (char *) beentry->st_ssl_clientdn); + localentry->backendStatus.st_ssl_clientdn = localsslclientdn; } if (save_changecount == beentry->st_changecount && *************** *** 2874,2879 **** pgstat_read_current_status(void) --- 2961,2969 ---- localentry++; localappname += NAMEDATALEN; + localsslversion += NAMEDATALEN; + localsslcipher += NAMEDATALEN; + localsslclientdn += NAMEDATALEN; localactivity += pgstat_track_activity_query_size; localNumBackends++; } *** a/src/backend/utils/adt/pgstatfuncs.c --- b/src/backend/utils/adt/pgstatfuncs.c *************** *** 821,826 **** pg_stat_get_activity(PG_FUNCTION_ARGS) --- 821,905 ---- } } + /* + * Returns SSL information for all connections, both regular backend and + * WAL senders. + */ + Datum + pg_stat_get_sslstatus(PG_FUNCTION_ARGS) + { + #define PG_STAT_GET_SSLSTATUS_COLS 7 + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + TupleDesc tupdesc; + Tuplestorestate *tupstore; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + int i; + + /* check to see if caller supports us returning a tuplestore */ + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + if (!(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not " \ + "allowed in this context"))); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(per_query_ctx); + + tupstore = tuplestore_begin_heap(true, false, work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + + MemoryContextSwitchTo(oldcontext); + + for (i = 0; i < pgstat_fetch_stat_numbackends(); i++) + { + Datum values[PG_STAT_GET_SSLSTATUS_COLS]; + bool nulls[PG_STAT_GET_SSLSTATUS_COLS]; + PgBackendStatus *beentry = &pgstat_fetch_stat_local_beentry(i+1)->backendStatus; + + if (beentry->st_procpid == 0) + continue; + + MemSet(nulls, 0, sizeof(nulls)); + /* XXX: somethign superuser only? */ + + values[0] = Int32GetDatum(beentry->st_procpid); + values[1] = BoolGetDatum(beentry->st_ssl); + if (beentry->st_ssl) + { + values[2] = Int32GetDatum(beentry->st_ssl_bits); + values[3] = BoolGetDatum(beentry->st_ssl_compression); + values[4] = CStringGetTextDatum(beentry->st_ssl_version); + values[5] = CStringGetTextDatum(beentry->st_ssl_cipher); + values[6] = CStringGetTextDatum(beentry->st_ssl_clientdn); + } + else + { + nulls[2] = true; /* bits */ + nulls[3] = true; /* compression */ + nulls[4] = true; /* version */ + nulls[5] = true; /* cipher */ + nulls[6] = true; /* clientdn */ + } + + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + } + + /* clean up and return the tuplestore */ + tuplestore_donestoring(tupstore); + + return (Datum) 0; + } Datum pg_backend_pid(PG_FUNCTION_ARGS) *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** *** 2693,2698 **** DATA(insert OID = 2022 ( pg_stat_get_activity PGNSP PGUID 12 1 100 0 0 f f f --- 2693,2700 ---- DESCR("statistics: information about currently active backends"); DATA(insert OID = 3099 ( pg_stat_get_wal_senders PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,25,3220,3220,3220,3220,23,25}" "{o,o,o,o,o,o,o,o}" "{pid,state,sent_location,write_location,flush_location,replay_location,sync_priority,sync_state}" _null_ pg_stat_get_wal_senders _null_ _null_ _null_ )); DESCR("statistics: information about currently active replication"); + DATA(insert OID = 3259 ( pg_stat_get_sslstatus PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{23,16,23,16,25,25,25}" "{o,o,o,o,o,o,o}" "{pid,ssl,bits,compression,version,cipher,clientdn}" _null_ pg_stat_get_sslstatus _null_ _null_ _null_ )); + DESCR("statistics: information about SSL connections"); DATA(insert OID = 2026 ( pg_backend_pid PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 23 "" _null_ _null_ _null_ _null_ pg_backend_pid _null_ _null_ _null_ )); DESCR("statistics: current backend PID"); DATA(insert OID = 1937 ( pg_stat_get_backend_pid PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 23 "23" _null_ _null_ _null_ _null_ pg_stat_get_backend_pid _null_ _null_ _null_ )); *** a/src/include/libpq/libpq-be.h --- b/src/include/libpq/libpq-be.h *************** *** 210,215 **** extern void be_tls_close(Port *port); --- 210,220 ---- extern ssize_t be_tls_read(Port *port, void *ptr, size_t len); extern ssize_t be_tls_write(Port *port, void *ptr, size_t len); + extern int be_tls_get_cipher_bits(Port *port); + extern bool be_tls_get_compression(Port *port); + extern void be_tls_get_version(Port *port, char *ptr, size_t len); + extern void be_tls_get_cipher(Port *port, char *ptr, size_t len); + extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len); #endif extern ProtocolVersion FrontendProtocol; *** a/src/include/pgstat.h --- b/src/include/pgstat.h *************** *** 732,737 **** typedef struct PgBackendStatus --- 732,745 ---- SockAddr st_clientaddr; char *st_clienthostname; /* MUST be null-terminated */ + /* Information about SSL connection */ + bool st_ssl; + int st_ssl_bits; + bool st_ssl_compression; + char *st_ssl_version; /* MUST be null-terminated */ + char *st_ssl_cipher; /* MUST be null-terminated */ + char *st_ssl_clientdn; /* MUST be null-terminated */ + /* Is backend currently waiting on an lmgr lock? */ bool st_waiting;
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers