GnuTLS: tweak debug output
[exim.git] / src / src / auths / get_data.c
CommitLineData
0756eb3c
PH
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
f9ba5e22 5/* Copyright (c) University of Cambridge 1995 - 2018 */
0756eb3c
PH
6/* See the file NOTICE for conditions of use and distribution. */
7
8#include "../exim.h"
9
10
37942ad8
JH
11/****************************************************************
12* Decode and split the argument of an AUTH command *
13****************************************************************/
14
15/* If data was supplied on the AUTH command, decode it, and split it up into
16multiple items at binary zeros. The strings are put into $auth1, $auth2, etc,
17up to a maximum. To retain backwards compatibility, they are also put int $1,
18$2, etc. If the data consists of the string "=" it indicates a single, empty
19string. */
20
21int
22auth_read_input(const uschar * data)
23{
24if (Ustrcmp(data, "=") == 0)
25 {
26 auth_vars[0] = expand_nstring[++expand_nmax] = US"";
27 expand_nlength[expand_nmax] = 0;
28 }
29else
30 {
31 uschar * clear, * end;
32 int len;
33
34 if ((len = b64decode(data, &clear)) < 0) return BAD64;
35 DEBUG(D_auth) debug_printf("auth input decode:");
36 for (end = clear + len; clear < end && expand_nmax < EXPAND_MAXN; )
37 {
38 DEBUG(D_auth) debug_printf(" '%s'", clear);
39 if (expand_nmax < AUTH_VARS) auth_vars[expand_nmax] = clear;
40 expand_nstring[++expand_nmax] = clear;
41 while (*clear != 0) clear++;
42 expand_nlength[expand_nmax] = clear++ - expand_nstring[expand_nmax];
43 }
44 DEBUG(D_auth) debug_printf("\n");
45 }
46return OK;
47}
48
49
50
51
0756eb3c
PH
52/*************************************************
53* Issue a challenge and get a response *
54*************************************************/
55
56/* This function is used by authentication drivers to output a challenge
57to the SMTP client and read the response line.
58
59Arguments:
60 aptr set to point to the response (which is in big_buffer)
61 challenge the challenge text (unencoded, may be binary)
62 challen the length of the challenge text
63
64Returns: OK on success
65 BAD64 if response too large for buffer
66 CANCELLED if response is "*"
67*/
68
69int
37942ad8 70auth_get_data(uschar ** aptr, const uschar * challenge, int challen)
0756eb3c
PH
71{
72int c;
73int p = 0;
925ac8e4 74smtp_printf("334 %s\r\n", FALSE, b64encode(challenge, challen));
bd8fbe36 75while ((c = receive_getc(GETC_BUFFER_UNLIMITED)) != '\n' && c != EOF)
0756eb3c
PH
76 {
77 if (p >= big_buffer_size - 1) return BAD64;
78 big_buffer[p++] = c;
79 }
80if (p > 0 && big_buffer[p-1] == '\r') p--;
81big_buffer[p] = 0;
898d150f 82DEBUG(D_receive) debug_printf("SMTP<< %s\n", big_buffer);
0756eb3c
PH
83if (Ustrcmp(big_buffer, "*") == 0) return CANCELLED;
84*aptr = big_buffer;
85return OK;
86}
87
37942ad8
JH
88
89
90int
91auth_prompt(const uschar * challenge)
92{
93int rc, len;
94uschar * resp, * clear, * end;
95
96if ((rc = auth_get_data(&resp, challenge, Ustrlen(challenge))) != OK)
97 return rc;
98if ((len = b64decode(resp, &clear)) < 0)
99 return BAD64;
100end = clear + len;
101
102/* This loop must run at least once, in case the length is zero */
103do
104 {
105 if (expand_nmax < AUTH_VARS) auth_vars[expand_nmax] = clear;
106 expand_nstring[++expand_nmax] = clear;
107 while (*clear != 0) clear++;
108 expand_nlength[expand_nmax] = clear++ - expand_nstring[expand_nmax];
109 }
110while (clear < end && expand_nmax < EXPAND_MAXN);
111return OK;
112}
113
114
115/***********************************************
116* Send an AUTH-negotiation item *
117************************************************/
118
119/* Expand and send one client auth item and read the response.
120Include the AUTH command and method if tagged as "first". Use the given buffer
121for receiving the b6-encoded reply; decode it it return it in the string arg.
122
123Return:
124 OK success
125 FAIL_SEND error after writing a command; errno is set
126 FAIL failed after reading a response;
127 either errno is set (for timeouts, I/O failures) or
128 the buffer contains the SMTP response line
129 CANCELLED the client cancelled authentication (often "fail" in expansion)
130 the buffer may contain a message; if not, *buffer = 0
131 ERROR local problem (typically expansion error); message in buffer
132 DEFER more items expected
133*/
134
135int
136auth_client_item(void * sx, auth_instance * ablock, const uschar ** inout,
137 unsigned flags, int timeout, uschar * buffer, int buffsize)
138{
139int len, clear_len;
140uschar * ss, * clear;
141
142ss = US expand_cstring(*inout);
143if (ss == *inout) ss = string_copy(ss);
144
145/* Forced expansion failure is not an error; authentication is abandoned. On
146all but the first string, we have to abandon the authentication attempt by
147sending a line containing "*". Save the failed expansion string, because it
148is in big_buffer, and that gets used by the sending function. */
149
150if (!ss)
151 {
152 if (!(flags & AUTH_ITEM_FIRST))
153 {
154 if (smtp_write_command(sx, SCMD_FLUSH, "*\r\n") >= 0)
155 (void) smtp_read_response(sx, US buffer, buffsize, '2', timeout);
156 }
157 if (f.expand_string_forcedfail)
158 {
159 *buffer = 0; /* No message */
160 return CANCELLED;
161 }
162 string_format(buffer, buffsize, "expansion of \"%s\" failed in %s "
163 "authenticator: %s", *inout, ablock->name, expand_string_message);
164 return ERROR;
165 }
166
167len = Ustrlen(ss);
168
169/* The character ^ is used as an escape for a binary zero character, which is
170needed for the PLAIN mechanism. It must be doubled if really needed. */
171
172for (int i = 0; i < len; i++)
173 if (ss[i] == '^')
174 if (ss[i+1] != '^')
175 ss[i] = 0;
176 else
f9fc9427 177 if (--len > ++i) memmove(ss + i, ss + i + 1, len - i);
37942ad8
JH
178
179/* The first string is attached to the AUTH command; others are sent
180unembellished. */
181
182if (flags & AUTH_ITEM_FIRST)
183 {
184 if (smtp_write_command(sx, SCMD_FLUSH, "AUTH %s%s%s\r\n",
185 ablock->public_name, len == 0 ? "" : " ", b64encode(CUS ss, len)) < 0)
186 return FAIL_SEND;
187 }
188else
189 if (smtp_write_command(sx, SCMD_FLUSH, "%s\r\n", b64encode(CUS ss, len)) < 0)
190 return FAIL_SEND;
191
192/* If we receive a success response from the server, authentication
193has succeeded. There may be more data to send, but is there any point
194in provoking an error here? */
195
14a806d6 196if (smtp_read_response(sx, buffer, buffsize, '2', timeout))
37942ad8
JH
197 {
198 *inout = NULL;
199 return OK;
200 }
201
202/* Not a success response. If errno != 0 there is some kind of transmission
203error. Otherwise, check the response code in the buffer. If it starts with
204'3', more data is expected. */
205
206if (errno != 0 || buffer[0] != '3') return FAIL;
207
208/* If there is no more data to send, we have to cancel the authentication
209exchange and return ERROR. */
210
211if (flags & AUTH_ITEM_LAST)
212 {
213 if (smtp_write_command(sx, SCMD_FLUSH, "*\r\n") >= 0)
214 (void)smtp_read_response(sx, US buffer, buffsize, '2', timeout);
215 string_format(buffer, buffsize, "Too few items in client_send in %s "
216 "authenticator", ablock->name);
217 return ERROR;
218 }
219
220/* Now that we know we'll continue, we put the received data into $auth<n>,
221if possible. First, decode it: buffer+4 skips over the SMTP status code. */
222
223clear_len = b64decode(buffer+4, &clear);
224
225/* If decoding failed, the default is to terminate the authentication, and
226return FAIL, with the SMTP response still in the buffer. However, if client_
227ignore_invalid_base64 is set, we ignore the error, and put an empty string
228into $auth<n>. */
229
230if (clear_len < 0)
231 {
232 uschar *save_bad = string_copy(buffer);
233 if (!(flags & AUTH_ITEM_IGN64))
234 {
235 if (smtp_write_command(sx, SCMD_FLUSH, "*\r\n") >= 0)
236 (void)smtp_read_response(sx, US buffer, buffsize, '2', timeout);
237 string_format(buffer, buffsize, "Invalid base64 string in server "
238 "response \"%s\"", save_bad);
239 return CANCELLED;
240 }
241 DEBUG(D_auth) debug_printf("bad b64 decode for '%s';"
242 " ignoring due to client_ignore_invalid_base64\n", save_bad);
243 clear = string_copy(US"");
244 clear_len = 0;
245 }
246
247*inout = clear;
248return DEFER;
249}
250
251
0756eb3c 252/* End of get_data.c */