Datum pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS) { int32 beid = PG_GETARG_INT32(0); PgBackendStatus *beentry; SockAddr zero_clientaddr; char remote_host[NI_MAXHOST]; int ret; if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); if (!has_privs_of_role(GetUserId(), beentry->st_userid)) PG_RETURN_NULL(); /* A zeroed client addr means we don't know */ memset(&zero_clientaddr, 0, sizeof(zero_clientaddr)); if (memcmp(&(beentry->st_clientaddr), &zero_clientaddr, sizeof(zero_clientaddr)) == 0) PG_RETURN_NULL(); switch (beentry->st_clientaddr.addr.ss_family) { case AF_INET: #ifdef HAVE_IPV6 case AF_INET6: #endif break; default: PG_RETURN_NULL(); } remote_host[0] = '\0'; ret = pg_getnameinfo_all(&beentry->st_clientaddr.addr, beentry->st_clientaddr.salen, remote_host, sizeof(remote_host), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); if (ret != 0) PG_RETURN_NULL(); clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, remote_host); PG_RETURN_INET_P(DirectFunctionCall1(inet_in, CStringGetDatum(remote_host))); }
Datum pg_stat_get_backend_client_port(PG_FUNCTION_ARGS) { int32 beid = PG_GETARG_INT32(0); PgBackendStatus *beentry; SockAddr zero_clientaddr; char remote_port[NI_MAXSERV]; int ret; if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); if (!superuser() && beentry->st_userid != GetUserId()) PG_RETURN_NULL(); /* A zeroed client addr means we don't know */ memset(&zero_clientaddr, 0, sizeof(zero_clientaddr)); if (memcmp(&(beentry->st_clientaddr), &zero_clientaddr, sizeof(zero_clientaddr) == 0)) PG_RETURN_NULL(); switch (beentry->st_clientaddr.addr.ss_family) { case AF_INET: #ifdef HAVE_IPV6 case AF_INET6: #endif break; case AF_UNIX: PG_RETURN_INT32(-1); default: PG_RETURN_NULL(); } remote_port[0] = '\0'; ret = pg_getnameinfo_all(&beentry->st_clientaddr.addr, beentry->st_clientaddr.salen, NULL, 0, remote_port, sizeof(remote_port), NI_NUMERICHOST | NI_NUMERICSERV); if (ret) PG_RETURN_NULL(); PG_RETURN_DATUM(DirectFunctionCall1(int4in, CStringGetDatum(remote_port))); }
static void check_rules(Port *port, int status) { int r; /* index of the backend process */ int index; /* limits */ bool per_user_overriden = false, per_database_overriden = false, per_ip_overriden = false; /* counters */ int per_user = 0, per_database = 0, per_ip = 0; /* * Any other plugins which use ClientAuthentication_hook. */ if (prev_client_auth_hook) prev_client_auth_hook(port, status); /* No point in checkin the connection rules after failed authentication. */ if (status != STATUS_OK) return; /* * Lock ProcArray (serialize the processes, so that we can use the * counters stored in the rule_r struct). * * TODO Use a private array of counters (same number of rules), so * that we don't need an exclusive lock. */ LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); /* reset the rule counters */ reset_rules(); /* attach the shared segment */ attach_procarray(); /* * Perform the actual check - loop through the backends (procArray), and * compare each valid backend against each rule. If it matches, increment * the counter (and if value exceeds the limit, make a failure). * * TODO First check the rules for the current backend, and then only check * those rules that match (because those are the only rules that may * be violated by this new connection). */ for (index = 0; index < procArray->numProcs; index++) { #if (PG_VERSION_NUM <= 90200) volatile PGPROC *proc = procArray->procs[index]; #else volatile PGPROC *proc = &ProcGlobal->allProcs[procArray->procs[index]]; #endif /* do not count prepared xacts */ if (proc->pid == 0) continue; /* * If this is the current backend, then update the local info. This * effectively resets info for crashed backends. * * FIXME Maybe this should happen explicitly before the loop. */ if (proc->backendId == MyBackendId) { /* lookup remote host name (unless already done) */ if (! port->remote_hostname) { char remote_hostname[NI_MAXHOST]; if (! pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen, remote_hostname, sizeof(remote_hostname), NULL, 0, 0)) port->remote_hostname = pstrdup(remote_hostname); } /* store the backend info into a cache */ backend_update_info(&backends[proc->backendId], proc, port->database_name, port->user_name, port->raddr, port->remote_hostname); } /* if the backend info is valid, */ if (backend_info_is_valid(backends[proc->backendId], proc)) { /* see if the database/user/IP matches */ per_database += (strcmp(backends[proc->backendId].database, port->database_name) == 0) ? 1 : 0; per_user += (strcmp(backends[proc->backendId].role, port->user_name) == 0) ? 1 : 0; per_ip += (memcmp(&backends[proc->backendId].socket, &port->raddr, sizeof(SockAddr)) == 0) ? 1 : 0; /* check all the rules for this backend */ for (r = 0; r < rules->n_rules; r++) { /* * The rule has to be matched by both the current and new session, otherwise * it can't be violated by the new one. * * FIXME This repeatedly checks all the rules for the current backend, which is not * needed. We only need to do this check (for the new session) once, and then * walk only the rules that match it. Althouth that may not detect the * default rules (per db, ...). */ if (rule_matches(rules->rules[r], port->database_name, port->user_name, port->raddr, port->remote_host)) { /* check if this rule overrides per-db, per-user or per-ip limits */ per_database_overriden |= rule_is_per_database(&rules->rules[r]); per_user_overriden |= rule_is_per_user(&rules->rules[r]); per_ip_overriden |= rule_is_per_ip(&rules->rules[r]); /* Check the rule for a existing backend (we assume it's valid thanks to backend_info_is_valid()). */ if (rule_matches(rules->rules[r], backends[proc->backendId].database, backends[proc->backendId].role, backends[proc->backendId].socket, backends[proc->backendId].hostname)) { /* increment the match count for this rule */ ++rules->rules[r].count; /* * We're looping over all backends (including the current backend), so the * rule is only violated if the limit is actually exceeded. */ if (rules->rules[r].count > rules->rules[r].limit) { if (! is_super_user(port->user_name)) elog(ERROR, "connection limit reached (rule %d, line %d, limit %d)", r, rules->rules[r].line, rules->rules[r].limit); else elog(WARNING, "connection limit reached (rule %d, line %d, limit %d), but the user is a superuser", r, rules->rules[r].line, rules->rules[r].limit); } } } } } } /* * Check the per-db/user/IP limits, unless there was an exact rule overriding * the defaults for that object, or unless the default was disabled (set to 0). */ /* check per-database limit */ if ((! per_database_overriden) && (default_per_database != 0) && (per_database > default_per_database)) { if (! is_super_user(port->user_name)) elog(ERROR, "per-database connection limit reached (limit %d)", default_per_database); else elog(WARNING, "per-database limit reached (limit %d), but the user is a superuser", default_per_database); } /* check per-user limit */ if ((! per_user_overriden) && (default_per_role != 0) && (per_user > default_per_role)) { if (! is_super_user(port->user_name)) elog(ERROR, "per-user connection limit reached (limit %d)", default_per_role); else elog(WARNING, "per-user connection limit reached (limit %d), but the user is a superuser", default_per_role); } /* check per-IP limit */ if ((! per_ip_overriden) && (default_per_ip != 0) && (per_ip > default_per_ip)) { if (! is_super_user(port->user_name)) elog(ERROR, "per-IP connection limit reached (limit %d)", default_per_ip); else elog(WARNING, "per-IP connection limit reached (limit %d), but the user is a superuser", default_per_ip); } LWLockRelease(ProcArrayLock); }
Datum pg_stat_get_activity(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; TupleDesc tupdesc; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); tupdesc = CreateTemplateTupleDesc(13, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "procpid", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 4, "application_name", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 5, "current_query", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 6, "waiting", BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 7, "act_start", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 8, "query_start", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 9, "backend_start", TIMESTAMPTZOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 10, "client_addr", INETOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 11, "client_port", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 12, "sess_id", INT4OID, -1, 0); /* GPDB */ TupleDescInitEntry(tupdesc, (AttrNumber) 13, "waiting_resource", BOOLOID, -1, 0); funcctx->tuple_desc = BlessTupleDesc(tupdesc); funcctx->user_fctx = palloc0(sizeof(int)); if (PG_ARGISNULL(0)) { /* Get all backends */ funcctx->max_calls = pgstat_fetch_stat_numbackends(); } else { /* * Get one backend - locate by pid. * * We lookup the backend early, so we can return zero rows if it * doesn't exist, instead of returning a single row full of NULLs. */ int pid = PG_GETARG_INT32(0); int i; int n = pgstat_fetch_stat_numbackends(); for (i = 1; i <= n; i++) { PgBackendStatus *be = pgstat_fetch_stat_beentry(i); if (be) { if (be->st_procpid == pid) { *(int *) (funcctx->user_fctx) = i; break; } } } if (*(int *) (funcctx->user_fctx) == 0) /* Pid not found, return zero rows */ funcctx->max_calls = 0; else funcctx->max_calls = 1; } MemoryContextSwitchTo(oldcontext); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); if (funcctx->call_cntr < funcctx->max_calls) { /* for each row */ Datum values[13]; bool nulls[13]; HeapTuple tuple; PgBackendStatus *beentry; SockAddr zero_clientaddr; MemSet(values, 0, sizeof(values)); MemSet(nulls, 0, sizeof(nulls)); if (*(int *) (funcctx->user_fctx) > 0) { /* Get specific pid slot */ beentry = pgstat_fetch_stat_beentry(*(int *) (funcctx->user_fctx)); } else { /* Get the next one in the list */ beentry = pgstat_fetch_stat_beentry(funcctx->call_cntr + 1); /* 1-based index */ } if (!beentry) { int i; for (i = 0; i < sizeof(nulls) / sizeof(nulls[0]); i++) nulls[i] = true; nulls[4] = false; values[4] = CStringGetTextDatum("<backend information not available>"); tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); } /* Values available to all callers */ values[0] = ObjectIdGetDatum(beentry->st_databaseid); values[1] = Int32GetDatum(beentry->st_procpid); values[2] = ObjectIdGetDatum(beentry->st_userid); if (beentry->st_appname) values[3] = CStringGetTextDatum(beentry->st_appname); else nulls[3] = true; /* Values only available to same user or superuser */ if (superuser() || beentry->st_userid == GetUserId()) { if (*(beentry->st_activity) == '\0') { values[4] = CStringGetTextDatum("<command string not enabled>"); } else { values[4] = CStringGetTextDatum(beentry->st_activity); } values[5] = BoolGetDatum(beentry->st_waiting); if (beentry->st_xact_start_timestamp != 0) values[6] = TimestampTzGetDatum(beentry->st_xact_start_timestamp); else nulls[6] = true; if (beentry->st_activity_start_timestamp != 0) values[7] = TimestampTzGetDatum(beentry->st_activity_start_timestamp); else nulls[7] = true; if (beentry->st_proc_start_timestamp != 0) values[8] = TimestampTzGetDatum(beentry->st_proc_start_timestamp); else nulls[8] = true; /* A zeroed client addr means we don't know */ memset(&zero_clientaddr, 0, sizeof(zero_clientaddr)); if (memcmp(&(beentry->st_clientaddr), &zero_clientaddr, sizeof(zero_clientaddr) == 0)) { nulls[9] = true; nulls[10] = true; } else { if (beentry->st_clientaddr.addr.ss_family == AF_INET #ifdef HAVE_IPV6 || beentry->st_clientaddr.addr.ss_family == AF_INET6 #endif ) { char remote_host[NI_MAXHOST]; char remote_port[NI_MAXSERV]; int ret; remote_host[0] = '\0'; remote_port[0] = '\0'; ret = pg_getnameinfo_all(&beentry->st_clientaddr.addr, beentry->st_clientaddr.salen, remote_host, sizeof(remote_host), remote_port, sizeof(remote_port), NI_NUMERICHOST | NI_NUMERICSERV); if (ret) { nulls[9] = true; nulls[10] = true; } else { clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, remote_host); values[9] = DirectFunctionCall1(inet_in, CStringGetDatum(remote_host)); values[10] = Int32GetDatum(atoi(remote_port)); } } else if (beentry->st_clientaddr.addr.ss_family == AF_UNIX) { /* * Unix sockets always reports NULL for host and -1 for * port, so it's possible to tell the difference to * connections we have no permissions to view, or with * errors. */ nulls[9] = true; values[10] = DatumGetInt32(-1); } else { /* Unknown address type, should never happen */ nulls[9] = true; nulls[10] = true; } } values[12] = BoolGetDatum(beentry->st_waiting_resource); } else { /* No permissions to view data about this session */ values[4] = CStringGetTextDatum("<insufficient privilege>"); nulls[5] = true; nulls[6] = true; nulls[7] = true; nulls[8] = true; nulls[9] = true; nulls[10] = true; nulls[12] = true; } values[11] = Int32GetDatum(beentry->st_session_id); /* GPDB */ tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); } else { /* nothing left */ SRF_RETURN_DONE(funcctx); } }
/* * getDnsAddress * * same as getDnsCachedAddress, but without caching. Looks like the * non-cached version was used inline inside of cdbgang.c, and since * it is needed now elsewhere, it is factored out to this routine. */ char * getDnsAddress(char *hostname, int port, int elevel) { int ret; char portNumberStr[32]; char *service; char *result = NULL; struct addrinfo *addrs = NULL, *addr; struct addrinfo hint; /* Initialize hint structure */ MemSet(&hint, 0, sizeof(hint)); hint.ai_socktype = SOCK_STREAM; hint.ai_family = AF_UNSPEC; snprintf(portNumberStr, sizeof(portNumberStr), "%d", port); service = portNumberStr; ret = pg_getaddrinfo_all(hostname, service, &hint, &addrs); if (ret || !addrs) { if (addrs) pg_freeaddrinfo_all(hint.ai_family, addrs); ereport(elevel, (errmsg("could not translate host name \"%s\", port \"%d\" to address: %s", hostname, port, gai_strerror(ret)))); } for (addr = addrs; addr; addr = addr->ai_next) { #ifdef HAVE_UNIX_SOCKETS /* Ignore AF_UNIX sockets, if any are returned. */ if (addr->ai_family == AF_UNIX) continue; #endif if (addr->ai_family == AF_INET) /* IPv4 address */ { char hostinfo[NI_MAXHOST]; /* Get a text representation of the IP address */ pg_getnameinfo_all((struct sockaddr_storage *) addr->ai_addr, addr->ai_addrlen, hostinfo, sizeof(hostinfo), NULL, 0, NI_NUMERICHOST); result = pstrdup(hostinfo); break; } } #ifdef HAVE_IPV6 /* * IPv6 should would work fine, we'd just need to make sure all the data structures are big enough for * the IPv6 address. And on some broken systems, you can get an IPv6 address, but not be able to bind to it * because IPv6 is disabled or missing in the kernel, so we'd only want to use the IPv6 address if there isn't * an IPv4 address. All we really need to do is test this. */ if (result == NULL && addrs->ai_family == AF_INET6) { char hostinfo[NI_MAXHOST]; addr = addrs; /* Get a text representation of the IP address */ pg_getnameinfo_all((struct sockaddr_storage *) addr->ai_addr, addr->ai_addrlen, hostinfo, sizeof(hostinfo), NULL, 0, NI_NUMERICHOST); result = pstrdup(hostinfo); } #endif pg_freeaddrinfo_all(hint.ai_family, addrs); return result; }
/* * Maintain a cache of names. * * The keys are all NAMEDATALEN long. */ static char * getDnsCachedAddress(char *name, int port, int elevel) { struct segment_ip_cache_entry *e; if (segment_ip_cache_htab == NULL) { HASHCTL hash_ctl; MemSet(&hash_ctl, 0, sizeof(hash_ctl)); hash_ctl.keysize = NAMEDATALEN + 1; hash_ctl.entrysize = sizeof(struct segment_ip_cache_entry); segment_ip_cache_htab = hash_create("segment_dns_cache", 256, &hash_ctl, HASH_ELEM); Assert(segment_ip_cache_htab != NULL); } e = (struct segment_ip_cache_entry *)hash_search(segment_ip_cache_htab, name, HASH_FIND, NULL); /* not in our cache, we've got to actually do the name lookup. */ if (e == NULL) { MemoryContext oldContext; int ret; char portNumberStr[32]; char *service; struct addrinfo *addrs = NULL, *addr; struct addrinfo hint; /* Initialize hint structure */ MemSet(&hint, 0, sizeof(hint)); hint.ai_socktype = SOCK_STREAM; hint.ai_family = AF_UNSPEC; snprintf(portNumberStr, sizeof(portNumberStr), "%d", port); service = portNumberStr; ret = pg_getaddrinfo_all(name, service, &hint, &addrs); if (ret || !addrs) { if (addrs) pg_freeaddrinfo_all(hint.ai_family, addrs); ereport(elevel, (errmsg("could not translate host name \"%s\", port \"%d\" to address: %s", name, port, gai_strerror(ret)))); return NULL; } /* save in the cache context */ oldContext = MemoryContextSwitchTo(TopMemoryContext); for (addr = addrs; addr; addr = addr->ai_next) { #ifdef HAVE_UNIX_SOCKETS /* Ignore AF_UNIX sockets, if any are returned. */ if (addr->ai_family == AF_UNIX) continue; #endif if (addr->ai_family == AF_INET) /* IPv4 address */ { char hostinfo[NI_MAXHOST]; pg_getnameinfo_all((struct sockaddr_storage *)addr->ai_addr, addr->ai_addrlen, hostinfo, sizeof(hostinfo), NULL, 0, NI_NUMERICHOST); /* INSERT INTO OUR CACHE HTAB HERE */ e = (struct segment_ip_cache_entry *)hash_search(segment_ip_cache_htab, name, HASH_ENTER, NULL); Assert(e != NULL); memcpy(e->hostinfo, hostinfo, sizeof(hostinfo)); break; } } #ifdef HAVE_IPV6 /* * IPv6 probably would work fine, we'd just need to make sure all the data structures are big enough for * the IPv6 address. And on some broken systems, you can get an IPv6 address, but not be able to bind to it * because IPv6 is disabled or missing in the kernel, so we'd only want to use the IPv6 address if there isn't * an IPv4 address. All we really need to do is test this. */ if (e == NULL && addrs->ai_family == AF_INET6) { char hostinfo[NI_MAXHOST]; addr = addrs; pg_getnameinfo_all((struct sockaddr_storage *)addr->ai_addr, addr->ai_addrlen, hostinfo, sizeof(hostinfo), NULL, 0, NI_NUMERICHOST); /* INSERT INTO OUR CACHE HTAB HERE */ e = (struct segment_ip_cache_entry *)hash_search(segment_ip_cache_htab, name, HASH_ENTER, NULL); Assert(e != NULL); memcpy(e->hostinfo, hostinfo, sizeof(hostinfo)); } #endif MemoryContextSwitchTo(oldContext); pg_freeaddrinfo_all(hint.ai_family, addrs); } /* return a pointer to our cache. */ return e->hostinfo; }
/* * Returns activity of PG backends. */ Datum pg_stat_get_activity(PG_FUNCTION_ARGS) { #define PG_STAT_GET_ACTIVITY_COLS 22 int num_backends = pgstat_fetch_stat_numbackends(); int curr_backend; int pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0); ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; TupleDesc tupdesc; Tuplestorestate *tupstore; MemoryContext per_query_ctx; MemoryContext oldcontext; /* 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); /* 1-based index */ for (curr_backend = 1; curr_backend <= num_backends; curr_backend++) { /* for each row */ Datum values[PG_STAT_GET_ACTIVITY_COLS]; bool nulls[PG_STAT_GET_ACTIVITY_COLS]; LocalPgBackendStatus *local_beentry; PgBackendStatus *beentry; MemSet(values, 0, sizeof(values)); MemSet(nulls, 0, sizeof(nulls)); if (pid != -1) { /* Skip any which are not the one we're looking for. */ PgBackendStatus *be = pgstat_fetch_stat_beentry(curr_backend); if (!be || be->st_procpid != pid) continue; } /* Get the next one in the list */ local_beentry = pgstat_fetch_stat_local_beentry(curr_backend); if (!local_beentry) continue; beentry = &local_beentry->backendStatus; if (!beentry) { int i; for (i = 0; i < sizeof(nulls) / sizeof(nulls[0]); i++) nulls[i] = true; nulls[5] = false; values[5] = CStringGetTextDatum("<backend information not available>"); tuplestore_putvalues(tupstore, tupdesc, values, nulls); continue; } /* Values available to all callers */ values[0] = ObjectIdGetDatum(beentry->st_databaseid); values[1] = Int32GetDatum(beentry->st_procpid); values[2] = ObjectIdGetDatum(beentry->st_userid); if (beentry->st_appname) values[3] = CStringGetTextDatum(beentry->st_appname); else nulls[3] = true; if (TransactionIdIsValid(local_beentry->backend_xid)) values[14] = TransactionIdGetDatum(local_beentry->backend_xid); else nulls[14] = true; if (TransactionIdIsValid(local_beentry->backend_xmin)) values[15] = TransactionIdGetDatum(local_beentry->backend_xmin); else nulls[15] = true; if (beentry->st_ssl) { values[16] = BoolGetDatum(true); /* ssl */ values[17] = CStringGetTextDatum(beentry->st_sslstatus->ssl_version); values[18] = CStringGetTextDatum(beentry->st_sslstatus->ssl_cipher); values[19] = Int32GetDatum(beentry->st_sslstatus->ssl_bits); values[20] = BoolGetDatum(beentry->st_sslstatus->ssl_compression); values[21] = CStringGetTextDatum(beentry->st_sslstatus->ssl_clientdn); } else { values[16] = BoolGetDatum(false); /* ssl */ nulls[17] = nulls[18] = nulls[19] = nulls[20] = nulls[21] = true; } /* Values only available to role member */ if (has_privs_of_role(GetUserId(), beentry->st_userid)) { SockAddr zero_clientaddr; switch (beentry->st_state) { case STATE_IDLE: values[4] = CStringGetTextDatum("idle"); break; case STATE_RUNNING: values[4] = CStringGetTextDatum("active"); break; case STATE_IDLEINTRANSACTION: values[4] = CStringGetTextDatum("idle in transaction"); break; case STATE_FASTPATH: values[4] = CStringGetTextDatum("fastpath function call"); break; case STATE_IDLEINTRANSACTION_ABORTED: values[4] = CStringGetTextDatum("idle in transaction (aborted)"); break; case STATE_DISABLED: values[4] = CStringGetTextDatum("disabled"); break; case STATE_UNDEFINED: nulls[4] = true; break; } values[5] = CStringGetTextDatum(beentry->st_activity); values[6] = BoolGetDatum(beentry->st_waiting); if (beentry->st_xact_start_timestamp != 0) values[7] = TimestampTzGetDatum(beentry->st_xact_start_timestamp); else nulls[7] = true; if (beentry->st_activity_start_timestamp != 0) values[8] = TimestampTzGetDatum(beentry->st_activity_start_timestamp); else nulls[8] = true; if (beentry->st_proc_start_timestamp != 0) values[9] = TimestampTzGetDatum(beentry->st_proc_start_timestamp); else nulls[9] = true; if (beentry->st_state_start_timestamp != 0) values[10] = TimestampTzGetDatum(beentry->st_state_start_timestamp); else nulls[10] = true; /* A zeroed client addr means we don't know */ memset(&zero_clientaddr, 0, sizeof(zero_clientaddr)); if (memcmp(&(beentry->st_clientaddr), &zero_clientaddr, sizeof(zero_clientaddr)) == 0) { nulls[11] = true; nulls[12] = true; nulls[13] = true; } else { if (beentry->st_clientaddr.addr.ss_family == AF_INET #ifdef HAVE_IPV6 || beentry->st_clientaddr.addr.ss_family == AF_INET6 #endif ) { char remote_host[NI_MAXHOST]; char remote_port[NI_MAXSERV]; int ret; remote_host[0] = '\0'; remote_port[0] = '\0'; ret = pg_getnameinfo_all(&beentry->st_clientaddr.addr, beentry->st_clientaddr.salen, remote_host, sizeof(remote_host), remote_port, sizeof(remote_port), NI_NUMERICHOST | NI_NUMERICSERV); if (ret == 0) { clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, remote_host); values[11] = DirectFunctionCall1(inet_in, CStringGetDatum(remote_host)); if (beentry->st_clienthostname && beentry->st_clienthostname[0]) values[12] = CStringGetTextDatum(beentry->st_clienthostname); else nulls[12] = true; values[13] = Int32GetDatum(atoi(remote_port)); } else { nulls[11] = true; nulls[12] = true; nulls[13] = true; } } else if (beentry->st_clientaddr.addr.ss_family == AF_UNIX) { /* * Unix sockets always reports NULL for host and -1 for * port, so it's possible to tell the difference to * connections we have no permissions to view, or with * errors. */ nulls[11] = true; nulls[12] = true; values[13] = DatumGetInt32(-1); } else { /* Unknown address type, should never happen */ nulls[11] = true; nulls[12] = true; nulls[13] = true; } } } else { /* No permissions to view data about this session */ values[5] = CStringGetTextDatum("<insufficient privilege>"); nulls[4] = true; nulls[6] = true; nulls[7] = true; nulls[8] = true; nulls[9] = true; nulls[10] = true; nulls[11] = true; nulls[12] = true; nulls[13] = true; } tuplestore_putvalues(tupstore, tupdesc, values, nulls); /* If only a single backend was requested, and we found it, break. */ if (pid != -1) break; } /* clean up and return the tuplestore */ tuplestore_donestoring(tupstore); return (Datum) 0; }
/* * Client authentication starts here. If there is an error, this * function does not return and the backend process is terminated. */ void ClientAuthentication(Port *port) { int status = STATUS_ERROR; /* * Get the authentication method to use for this frontend/database * combination. Note: a failure return indicates a problem with the hba * config file, not with the request. hba.c should have dropped an error * message into the postmaster logfile if it failed. */ if (hba_getauthmethod(port) != STATUS_OK) ereport(FATAL, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("missing or erroneous pg_hba.conf file"), errhint("See server log for details."))); switch (port->auth_method) { case uaReject: /* * This could have come from an explicit "reject" entry in * pg_hba.conf, but more likely it means there was no matching * entry. Take pity on the poor user and issue a helpful error * message. NOTE: this is not a security breach, because all the * info reported here is known at the frontend and must be assumed * known to bad guys. We're merely helping out the less clueful * good guys. */ { char hostinfo[NI_MAXHOST]; pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen, hostinfo, sizeof(hostinfo), NULL, 0, NI_NUMERICHOST); #ifdef USE_SSL ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s", hostinfo, port->user_name, port->database_name, port->ssl ? _("SSL on") : _("SSL off")))); #else ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\"", hostinfo, port->user_name, port->database_name))); #endif break; } case uaKrb5: sendAuthRequest(port, AUTH_REQ_KRB5); status = pg_krb5_recvauth(port); break; case uaGSS: sendAuthRequest(port, AUTH_REQ_GSS); status = pg_GSS_recvauth(port); break; case uaSSPI: sendAuthRequest(port, AUTH_REQ_SSPI); status = pg_SSPI_recvauth(port); break; case uaIdent: /* * If we are doing ident on unix-domain sockets, use SCM_CREDS * only if it is defined and SO_PEERCRED isn't. */ #if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED) && \ (defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || \ (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))) if (port->raddr.addr.ss_family == AF_UNIX) { #if defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED) /* * Receive credentials on next message receipt, BSD/OS, * NetBSD. We need to set this before the client sends the * next packet. */ int on = 1; if (setsockopt(port->sock, 0, LOCAL_CREDS, &on, sizeof(on)) < 0) ereport(FATAL, (errcode_for_socket_access(), errmsg("could not enable credential reception: %m"))); #endif sendAuthRequest(port, AUTH_REQ_SCM_CREDS); } #endif status = authident(port); break; case uaMD5: sendAuthRequest(port, AUTH_REQ_MD5); status = recv_and_check_password_packet(port); break; case uaCrypt: sendAuthRequest(port, AUTH_REQ_CRYPT); status = recv_and_check_password_packet(port); break; case uaPassword: sendAuthRequest(port, AUTH_REQ_PASSWORD); status = recv_and_check_password_packet(port); break; #ifdef USE_PAM case uaPAM: pam_port_cludge = port; status = CheckPAMAuth(port, port->user_name, ""); break; #endif /* USE_PAM */ #ifdef USE_LDAP case uaLDAP: status = CheckLDAPAuth(port); break; #endif case uaTrust: status = STATUS_OK; break; } if (status == STATUS_OK) sendAuthRequest(port, AUTH_REQ_OK); else auth_failed(port, status); }