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