Mercurial > dovecot > core-2.2
changeset 9754:b9ad5b841f7e HEAD
*-login: Use a common client_send_line() API.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 09 Aug 2009 17:55:43 -0400 |
parents | fc025d93b274 |
children | c2037505a66b |
files | src/imap-login/client-authenticate.c src/imap-login/client-authenticate.h src/imap-login/client.c src/imap-login/client.h src/imap-login/imap-proxy.c src/login-common/client-common.h src/pop3-login/client-authenticate.c src/pop3-login/client.c src/pop3-login/client.h src/pop3-login/pop3-proxy.c |
diffstat | 10 files changed, 342 insertions(+), 167 deletions(-) [+] |
line wrap: on
line diff
--- a/src/imap-login/client-authenticate.c Sun Aug 09 16:20:31 2009 -0400 +++ b/src/imap-login/client-authenticate.c Sun Aug 09 17:55:43 2009 -0400 @@ -189,6 +189,8 @@ .. [REFERRAL ..] (Reason from auth server) */ reply = t_str_new(128); + str_append(reply, client->cmd_tag); + str_append_c(reply, ' '); str_append(reply, nologin ? "NO " : "OK "); str_printfa(reply, "[REFERRAL imap://%s;AUTH=%s@%s", destuser, client->common.auth_mech_name, host); @@ -203,7 +205,8 @@ str_append(reply, "Logged in, but you should use " "this server instead."); } - client_send_tagline(client, str_c(reply)); + str_append(reply, "\r\n"); + client_send_raw(client, str_c(reply)); if (!nologin) { client_destroy_success(client, "Login with referral"); return TRUE; @@ -211,18 +214,23 @@ } else if (nologin) { /* Authentication went ok, but for some reason user isn't allowed to log in. Shouldn't probably happen. */ - reply = t_str_new(128); - if (reason != NULL) - str_printfa(reply, "NO [ALERT] %s", reason); - else if (temp) { - str_append(reply, "NO ["IMAP_RESP_CODE_UNAVAILABLE"] " - AUTH_TEMP_FAILED_MSG); + if (reason != NULL) { + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAIL_REASON, + reason); + } else if (temp) { + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, + AUTH_TEMP_FAILED_MSG); } else if (authz_failure) { - str_append(reply, "NO "IMAP_AUTHZ_FAILED_MSG); + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTHZ_FAILED, + "Authorization failed"); } else { - str_append(reply, "NO "IMAP_AUTH_FAILED_MSG); + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAILED, + AUTH_FAILED_MSG); } - client_send_tagline(client, str_c(reply)); } else { /* normal login/failure */ return FALSE; @@ -240,7 +248,6 @@ { struct imap_client *client = (struct imap_client *)_client; struct const_iovec iov[3]; - const char *msg; size_t data_len; bool nodelay; @@ -267,13 +274,18 @@ break; } - if (reply == SASL_SERVER_REPLY_AUTH_ABORTED) - msg = "BAD Authentication aborted by client."; - else if (data == NULL) - msg = "NO "IMAP_AUTH_FAILED_MSG; - else - msg = t_strconcat("NO [ALERT] ", data, NULL); - client_send_tagline(client, msg); + if (reply == SASL_SERVER_REPLY_AUTH_ABORTED) { + client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + "Authentication aborted by client."); + } else if (data == NULL) { + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAILED, + AUTH_FAILED_MSG); + } else { + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAIL_REASON, + data); + } if (!client->destroyed) client_auth_failed(client, nodelay); @@ -282,8 +294,8 @@ if (data == NULL) client_destroy_internal_failure(client); else { - client_send_tagline(client, - t_strconcat("NO ", data, NULL)); + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, data); /* authentication itself succeeded, we just hit some internal failure. */ client_destroy_success(client, data); @@ -365,8 +377,8 @@ "SSL required for authentication"); } client->common.auth_attempts++; - client_send_tagline(client, - "NO ["IMAP_RESP_CODE_PRIVACYREQUIRED"] " + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL, "Authentication not allowed until SSL/TLS is enabled."); return 1; } @@ -401,12 +413,13 @@ } client->common.auth_tried_disabled_plaintext = TRUE; client->common.auth_attempts++; - client_send_line(client, + client_send_raw(client, "* BAD [ALERT] Plaintext authentication not allowed " "without SSL/TLS, but your client did it anyway. " - "If anyone was listening, the password was exposed."); - client_send_tagline(client, "NO ["IMAP_RESP_CODE_CLIENTBUG"] " - AUTH_PLAINTEXT_DISABLED_MSG); + "If anyone was listening, the password was exposed.\r\n"); + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL, + AUTH_PLAINTEXT_DISABLED_MSG); return 1; }
--- a/src/imap-login/client-authenticate.h Sun Aug 09 16:20:31 2009 -0400 +++ b/src/imap-login/client-authenticate.h Sun Aug 09 17:55:43 2009 -0400 @@ -3,11 +3,6 @@ struct imap_arg; -#define IMAP_AUTH_FAILED_MSG \ - "["IMAP_RESP_CODE_AUTHFAILED"] "AUTH_FAILED_MSG -#define IMAP_AUTHZ_FAILED_MSG \ - "["IMAP_RESP_CODE_AUTHZFAILED"] Authorization failed" - const char *client_authenticate_get_capabilities(struct imap_client *client); int cmd_login(struct imap_client *client, const struct imap_arg *args);
--- a/src/imap-login/client.c Sun Aug 09 16:20:31 2009 -0400 +++ b/src/imap-login/client.c Sun Aug 09 17:55:43 2009 -0400 @@ -11,6 +11,7 @@ #include "strescape.h" #include "imap-parser.h" #include "imap-id.h" +#include "imap-resp-code.h" #include "master-service.h" #include "master-auth.h" #include "client.h" @@ -45,9 +46,9 @@ #endif #define AUTH_SERVER_WAITING_MSG \ - "* OK Waiting for authentication process to respond.." + "Waiting for authentication process to respond.." #define AUTH_MASTER_WAITING_MSG \ - "* OK Waiting for authentication master process to respond.." + "Waiting for authentication master process to respond.." const char *login_protocol = "imap"; const char *login_process_name = "imap-login"; @@ -116,9 +117,10 @@ CAPABILITY commands. */ if (!client->starttls) client->client_ignores_capability_resp_code = TRUE; - client_send_line(client, t_strconcat( - "* CAPABILITY ", get_capability(client), NULL)); - client_send_tagline(client, "OK Capability completed."); + client_send_raw(client, t_strconcat( + "* CAPABILITY ", get_capability(client), "\r\n", NULL)); + client_send_line(&client->common, CLIENT_CMD_REPLY_OK, + "Capability completed."); return 1; } @@ -133,7 +135,8 @@ fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip, client->common.set, &client->common.proxy); if (fd_ssl == -1) { - client_send_line(client, "* BYE TLS initialization failed."); + client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, + "TLS initialization failed."); client_destroy(client, "Disconnected: TLS initialization failed."); return; @@ -176,12 +179,14 @@ static int cmd_starttls(struct imap_client *client) { if (client->common.tls) { - client_send_tagline(client, "BAD TLS is already active."); + client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + "TLS is already active."); return 1; } if (!ssl_initialized) { - client_send_tagline(client, "BAD TLS support isn't enabled."); + client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + "TLS support isn't enabled."); return 1; } @@ -190,7 +195,8 @@ if (client->io != NULL) io_remove(&client->io); - client_send_tagline(client, "OK Begin TLS negotiation now."); + client_send_line(&client->common, CLIENT_CMD_REPLY_OK, + "Begin TLS negotiation now."); /* uncork the old fd */ o_stream_uncork(client->output); @@ -249,31 +255,34 @@ } env = getenv("IMAP_ID_SEND"); - client_send_line(client, t_strdup_printf("* ID %s", - imap_id_reply_generate(env))); - client_send_tagline(client, "OK ID completed."); + client_send_raw(client, + t_strdup_printf("* ID %s\r\n", imap_id_reply_generate(env))); + client_send_line(&client->common, CLIENT_CMD_REPLY_OK, "ID completed."); return 1; } static int cmd_noop(struct imap_client *client) { - client_send_tagline(client, "OK NOOP completed."); + client_send_line(&client->common, CLIENT_CMD_REPLY_OK, + "NOOP completed."); return 1; } static int cmd_logout(struct imap_client *client) { - client_send_line(client, "* BYE Logging out"); - client_send_tagline(client, "OK Logout completed."); + client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, + "Logging out"); + client_send_line(&client->common, CLIENT_CMD_REPLY_OK, + "Logout completed."); client_destroy(client, "Aborted login"); return 1; } static int cmd_enable(struct imap_client *client) { - client_send_line(client, "* ENABLED"); - client_send_tagline(client, - "OK ENABLE ignored in non-authenticated state."); + client_send_raw(client, "* ENABLED\r\n"); + client_send_line(&client->common, CLIENT_CMD_REPLY_OK, + "ENABLE ignored in non-authenticated state."); return 1; } @@ -345,14 +354,14 @@ /* error */ msg = imap_parser_get_error(client->parser, &fatal); if (fatal) { - client_send_line(client, t_strconcat("* BYE ", - msg, NULL)); + client_send_line(&client->common, + CLIENT_CMD_REPLY_BYE, msg); client_destroy(client, t_strconcat("Disconnected: ", msg, NULL)); return FALSE; } - client_send_tagline(client, t_strconcat("BAD ", msg, NULL)); + client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, msg); client->cmd_finished = TRUE; client->skip_line = TRUE; return TRUE; @@ -374,14 +383,14 @@ if (*client->cmd_tag == '\0') client->cmd_tag = "*"; if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) { - client_send_line(client, - "* BYE Too many invalid IMAP commands."); + client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, + "Too many invalid IMAP commands."); client_destroy(client, "Disconnected: Too many invalid commands"); return FALSE; } - client_send_tagline(client, - "BAD Error in IMAP command received by server."); + client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + "Error in IMAP command received by server."); } return ret != 0; @@ -392,7 +401,8 @@ switch (i_stream_read(client->common.input)) { case -2: /* buffer full */ - client_send_line(client, "* BYE Input buffer full, aborting"); + client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, + "Input buffer full, aborting"); client_destroy(client, "Disconnected: Input buffer full"); return FALSE; case -1: @@ -419,7 +429,8 @@ if (!auth_client_is_connected(auth_client)) { /* we're not yet connected to auth process - don't allow any commands */ - client_send_line(client, AUTH_SERVER_WAITING_MSG); + client_send_line(&client->common, CLIENT_CMD_REPLY_STATUS, + AUTH_SERVER_WAITING_MSG); if (client->to_auth_waiting != NULL) timeout_remove(&client->to_auth_waiting); @@ -480,20 +491,23 @@ str_append(greet, "* OK "); str_printfa(greet, "[CAPABILITY %s] ", get_capability(client)); str_append(greet, client->common.set->login_greeting); + str_append(greet, "\r\n"); - client_send_line(client, str_c(greet)); + client_send_raw(client, str_c(greet)); client->greeting_sent = TRUE; } static void client_idle_disconnect_timeout(struct imap_client *client) { - client_send_line(client, "* BYE Disconnected for inactivity."); + client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + "Disconnected for inactivity."); client_destroy(client, "Disconnected: Inactivity"); } static void client_auth_waiting_timeout(struct imap_client *client) { - client_send_line(client, client->common.master_tag == 0 ? + client_send_line(&client->common, CLIENT_CMD_REPLY_STATUS, + client->common.master_tag == 0 ? AUTH_SERVER_WAITING_MSG : AUTH_MASTER_WAITING_MSG); timeout_remove(&client->to_auth_waiting); } @@ -631,7 +645,8 @@ void client_destroy_internal_failure(struct imap_client *client) { - client_send_line(client, "* BYE Internal login failure. " + client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, + "Internal login failure. " "Refer to server log for more information."); client_destroy(client, "Internal login failure"); } @@ -667,29 +682,84 @@ return FALSE; } -void client_send_line(struct imap_client *client, const char *line) +static void +client_send_raw_data(struct imap_client *client, const void *data, size_t size) { - struct const_iovec iov[2]; ssize_t ret; - iov[0].iov_base = line; - iov[0].iov_len = strlen(line); - iov[1].iov_base = "\r\n"; - iov[1].iov_len = 2; - - ret = o_stream_sendv(client->output, iov, 2); - if (ret < 0 || (size_t)ret != iov[0].iov_len + iov[1].iov_len) { - /* either disconnection or buffer full. in either case we - want this connection destroyed. however destroying it here - might break things if client is still tried to be accessed - without being referenced.. */ + ret = o_stream_send(client->output, data, size); + if (ret < 0 || (size_t)ret != size) { + /* either disconnection or buffer full. in either case we want + this connection destroyed. however destroying it here might + break things if client is still tried to be accessed without + being referenced.. */ i_stream_close(client->common.input); } } -void client_send_tagline(struct imap_client *client, const char *line) +void client_send_raw(struct imap_client *client, const char *data) +{ + client_send_raw_data(client, data, strlen(data)); +} + +void client_send_line(struct client *client, enum client_cmd_reply reply, + const char *text) { - client_send_line(client, t_strconcat(client->cmd_tag, " ", line, NULL)); + struct imap_client *imap_client = (struct imap_client *)client; + const char *resp_code = NULL; + const char *prefix = "NO"; + bool tagged = TRUE; + + switch (reply) { + case CLIENT_CMD_REPLY_OK: + prefix = "OK"; + break; + case CLIENT_CMD_REPLY_AUTH_FAILED: + resp_code = IMAP_RESP_CODE_AUTHFAILED; + break; + case CLIENT_CMD_REPLY_AUTHZ_FAILED: + resp_code = IMAP_RESP_CODE_AUTHZFAILED; + break; + case CLIENT_CMD_REPLY_AUTH_FAIL_TEMP: + resp_code = IMAP_RESP_CODE_UNAVAILABLE; + break; + case CLIENT_CMD_REPLY_AUTH_FAIL_REASON: + resp_code = "ALERT"; + break; + case CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL: + resp_code = IMAP_RESP_CODE_PRIVACYREQUIRED; + break; + case CLIENT_CMD_REPLY_BAD: + prefix = "BAD"; + break; + case CLIENT_CMD_REPLY_BYE: + prefix = "BYE"; + tagged = FALSE; + break; + case CLIENT_CMD_REPLY_STATUS: + prefix = "OK"; + tagged = FALSE; + break; + } + + T_BEGIN { + string_t *line = t_str_new(256); + + if (tagged) + str_append(line, imap_client->cmd_tag); + else + str_append_c(line, '*'); + str_append_c(line, ' '); + str_append(line, prefix); + str_append_c(line, ' '); + if (resp_code != NULL) + str_printfa(line, "[%s] ", resp_code); + str_append(line, text); + str_append(line, "\r\n"); + + client_send_raw_data(imap_client, str_data(line), + str_len(line)); + } T_END; } void clients_notify_auth_connected(void)
--- a/src/imap-login/client.h Sun Aug 09 16:20:31 2009 -0400 +++ b/src/imap-login/client.h Sun Aug 09 17:55:43 2009 -0400 @@ -45,13 +45,11 @@ void client_destroy_success(struct imap_client *client, const char *reason); void client_destroy_internal_failure(struct imap_client *client); -void client_send_line(struct imap_client *client, const char *line); -void client_send_tagline(struct imap_client *client, const char *line); - bool client_read(struct imap_client *client); bool client_skip_line(struct imap_client *client); void client_input(struct imap_client *client); +void client_send_raw(struct imap_client *client, const char *data); void client_ref(struct imap_client *client); bool client_unref(struct imap_client *client);
--- a/src/imap-login/imap-proxy.c Sun Aug 09 16:20:31 2009 -0400 +++ b/src/imap-login/imap-proxy.c Sun Aug 09 17:55:43 2009 -0400 @@ -17,9 +17,6 @@ #include <stdlib.h> -#define PROXY_FAILURE_MSG \ - "NO ["IMAP_RESP_CODE_UNAVAILABLE"] "AUTH_TEMP_FAILED_MSG - static void proxy_write_id(struct imap_client *client, string_t *str) { str_printfa(str, "I ID (" @@ -44,8 +41,11 @@ static void proxy_failed(struct imap_client *client, bool send_tagline) { - if (send_tagline) - client_send_tagline(client, PROXY_FAILURE_MSG); + if (send_tagline) { + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, + AUTH_TEMP_FAILED_MSG); + } login_proxy_free(&client->proxy); proxy_free_password(client); @@ -271,9 +271,13 @@ the remote is sending a different error message an attacker can't find out what users exist in the system. */ - line = "NO "IMAP_AUTH_FAILED_MSG; + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAILED, + AUTH_FAILED_MSG); } else if (strncmp(line, "NO [", 4) == 0) { /* remote sent some other resp-code. forward it. */ + client_send_raw(client, t_strconcat( + client->cmd_tag, " ", line, "\r\n", NULL)); } else { /* there was no [resp-code], so remote isn't Dovecot v1.2+. we could either forward the line as-is and @@ -282,9 +286,10 @@ failures. since other errors are pretty rare, it's safer to just hide them. they're still available in logs though. */ - line = "NO "IMAP_AUTH_FAILED_MSG; + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAILED, + AUTH_FAILED_MSG); } - client_send_tagline(client, line); proxy_failed(client, FALSE); return -1; @@ -298,7 +303,7 @@ return 0; } else if (strncmp(line, "* ", 2) == 0) { /* untagged reply. just foward it. */ - client_send_line(client, line); + client_send_raw(client, t_strconcat(line, "\r\n", NULL)); return 0; } else { /* tagged reply, shouldn't happen. */ @@ -358,7 +363,9 @@ if (password == NULL) { client_syslog_err(&client->common, "proxy: password not given"); - client_send_tagline(client, PROXY_FAILURE_MSG); + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, + AUTH_TEMP_FAILED_MSG); return -1; } @@ -371,14 +378,18 @@ } if (login_proxy_is_ourself(&client->common, host, port, user)) { client_syslog_err(&client->common, "Proxying loops to itself"); - client_send_tagline(client, PROXY_FAILURE_MSG); + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, + AUTH_TEMP_FAILED_MSG); return -1; } client->proxy = login_proxy_new(&client->common, host, port, ssl_flags, proxy_input, client); if (client->proxy == NULL) { - client_send_tagline(client, PROXY_FAILURE_MSG); + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, + AUTH_TEMP_FAILED_MSG); return -1; }
--- a/src/login-common/client-common.h Sun Aug 09 16:20:31 2009 -0400 +++ b/src/login-common/client-common.h Sun Aug 09 17:55:43 2009 -0400 @@ -12,6 +12,18 @@ */ #define LOGIN_MAX_INBUF_SIZE 4096 +enum client_cmd_reply { + CLIENT_CMD_REPLY_OK, + CLIENT_CMD_REPLY_AUTH_FAILED, + CLIENT_CMD_REPLY_AUTHZ_FAILED, + CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, + CLIENT_CMD_REPLY_AUTH_FAIL_REASON, + CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL, + CLIENT_CMD_REPLY_BAD, + CLIENT_CMD_REPLY_BYE, + CLIENT_CMD_REPLY_STATUS +}; + struct client { struct client *prev, *next; pool_t pool; @@ -57,6 +69,9 @@ void client_unlink(struct client *client); unsigned int clients_get_count(void) ATTR_PURE; +void client_send_line(struct client *client, enum client_cmd_reply reply, + const char *text); + void client_syslog(struct client *client, const char *msg); void client_syslog_err(struct client *client, const char *msg); const char *client_get_extra_disconnect_reason(struct client *client);
--- a/src/pop3-login/client-authenticate.c Sun Aug 09 16:20:31 2009 -0400 +++ b/src/pop3-login/client-authenticate.c Sun Aug 09 17:55:43 2009 -0400 @@ -46,9 +46,9 @@ str_append_c(str, ' '); str_append(str, mech[i].name); } - str_append(str, "\r\n."); + str_append(str, "\r\n.\r\n"); - client_send_line(client, str_c(str)); + client_send_raw(client, str_c(str)); return TRUE; } @@ -121,7 +121,6 @@ const char *master_user = NULL; const char *key, *value, *p; enum login_proxy_ssl_flags ssl_flags = 0; - string_t *reply; unsigned int port = 110; bool proxy = FALSE, temp = FALSE, nologin = !success; @@ -181,16 +180,17 @@ if (!nologin) return FALSE; - reply = t_str_new(128); - str_append(reply, "-ERR "); - if (reason != NULL) - str_append(reply, reason); - else if (temp) - str_append(reply, "[IN-USE] "AUTH_TEMP_FAILED_MSG); - else - str_append(reply, AUTH_FAILED_MSG); - - client_send_line(client, str_c(reply)); + if (reason != NULL) { + client_send_line(&client->common, CLIENT_CMD_REPLY_AUTH_FAILED, + reason); + } else if (temp) { + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, + AUTH_TEMP_FAILED_MSG); + } else { + client_send_line(&client->common, CLIENT_CMD_REPLY_AUTH_FAILED, + AUTH_FAILED_MSG); + } if (!client->destroyed) client_auth_failed(client, *nodelay_r); @@ -202,7 +202,6 @@ { struct pop3_client *client = (struct pop3_client *)_client; struct const_iovec iov[3]; - const char *msg; size_t data_len; bool nodelay; @@ -226,13 +225,18 @@ break; } - if (reply == SASL_SERVER_REPLY_AUTH_ABORTED) - msg = "-ERR Authentication aborted by client."; - else if (data == NULL) - msg = "-ERR "AUTH_FAILED_MSG; - else - msg = t_strconcat("-ERR ", data, NULL); - client_send_line(client, msg); + if (reply == SASL_SERVER_REPLY_AUTH_ABORTED) { + client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + "Authentication aborted by client."); + } else if (data == NULL) { + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAILED, + AUTH_FAILED_MSG); + } else { + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAIL_REASON, + data); + } if (!client->destroyed) client_auth_failed(client, nodelay); @@ -241,8 +245,10 @@ if (data == NULL) client_destroy_internal_failure(client); else { - client_send_line(client, - t_strconcat("-ERR [IN-USE] ", data, NULL)); + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, data); + /* authentication itself succeeded, we just hit some + internal failure. */ client_destroy_success(client, data); } break; @@ -281,8 +287,9 @@ "SSL required for authentication"); } client->common.auth_attempts++; - client_send_line(client, "-ERR Authentication not allowed " - "until SSL/TLS is enabled."); + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL, + "Authentication not allowed until SSL/TLS is enabled."); return TRUE; } @@ -290,16 +297,18 @@ /* Old-style SASL discovery, used by MS Outlook */ unsigned int i, count; - client_send_line(client, "+OK"); + client_send_raw(client, "+OK\r\n"); mech = auth_client_get_available_mechs(auth_client, &count); for (i = 0; i < count; i++) { if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 && (client->common.secured || client->common.set->disable_plaintext_auth || - (mech[i].flags & MECH_SEC_PLAINTEXT) == 0)) - client_send_line(client, mech[i].name); + (mech[i].flags & MECH_SEC_PLAINTEXT) == 0)) { + client_send_raw(client, mech[i].name); + client_send_raw(client, "\r\n"); + } } - client_send_line(client, "."); + client_send_raw(client, ".\r\n"); return TRUE; } @@ -335,7 +344,8 @@ client_syslog(&client->common, "Login failed: " "Plaintext authentication disabled"); } - client_send_line(client, "-ERR "AUTH_PLAINTEXT_DISABLED_MSG); + client_send_line(&client->common, CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL, + AUTH_PLAINTEXT_DISABLED_MSG); client->common.auth_tried_disabled_plaintext = TRUE; client->common.auth_attempts++; return FALSE; @@ -349,7 +359,7 @@ i_free(client->last_user); client->last_user = i_strdup(args); - client_send_line(client, "+OK"); + client_send_raw(client, "+OK\r\n"); return TRUE; } @@ -363,7 +373,8 @@ if (!check_plaintext_auth(client)) return TRUE; - client_send_line(client, "-ERR No username given."); + client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + "No username given."); return TRUE; } @@ -405,7 +416,8 @@ client_syslog(&client->common, "APOP failed: APOP not enabled"); } - client_send_line(client, "-ERR APOP not enabled."); + client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + "APOP not enabled."); return TRUE; } @@ -416,7 +428,8 @@ client_syslog(&client->common, "APOP failed: Invalid parameters"); } - client_send_line(client, "-ERR Invalid parameters."); + client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + "Invalid parameters."); return TRUE; } @@ -432,8 +445,8 @@ client_syslog(&client->common, "APOP failed: " "Invalid characters in MD5 response"); } - client_send_line(client, - "-ERR Invalid characters in MD5 response."); + client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + "Invalid characters in MD5 response."); return TRUE; }
--- a/src/pop3-login/client.c Sun Aug 09 16:20:31 2009 -0400 +++ b/src/pop3-login/client.c Sun Aug 09 17:55:43 2009 -0400 @@ -9,6 +9,7 @@ #include "randgen.h" #include "process-title.h" #include "safe-memset.h" +#include "str.h" #include "strescape.h" #include "master-service.h" #include "master-auth.h" @@ -72,7 +73,8 @@ fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip, client->common.set, &client->common.proxy); if (fd_ssl == -1) { - client_send_line(client, "-ERR TLS initialization failed."); + client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, + "TLS initialization failed."); client_destroy(client, "Disconnected: TLS initialization failed."); return; @@ -111,12 +113,14 @@ static bool cmd_stls(struct pop3_client *client) { if (client->common.tls) { - client_send_line(client, "-ERR TLS is already active."); + client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + "TLS is already active."); return TRUE; } if (!ssl_initialized) { - client_send_line(client, "-ERR TLS support isn't enabled."); + client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + "TLS support isn't enabled."); return TRUE; } @@ -125,7 +129,8 @@ if (client->io != NULL) io_remove(&client->io); - client_send_line(client, "+OK Begin TLS negotiation now."); + client_send_line(&client->common, CLIENT_CMD_REPLY_OK, + "Begin TLS negotiation now."); /* uncork the old fd */ o_stream_uncork(client->output); @@ -143,7 +148,7 @@ static bool cmd_quit(struct pop3_client *client) { - client_send_line(client, "+OK Logging out"); + client_send_line(&client->common, CLIENT_CMD_REPLY_OK, "Logging out"); client_destroy(client, "Aborted login"); return TRUE; } @@ -167,7 +172,8 @@ if (strcmp(cmd, "QUIT") == 0) return cmd_quit(client); - client_send_line(client, "-ERR Unknown command."); + client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, + "Unknown command."); return FALSE; } @@ -176,7 +182,8 @@ switch (i_stream_read(client->common.input)) { case -2: /* buffer full */ - client_send_line(client, "-ERR Input line too long, aborting"); + client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, + "Input buffer full, aborting"); client_destroy(client, "Disconnected: Input buffer full"); return FALSE; case -1: @@ -217,7 +224,8 @@ args != NULL ? args : "")) client->bad_counter = 0; else if (++client->bad_counter > CLIENT_MAX_BAD_COMMANDS) { - client_send_line(client, "-ERR Too many bad commands."); + client_send_line(&client->common, CLIENT_CMD_REPLY_BYE, + "Too many invalid IMAP commands."); client_destroy(client, "Disconnected: Too many bad commands"); } @@ -292,11 +300,14 @@ client->io = io_add(client->common.fd, IO_READ, client_input, client); client->apop_challenge = get_apop_challenge(client); - client_send_line(client, t_strconcat("+OK ", - client->common.set->login_greeting, - client->apop_challenge != NULL ? - " " : NULL, - client->apop_challenge, NULL)); + if (client->apop_challenge == NULL) { + client_send_line(&client->common, CLIENT_CMD_REPLY_OK, + client->common.set->login_greeting); + } else { + client_send_line(&client->common, CLIENT_CMD_REPLY_OK, + t_strconcat(client->common.set->login_greeting, " ", + client->apop_challenge, NULL)); + } } static void client_idle_disconnect_timeout(struct pop3_client *client) @@ -423,7 +434,8 @@ void client_destroy_internal_failure(struct pop3_client *client) { - client_send_line(client, "-ERR [IN-USE] Internal login failure. " + client_send_line(&client->common, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, + "Internal login failure. " "Refer to server log for more information."); client_destroy(client, "Internal login failure"); } @@ -459,26 +471,65 @@ return FALSE; } -void client_send_line(struct pop3_client *client, const char *line) +static void +client_send_raw_data(struct pop3_client *client, const void *data, size_t size) { - struct const_iovec iov[2]; ssize_t ret; - iov[0].iov_base = line; - iov[0].iov_len = strlen(line); - iov[1].iov_base = "\r\n"; - iov[1].iov_len = 2; - - ret = o_stream_sendv(client->output, iov, 2); - if (ret < 0 || (size_t)ret != iov[0].iov_len + iov[1].iov_len) { - /* either disconnection or buffer full. in either case we - want this connection destroyed. however destroying it here - might break things if client is still tried to be accessed - without being referenced.. */ + ret = o_stream_send(client->output, data, size); + if (ret < 0 || (size_t)ret != size) { + /* either disconnection or buffer full. in either case we want + this connection destroyed. however destroying it here might + break things if client is still tried to be accessed without + being referenced.. */ i_stream_close(client->common.input); } } +void client_send_raw(struct pop3_client *client, const char *data) +{ + client_send_raw_data(client, data, strlen(data)); +} + +void client_send_line(struct client *client, enum client_cmd_reply reply, + const char *text) +{ + struct pop3_client *pop3_client = (struct pop3_client *)client; + const char *prefix = "-ERR"; + + switch (reply) { + case CLIENT_CMD_REPLY_OK: + prefix = "+OK"; + break; + case CLIENT_CMD_REPLY_AUTH_FAIL_TEMP: + prefix = "-ERR [IN-USE]"; + break; + case CLIENT_CMD_REPLY_AUTH_FAILED: + case CLIENT_CMD_REPLY_AUTHZ_FAILED: + case CLIENT_CMD_REPLY_AUTH_FAIL_REASON: + case CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL: + case CLIENT_CMD_REPLY_BAD: + case CLIENT_CMD_REPLY_BYE: + break; + case CLIENT_CMD_REPLY_STATUS: + /* can't send status notifications */ + return; + } + + T_BEGIN { + string_t *line = t_str_new(256); + + str_append(line, prefix); + str_append_c(line, ' '); + str_append(line, text); + str_append(line, "\r\n"); + + client_send_raw_data(pop3_client, str_data(line), + str_len(line)); + } T_END; +} + + void clients_notify_auth_connected(void) { struct client *client;
--- a/src/pop3-login/client.h Sun Aug 09 16:20:31 2009 -0400 +++ b/src/pop3-login/client.h Sun Aug 09 17:55:43 2009 -0400 @@ -46,11 +46,11 @@ void client_destroy_success(struct pop3_client *client, const char *reason); void client_destroy_internal_failure(struct pop3_client *client); -void client_send_line(struct pop3_client *client, const char *line); - bool client_read(struct pop3_client *client); void client_input(struct pop3_client *client); +void client_send_raw(struct pop3_client *client, const char *data); + void client_ref(struct pop3_client *client); bool client_unref(struct pop3_client *client);
--- a/src/pop3-login/pop3-proxy.c Sun Aug 09 16:20:31 2009 -0400 +++ b/src/pop3-login/pop3-proxy.c Sun Aug 09 17:55:43 2009 -0400 @@ -11,8 +11,6 @@ #include "client.h" #include "pop3-proxy.h" -#define PROXY_FAILURE_MSG "-ERR [IN-USE] "AUTH_TEMP_FAILED_MSG - static void proxy_free_password(struct pop3_client *client) { if (client->proxy_password == NULL) @@ -24,8 +22,11 @@ static void proxy_failed(struct pop3_client *client, bool send_line) { - if (send_line) - client_send_line(client, PROXY_FAILURE_MSG); + if (send_line) { + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, + AUTH_TEMP_FAILED_MSG); + } login_proxy_free(&client->proxy); proxy_free_password(client); @@ -184,9 +185,11 @@ shouldn't be a real problem since of course everyone will be using only Dovecot as their backend :) */ if (strncmp(line, "-ERR ", 5) != 0) - client_send_line(client, "-ERR "AUTH_FAILED_MSG); - else - client_send_line(client, line); + client_send_line(&client->common, CLIENT_CMD_REPLY_AUTH_FAILED, + AUTH_FAILED_MSG); + else { + client_send_raw(client, t_strconcat(line, "\r\n", NULL)); + } if (client->common.set->verbose_auth) { str = t_str_new(128); @@ -267,7 +270,9 @@ if (password == NULL) { client_syslog_err(&client->common, "proxy: password not given"); - client_send_line(client, PROXY_FAILURE_MSG); + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, + AUTH_TEMP_FAILED_MSG); return -1; } @@ -280,14 +285,18 @@ } if (login_proxy_is_ourself(&client->common, host, port, user)) { client_syslog_err(&client->common, "Proxying loops to itself"); - client_send_line(client, PROXY_FAILURE_MSG); + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, + AUTH_TEMP_FAILED_MSG); return -1; } client->proxy = login_proxy_new(&client->common, host, port, ssl_flags, proxy_input, client); if (client->proxy == NULL) { - client_send_line(client, PROXY_FAILURE_MSG); + client_send_line(&client->common, + CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, + AUTH_TEMP_FAILED_MSG); return -1; }