1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) University of Cambridge 1995 - 2018 */
6 /* See the file NOTICE for conditions of use and distribution. */
11 /****************************************************************
12 * Decode and split the argument of an AUTH command *
13 ****************************************************************/
15 /* If data was supplied on the AUTH command, decode it, and split it up into
16 multiple items at binary zeros. The strings are put into $auth1, $auth2, etc,
17 up 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
22 auth_read_input(const uschar * data)
24 if (Ustrcmp(data, "=") == 0)
26 auth_vars[0] = expand_nstring[++expand_nmax] = US"";
27 expand_nlength[expand_nmax] = 0;
31 uschar * clear, * end;
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; )
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];
44 DEBUG(D_auth) debug_printf("\n");
52 /*************************************************
53 * Issue a challenge and get a response *
54 *************************************************/
56 /* This function is used by authentication drivers to output a challenge
57 to the SMTP client and read the response line.
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
64 Returns: OK on success
65 BAD64 if response too large for buffer
66 CANCELLED if response is "*"
70 auth_get_data(uschar ** aptr, const uschar * challenge, int challen)
74 smtp_printf("334 %s\r\n", FALSE, b64encode(challenge, challen));
75 while ((c = receive_getc(GETC_BUFFER_UNLIMITED)) != '\n' && c != EOF)
77 if (p >= big_buffer_size - 1) return BAD64;
80 if (p > 0 && big_buffer[p-1] == '\r') p--;
82 DEBUG(D_receive) debug_printf("SMTP<< %s\n", big_buffer);
83 if (Ustrcmp(big_buffer, "*") == 0) return CANCELLED;
91 auth_prompt(const uschar * challenge)
94 uschar * resp, * clear, * end;
96 if ((rc = auth_get_data(&resp, challenge, Ustrlen(challenge))) != OK)
98 if ((len = b64decode(resp, &clear)) < 0)
102 /* This loop must run at least once, in case the length is zero */
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];
110 while (clear < end && expand_nmax < EXPAND_MAXN);
115 /***********************************************
116 * Send an AUTH-negotiation item *
117 ************************************************/
119 /* Expand and send one client auth item and read the response.
120 Include the AUTH command and method if tagged as "first". Use the given buffer
121 for receiving the b6-encoded reply; decode it it return it in the string arg.
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
136 auth_client_item(void * sx, auth_instance * ablock, const uschar ** inout,
137 unsigned flags, int timeout, uschar * buffer, int buffsize)
140 uschar * ss, * clear;
142 ss = US expand_cstring(*inout);
143 if (ss == *inout) ss = string_copy(ss);
145 /* Forced expansion failure is not an error; authentication is abandoned. On
146 all but the first string, we have to abandon the authentication attempt by
147 sending a line containing "*". Save the failed expansion string, because it
148 is in big_buffer, and that gets used by the sending function. */
152 if (!(flags & AUTH_ITEM_FIRST))
154 if (smtp_write_command(sx, SCMD_FLUSH, "*\r\n") >= 0)
155 (void) smtp_read_response(sx, US buffer, buffsize, '2', timeout);
157 if (f.expand_string_forcedfail)
159 *buffer = 0; /* No message */
162 string_format(buffer, buffsize, "expansion of \"%s\" failed in %s "
163 "authenticator: %s", *inout, ablock->name, expand_string_message);
169 /* The character ^ is used as an escape for a binary zero character, which is
170 needed for the PLAIN mechanism. It must be doubled if really needed. */
172 for (int i = 0; i < len; i++)
177 if (--len > ++i) memmove(ss + i, ss + i + 1, len - i);
179 /* The first string is attached to the AUTH command; others are sent
182 if (flags & AUTH_ITEM_FIRST)
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)
189 if (smtp_write_command(sx, SCMD_FLUSH, "%s\r\n", b64encode(CUS ss, len)) < 0)
192 /* If we receive a success response from the server, authentication
193 has succeeded. There may be more data to send, but is there any point
194 in provoking an error here? */
196 if (smtp_read_response(sx, US buffer, buffsize, '2', timeout))
202 /* Not a success response. If errno != 0 there is some kind of transmission
203 error. Otherwise, check the response code in the buffer. If it starts with
204 '3', more data is expected. */
206 if (errno != 0 || buffer[0] != '3') return FAIL;
208 /* If there is no more data to send, we have to cancel the authentication
209 exchange and return ERROR. */
211 if (flags & AUTH_ITEM_LAST)
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);
220 /* Now that we know we'll continue, we put the received data into $auth<n>,
221 if possible. First, decode it: buffer+4 skips over the SMTP status code. */
223 clear_len = b64decode(buffer+4, &clear);
225 /* If decoding failed, the default is to terminate the authentication, and
226 return FAIL, with the SMTP response still in the buffer. However, if client_
227 ignore_invalid_base64 is set, we ignore the error, and put an empty string
232 uschar *save_bad = string_copy(buffer);
233 if (!(flags & AUTH_ITEM_IGN64))
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);
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"");
252 /* End of get_data.c */