Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * oauth-debug.h
4 : : * Parsing logic for PGOAUTHDEBUG environment variable
5 : : *
6 : : * Both libpq and libpq-oauth need this logic, so it's packaged in a small
7 : : * header for convenience. This is not quite a standalone header, due to the
8 : : * complication introduced by libpq_gettext(); see note below.
9 : : *
10 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
11 : : * Portions Copyright (c) 1994, Regents of the University of California
12 : : *
13 : : * IDENTIFICATION
14 : : * src/interfaces/libpq/oauth-debug.h
15 : : *
16 : : *-------------------------------------------------------------------------
17 : : */
18 : :
19 : : #ifndef OAUTH_DEBUG_H
20 : : #define OAUTH_DEBUG_H
21 : :
22 : : #include "postgres_fe.h"
23 : :
24 : : /*
25 : : * XXX libpq-oauth can't compile against libpq-int.h, so clients of this header
26 : : * need to provide the declaration of libpq_gettext() before #including it.
27 : : * Fortunately, there are only two such clients.
28 : : */
29 : : /* #include "libpq-int.h" */
30 : :
31 : : /*
32 : : * Debug flags for the PGOAUTHDEBUG environment variable. Each flag controls a
33 : : * specific debug feature. OAUTHDEBUG_UNSAFE_* flags require the envvar to have
34 : : * a literal "UNSAFE:" prefix.
35 : : */
36 : :
37 : : /* allow HTTP (unencrypted) connections */
38 : : #define OAUTHDEBUG_UNSAFE_HTTP (1<<0)
39 : : /* log HTTP traffic (exposes secrets) */
40 : : #define OAUTHDEBUG_UNSAFE_TRACE (1<<1)
41 : : /* allow zero-second retry intervals */
42 : : #define OAUTHDEBUG_UNSAFE_DOS_ENDPOINT (1<<2)
43 : :
44 : : /* mind the gap in values; see OAUTHDEBUG_UNSAFE_MASK below */
45 : :
46 : : /* print PQconnectPoll statistics */
47 : : #define OAUTHDEBUG_CALL_COUNT (1<<16)
48 : : /* print plugin loading errors */
49 : : #define OAUTHDEBUG_PLUGIN_ERRORS (1<<17)
50 : :
51 : : /* all safe and unsafe flags, for the legacy UNSAFE behavior */
52 : : #define OAUTHDEBUG_LEGACY_UNSAFE ((uint32) ~0)
53 : :
54 : : /* Flags are divided into "safe" and "unsafe" based on bit position. */
55 : : #define OAUTHDEBUG_UNSAFE_MASK ((uint32) 0x0000FFFF)
56 : :
57 : : static_assert(OAUTHDEBUG_CALL_COUNT == OAUTHDEBUG_UNSAFE_MASK + 1,
58 : : "the first safe OAUTHDEBUG flag should be above OAUTHDEBUG_UNSAFE_MASK");
59 : :
60 : : /*
61 : : * Parses the PGOAUTHDEBUG environment variable and returns debug flags.
62 : : *
63 : : * Supported formats:
64 : : * PGOAUTHDEBUG=UNSAFE - legacy format, enables all features
65 : : * PGOAUTHDEBUG=option1,option2 - enable safe features only
66 : : * PGOAUTHDEBUG=UNSAFE:opt1,opt2 - enable unsafe and/or safe features
67 : : *
68 : : * Prints a warning and skips the invalid option if:
69 : : * - An unrecognized option is specified
70 : : * - An unsafe option is specified without the UNSAFE: prefix
71 : : *
72 : : * XXX The parsing, and any warnings, will happen each time the function is
73 : : * called, so consider caching the result in cases where that might get
74 : : * annoying. But don't try to cache inside this function, unless you also have a
75 : : * plan for getting libpq and libpq-oauth to share that cache safely... probably
76 : : * not worth the effort for a debugging aid?
77 : : */
78 : : static uint32
28 jchampion@postgresql 79 :GNC 59 : oauth_parse_debug_flags(void)
80 : : {
81 : 59 : uint32 flags = 0;
82 : 59 : const char *env = getenv("PGOAUTHDEBUG");
83 : : char *options_str;
84 : : char *option;
85 : 59 : char *saveptr = NULL;
86 : 59 : bool unsafe_allowed = false;
87 : :
88 [ + + - + ]: 59 : if (!env || env[0] == '\0')
89 : 3 : return flags;
90 : :
91 [ + + ]: 56 : if (strcmp(env, "UNSAFE") == 0)
92 : 1 : return OAUTHDEBUG_LEGACY_UNSAFE;
93 : :
94 [ + + ]: 55 : if (strncmp(env, "UNSAFE:", 7) == 0)
95 : : {
96 : 54 : unsafe_allowed = true;
97 : 54 : env += 7;
98 : : }
99 : :
100 : 55 : options_str = strdup(env);
101 [ - + ]: 55 : if (!options_str)
28 jchampion@postgresql 102 :UNC 0 : return flags;
103 : :
28 jchampion@postgresql 104 :GNC 55 : option = strtok_r(options_str, ",", &saveptr);
105 [ + + ]: 218 : while (option != NULL)
106 : : {
107 : 163 : uint32 flag = 0;
108 : :
109 [ + + ]: 163 : if (strcmp(option, "http") == 0)
110 : 1 : flag = OAUTHDEBUG_UNSAFE_HTTP;
111 [ + + ]: 162 : else if (strcmp(option, "trace") == 0)
112 : 54 : flag = OAUTHDEBUG_UNSAFE_TRACE;
113 [ + + ]: 108 : else if (strcmp(option, "dos-endpoint") == 0)
114 : 54 : flag = OAUTHDEBUG_UNSAFE_DOS_ENDPOINT;
115 [ + - ]: 54 : else if (strcmp(option, "call-count") == 0)
116 : 54 : flag = OAUTHDEBUG_CALL_COUNT;
28 jchampion@postgresql 117 [ # # ]:UNC 0 : else if (strcmp(option, "plugin-errors") == 0)
118 : 0 : flag = OAUTHDEBUG_PLUGIN_ERRORS;
119 : : else
120 : 0 : fprintf(stderr,
121 : 0 : libpq_gettext("WARNING: unrecognized PGOAUTHDEBUG option \"%s\" (ignored)\n"),
122 : : option);
123 : :
28 jchampion@postgresql 124 [ + + + - ]:GNC 163 : if (!unsafe_allowed && ((flag & OAUTHDEBUG_UNSAFE_MASK) != 0))
125 : : {
126 : 1 : flag = 0;
127 : :
128 : 1 : fprintf(stderr,
129 : 1 : libpq_gettext("WARNING: PGOAUTHDEBUG option \"%s\" is unsafe (ignored)\n"),
130 : : option);
131 : : }
132 : :
133 : 163 : flags |= flag;
134 : 163 : option = strtok_r(NULL, ",", &saveptr);
135 : : }
136 : :
137 : 55 : free(options_str);
138 : :
139 : 55 : return flags;
140 : : }
141 : :
142 : : #endif /* OAUTH_DEBUG_H */
|