PostgreSQL Source Code  git master
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-2024, 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  * Mechanisms must take care not to reveal to the client that a user entry
34  * does not exist; ideally, the external failure mode is identical to that
35  * of an incorrect password. Mechanisms may instead use the logdetail
36  * output parameter to internally differentiate between failure cases and
37  * assist debugging by the server admin.
38  *
39  * A mechanism is not required to utilize a shadow entry, or even a password
40  * system at all; for these cases, shadow_pass may be ignored and the caller
41  * should just pass NULL.
42  */
43 int
44 CheckSASLAuth(const pg_be_sasl_mech *mech, Port *port, char *shadow_pass,
45  const char **logdetail)
46 {
47  StringInfoData sasl_mechs;
48  int mtype;
50  void *opaq = NULL;
51  char *output = NULL;
52  int outputlen = 0;
53  const char *input;
54  int inputlen;
55  int result;
56  bool initial;
57 
58  /*
59  * Send the SASL authentication request to user. It includes the list of
60  * authentication mechanisms that are supported.
61  */
62  initStringInfo(&sasl_mechs);
63 
64  mech->get_mechanisms(port, &sasl_mechs);
65  /* Put another '\0' to mark that list is finished. */
66  appendStringInfoChar(&sasl_mechs, '\0');
67 
68  sendAuthRequest(port, AUTH_REQ_SASL, sasl_mechs.data, sasl_mechs.len);
69  pfree(sasl_mechs.data);
70 
71  /*
72  * Loop through SASL message exchange. This exchange can consist of
73  * multiple messages sent in both directions. First message is always
74  * from the client. All messages from client to server are password
75  * packets (type 'p').
76  */
77  initial = true;
78  do
79  {
81  mtype = pq_getbyte();
82  if (mtype != PqMsg_SASLResponse)
83  {
84  /* Only log error if client didn't disconnect. */
85  if (mtype != EOF)
86  {
87  ereport(ERROR,
88  (errcode(ERRCODE_PROTOCOL_VIOLATION),
89  errmsg("expected SASL response, got message type %d",
90  mtype)));
91  }
92  else
93  return STATUS_EOF;
94  }
95 
96  /* Get the actual SASL message */
99  {
100  /* EOF - pq_getmessage already logged error */
101  pfree(buf.data);
102  return STATUS_ERROR;
103  }
104 
105  elog(DEBUG4, "processing received SASL response of length %d", buf.len);
106 
107  /*
108  * The first SASLInitialResponse message is different from the others.
109  * It indicates which SASL mechanism the client selected, and contains
110  * an optional Initial Client Response payload. The subsequent
111  * SASLResponse messages contain just the SASL payload.
112  */
113  if (initial)
114  {
115  const char *selected_mech;
116 
117  selected_mech = pq_getmsgrawstring(&buf);
118 
119  /*
120  * Initialize the status tracker for message exchanges.
121  *
122  * If the user doesn't exist, or doesn't have a valid password, or
123  * it's expired, we still go through the motions of SASL
124  * authentication, but tell the authentication method that the
125  * authentication is "doomed". That is, it's going to fail, no
126  * matter what.
127  *
128  * This is because we don't want to reveal to an attacker what
129  * usernames are valid, nor which users have a valid password.
130  */
131  opaq = mech->init(port, selected_mech, shadow_pass);
132 
133  inputlen = pq_getmsgint(&buf, 4);
134  if (inputlen == -1)
135  input = NULL;
136  else
137  input = pq_getmsgbytes(&buf, inputlen);
138 
139  initial = false;
140  }
141  else
142  {
143  inputlen = buf.len;
144  input = pq_getmsgbytes(&buf, buf.len);
145  }
146  pq_getmsgend(&buf);
147 
148  /*
149  * The StringInfo guarantees that there's a \0 byte after the
150  * response.
151  */
152  Assert(input == NULL || input[inputlen] == '\0');
153 
154  /*
155  * Hand the incoming message to the mechanism implementation.
156  */
157  result = mech->exchange(opaq, input, inputlen,
158  &output, &outputlen,
159  logdetail);
160 
161  /* input buffer no longer used */
162  pfree(buf.data);
163 
164  if (output)
165  {
166  /*
167  * PG_SASL_EXCHANGE_FAILURE with some output is forbidden by SASL.
168  * Make sure here that the mechanism used got that right.
169  */
170  if (result == PG_SASL_EXCHANGE_FAILURE)
171  elog(ERROR, "output message found after SASL exchange failure");
172 
173  /*
174  * Negotiation generated data to be sent to the client.
175  */
176  elog(DEBUG4, "sending SASL challenge of length %d", outputlen);
177 
178  if (result == PG_SASL_EXCHANGE_SUCCESS)
180  else
182 
183  pfree(output);
184  }
185  } while (result == PG_SASL_EXCHANGE_CONTINUE);
186 
187  /* Oops, Something bad happened */
188  if (result != PG_SASL_EXCHANGE_SUCCESS)
189  {
190  return STATUS_ERROR;
191  }
192 
193  return STATUS_OK;
194 }
int CheckSASLAuth(const pg_be_sasl_mech *mech, Port *port, char *shadow_pass, const char **logdetail)
Definition: auth-sasl.c:44
void sendAuthRequest(Port *port, AuthRequest areq, const char *extradata, int extralen)
Definition: auth.c:676
#define STATUS_OK
Definition: c.h:1172
#define Assert(condition)
Definition: c.h:861
#define STATUS_EOF
Definition: c.h:1174
#define STATUS_ERROR
Definition: c.h:1173
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
#define DEBUG4
Definition: elog.h:27
FILE * input
FILE * output
void pfree(void *pointer)
Definition: mcxt.c:1521
static int port
Definition: pg_regress.c:115
static char * buf
Definition: pg_test_fsync.c:72
int pq_getmessage(StringInfo s, int maxlen)
Definition: pqcomm.c:1203
int pq_getbyte(void)
Definition: pqcomm.c:964
void pq_startmsgread(void)
Definition: pqcomm.c:1141
unsigned int pq_getmsgint(StringInfo msg, int b)
Definition: pqformat.c:415
void pq_getmsgend(StringInfo msg)
Definition: pqformat.c:635
const char * pq_getmsgbytes(StringInfo msg, int datalen)
Definition: pqformat.c:508
const char * pq_getmsgrawstring(StringInfo msg)
Definition: pqformat.c:608
#define AUTH_REQ_SASL_CONT
Definition: protocol.h:85
#define PqMsg_SASLResponse
Definition: protocol.h:33
#define AUTH_REQ_SASL
Definition: protocol.h:84
#define AUTH_REQ_SASL_FIN
Definition: protocol.h:86
#define PG_SASL_EXCHANGE_FAILURE
Definition: sasl.h:27
#define PG_SASL_EXCHANGE_CONTINUE
Definition: sasl.h:25
#define PG_SASL_EXCHANGE_SUCCESS
Definition: sasl.h:26
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:191
void initStringInfo(StringInfo str)
Definition: stringinfo.c:56
Definition: libpq-be.h:133
int(* exchange)(void *state, const char *input, int inputlen, char **output, int *outputlen, const char **logdetail)
Definition: sasl.h:134
void(* get_mechanisms)(Port *port, StringInfo buf)
Definition: sasl.h:64
void *(* init)(Port *port, const char *mech, const char *shadow_pass)
Definition: sasl.h:88
int max_message_length
Definition: sasl.h:140