1 #include "base64.h"
2 #include "buf.h"
3 #include "auth.h"
4 #include "token.h"
5 #include "io.h"
6 #include "sasl_plain.h"
7 #include "managesieve.h"
8 #include "managesieve_in.h"
9 #include "managesieve_write.h"
10 #include "acap_token.h"
11 #include "queue_func.h"
12 #include "unused.h"
13 #include "options.h"
14 
15 #ifdef WITH_PAM_SUPPORT
16 /**********************************************************************
17  * managesieve_in_authenticate
18  * Authenticate an incoming session
19  * Not really needed if we are going to authenticate with a real-server,
20  * but it may be useful in some cases
21  * pre: auth: login credentials
22  *	io: io_t to write any errors to
23  *	tag: ignored
24  * post: An attemped is made to authenticate the user locally.
25  *	 If this fails then an error message is written to io
26  *	 Else there is no output to io
27  * return: 1 if authentication is successful
28  *	   0 if authentication is unsuccessful
29  *	   -1 on error
30  **********************************************************************/
31 
managesieve_in_authenticate(const struct auth * UNUSED (auth),io_t * UNUSED (io),const token_t * UNUSED (tag))32 int managesieve_in_authenticate(const struct auth *UNUSED(auth),
33 				io_t *UNUSED(io), const token_t *UNUSED(tag))
34 {
35 	return -1;
36 }
37 #endif /* WITH_PAM_SUPPORT */
38 
39 struct managesieve_in_auth_status {
40 	struct auth auth;
41 	int status;
42 };
43 
44 #define STRUCT_MANAGESIEVE_IN_AUTH_STATUS(name) \
45 	struct managesieve_in_auth_status (name) = { .status = -3 }
46 
47 /**********************************************************************
48  * managesieve_in_sasl_plain
49  * Handle a NOOP command
50  * pre: io: io_t to write to and read from
51  *      q: queue of tokens read from client.
52  * post: q is destroyed
53  * return: .auth: seeded auth structure
54  *         .status: 0 on MANAGESIEVE_OK (password structure is filled in)
55  *                  -1 on MANAGESIEVE_NO
56  *                  -2 on MANAGESIEVE_BYE
57  *                  -3 on internal error
58  **********************************************************************/
59 
60 static struct managesieve_in_auth_status
managesieve_in_sasl_plain(io_t * io,vanessa_queue_t * q)61 managesieve_in_sasl_plain(io_t *io, vanessa_queue_t *q)
62 {
63 	STRUCT_AUTH_STATUS(as);
64 	STRUCT_MANAGESIEVE_IN_AUTH_STATUS(auth_status);
65 	STRUCT_ACAP_TOKEN_WRAPPER_STATUS(wrapper_status);
66 
67 	if (vanessa_queue_length(q) != 1) {
68 		if (managesieve_no(io, NULL, "Incorrect argument count, "
69 			     "expected AUTHENTICATE \"PLAIN\" <challenge>, "
70 			     "mate") < 0) {
71 			VANESSA_LOGGER_DEBUG("managesieve_no");
72 			goto err;
73 		}
74 		auth_status.status = -1;
75 		goto err;
76 	}
77 
78 	wrapper_status = acap_token_wrapper(io, PERDITION_CLIENT, q);
79 	vanessa_queue_destroy(wrapper_status.q);
80 	q = NULL; /* acap_token_wrapper() consumes q */
81 
82 	if (wrapper_status.status == -2) {
83 		VANESSA_LOGGER_DEBUG("acap_token_wrapper");
84 		goto err;
85 	}
86 	if (wrapper_status.status ||
87 	    !acap_token_wrapper_status_is_eol(&wrapper_status) ||
88 	    wrapper_status.type == acap_atom) {
89 		if (managesieve_no(io, NULL, "Invalid AUTHENTICATE \"PLAIN\" "
90 			     "challenge, mate") < 0) {
91 			VANESSA_LOGGER_DEBUG("managesieve_no");
92 			goto err;
93 		}
94 		auth_status.status = -1;
95 		goto err;
96 	}
97 
98 	as = sasl_plain_challenge_decode(wrapper_status.str);
99 	switch (as.status) {
100 	case auth_status_ok:
101 		break;
102 	case auth_status_invalid:
103 		if (managesieve_no(io, NULL, as.reason) < 0) {
104 			VANESSA_LOGGER_DEBUG("managesieve_no");
105 			goto err;
106 		}
107 	case auth_status_error:
108 		auth_status.status = -1;
109 		goto err;
110 	}
111 
112 	auth_status.auth = as.auth;
113 	auth_status.status = 0;
114 err:
115 	free(wrapper_status.str);
116 	vanessa_queue_destroy(q);
117 	return auth_status;
118 }
119 
120 /**********************************************************************
121  * managesieve_in_authenticate_cmd
122  * Handle a NOOP command
123  * pre: io: io_t to write to and read from
124  *      q: queue of tokens read from client.
125  * post: q is destroyed
126  * return: .auth: seeded auth structure
127  *         .status: 0 on MANAGESIEVE_OK (password structure is filled in)
128  *                  -1 on MANAGESIEVE_NO
129  *                  -2 on MANAGESIEVE_BYE
130  *                  -3 on internal error
131  **********************************************************************/
132 
133 static struct managesieve_in_auth_status
managesieve_in_authenticate_cmd(io_t * io,vanessa_queue_t * q)134 managesieve_in_authenticate_cmd(io_t *io, vanessa_queue_t *q)
135 {
136 	token_t *t = NULL;
137 	STRUCT_MANAGESIEVE_IN_AUTH_STATUS(auth_status);
138 
139 	if (vanessa_queue_length(q) == 0 || vanessa_queue_length(q) > 2) {
140 		if (managesieve_no(io, NULL, "Incorrect argument count, "
141 			     "expected AUTHENTICATE <mechanism> [<challenge>], "
142 			     "mate") < 0) {
143 			VANESSA_LOGGER_DEBUG("managesieve_no");
144 			goto err;
145 		}
146 		auth_status.status = -1;
147 		goto err;
148 	}
149 
150 	q = vanessa_queue_pop(q, (void **)&t);
151 	if (!q) {
152 		VANESSA_LOGGER_DEBUG("vanessa_queue_pop");
153 		goto err;
154 	}
155 
156 	if (token_casecmp_string(t, "\"" SASL_MECHANISM_PLAIN "\"")) {
157 		auth_status = managesieve_in_sasl_plain(io, q);
158 		q = NULL; /* managesieve_in_sasl_plain consumes q */
159 		if (!auth_status.status)
160 			auth_status.status = 1;
161 		goto err;
162 	} else {
163 		if (managesieve_no(io, NULL,
164 				   "Unknown SASL mechanism, mate") < 0) {
165 			VANESSA_LOGGER_DEBUG("managesieve_no");
166 			goto err;
167 		}
168 		auth_status.status = -1;
169 		goto err;
170 	}
171 
172 	auth_status.status = 0;
173 err:
174 	vanessa_queue_destroy(q);
175 	token_destroy(&t);
176 	return auth_status;
177 }
178 
179 /**********************************************************************
180  * managesieve_in_capability_cmd
181  * Handle a CAPABILITY command
182  * pre: io: io_t to write to and read from
183  *      q: queue of tokens read from client.
184  *      tls_flags: the encryption flags that have been set
185  *      tls_state: the current state of encryption for the session
186  * post: q is destroyed
187  * return: 0 on MANAGESIEVE_OK
188  *         -1 on MANAGESIEVE_NO
189  *         -2 on MANAGESIEVE_BYE
190  *         -3 on internal error
191  **********************************************************************/
192 
managesieve_in_capability_cmd(io_t * io,vanessa_queue_t * q,flag_t tls_flags,flag_t tls_state)193 static int managesieve_in_capability_cmd(io_t *io, vanessa_queue_t *q,
194 					 flag_t tls_flags, flag_t tls_state)
195 {
196 	int status = -3;
197 	char *msg = NULL;
198 
199 	if (vanessa_queue_length(q) != 0) {
200 		if (managesieve_no(io, NULL, "Too many arguments, "
201 			     "expected CAPABILITY, mate") < 0) {
202 			VANESSA_LOGGER_DEBUG("managesieve_no");
203 			goto err;
204 		}
205 		status = -1;
206 		goto err;
207 	}
208 
209 	msg = managesieve_capability_msg(tls_flags, tls_state,
210 				   "CAPABILITY completed, mate");
211 	if (!msg) {
212 		VANESSA_LOGGER_DEBUG("managesieve_capability_msg");
213 		goto err;
214 	}
215 
216 	if (managesieve_write_raw(io, msg) < 0) {
217 		VANESSA_LOGGER_DEBUG("managesieve_write_raw");
218 		goto err;
219 	}
220 
221 	status = 0;
222 err:
223 	vanessa_queue_destroy(q);
224 	free(msg);
225 	return status;
226 }
227 
228 /**********************************************************************
229  * managesieve_in_logout_cmd
230  * Handle a LOGOUT command
231  * pre: io: io_t to write to and read from
232  *      q: queue of tokens read from client.
233  * post: q is destroyed
234  * return: 0 on MANAGESIEVE_OK
235  *         -1 on MANAGESIEVE_NO
236  *         -2 on MANAGESIEVE_BYE
237  *         -3 on internal error
238  **********************************************************************/
239 
managesieve_in_logout_cmd(io_t * io,vanessa_queue_t * q)240 static int managesieve_in_logout_cmd(io_t *io, vanessa_queue_t *q)
241 {
242 	int status = -3;
243 
244 	if (vanessa_queue_length(q) == 0) {
245 		if (managesieve_ok(io, NULL, "LOGOUT completed, mate") < 0) {
246 			VANESSA_LOGGER_DEBUG("managesieve_ok");
247 			goto err;
248 		}
249 	} else {
250 		if (managesieve_no(io, NULL, "Too many arguments, "
251 				   "expected LOGOUT, mate") < 0) {
252 			VANESSA_LOGGER_DEBUG("managesieve_no");
253 			goto err;
254 		}
255 		status = -1;
256 		goto err;
257 	}
258 
259 	status = 0;
260 err:
261 	vanessa_queue_destroy(q);
262 	return status;
263 }
264 
265 /**********************************************************************
266  * managesieve_in_noop_cmd_str
267  * Handle the string argument of a NOOP command
268  * pre: io: io_t to write to and read from
269  *      q: queue of tokens read from client.
270  * post: q is destroyed
271  * return: 0 on MANAGESIEVE_OK
272  *         -1 on MANAGESIEVE_NO
273  *         -2 on MANAGESIEVE_BYE
274  *         -3 on internal error
275  **********************************************************************/
276 
managesieve_in_noop_cmd_str(io_t * io,vanessa_queue_t * q)277 static int managesieve_in_noop_cmd_str(io_t *io, vanessa_queue_t *q)
278 {
279 	int status = -3;
280 	token_t *t = NULL;
281 	char *rc_arg[2];
282 	STRUCT_MANAGESIEVE_RESPONSE_CODE(rc);
283 	STRUCT_ACAP_TOKEN_WRAPPER_STATUS(wrapper_status);
284 
285 	wrapper_status = acap_token_wrapper(io, PERDITION_CLIENT, q);
286 	vanessa_queue_destroy(wrapper_status.q);
287 	q = NULL; /* acap_token_wrapper() consumes q */
288 
289 	if (wrapper_status.status == -2) {
290 		VANESSA_LOGGER_DEBUG("acap_token_wrapper");
291 		goto err;
292 	}
293 	if (wrapper_status.status ||
294 	    !acap_token_wrapper_status_is_eol(&wrapper_status) ||
295 	    wrapper_status.type == acap_atom) {
296 		if (managesieve_no(io, NULL, "Invalid AUTHENTICATE PLAIN "
297 				   "challenge, mate") < 0) {
298 			VANESSA_LOGGER_DEBUG("managesieve_no");
299 			goto err;
300 		}
301 		status = -1;
302 		goto err;
303 	}
304 
305 	rc_arg[0] = wrapper_status.str;
306 	rc_arg[1] = NULL;
307 	rc.atom = "TAG";
308 	rc.arg = rc_arg;
309 	if (managesieve_ok(io, &rc, "NOOP completed, mate") < 0) {
310 		VANESSA_LOGGER_DEBUG("managesieve_no");
311 		goto err;
312 	}
313 
314 	status = 0;
315 err:
316 	vanessa_queue_destroy(q);
317 	token_destroy(&t);
318 	free(wrapper_status.str);
319 	return status;
320 }
321 
322 /**********************************************************************
323  * managesieve_in_noop_cmd
324  * Handle a NOOP command
325  * pre: io: io_t to write to and read from
326  *      q: queue of tokens read from client.
327  * post: q is destroyed
328  * return: 0 on MANAGESIEVE_OK
329  *         -1 on MANAGESIEVE_NO
330  *         -2 on MANAGESIEVE_BYE
331  *         -3 on internal error
332  **********************************************************************/
333 
managesieve_in_noop_cmd(io_t * io,vanessa_queue_t * q)334 static int managesieve_in_noop_cmd(io_t *io, vanessa_queue_t *q)
335 {
336 	int status = -3;
337 
338 	if (vanessa_queue_length(q) == 0) {
339 		if (managesieve_ok(io, NULL, "NOOP completed, mate") < 0) {
340 			VANESSA_LOGGER_DEBUG("managesieve_ok");
341 			goto err;
342 		}
343 	} else if (vanessa_queue_length(q) == 1) {
344 		status = managesieve_in_noop_cmd_str(io, q);
345 		q = NULL; /* managesieve_in_noop_cmd_str consumes q */
346 		if (status < 0) {
347 			VANESSA_LOGGER_DEBUG("managesieve_in_noop_cmd_str");
348 			goto err;
349 		}
350 	} else {
351 		if (managesieve_no(io, NULL,
352 				   "Too many arguments, expected NOOP "
353 				   "[String], mate") < 0) {
354 			VANESSA_LOGGER_DEBUG("managesieve_no");
355 			goto err;
356 		}
357 		status = -1;
358 		goto err;
359 	}
360 
361 	status = 0;
362 err:
363 	vanessa_queue_destroy(q);
364 	return status;
365 }
366 
367 #ifdef WITH_SSL_SUPPORT
368 /**********************************************************************
369  * managesieve_in_starttls_cmd
370  * Handle a STARTTLS command
371  * pre: io: io_t to write to and read from
372  *      q: queue of tokens read from client.
373  * post: q is destroyed
374  * return: 0 on MANAGESIEVE_OK
375  *         -1 on MANAGESIEVE_NO
376  *         -2 on MANAGESIEVE_BYE
377  *         -3 on internal error
378  **********************************************************************/
379 
managesieve_in_starttls_cmd(io_t * io,vanessa_queue_t * q)380 static int managesieve_in_starttls_cmd(io_t *io, vanessa_queue_t *q)
381 {
382 	int status = -3;
383 
384 	if (vanessa_queue_length(q) != 0) {
385 		if (managesieve_no(io, NULL, "Too many arguments, "
386 			     "expected STARTTLS, mate") < 0) {
387 			VANESSA_LOGGER_DEBUG("managesieve_no");
388 			goto err;
389 		}
390 		status = -1;
391 		goto err;
392 	}
393 
394 	if (managesieve_ok(io, NULL, "Begin TLS negotiation, mate") < 0) {
395 		VANESSA_LOGGER_DEBUG("managesieve_ok");
396 		goto err;
397 	}
398 
399 	status = 0;
400 err:
401 	vanessa_queue_destroy(q);
402 	return status;
403 }
404 
managesieve_starttls_post(io_t * io,int tls_flags,int tls_state)405 static int managesieve_starttls_post(io_t *io, int tls_flags, int tls_state)
406 {
407 	char *msg;
408 	int status = -1;
409 
410 	if (!(tls_state & SSL_MODE_TLS_LISTEN))
411 		return 0;
412 
413 	msg = managesieve_capability_msg(tls_flags, tls_state,
414 				   "TLS negotiation completed, mate");
415 	if (!msg) {
416 		VANESSA_LOGGER_DEBUG("managesieve_capability_msg");
417 		goto err;
418 	}
419 
420 	if (managesieve_write_raw(io, msg) < 0) {
421 		VANESSA_LOGGER_DEBUG("managesieve_write_raw");
422 		goto err;
423 	}
424 
425 	status = 0;
426 err:
427 	free(msg);
428 	return status;
429 }
430 #else
managesieve_starttls_post(io_t * UNUSED (io),int UNUSED (tls_flags),int UNUSED (tls_state))431 static int managesieve_starttls_post(io_t *UNUSED(io), int UNUSED(tls_flags),
432 				     int UNUSED(tls_state))
433 {
434 	return 0;
435 }
436 #endif
437 
438 /**********************************************************************
439  * managesieve_in_get_auth_loop
440  * allocated by this function
441  * pre: io: io_t to write to and read from
442  *      tls_flags: the encryption flags that have been set
443  *      tls_state: the current state of encryption for the session
444  * post: auth_return is seeded
445  * return: 3 starttls
446  *	   2 logout
447  *         1 auth obtained
448  *         0 on MANAGESIEVE_OK
449  *         -1 on MANAGESIEVE_NO
450  *         -2 on MANAGESIEVE_BYE
451  *         -3 on internal error
452  **********************************************************************/
453 
454 static struct managesieve_in_auth_status
managesieve_in_get_auth_loop(io_t * io,flag_t tls_flags,flag_t tls_state)455 managesieve_in_get_auth_loop(io_t *io, flag_t tls_flags, flag_t tls_state)
456 {
457 	vanessa_queue_t *q = NULL;
458 	token_t *t = NULL;
459 	STRUCT_MANAGESIEVE_IN_AUTH_STATUS(as);
460 
461 	q = read_line(io, NULL, NULL, TOKEN_MANAGESIEVE, 0,
462 		     PERDITION_LOG_STR_CLIENT);
463 	if (!q) {
464 		VANESSA_LOGGER_DEBUG("read_line");
465 		goto err;;
466 	}
467 
468 	q = vanessa_queue_pop(q, (void **)&t);
469 	if (!q) {
470 		VANESSA_LOGGER_DEBUG("vanessa_queue_pop");
471 		goto err;
472 	}
473 
474 	if (token_casecmp_string(t, MANAGESIEVE_CMD_AUTHENTICATE)) {
475 		as = managesieve_in_authenticate_cmd(io, q);
476 		q = NULL; /* managesieve_in_logout_cmd consumes q */
477 		if (!as.status)
478 			as.status = 1;
479 		goto err;
480 	} else if (token_casecmp_string(t, MANAGESIEVE_CMD_CAPABILITY)) {
481 		as.status = managesieve_in_capability_cmd(io, q, tls_flags,
482 							  tls_state);
483 		q = NULL; /* managesieve_in_capability_cmd consumes q */
484 		if (as.status < 0)
485 			goto err;
486 	} else if (token_casecmp_string(t, MANAGESIEVE_CMD_LOGOUT)) {
487 		as.status = managesieve_in_logout_cmd(io, q);
488 		q = NULL; /* managesieve_in_logout_cmd consumes q */
489 		if (!as.status)
490 			as.status = 2;
491 		goto err;
492 	} else if (token_casecmp_string(t, MANAGESIEVE_CMD_NOOP)) {
493 		as.status = managesieve_in_noop_cmd(io, q);
494 		q = NULL; /* managesieve_in_noop_cmd consumes q */
495 		if (as.status < 0)
496 			goto err;
497 #ifdef WITH_SSL_SUPPORT
498 	} else if (tls_flags & SSL_MODE_TLS_LISTEN &&
499 		   io_get_type(io) != io_type_ssl &&
500 		   token_casecmp_string(t, MANAGESIEVE_CMD_STARTTLS)) {
501 		as.status = managesieve_in_starttls_cmd(io, q);
502 		q = NULL; /* managesieve_in_starttls_cmd consumes q */
503 		if (!as.status)
504 			as.status = 3;
505 		goto err;
506 #endif
507 	} else if (managesieve_no(io, NULL, "Unknown command, mate") < 0) {
508 		VANESSA_LOGGER_DEBUG("managesieve_no");
509 		as.status = -1;
510 		goto err;
511 	}
512 
513 	as.status = 0;
514 err:
515 	vanessa_queue_destroy(q);
516 	token_destroy(&t);
517 	return as;
518 }
519 
520 /**********************************************************************
521  * managesieve_in_get_auth
522  * allocated by this function
523  * pre: io: io_t to write to and read from
524  *	tls_flags: the encryption flags that have been set
525  *	tls_state: the current state of encryption for the session
526  *	return_auth: pointer to an allocated struct auth,
527  *		     where login credentials will be returned
528  *	return_tag: ignored
529  * post: auth_return is seeded
530  * return: 0 on success
531  *	   1 if user quits (LOGOUT command)
532  *	   2 if TLS negotiation should be done
533  *	   -1 on error
534  **********************************************************************/
535 
managesieve_in_get_auth(io_t * io,flag_t tls_flags,flag_t tls_state,struct auth * return_auth,token_t ** UNUSED (return_tag))536 int managesieve_in_get_auth(io_t *io, flag_t tls_flags, flag_t tls_state,
537 			    struct auth *return_auth,
538 			    token_t **UNUSED(return_tag))
539 {
540 	struct managesieve_in_auth_status as;
541 
542 	if (managesieve_starttls_post(io, tls_flags, tls_state) < 0)
543 	{
544 		VANESSA_LOGGER_DEBUG("managesieve_starttls_post");
545 		return -1;
546 	}
547 
548 	while (1) {
549 		as = managesieve_in_get_auth_loop(io, tls_flags, tls_state);
550 		if (as.status != 0 && as.status != -1)
551 			break;
552 	}
553 
554 	if (as.status > 0)
555 		as.status--;
556 	if (!as.status)
557 		*return_auth = as.auth;
558 
559 	return as.status;
560 }
561