Line data Source code
1 71 : int state_authorization(LOGGER log, CONNECTION* client, DATABASE* database){
2 71 : CLIENT* client_data = (CLIENT*)client->aux_data;
3 71 : LISTENER* listener_data = (LISTENER*)client_data->listener->aux_data;
4 :
5 71 : int status = SASL_OK;
6 71 : char* method = NULL;
7 71 : char* parameter = NULL;
8 71 : char* challenge = NULL;
9 :
10 : #ifndef CMAIL_NO_TLS
11 : //disable login on tls-required auth
12 71 : if(client->tls_mode == TLS_ONLY || !listener_data->tls_require){
13 : #endif
14 :
15 : //handle sasl continuation
16 45 : if(client_data->auth.method == AUTH_SASL && !client_data->auth.auth_ok){
17 2 : if(!strcmp(client_data->recv_buffer, "*")){
18 : //cancel authentication
19 1 : logprintf(log, LOG_INFO, "Client cancelled authentication\n");
20 1 : sasl_cancel(&(client_data->auth.ctx));
21 1 : auth_reset(&(client_data->auth));
22 1 : client_send(log, client, "-ERR Authentication canceled\r\n");
23 1 : return 0;
24 : }
25 1 : else if(client_data->auth.ctx.method != SASL_INVALID){
26 1 : status = sasl_continue(log, &(client_data->auth.ctx), client_data->recv_buffer, &challenge);
27 : //do not return immediately, response handled below
28 : }
29 : else{
30 : //This should never happen
31 0 : logprintf(log, LOG_INFO, "Client in invalid SASL state, resetting\n");
32 0 : sasl_cancel(&(client_data->auth.ctx));
33 0 : auth_reset(&(client_data->auth));
34 0 : client_send(log, client, "-ERR Invalid state\r\n");
35 0 : return -1;
36 : }
37 : }
38 :
39 : //handle sasl initiation
40 44 : if(status == SASL_OK && !strncasecmp(client_data->recv_buffer, "auth ", 5)){
41 6 : client_data->auth.method = AUTH_SASL;
42 :
43 : //tokenize along spaces
44 6 : method = strtok(client_data->recv_buffer + 5, " ");
45 6 : if(method){
46 5 : parameter = strtok(NULL, " ");
47 5 : logprintf(log, LOG_DEBUG, "Beginning SASL with method %s parameter %s\n", method, parameter?parameter:"null");
48 5 : status = sasl_begin(log, &(client_data->auth.ctx), &(client_data->auth.user), method, parameter, &challenge);
49 : }
50 : else{
51 1 : logprintf(log, LOG_WARNING, "Client tried auth without supplying method\n");
52 1 : status = SASL_UNKNOWN_METHOD;
53 : }
54 : }
55 :
56 : //handle SASL outcome
57 44 : if(status != SASL_OK){
58 7 : switch(status){
59 : case SASL_ERROR_PROCESSING:
60 0 : logprintf(log, LOG_ERROR, "SASL processing error\r\n");
61 0 : auth_reset(&(client_data->auth));
62 0 : client_send(log, client, "-ERR SASL Internal Error\r\n");
63 0 : return -1;
64 : case SASL_ERROR_DATA:
65 2 : logprintf(log, LOG_ERROR, "SASL failed to parse data\r\n");
66 2 : auth_reset(&(client_data->auth));
67 2 : client_send(log, client, "-ERR SASL Invalid data provided\r\n");
68 2 : return -1;
69 : case SASL_UNKNOWN_METHOD:
70 1 : logprintf(log, LOG_WARNING, "Client tried unsupported authentication method: %s\n", client_data->recv_buffer+5);
71 1 : auth_reset(&(client_data->auth));
72 1 : client_send(log, client, "-ERR Unknown SASL mechanism\r\n");
73 1 : return -1;
74 : case SASL_CONTINUE:
75 2 : logprintf(log, LOG_INFO, "Asking for SASL continuation\r\n");
76 2 : client_send(log, client, "+ %s\r\n", challenge ? challenge:"");
77 2 : return 0;
78 : case SASL_DATA_OK:
79 2 : sasl_reset_ctx(&(client_data->auth.ctx), true);
80 :
81 : //check auth data
82 2 : if(!challenge || auth_validate(log, database, client_data->auth.user.authenticated, challenge, &(client_data->auth.user.authorized)) < 0){
83 : //login failed
84 1 : auth_reset(&(client_data->auth));
85 1 : logprintf(log, LOG_INFO, "Client failed to authenticate\n");
86 1 : client_send(log, client, "-ERR Authentication failed\r\n");
87 1 : return -1;
88 : }
89 :
90 1 : client_data->auth.auth_ok = true; //FIXME is this actually needed?
91 :
92 1 : if(!client_data->auth.user.authorized){
93 0 : auth_reset(&(client_data->auth));
94 0 : logprintf(log, LOG_ERROR, "Failed to allocate memory for authorized user\n");
95 0 : client_send(log, client, "-ERR Failed to allocate memory\r\n");
96 0 : return 0;
97 : }
98 :
99 1 : logprintf(log, LOG_INFO, "Client authenticated as user %s\n", client_data->auth.user.authenticated);
100 :
101 : //authentication ok, try to acquire the maildrop
102 1 : if(maildrop_acquire(log, database, &(client_data->maildrop), client_data->auth.user.authorized) < 0){
103 0 : auth_reset(&(client_data->auth));
104 0 : client_send(log, client, "-ERR Failed to lock the maildrop\r\n");
105 0 : return 0;
106 : }
107 :
108 1 : client_data->state = STATE_TRANSACTION;
109 1 : client_send(log, client, "+OK Lock and load\r\n");
110 :
111 : //decrease the failscore
112 1 : return 1;
113 : }
114 :
115 0 : logprintf(log, LOG_ERROR, "Invalid branch reached in AUTH\n");
116 0 : return -1;
117 : }
118 :
119 37 : if(!strncasecmp(client_data->recv_buffer, "user ", 5)){
120 12 : if(client_data->auth.user.authenticated){
121 2 : logprintf(log, LOG_INFO, "Client issued multiple USER commands\n");
122 2 : client_send(log, client, "-ERR User already set\r\n");
123 2 : return -1;
124 : }
125 :
126 10 : logprintf(log, LOG_INFO, "Client sends user %s\n", client_data->recv_buffer+5);
127 10 : client_data->auth.method = AUTH_USER;
128 10 : client_data->auth.user.authenticated = common_strdup(client_data->recv_buffer+5);
129 10 : if(!client_data->auth.user.authenticated){
130 0 : logprintf(log, LOG_WARNING, "Failed to allocate memory for user name\n");
131 0 : client_send(log, client, "-ERR Out of memory\r\n");
132 0 : return -1;
133 : }
134 :
135 10 : client_send(log, client, "+OK Go ahead\r\n");
136 10 : return 0;
137 : }
138 :
139 25 : if(!strncasecmp(client_data->recv_buffer, "pass ", 5)){
140 12 : if(!client_data->auth.user.authenticated || client_data->auth.method != AUTH_USER){
141 2 : logprintf(log, LOG_WARNING, "Client tried PASS without user or in another method\n");
142 2 : client_send(log, client, "-ERR Not possible now\r\n");
143 2 : return -1;
144 : }
145 :
146 10 : if(auth_validate(log, database, client_data->auth.user.authenticated, client_data->recv_buffer + 5, &(client_data->auth.user.authorized)) < 0){
147 : //failed to authenticate
148 4 : logprintf(log, LOG_INFO, "Failed to authenticate client\n");
149 4 : auth_reset(&(client_data->auth));
150 4 : client_send(log, client, "-ERR Login failed\r\n");
151 :
152 : //increase failscore
153 4 : return -1;
154 : }
155 :
156 6 : client_data->auth.auth_ok = true; //FIXME is this actually needed?
157 :
158 6 : if(!client_data->auth.user.authorized){
159 0 : logprintf(log, LOG_ERROR, "Failed to allocate memory for authorization user name\n");
160 0 : auth_reset(&(client_data->auth));
161 0 : client_send(log, client, "-ERR Internal error\r\n");
162 0 : return 0;
163 : }
164 :
165 : //authentication ok, try to acquire the maildrop
166 6 : if(maildrop_acquire(log, database, &(client_data->maildrop), client_data->auth.user.authorized) < 0){
167 0 : auth_reset(&(client_data->auth));
168 0 : client_send(log, client, "-ERR Failed to lock the maildrop\r\n");
169 0 : return 0;
170 : }
171 :
172 6 : logprintf(log, LOG_INFO, "Client authenticated as user %s\n", client_data->auth.user.authenticated);
173 6 : client_data->state = STATE_TRANSACTION;
174 6 : client_send(log, client, "+OK Lock and load\r\n");
175 :
176 : //decrease the failscore
177 6 : return 1;
178 : }
179 : #ifndef CMAIL_NO_TLS
180 : }
181 : #endif
182 :
183 39 : if(!strncasecmp(client_data->recv_buffer, "capa", 4)){
184 10 : return pop_capa(log, client, database);
185 : }
186 :
187 29 : if(!strncasecmp(client_data->recv_buffer, "quit", 4)){
188 4 : return pop_quit(log, client, database);
189 : }
190 :
191 : #ifndef CMAIL_NO_TLS
192 25 : if(!strncasecmp(client_data->recv_buffer, "stls", 4)){
193 10 : if(client->tls_mode != TLS_NONE || client_data->listener->tls_mode != TLS_NEGOTIATE){
194 2 : logprintf(log, LOG_WARNING, "Client tried STARTTLS at wrong time\n");
195 2 : client_send(log, client, "-ERR Not possible now\r\n");
196 2 : return -1;
197 : }
198 :
199 8 : client_send(log, client, "+OK Start TLS negotiation\r\n");
200 :
201 8 : client->tls_mode = TLS_NEGOTIATE;
202 :
203 : //FIXME this should be properly handled with a conditional
204 8 : return tls_init_serverpeer(log, client, listener_data->tls_priorities, listener_data->tls_cert);
205 : }
206 : #endif
207 :
208 15 : if(!strncasecmp(client_data->recv_buffer, "xyzzy", 5)){
209 2 : return pop_xyzzy(log, client, database);
210 : }
211 :
212 13 : client_send(log, client, "-ERR Unkown command\r\n");
213 13 : return -1;
214 : }
215 :
216 12 : int state_transaction(LOGGER log, CONNECTION* client, DATABASE* database){
217 12 : CLIENT* client_data = (CLIENT*)client->aux_data;
218 :
219 12 : if(!strncasecmp(client_data->recv_buffer, "capa", 4)){
220 2 : return pop_capa(log, client, database);
221 : }
222 :
223 10 : if(!strncasecmp(client_data->recv_buffer, "quit", 4)){
224 4 : return pop_quit(log, client, database);
225 : }
226 :
227 6 : if(!strncasecmp(client_data->recv_buffer, "xyzzy", 5)){
228 2 : return pop_xyzzy(log, client, database);
229 : }
230 :
231 4 : if(!strncasecmp(client_data->recv_buffer, "stat", 4)){
232 0 : return pop_stat(log, client, database);
233 : }
234 :
235 4 : if(!strncasecmp(client_data->recv_buffer, "list", 4)){
236 0 : return pop_list(log, client, database, strtoul(client_data->recv_buffer+4, NULL, 10));
237 : }
238 :
239 4 : if(!strncasecmp(client_data->recv_buffer, "retr", 4)){
240 0 : return pop_retr(log, client, database, strtoul(client_data->recv_buffer+4, NULL, 10));
241 : }
242 :
243 4 : if(!strncasecmp(client_data->recv_buffer, "dele", 4)){
244 0 : return pop_dele(log, client, database, strtoul(client_data->recv_buffer+4, NULL, 10));
245 : }
246 :
247 4 : if(!strncasecmp(client_data->recv_buffer, "uidl", 4)){
248 0 : return pop_uidl(log, client, database, strtoul(client_data->recv_buffer+4, NULL, 10));
249 : }
250 :
251 4 : if(!strncasecmp(client_data->recv_buffer, "rset", 4)){
252 0 : return pop_rset(log, client, database);
253 : }
254 :
255 4 : if(!strncasecmp(client_data->recv_buffer, "noop", 4)){
256 2 : logprintf(log, LOG_DEBUG, "Client noop\n");
257 2 : client_send(log, client, "+OK Nothing happens\r\n");
258 2 : return 0;
259 : }
260 :
261 2 : client_send(log, client, "-ERR Unkown command\r\n");
262 2 : return -1;
263 : }
264 :
265 0 : int state_update(LOGGER log, CONNECTION* client, DATABASE* database){
266 0 : CLIENT* client_data = (CLIENT*)client->aux_data;
267 :
268 : //this should probably never be reached
269 0 : logprintf(log, LOG_WARNING, "Commands received while in UPDATE state\n");
270 :
271 0 : if(!strncasecmp(client_data->recv_buffer, "quit", 4)){
272 0 : return pop_quit(log, client, database);
273 : }
274 :
275 0 : client_send(log, client, "-ERR Unkown command\r\n");
276 0 : return -1;
277 : }
|