1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "login-common.h"
4 #include "base64.h"
5 #include "buffer.h"
6 #include "ioloop.h"
7 #include "istream.h"
8 #include "ostream.h"
9 #include "safe-memset.h"
10 #include "str.h"
11 #include "str-sanitize.h"
12 #include "net.h"
13 #include "imap-resp-code.h"
14 #include "imap-parser.h"
15 #include "imap-url.h"
16 #include "auth-client.h"
17 #include "imap-login-client.h"
18 #include "client-authenticate.h"
19 #include "imap-proxy.h"
20 
21 
client_authenticate_get_capabilities(struct client * client,string_t * str)22 void client_authenticate_get_capabilities(struct client *client, string_t *str)
23 {
24 	const struct auth_mech_desc *mech;
25 	unsigned int i, count;
26 
27 	mech = sasl_server_get_advertised_mechs(client, &count);
28 	for (i = 0; i < count; i++) {
29 		str_append_c(str, ' ');
30 		str_append(str, "AUTH=");
31 		str_append(str, mech[i].name);
32 	}
33 }
34 
imap_client_auth_result(struct client * client,enum client_auth_result result,const struct client_auth_reply * reply,const char * text)35 void imap_client_auth_result(struct client *client,
36 			     enum client_auth_result result,
37 			     const struct client_auth_reply *reply,
38 			     const char *text)
39 {
40 	struct imap_url url;
41 	string_t *referral;
42 
43 	switch (result) {
44 	case CLIENT_AUTH_RESULT_SUCCESS:
45 		/* nothing to be done for IMAP */
46 		break;
47 	case CLIENT_AUTH_RESULT_REFERRAL_SUCCESS:
48 	case CLIENT_AUTH_RESULT_REFERRAL_NOLOGIN:
49 		/* IMAP referral
50 
51 		   [nologin] referral host=.. [port=..] [destuser=..]
52 		   [reason=..]
53 
54 		   NO [REFERRAL imap://destuser;AUTH=..@host:port/] Can't login.
55 		   OK [...] Logged in, but you should use this server instead.
56 		   .. [REFERRAL ..] (Reason from auth server)
57 		*/
58 		referral = t_str_new(128);
59 
60 		i_zero(&url);
61 		url.userid = reply->destuser;
62 		url.auth_type = client->auth_mech_name;
63 		url.host.name = reply->host;
64 		if (reply->port != 143)
65 			url.port = reply->port;
66 		str_append(referral, "REFERRAL ");
67 		str_append(referral, imap_url_create(&url));
68 
69 		if (result == CLIENT_AUTH_RESULT_REFERRAL_SUCCESS) {
70 			client_send_reply_code(client, IMAP_CMD_REPLY_OK,
71 					       str_c(referral), text);
72 		} else {
73 			client_send_reply_code(client, IMAP_CMD_REPLY_NO,
74 					       str_c(referral), text);
75 		}
76 		break;
77 	case CLIENT_AUTH_RESULT_INVALID_BASE64:
78 	case CLIENT_AUTH_RESULT_ABORTED:
79 		client_send_reply(client, IMAP_CMD_REPLY_BAD, text);
80 		break;
81 	case CLIENT_AUTH_RESULT_AUTHFAILED_REASON:
82 	case CLIENT_AUTH_RESULT_MECH_INVALID:
83 		if (text[0] == '[')
84 			client_send_reply(client, IMAP_CMD_REPLY_NO, text);
85 		else {
86 			client_send_reply_code(client, IMAP_CMD_REPLY_NO,
87 					       "ALERT", text);
88 		}
89 		break;
90 	case CLIENT_AUTH_RESULT_AUTHZFAILED:
91 		client_send_reply_code(client, IMAP_CMD_REPLY_NO,
92 				       IMAP_RESP_CODE_AUTHZFAILED, text);
93 		break;
94 	case CLIENT_AUTH_RESULT_TEMPFAIL:
95 		client_send_reply_code(client, IMAP_CMD_REPLY_NO,
96 				       IMAP_RESP_CODE_UNAVAILABLE, text);
97 		break;
98 	case CLIENT_AUTH_RESULT_SSL_REQUIRED:
99 	case CLIENT_AUTH_RESULT_MECH_SSL_REQUIRED:
100 		client_send_reply_code(client, IMAP_CMD_REPLY_NO,
101 				       IMAP_RESP_CODE_PRIVACYREQUIRED, text);
102 		break;
103 	case CLIENT_AUTH_RESULT_PASS_EXPIRED:
104 		client_send_reply_code(client, IMAP_CMD_REPLY_NO,
105 				       IMAP_RESP_CODE_EXPIRED, text);
106 		break;
107 	case CLIENT_AUTH_RESULT_LOGIN_DISABLED:
108 	case CLIENT_AUTH_RESULT_ANONYMOUS_DENIED:
109 		client_send_reply_code(client, IMAP_CMD_REPLY_NO,
110 				       IMAP_RESP_CODE_CONTACTADMIN, text);
111 		break;
112 	case CLIENT_AUTH_RESULT_AUTHFAILED:
113 		client_send_reply_code(client, IMAP_CMD_REPLY_NO,
114 				       IMAP_RESP_CODE_AUTHFAILED, text);
115 		break;
116 	}
117 }
118 
119 static int
imap_client_auth_begin(struct imap_client * imap_client,const char * mech_name,const char * init_resp)120 imap_client_auth_begin(struct imap_client *imap_client, const char *mech_name,
121 		       const char *init_resp)
122 {
123 	char *prefix;
124 
125 	prefix = i_strdup_printf("%d%s",
126 			imap_client->client_ignores_capability_resp_code ? 1 : 0,
127 			imap_client->cmd_tag);
128 
129 	i_free(imap_client->common.master_data_prefix);
130 	imap_client->common.master_data_prefix = (void *)prefix;
131 	imap_client->common.master_data_prefix_len = strlen(prefix)+1;
132 
133 	if (*init_resp == '\0')
134 		init_resp = NULL;
135 	else if (strcmp(init_resp, "=") == 0)
136 		init_resp = "";
137 	return client_auth_begin(&imap_client->common, mech_name, init_resp);
138 }
139 
cmd_authenticate(struct imap_client * imap_client,bool * parsed_r)140 int cmd_authenticate(struct imap_client *imap_client, bool *parsed_r)
141 {
142 	/* NOTE: This command's input is handled specially because the
143 	   SASL-IR can be large. */
144 	struct client *client = &imap_client->common;
145 	const unsigned char *data;
146 	size_t i, size;
147 	int ret;
148 
149 	*parsed_r = FALSE;
150 
151 	/* <auth mechanism name> [<initial SASL response>] */
152 	if (!imap_client->auth_mech_name_parsed) {
153 		data = i_stream_get_data(client->input, &size);
154 		for (i = 0; i < size; i++) {
155 			if (data[i] == ' ' ||
156 			    data[i] == '\r' || data[i] == '\n')
157 				break;
158 		}
159 		if (i == size)
160 			return 0;
161 		if (i == 0) {
162 			/* empty mechanism name */
163 			imap_client->skip_line = TRUE;
164 			return -1;
165 		}
166 		i_free(client->auth_mech_name);
167 		client->auth_mech_name = i_strndup(data, i);
168 		imap_client->auth_mech_name_parsed = TRUE;
169 		if (data[i] == ' ')
170 			i++;
171 		i_stream_skip(client->input, i);
172 	}
173 
174 	/* get SASL-IR, if any */
175 	if ((ret = client_auth_read_line(client)) <= 0)
176 		return ret;
177 
178 	*parsed_r = TRUE;
179 	imap_client->auth_mech_name_parsed = FALSE;
180 	return imap_client_auth_begin(imap_client,
181 				      t_strdup(client->auth_mech_name),
182 				      t_strdup(str_c(client->auth_response)));
183 }
184 
cmd_login(struct imap_client * imap_client,const struct imap_arg * args)185 int cmd_login(struct imap_client *imap_client, const struct imap_arg *args)
186 {
187 	struct client *client = &imap_client->common;
188 	const char *user, *pass;
189 	string_t *plain_login, *base64;
190 
191 	/* two arguments: username and password */
192 	if (!imap_arg_get_astring(&args[0], &user) ||
193 	    !imap_arg_get_astring(&args[1], &pass) ||
194 	    !IMAP_ARG_IS_EOL(&args[2]))
195 		return -1;
196 
197 	if (!client_check_plaintext_auth(client, TRUE)) {
198 		if (client->virtual_user == NULL)
199 			client->virtual_user = i_strdup(user);
200 		return 1;
201 	}
202 
203 	/* authorization ID \0 authentication ID \0 pass */
204 	plain_login = t_buffer_create(64);
205 	buffer_append_c(plain_login, '\0');
206 	buffer_append(plain_login, user, strlen(user));
207 	buffer_append_c(plain_login, '\0');
208 	buffer_append(plain_login, pass, strlen(pass));
209 
210 	base64 = t_buffer_create(MAX_BASE64_ENCODED_SIZE(plain_login->used));
211 	base64_encode(plain_login->data, plain_login->used, base64);
212 	return imap_client_auth_begin(imap_client, "PLAIN", str_c(base64));
213 }
214