Skip to content

Commit edc9a57

Browse files
committed
feat: polar_audit (DDL and utility audit events)
This commit supports audit events for DDL and utility operations via ProcessUtility hook.
1 parent 0045016 commit edc9a57

File tree

1 file changed

+133
-1
lines changed

1 file changed

+133
-1
lines changed

external/polar_audit/polar_audit.c

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,27 @@ static AuditEventStackItem * auditEventStack = NULL;
180180
*/
181181
static int64 stackTotal = 0;
182182

183+
/*
184+
* Check that an item is on the stack. If not, an error will be raised since
185+
* this is a bad state to be in and it might mean audit records are being lost.
186+
*/
187+
static void
188+
stack_valid(int64 stackId)
189+
{
190+
AuditEventStackItem *nextItem = auditEventStack;
191+
192+
/* Look through the stack for the stack entry */
193+
while (nextItem != NULL && nextItem->stackId != stackId)
194+
nextItem = nextItem->next;
195+
196+
/* If we didn't find it, something went wrong. */
197+
if (nextItem == NULL)
198+
elog(ERROR, "plaudit stack item " INT64_FORMAT
199+
" not found - top of stack is " INT64_FORMAT "",
200+
stackId,
201+
auditEventStack == NULL ? (int64) -1 : auditEventStack->stackId);
202+
}
203+
183204
/*
184205
* Respond to callbacks registered with MemoryContextRegisterResetCallback().
185206
* Removes the event(s) off the stack that have become obsolete once the
@@ -495,7 +516,118 @@ polar_audit_ProcessUtility_hook(PlannedStmt *pstmt,
495516
DestReceiver *dest,
496517
QueryCompletion *qc)
497518
{
498-
return;
519+
int64 stackId = 0;
520+
AuditEventStackItem *stackItem = NULL;
521+
522+
/*
523+
* Don't audit substatements. All the substatements we care about should
524+
* be covered by the event triggers.
525+
*/
526+
if (context <= PROCESS_UTILITY_QUERY && !IsAbortedTransactionBlockState())
527+
{
528+
/* Process top level utility statement */
529+
if (context == PROCESS_UTILITY_TOPLEVEL)
530+
{
531+
/*
532+
* If the stack is not empty then the only allowed entries are
533+
* call statements or open, select, show, and explain cursors
534+
*/
535+
if (auditEventStack != NULL)
536+
{
537+
AuditEventStackItem *nextItem = auditEventStack;
538+
539+
do
540+
{
541+
if (nextItem->auditEvent.commandTag != T_SelectStmt &&
542+
nextItem->auditEvent.commandTag != T_VariableShowStmt &&
543+
nextItem->auditEvent.commandTag != T_ExplainStmt &&
544+
nextItem->auditEvent.commandTag != T_CallStmt)
545+
{
546+
elog(ERROR, "plaudit stack is not empty");
547+
}
548+
549+
nextItem = nextItem->next;
550+
}
551+
while (nextItem != NULL);
552+
}
553+
554+
stackItem = stack_push();
555+
stackItem->auditEvent.paramList = copyParamList(params);
556+
}
557+
else
558+
stackItem = stack_push();
559+
560+
stackId = stackItem->stackId;
561+
stackItem->auditEvent.logStmtLevel = GetCommandLogLevel(pstmt->utilityStmt);
562+
stackItem->auditEvent.commandTag = nodeTag(pstmt->utilityStmt);
563+
stackItem->auditEvent.command = CreateCommandTag(pstmt->utilityStmt);
564+
stackItem->auditEvent.commandText = queryString;
565+
stackItem->auditEvent.auditOid = get_role_oid(auditRole, true);
566+
567+
/*
568+
* If this is a DO block log it before calling the next ProcessUtility
569+
* hook.
570+
*/
571+
if (auditLogBitmap & LOG_FUNCTION &&
572+
stackItem->auditEvent.commandTag == T_DoStmt &&
573+
!IsAbortedTransactionBlockState())
574+
log_audit_event(stackItem);
575+
576+
/*
577+
* If this is a create/alter extension command log it before calling
578+
* the next ProcessUtility hook. Otherwise, any warnings will be
579+
* emitted before the create/alter is logged and errors will prevent
580+
* it from being logged at all.
581+
*/
582+
if (auditLogBitmap & LOG_DDL &&
583+
(stackItem->auditEvent.commandTag == T_CreateExtensionStmt ||
584+
stackItem->auditEvent.commandTag == T_AlterExtensionStmt) &&
585+
!IsAbortedTransactionBlockState())
586+
log_audit_event(stackItem);
587+
588+
/*
589+
* A close will free the open cursor which will also free the close
590+
* audit entry. Immediately log the close and set stackItem to NULL so
591+
* it won't be logged later.
592+
*/
593+
if (stackItem->auditEvent.commandTag == T_ClosePortalStmt)
594+
{
595+
if (auditLogBitmap & LOG_MISC && !IsAbortedTransactionBlockState())
596+
log_audit_event(stackItem);
597+
598+
stackItem = NULL;
599+
}
600+
}
601+
602+
/* Call the standard process utility chain. */
603+
if (next_ProcessUtility_hook)
604+
(*next_ProcessUtility_hook) (pstmt, queryString, readOnlyTree, context,
605+
params, queryEnv, dest, qc);
606+
else
607+
standard_ProcessUtility(pstmt, queryString, readOnlyTree, context,
608+
params, queryEnv, dest, qc);
609+
610+
/*
611+
* Process the audit event if there is one. Also check that this event
612+
* was not popped off the stack by a memory context being free'd
613+
* elsewhere.
614+
*/
615+
if (stackItem && !IsAbortedTransactionBlockState())
616+
{
617+
/*
618+
* Make sure the item we want to log is still on the stack - if not
619+
* then something has gone wrong and an error will be raised.
620+
*/
621+
stack_valid(stackId);
622+
623+
/*
624+
* Log the utility command if logging is on, the command has not
625+
* already been logged by another hook, and the transaction is not
626+
* aborted.
627+
*/
628+
if (auditLogBitmap != 0 && !stackItem->auditEvent.logged)
629+
log_audit_event(stackItem);
630+
}
499631
}
500632

501633
/*

0 commit comments

Comments
 (0)