PostgreSQL Source Code git master
Loading...
Searching...
No Matches
auth-sasl.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * auth-sasl.c
4 * Routines to handle authentication via SASL
5 *
6 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/libpq/auth-sasl.c
12 *
13 *-------------------------------------------------------------------------
14 */
15
16#include "postgres.h"
17
18#include "libpq/auth.h"
19#include "libpq/libpq.h"
20#include "libpq/pqformat.h"
21#include "libpq/sasl.h"
22
23/*
24 * Perform a SASL exchange with a libpq client, using a specific mechanism
25 * implementation.
26 *
27 * shadow_pass is an optional pointer to the stored secret of the role
28 * authenticated, from pg_authid.rolpassword. For mechanisms that use
29 * shadowed passwords, a NULL pointer here means that an entry could not
30 * be found for the role (or the user does not exist), and the mechanism
31 * should fail the authentication exchange.
32 *
33 * Some SASL mechanisms (e.g. OAUTHBEARER) define special exchanges for
34 * parameter discovery. These exchanges will always result in STATUS_ERROR,
35 * since we can't let the connection continue, but we shouldn't consider them to
36 * be failed authentication attempts. *abandoned will be set to true in this
37 * case.
38 *
39 * Mechanisms must take care not to reveal to the client that a user entry
40 * does not exist; ideally, the external failure mode is identical to that
41 * of an incorrect password. Mechanisms may instead use the logdetail
42 * output parameter to internally differentiate between failure cases and
43 * assist debugging by the server admin.
44 *
45 * A mechanism is not required to utilize a shadow entry, or even a password
46 * system at all; for these cases, shadow_pass may be ignored and the caller
47 * should just pass NULL.
48 */
49int
51 const char **logdetail, bool *abandoned)
52{
54 int mtype;
56 void *opaq = NULL;
57 char *output = NULL;
58 int outputlen = 0;
59 const char *input;
60 int inputlen;
61 int result;
62 bool initial;
63
64 /*
65 * Send the SASL authentication request to user. It includes the list of
66 * authentication mechanisms that are supported.
67 */
69
70 mech->get_mechanisms(port, &sasl_mechs);
71 /* Put another '\0' to mark that list is finished. */
73
75 pfree(sasl_mechs.data);
76
77 /*
78 * Loop through SASL message exchange. This exchange can consist of
79 * multiple messages sent in both directions. First message is always
80 * from the client. All messages from client to server are password
81 * packets (type 'p').
82 */
83 initial = true;
84 do
85 {
87 mtype = pq_getbyte();
89 {
90 /* Only log error if client didn't disconnect. */
91 if (mtype != EOF)
92 {
95 errmsg("expected SASL response, got message type %d",
96 mtype)));
97 }
98 else
99 return STATUS_EOF;
100 }
101
102 /* Get the actual SASL message */
104 if (pq_getmessage(&buf, mech->max_message_length))
105 {
106 /* EOF - pq_getmessage already logged error */
107 pfree(buf.data);
108 return STATUS_ERROR;
109 }
110
111 elog(DEBUG4, "processing received SASL response of length %d", buf.len);
112
113 /*
114 * The first SASLInitialResponse message is different from the others.
115 * It indicates which SASL mechanism the client selected, and contains
116 * an optional Initial Client Response payload. The subsequent
117 * SASLResponse messages contain just the SASL payload.
118 */
119 if (initial)
120 {
121 const char *selected_mech;
122
124
125 /*
126 * Initialize the status tracker for message exchanges.
127 *
128 * If the user doesn't exist, or doesn't have a valid password, or
129 * it's expired, we still go through the motions of SASL
130 * authentication, but tell the authentication method that the
131 * authentication is "doomed". That is, it's going to fail, no
132 * matter what.
133 *
134 * This is because we don't want to reveal to an attacker what
135 * usernames are valid, nor which users have a valid password.
136 */
138
140 if (inputlen == -1)
141 input = NULL;
142 else
144
145 initial = false;
146 }
147 else
148 {
149 inputlen = buf.len;
150 input = pq_getmsgbytes(&buf, buf.len);
151 }
153
154 /*
155 * The StringInfo guarantees that there's a \0 byte after the
156 * response.
157 */
158 Assert(input == NULL || input[inputlen] == '\0');
159
160 /*
161 * Hand the incoming message to the mechanism implementation.
162 */
163 result = mech->exchange(opaq, input, inputlen,
164 &output, &outputlen,
165 logdetail);
166
167 /* input buffer no longer used */
168 pfree(buf.data);
169
170 if (output)
171 {
172 /*
173 * PG_SASL_EXCHANGE_FAILURE with some output is forbidden by SASL.
174 * Make sure here that the mechanism used got that right.
175 */
177 elog(ERROR, "output message found after SASL exchange failure");
178
179 /*
180 * Negotiation generated data to be sent to the client.
181 */
182 elog(DEBUG4, "sending SASL challenge of length %d", outputlen);
183
186 else
188
189 pfree(output);
190 }
192
194 {
195 if (!abandoned)
196 {
197 /*
198 * Programmer error: caller needs to track the abandoned state for
199 * this mechanism.
200 */
201 elog(ERROR, "SASL exchange was abandoned, but CheckSASLAuth isn't tracking it");
202 }
203
204 *abandoned = true;
205 }
206
207 /* Oops, Something bad happened */
209 {
210 return STATUS_ERROR;
211 }
212
213 return STATUS_OK;
214}
int CheckSASLAuth(const pg_be_sasl_mech *mech, Port *port, char *shadow_pass, const char **logdetail, bool *abandoned)
Definition auth-sasl.c:50
void sendAuthRequest(Port *port, AuthRequest areq, const void *extradata, int extralen)
Definition auth.c:695
#define STATUS_OK
Definition c.h:1258
#define Assert(condition)
Definition c.h:943
#define STATUS_EOF
Definition c.h:1260
#define STATUS_ERROR
Definition c.h:1259
uint32 result
int errcode(int sqlerrcode)
Definition elog.c:874
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:227
#define ereport(elevel,...)
Definition elog.h:151
#define DEBUG4
Definition elog.h:27
#define ERRCODE_PROTOCOL_VIOLATION
Definition fe-connect.c:96
FILE * input
FILE * output
void pfree(void *pointer)
Definition mcxt.c:1616
static char * errmsg
static int port
Definition pg_regress.c:117
static char buf[DEFAULT_XLOG_SEG_SIZE]
int pq_getmessage(StringInfo s, int maxlen)
Definition pqcomm.c:1204
int pq_getbyte(void)
Definition pqcomm.c:964
void pq_startmsgread(void)
Definition pqcomm.c:1142
unsigned int pq_getmsgint(StringInfo msg, int b)
Definition pqformat.c:414
void pq_getmsgend(StringInfo msg)
Definition pqformat.c:634
const char * pq_getmsgrawstring(StringInfo msg)
Definition pqformat.c:607
const char * pq_getmsgbytes(StringInfo msg, int datalen)
Definition pqformat.c:507
static int fb(int x)
#define AUTH_REQ_SASL_CONT
Definition protocol.h:107
#define PqMsg_SASLResponse
Definition protocol.h:33
#define AUTH_REQ_SASL
Definition protocol.h:106
#define AUTH_REQ_SASL_FIN
Definition protocol.h:108
#define PG_SASL_EXCHANGE_FAILURE
Definition sasl.h:27
#define PG_SASL_EXCHANGE_CONTINUE
Definition sasl.h:25
#define PG_SASL_EXCHANGE_ABANDONED
Definition sasl.h:28
#define PG_SASL_EXCHANGE_SUCCESS
Definition sasl.h:26
void appendStringInfoChar(StringInfo str, char ch)
Definition stringinfo.c:242
void initStringInfo(StringInfo str)
Definition stringinfo.c:97