int client_auth_begin(struct client *client, const char *mech_name,
		      const char *init_resp)
{
	if (!client->secured && strcmp(client->ssl_set->ssl, "required") == 0) {
		if (client->set->auth_verbose) {
			client_log(client, "Login failed: "
				   "SSL required for authentication");
		}
		client->auth_attempts++;
		client_auth_result(client, CLIENT_AUTH_RESULT_SSL_REQUIRED, NULL,
			"Authentication not allowed until SSL/TLS is enabled.");
		return 1;
	}


	client_ref(client);
	client->auth_initializing = TRUE;
	sasl_server_auth_begin(client, login_binary->protocol, mech_name,
			       init_resp, sasl_callback);
	client->auth_initializing = FALSE;
	if (!client->authenticating)
		return 1;

	/* don't handle input until we get the initial auth reply */
	if (client->io != NULL)
		io_remove(&client->io);
	client_set_auth_waiting(client);
	return 0;
}
Beispiel #2
0
bool client_check_plaintext_auth(struct client *client, bool pass_sent)
{
	if (client->secured || !client->set->disable_plaintext_auth)
		return TRUE;

	if (client->set->auth_verbose) {
		client_log(client, "Login failed: "
			   "Plaintext authentication disabled");
	}
	if (pass_sent) {
		client_notify_status(client, TRUE,
			 "Plaintext authentication not allowed "
			 "without SSL/TLS, but your client did it anyway. "
			 "If anyone was listening, the password was exposed.");
	}
	client_auth_result(client, CLIENT_AUTH_RESULT_SSL_REQUIRED, NULL,
			   AUTH_PLAINTEXT_DISABLED_MSG);
	client->auth_tried_disabled_plaintext = TRUE;
	client->auth_attempts++;
	return FALSE;
}
static void
sasl_callback(struct client *client, enum sasl_server_reply sasl_reply,
	      const char *data, const char *const *args)
{
	struct client_auth_reply reply;

	i_assert(!client->destroyed ||
		 sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED ||
		 sasl_reply == SASL_SERVER_REPLY_MASTER_FAILED);

	switch (sasl_reply) {
	case SASL_SERVER_REPLY_SUCCESS:
		if (client->to_auth_waiting != NULL)
			timeout_remove(&client->to_auth_waiting);
		if (args != NULL) {
			client_auth_parse_args(client, args, &reply);
			if (client_auth_handle_reply(client, &reply, TRUE))
				break;
		}
		client_auth_result(client, CLIENT_AUTH_RESULT_SUCCESS,
				   NULL, NULL);
		client_destroy_success(client, "Login");
		break;
	case SASL_SERVER_REPLY_AUTH_FAILED:
	case SASL_SERVER_REPLY_AUTH_ABORTED:
		if (client->to_auth_waiting != NULL)
			timeout_remove(&client->to_auth_waiting);
		if (args != NULL) {
			client_auth_parse_args(client, args, &reply);
			reply.nologin = TRUE;
			if (client_auth_handle_reply(client, &reply, FALSE))
				break;
		}

		if (sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED) {
			client_auth_result(client, CLIENT_AUTH_RESULT_ABORTED,
				NULL, "Authentication aborted by client.");
		} else if (data == NULL) {
			client_auth_result(client,
				CLIENT_AUTH_RESULT_AUTHFAILED, NULL,
				AUTH_FAILED_MSG);
		} else {
			client_auth_result(client,
				CLIENT_AUTH_RESULT_AUTHFAILED_REASON, NULL,
				data);
		}

		if (!client->destroyed)
			client_auth_failed(client);
		break;
	case SASL_SERVER_REPLY_MASTER_FAILED:
		if (data != NULL) {
			/* authentication itself succeeded, we just hit some
			   internal failure. */
			client_auth_result(client, CLIENT_AUTH_RESULT_TEMPFAIL,
					   NULL, data);
		}

		/* the fd may still be hanging somewhere in kernel or another
		   process. make sure the client gets disconnected. */
		if (shutdown(client->fd, SHUT_RDWR) < 0 && errno != ENOTCONN)
			i_error("shutdown() failed: %m");

		if (data == NULL)
			client_destroy_internal_failure(client);
		else
			client_destroy_success(client, data);
		break;
	case SASL_SERVER_REPLY_CONTINUE:
		i_assert(client->v.auth_send_challenge != NULL);
		client->v.auth_send_challenge(client, data);

		if (client->to_auth_waiting != NULL)
			timeout_remove(&client->to_auth_waiting);

		if (client->auth_response != NULL)
			str_truncate(client->auth_response, 0);

		i_assert(client->io == NULL);
		client->auth_waiting = TRUE;
		client->io = io_add(client->fd, IO_READ,
				    client_auth_input, client);
		client_auth_input(client);
		return;
	}

	client_unref(&client);
}
static bool
client_auth_handle_reply(struct client *client,
			 const struct client_auth_reply *reply, bool success)
{
	if (reply->proxy) {
		/* we want to proxy the connection to another server.
		   don't do this unless authentication succeeded. with
		   master user proxying we can get FAIL with proxy still set.

		   proxy host=.. [port=..] [destuser=..] pass=.. */
		if (!success)
			return FALSE;
		if (proxy_start(client, reply) < 0)
			client_auth_failed(client);
		return TRUE;
	}

	if (reply->host != NULL) {
		const char *reason;

		if (reply->reason != NULL)
			reason = reply->reason;
		else if (reply->nologin)
			reason = "Try this server instead.";
		else
			reason = "Logged in, but you should use this server instead.";

		if (reply->nologin) {
			client_auth_result(client,
				CLIENT_AUTH_RESULT_REFERRAL_NOLOGIN,
				reply, reason);
		} else {
			client_auth_result(client,
				CLIENT_AUTH_RESULT_REFERRAL_SUCCESS,
				reply, reason);
			return TRUE;
		}
	} else if (reply->nologin) {
		/* Authentication went ok, but for some reason user isn't
		   allowed to log in. Shouldn't probably happen. */
		if (reply->reason != NULL) {
			client_auth_result(client,
				CLIENT_AUTH_RESULT_AUTHFAILED_REASON,
				reply, reply->reason);
		} else if (reply->temp) {
			const char *timestamp, *msg;

			timestamp = t_strflocaltime("%Y-%m-%d %H:%M:%S", ioloop_time);
			msg = t_strdup_printf(AUTH_TEMP_FAILED_MSG" [%s:%s]",
				      my_hostname, timestamp);
			client_auth_result(client, CLIENT_AUTH_RESULT_TEMPFAIL,
					   reply, msg);
		} else if (reply->authz_failure) {
			client_auth_result(client,
				CLIENT_AUTH_RESULT_AUTHZFAILED, reply,
				"Authorization failed");
		} else {
			client_auth_result(client,
				CLIENT_AUTH_RESULT_AUTHFAILED, reply,
				AUTH_FAILED_MSG);
		}
	} else {
		/* normal login/failure */
		return FALSE;
	}

	i_assert(reply->nologin);

	if (!client->destroyed)
		client_auth_failed(client);
	return TRUE;
}