1 /* imtest.c -- IMAP/POP3/NNTP/LMTP/SMTP/MUPDATE/MANAGESIEVE test client
2 * Ken Murchison (multi-protocol implementation)
3 * Tim Martin (SASL implementation)
4 *
5 * Copyright (c) 1994-2008 Carnegie Mellon University. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * 3. The name "Carnegie Mellon University" must not be used to
20 * endorse or promote products derived from this software without
21 * prior written permission. For permission or any legal
22 * details, please contact
23 * Carnegie Mellon University
24 * Center for Technology Transfer and Enterprise Creation
25 * 4615 Forbes Avenue
26 * Suite 302
27 * Pittsburgh, PA 15213
28 * (412) 268-7393, fax: (412) 268-7395
29 * innovation@andrew.cmu.edu
30 *
31 * 4. Redistributions of any form whatsoever must retain the following
32 * acknowledgment:
33 * "This product includes software developed by Computing Services
34 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
35 *
36 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
37 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
38 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
39 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
40 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
41 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
42 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
43 */
44
45 #include "config.h"
46
47 #include <fcntl.h>
48 #include <limits.h>
49 #include <netdb.h>
50 #include <netinet/in.h>
51 #include <pwd.h>
52 #include <signal.h>
53 #include <stdarg.h>
54 #include <stdlib.h>
55 #include <stdio.h>
56 #include <string.h>
57 #include <sys/file.h>
58 #include <sys/msg.h>
59 #include <sys/ipc.h>
60 #include <sys/socket.h>
61 #include <sys/stat.h>
62 #include <sys/time.h>
63 #include <sys/types.h>
64 #include <sys/un.h>
65 #include <unistd.h>
66 #include <ctype.h>
67
68 #include <sasl/sasl.h>
69 #include <sasl/saslutil.h>
70
71 #include "hash.h"
72 #include "imparse.h"
73 #include "iptostring.h"
74 #include "md5.h"
75 #include "prot.h"
76 #include "retry.h"
77 #include "strarray.h"
78 #include "stristr.h"
79 #include "util.h"
80 #include "xmalloc.h"
81 #include "xstrlcat.h"
82 #include "xstrlcpy.h"
83
84 #ifdef HAVE_SSL
85 #include <openssl/ssl.h>
86
87 static SSL_CTX *tls_ctx = NULL;
88 static SSL *tls_conn = NULL;
89 static SSL_SESSION *tls_sess = NULL;
90
91 #endif /* HAVE_SSL */
92
93 #define IMTEST_OK 0
94 #define IMTEST_FAIL -1
95 #define IMTEST_CLOSEME -2
96
97 typedef enum {
98 STAT_CONT = 0,
99 STAT_NO = 1,
100 STAT_OK = 2
101 } imt_stat;
102
103 /* global vars */
104 static sasl_conn_t *conn;
105 static int sock; /* socket descriptor */
106
107 static int verbose=0;
108
109 static struct protstream *pout, *pin;
110
111 static char *authname = NULL;
112 static char *username = NULL;
113 static char *realm = NULL;
114 static char *cmdline_password = NULL;
115
116 static char *output_socket = NULL;
117 static int output_socket_opened = 0;
118 static ino_t output_socket_ino = 0;
119
120 #define CONFIGHASHSIZE 30 /* relatively small */
121
122 static struct hash_table confighash;
123 static int mysasl_config(void*, const char*, const char*, const char**, unsigned*);
124
125 extern int _sasl_debug;
126 extern char *optarg;
127
128 static strarray_t stashed_strings = STRARRAY_INITIALIZER;
129
130 /* callbacks we support */
131 static sasl_callback_t callbacks[] = {
132 {
133 SASL_CB_ECHOPROMPT, NULL, NULL
134 }, {
135 SASL_CB_NOECHOPROMPT, NULL, NULL
136 }, {
137 #ifdef SASL_CB_GETREALM
138 SASL_CB_GETREALM, NULL, NULL
139 }, {
140 #endif
141 SASL_CB_USER, NULL, NULL
142 }, {
143 SASL_CB_AUTHNAME, NULL, NULL
144 }, {
145 SASL_CB_PASS, NULL, NULL
146 }, {
147 #if GCC_VERSION >= 80000
148 SASL_CB_GETOPT, (void*)&mysasl_config, NULL
149 #else
150 SASL_CB_GETOPT, (int (*)(void))&mysasl_config, NULL
151 #endif
152 }, {
153 SASL_CB_LIST_END, NULL, NULL
154 }
155 };
156
157 struct protocol_t;
158
159 struct banner_t {
160 u_char is_capa; /* banner is capability response */
161 char *resp; /* end of banner response */
162 void *(*parse_banner)(char *str);
163 /* [OPTIONAL] parse banner, returns 'rock' */
164 };
165
166 struct capa_cmd_t {
167 char *cmd; /* capability command string (NULL = no capa cmd) */
168 char *resp; /* end of capability response */
169 char *tls; /* [OPTIONAL] TLS capability string */
170 char *login; /* [OPTIONAL] plaintext login cmd capability string */
171 char *auth; /* [OPTIONAL] AUTH (SASL) capability string */
172 char *compress; /* [OPTIONAL] COMPRESS capability string */
173 void (*parse_mechlist)(struct buf *list, const char *str,
174 struct protocol_t *prot, unsigned long *capabilities);
175 /* [OPTIONAL] parse capability string,
176 returns space-separated list of mechs */
177 };
178
179 struct tls_cmd_t {
180 char *cmd; /* tls command string */
181 char *ok; /* start tls prompt */
182 char *fail; /* failure response */
183 u_char auto_capa; /* capability response sent automatically after TLS */
184 };
185
186 struct sasl_cmd_t {
187 char *cmd; /* auth command string */
188 u_short maxlen; /* maximum command line length,
189 (0 = initial response unsupported by protocol) */
190 u_char quote; /* quote arguments (literal for base64 data) */
191 char *ok; /* success response string */
192 char *fail; /* failure response string */
193 char *cont; /* continue response string
194 (NULL = send/receive literals) */
195 char *cancel; /* cancel auth string */
196 char *(*parse_success)(char *str);
197 /* [OPTIONAL] parse response for success data */
198 u_char auto_capa; /* capability response sent automatically
199 after AUTH with SASL security layer */
200 };
201
202 struct compress_cmd_t {
203 char *cmd; /* compress command string */
204 char *ok; /* success response string */
205 char *fail; /* failure response string */
206 };
207
208 struct logout_cmd_t {
209 char *cmd; /* logout command string */
210 char *resp; /* logout response */
211 };
212
213 struct protocol_t {
214 char *protocol; /* protocol service name */
215 char *sprotocol; /* SSL-wrapped service name (NULL = unsupported) */
216 char *service; /* SASL service name */
217 int login_enabled; /* [OPTIONAL] login command on/off by default;
218 toggled by capability string */
219 struct banner_t banner;
220 struct capa_cmd_t capa_cmd;
221 struct tls_cmd_t tls_cmd;
222 struct sasl_cmd_t sasl_cmd;
223 struct compress_cmd_t compress_cmd;
224 int (*do_auth)(struct sasl_cmd_t *sasl_cmd, void *rock,
225 int login_enabled, char *mech, const char *mechlist);
226 /* [OPTIONAL] perform protocol-specific authentication;
227 based on rock, login_enabled, mech, mechlist */
228 struct logout_cmd_t logout_cmd;
229 char *unauth_cmd;
230
231 /* these 3 are used for maintaining connection state */
232 void *(*init_conn)(void); /* generate a context (if needed). This context
233 * must be malloc()ed and will be freed by
234 * interactive() as each connection is reused */
235 int (*pipe)(char *buf, int len, void *rock); /* pipe a buffer to pout
236 * may be necessary to keep
237 * connection state */
238 int (*reset)(void *rock); /* perform any protocol-specific reset when we
239 * lose connection on a unix domain socket
240 * during interactive mode. If this is NULL we
241 * assume that we should not attempt to reuse
242 * connections (and just die at the end of one)
243 */
244 };
245
246
247 static void
248 __attribute__((noreturn, format(printf, 1, 2)))
imtest_fatal(const char * msg,...)249 imtest_fatal(const char *msg, ...)
250 {
251 struct stat sbuf;
252 if (output_socket && output_socket_opened &&
253 stat(output_socket, &sbuf) != -1 &&
254 sbuf.st_ino == output_socket_ino) {
255 unlink(output_socket);
256 }
257 if (msg != NULL) {
258 va_list ap;
259 va_start(ap, msg);
260 fprintf(stderr, "failure: ");
261 vfprintf(stderr, msg, ap);
262 fprintf(stderr, "\n");
263 va_end(ap);
264 }
265 exit(1);
266 }
267
268 /* libcyrus makes us define this */
fatal(const char * msg,int code)269 EXPORTED void fatal(const char *msg, int code __attribute__((unused)))
270 {
271 imtest_fatal("%s", msg);
272 }
273
mysasl_config(void * context,const char * plugin_name,const char * option,const char ** result,unsigned * len)274 int mysasl_config(void *context __attribute__((unused)),
275 const char *plugin_name,
276 const char *option,
277 const char **result,
278 unsigned *len)
279 {
280 *result = NULL;
281
282 if (plugin_name) {
283 /* first try it with the plugin name */
284 char opt[1024];
285
286 strlcpy(opt, plugin_name, sizeof(opt));
287 strlcat(opt, "_", sizeof(opt));
288 strlcat(opt, option, sizeof(opt));
289 *result = hash_lookup(opt, &confighash);
290 }
291
292 if (*result == NULL) {
293 /* try without the plugin name */
294 *result = hash_lookup(option, &confighash);
295 }
296
297 if (*result != NULL) {
298 if (len) { *len = strlen(*result); }
299 return SASL_OK;
300 }
301
302 return SASL_FAIL;
303 }
304
305 #ifdef HAVE_SSL
306
307 static int verify_depth;
308 static int verify_error = X509_V_OK;
309 static int do_dump = 0;
310
311 #define CCERT_BUFSIZ 256
312 static char peer_CN[CCERT_BUFSIZ];
313 static char issuer_CN[CCERT_BUFSIZ];
314
315 static char *tls_peer_CN = NULL;
316 static char *tls_issuer_CN = NULL;
317
318 static const char *tls_protocol = NULL;
319 static const char *tls_cipher_name = NULL;
320 static int tls_cipher_usebits = 0;
321 static int tls_cipher_algbits = 0;
322
323 /*
324 * Set up the cert things on the server side. We do need both the
325 * private key (in key_file) and the cert (in cert_file).
326 * Both files may be identical.
327 *
328 * This function is taken from OpenSSL apps/s_cb.c
329 */
330
set_cert_stuff(SSL_CTX * ctx,char * cert_file,char * key_file)331 static int set_cert_stuff(SSL_CTX * ctx, char *cert_file, char *key_file)
332 {
333 if (cert_file != NULL) {
334 if (SSL_CTX_use_certificate_chain_file(ctx, cert_file) <= 0) {
335 printf("unable to get certificate from '%s'\n", cert_file);
336 return (0);
337 }
338 if (key_file == NULL)
339 key_file = cert_file;
340 if (SSL_CTX_use_PrivateKey_file(ctx, key_file,
341 SSL_FILETYPE_PEM) <= 0) {
342 printf("unable to get private key from '%s'\n", key_file);
343 return (0);
344 }
345 /* Now we know that a key and cert have been set against
346 * the SSL context */
347 if (!SSL_CTX_check_private_key(ctx)) {
348 printf("Private key does not match the certificate public key\n");
349 return (0);
350 }
351 }
352 return (1);
353 }
354
355 /* taken from OpenSSL apps/s_cb.c */
356
verify_callback(int ok,X509_STORE_CTX * ctx)357 static int verify_callback(int ok, X509_STORE_CTX * ctx)
358 {
359 char buf[256];
360 X509 *err_cert;
361 int err;
362 int depth;
363
364 err_cert = X509_STORE_CTX_get_current_cert(ctx);
365 err = X509_STORE_CTX_get_error(ctx);
366 depth = X509_STORE_CTX_get_error_depth(ctx);
367
368 X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
369
370 if (verbose==1)
371 printf("Peer cert verify depth=%d %s\n", depth, buf);
372
373 if (!ok) {
374 printf("verify error:num=%d:%s\n", err,
375 X509_verify_cert_error_string(err));
376 if (verify_depth >= depth) {
377 ok = 1;
378 verify_error = X509_V_OK;
379 } else {
380 ok = 0;
381 verify_error = X509_V_ERR_CERT_CHAIN_TOO_LONG;
382 }
383 }
384 switch (err) {
385 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
386 X509_NAME_oneline(X509_get_issuer_name(err_cert), buf, 256);
387 printf("issuer= %s\n", buf);
388 break;
389 case X509_V_ERR_CERT_NOT_YET_VALID:
390 case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
391 printf("cert not yet valid\n");
392 break;
393 case X509_V_ERR_CERT_HAS_EXPIRED:
394 case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
395 printf("cert has expired\n");
396 break;
397 }
398
399 if (verbose==1)
400 printf("verify return:%d\n", ok);
401
402 return (ok);
403 }
404
405
406 #if OPENSSL_VERSION_NUMBER < 0x10100000L
407 /* taken from OpenSSL apps/s_cb.c */
tmp_rsa_cb(SSL * s,int export,int keylength)408 static RSA *tmp_rsa_cb(SSL * s __attribute__((unused)),
409 int export __attribute__((unused)), int keylength)
410 {
411 static RSA *rsa_tmp = NULL;
412
413 if (rsa_tmp == NULL) {
414 rsa_tmp = RSA_generate_key(keylength, RSA_F4, NULL, NULL);
415 }
416 return (rsa_tmp);
417 }
418 #endif
419
420 /* taken from OpenSSL apps/s_cb.c
421 * tim - this seems to just be giving logging messages
422 */
423
apps_ssl_info_callback(const SSL * s,int where,int ret)424 static void apps_ssl_info_callback(const SSL * s, int where, int ret)
425 {
426 char *str;
427 int w;
428
429 if (verbose==0) return;
430
431 w = where & ~SSL_ST_MASK;
432
433 if (w & SSL_ST_CONNECT)
434 str = "SSL_connect";
435 else if (w & SSL_ST_ACCEPT)
436 str = "SSL_accept";
437 else
438 str = "undefined";
439
440 if (where & SSL_CB_LOOP) {
441 printf("%s:%s\n", str, SSL_state_string_long(s));
442 } else if (where & SSL_CB_ALERT) {
443 str = (where & SSL_CB_READ) ? "read" : "write";
444 if ((ret & 0xff) != SSL3_AD_CLOSE_NOTIFY)
445 printf("SSL3 alert %s:%s:%s\n", str,
446 SSL_alert_type_string_long(ret),
447 SSL_alert_desc_string_long(ret));
448 } else if (where & SSL_CB_EXIT) {
449 if (ret == 0)
450 printf("%s:failed in %s\n",
451 str, SSL_state_string_long(s));
452 else if (ret < 0) {
453 printf("%s:error in %s %i\n",
454 str, SSL_state_string_long(s),ret);
455 }
456 }
457 }
458
459
460 /*
461 * Seed the random number generator.
462 */
tls_rand_init(void)463 static int tls_rand_init(void)
464 {
465 #ifdef EGD_SOCKET
466 return (RAND_egd(EGD_SOCKET));
467 #else
468 /* otherwise let OpenSSL do it internally */
469 return 0;
470 #endif
471 }
472
473
474 static char *var_tls_CAfile="";
475 static char *var_tls_CApath="";
476 /*
477 * This is the setup routine for the SSL client.
478 *
479 * The skeleton of this function is taken from OpenSSL apps/s_client.c.
480 */
481
tls_init_clientengine(int verifydepth,char * var_tls_cert_file,char * var_tls_key_file)482 static int tls_init_clientengine(int verifydepth, char *var_tls_cert_file, char *var_tls_key_file)
483 {
484 int off = 0;
485 int verify_flags = SSL_VERIFY_NONE;
486 char *CApath;
487 char *CAfile;
488 char *c_cert_file;
489 char *c_key_file;
490
491
492 if (verbose==1)
493 printf("starting TLS engine\n");
494
495 SSL_load_error_strings();
496 SSLeay_add_ssl_algorithms();
497 if (tls_rand_init() == -1) {
498 printf("TLS engine: cannot seed PRNG\n");
499 return IMTEST_FAIL;
500 }
501
502 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
503 tls_ctx = SSL_CTX_new(TLS_client_method());
504 #else
505 tls_ctx = SSL_CTX_new(SSLv23_client_method());
506 #endif
507 if (tls_ctx == NULL) {
508 return IMTEST_FAIL;
509 };
510
511 off |= SSL_OP_ALL; /* Work around all known bugs */
512 off |= SSL_OP_NO_SSLv2; /* Disable insecure SSLv2 */
513 off |= SSL_OP_NO_SSLv3; /* Disable insecure SSLv3 */
514 off |= SSL_OP_NO_COMPRESSION; /* Disable TLS compression */
515 SSL_CTX_set_options(tls_ctx, off);
516 SSL_CTX_set_info_callback(tls_ctx, apps_ssl_info_callback);
517
518 if (strlen(var_tls_CAfile) == 0)
519 CAfile = NULL;
520 else
521 CAfile = var_tls_CAfile;
522 if (strlen(var_tls_CApath) == 0)
523 CApath = NULL;
524 else
525 CApath = var_tls_CApath;
526
527 if (CAfile || CApath)
528 if ((!SSL_CTX_load_verify_locations(tls_ctx, CAfile, CApath)) ||
529 (!SSL_CTX_set_default_verify_paths(tls_ctx))) {
530 printf("TLS engine: cannot load CA data\n");
531 return IMTEST_FAIL;
532 }
533 if (strlen(var_tls_cert_file) == 0)
534 c_cert_file = NULL;
535 else
536 c_cert_file = var_tls_cert_file;
537 if (strlen(var_tls_key_file) == 0)
538 c_key_file = NULL;
539 else
540 c_key_file = var_tls_key_file;
541
542 if (c_cert_file || c_key_file)
543 if (!set_cert_stuff(tls_ctx, c_cert_file, c_key_file)) {
544 printf("TLS engine: cannot load cert/key data, may be a cert/key mismatch?\n");
545 return IMTEST_FAIL;
546 }
547 #if OPENSSL_VERSION_NUMBER < 0x10100000L
548 SSL_CTX_set_tmp_rsa_callback(tls_ctx, tmp_rsa_cb);
549 #endif
550
551 verify_depth = verifydepth;
552 SSL_CTX_set_verify(tls_ctx, verify_flags, verify_callback);
553
554 return IMTEST_OK;
555 }
556
557 /*
558 * taken from OpenSSL crypto/bio/b_dump.c, modified to save a lot of strcpy
559 * and strcat by Matti Aarnio.
560 */
561
562 #define TRUNCATE
563 #define DUMP_WIDTH 16
564
tls_dump(const char * s,int len)565 static int tls_dump(const char *s, int len)
566 {
567 int ret = 0;
568 char buf[160 + 1];
569 char *ss;
570 int i;
571 int j;
572 int rows;
573 int trunc;
574 unsigned char ch;
575
576 trunc = 0;
577
578 #ifdef TRUNCATE
579 for (; (len > 0) && ((s[len - 1] == ' ') || (s[len - 1] == '\0')); len--)
580 trunc++;
581 #endif
582
583 rows = (len / DUMP_WIDTH);
584 if ((rows * DUMP_WIDTH) < len)
585 rows++;
586
587 for (i = 0; i < rows; i++) {
588 buf[0] = '\0'; /* start with empty string */
589 ss = buf;
590
591 sprintf(ss, "%04x ", i * DUMP_WIDTH);
592 ss += strlen(ss);
593 for (j = 0; j < DUMP_WIDTH; j++) {
594 if (((i * DUMP_WIDTH) + j) >= len) {
595 strcpy(ss, " ");
596 } else {
597 ch = ((unsigned char) *((char *) (s) + i * DUMP_WIDTH + j))
598 & 0xff;
599 sprintf(ss, "%02x%c", ch, j == 7 ? '|' : ' ');
600 ss += 3;
601 }
602 }
603 ss += strlen(ss);
604 *ss+= ' ';
605 for (j = 0; j < DUMP_WIDTH; j++) {
606 if (((i * DUMP_WIDTH) + j) >= len)
607 break;
608 ch = ((unsigned char) *((char *) (s) + i * DUMP_WIDTH + j)) & 0xff;
609 *ss+= (((ch >= ' ') && (ch <= '~')) ? ch : '.');
610 if (j == 7) *ss+= ' ';
611 }
612 *ss = 0;
613 /*
614 * if this is the last call then update the ddt_dump thing so that
615 * we will move the selection point in the debug window
616 */
617 printf("%s\n", buf);
618 ret += strlen(buf);
619 }
620 #ifdef TRUNCATE
621 if (trunc > 0) {
622 sprintf(buf, "%04x - <SPACES/NULS>\n", len+ trunc);
623 printf("%s\n", buf);
624 ret += strlen(buf);
625 }
626 #endif
627 return (ret);
628 }
629
630
631 /* taken from OpenSSL apps/s_cb.c */
632
bio_dump_cb(BIO * bio,int cmd,const char * argp,int argi,long argl,long ret)633 static long bio_dump_cb(BIO * bio, int cmd, const char *argp, int argi,
634 long argl __attribute__((unused)), long ret)
635 {
636 if (!do_dump)
637 return (ret);
638
639 if (cmd == (BIO_CB_READ | BIO_CB_RETURN)) {
640 printf("read from %08lX [%08lX] (%d bytes => %ld (0x%lX))\n",
641 (unsigned long) bio, (unsigned long) argp,
642 argi, ret, ret);
643 tls_dump(argp, (int) ret);
644 return (ret);
645 } else if (cmd == (BIO_CB_WRITE | BIO_CB_RETURN)) {
646 printf("write to %08lX [%08lX] (%d bytes => %ld (0x%lX))\n",
647 (unsigned long) bio, (unsigned long) argp,
648 argi, ret, ret);
649 tls_dump(argp, (int) ret);
650 }
651 return (ret);
652 }
653
tls_start_clienttls(unsigned * layer,char ** authid)654 static int tls_start_clienttls(unsigned *layer, char **authid)
655 {
656 int sts;
657 const SSL_CIPHER *cipher;
658 X509 *peer;
659
660 if (verbose==1)
661 printf("setting up TLS connection\n");
662
663 if (tls_conn == NULL) {
664 tls_conn = (SSL *) SSL_new(tls_ctx);
665 }
666 if (tls_conn == NULL) {
667 printf("Could not allocate 'con' with SSL_new()\n");
668 return IMTEST_FAIL;
669 }
670 SSL_clear(tls_conn);
671
672 if (!SSL_set_fd(tls_conn, sock)) {
673 printf("SSL_set_fd failed\n");
674 return IMTEST_FAIL;
675 }
676 /*
677 * This is the actual handshake routine. It will do all the negotiations
678 * and will check the client cert etc.
679 */
680 SSL_set_connect_state(tls_conn);
681
682
683 /*
684 * We do have an SSL_set_fd() and now suddenly a BIO_ routine is called?
685 * Well there is a BIO below the SSL routines that is automatically
686 * created for us, so we can use it for debugging purposes.
687 */
688 if (verbose==1)
689 BIO_set_callback(SSL_get_rbio(tls_conn), bio_dump_cb);
690
691 /* Dump the negotiation for loglevels 3 and 4 */
692 if (verbose==1)
693 do_dump = 1;
694
695 if (tls_sess) /* Reuse a session if we have one */
696 SSL_set_session(tls_conn, tls_sess);
697
698 if ((sts = SSL_connect(tls_conn)) <= 0) {
699 SSL_SESSION *currsess;
700
701 printf("SSL_connect error %d\n", sts);
702 currsess = SSL_get_session(tls_conn);
703 if (currsess) {
704 SSL_CTX_remove_session(tls_ctx, currsess);
705 printf("SSL session removed\n");
706 }
707 if (tls_sess) {
708 SSL_SESSION_free(tls_sess);
709 tls_sess = NULL;
710 }
711 if (tls_conn!=NULL)
712 SSL_free(tls_conn);
713 tls_conn = NULL;
714 return IMTEST_FAIL;
715 }
716
717 /*
718 * Lets see, whether a peer certificate is available and what is
719 * the actual information. We want to save it for later use.
720 */
721 peer = SSL_get_peer_certificate(tls_conn);
722 if (peer != NULL) {
723 X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
724 NID_commonName, peer_CN, CCERT_BUFSIZ);
725 tls_peer_CN = peer_CN;
726 X509_NAME_get_text_by_NID(X509_get_issuer_name(peer),
727 NID_commonName, issuer_CN, CCERT_BUFSIZ);
728 if (verbose==1)
729 printf("subject_CN=%s, issuer_CN=%s\n", peer_CN, issuer_CN);
730 tls_issuer_CN = issuer_CN;
731
732 }
733 tls_protocol = SSL_get_version(tls_conn);
734 cipher = SSL_get_current_cipher(tls_conn);
735 tls_cipher_name = SSL_CIPHER_get_name(cipher);
736 tls_cipher_usebits = SSL_CIPHER_get_bits(cipher,
737 &tls_cipher_algbits);
738
739 if (layer!=NULL)
740 *layer = tls_cipher_usebits;
741
742 if (authid!=NULL)
743 *authid = tls_peer_CN;
744
745 printf("TLS connection established: %s with cipher %s (%d/%d bits)\n",
746 tls_protocol, tls_cipher_name,
747 tls_cipher_usebits, tls_cipher_algbits);
748 return IMTEST_OK;
749 }
750
do_starttls(int ssl,char * keyfile,unsigned * ssf)751 static void do_starttls(int ssl, char *keyfile, unsigned *ssf)
752 {
753 int result;
754 char *auth_id;
755
756 result=tls_init_clientengine(10, keyfile, keyfile);
757 if (result!=IMTEST_OK)
758 {
759 if (ssl) {
760 imtest_fatal("Start TLS engine failed\n");
761 } else {
762 printf("Start TLS engine failed\n");
763 return;
764 }
765 } else {
766 result=tls_start_clienttls(ssf, &auth_id);
767
768 if (result!=IMTEST_OK)
769 imtest_fatal("TLS negotiation failed!\n");
770 }
771
772 /* TLS negotiation succeeded */
773 if (tls_sess)
774 SSL_SESSION_free(tls_sess);
775 tls_sess = SSL_get1_session(tls_conn); /* Save the session for reuse */
776
777 /* tell SASL about the negotiated layer */
778 result=sasl_setprop(conn,
779 SASL_SSF_EXTERNAL,
780 ssf);
781 if (result!=SASL_OK)
782 imtest_fatal("Error setting SASL property (external ssf)");
783
784 result=sasl_setprop(conn,
785 SASL_AUTH_EXTERNAL,
786 auth_id);
787 if (result!=SASL_OK)
788 imtest_fatal("Error setting SASL property (external auth_id)");
789
790 #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL)
791 static unsigned char finished[EVP_MAX_MD_SIZE];
792 static struct sasl_channel_binding cbinding;
793
794 if (SSL_session_reused(tls_conn)) {
795 cbinding.len = SSL_get_peer_finished(tls_conn,
796 finished, sizeof(finished));
797 }
798 else {
799 cbinding.len = SSL_get_finished(tls_conn, finished, sizeof(finished));
800 }
801
802 cbinding.name = "tls-unique";
803 cbinding.critical = 0;
804 cbinding.data = finished;
805
806 result = sasl_setprop(conn, SASL_CHANNEL_BINDING, &cbinding);
807 if (result!=SASL_OK)
808 imtest_fatal("Error setting SASL property (channel binding)");
809 #endif /* (OPENSSL_VERSION_NUMBER >= 0x0090800fL) */
810
811 prot_settls (pin, tls_conn);
812 prot_settls (pout, tls_conn);
813 }
814 #endif /* HAVE_SSL */
815
816
make_secprops(int min,int max)817 static sasl_security_properties_t *make_secprops(int min,int max)
818 {
819 sasl_security_properties_t *ret=(sasl_security_properties_t *)
820 malloc(sizeof(sasl_security_properties_t));
821
822 ret->maxbufsize=1024;
823 ret->min_ssf=min;
824 ret->max_ssf=max;
825
826 ret->security_flags=0;
827 ret->property_names=NULL;
828 ret->property_values=NULL;
829
830 return ret;
831 }
832
833 /*
834 * Initialize SASL and set necessary options
835 */
init_sasl(char * service,char * serverFQDN,int minssf,int maxssf,unsigned flags)836 static int init_sasl(char *service, char *serverFQDN, int minssf, int maxssf,
837 unsigned flags)
838 {
839 int saslresult;
840 sasl_security_properties_t *secprops=NULL;
841 socklen_t addrsize;
842 char localip[60], remoteip[60];
843 struct sockaddr_storage saddr_l;
844 struct sockaddr_storage saddr_r;
845
846 addrsize=sizeof(struct sockaddr_storage);
847 if (getpeername(sock,(struct sockaddr *)&saddr_r,&addrsize)!=0)
848 return IMTEST_FAIL;
849
850 addrsize=sizeof(struct sockaddr_storage);
851 if (getsockname(sock,(struct sockaddr *)&saddr_l,&addrsize)!=0)
852 return IMTEST_FAIL;
853
854 if(iptostring((struct sockaddr *)&saddr_l, addrsize, localip, 60))
855 return IMTEST_FAIL;
856
857 if(iptostring((struct sockaddr *)&saddr_r, addrsize, remoteip, 60))
858 return IMTEST_FAIL;
859
860
861 /* client new connection */
862 #if defined(SASL_NEED_HTTP) && defined(SASL_HTTP_REQUEST)
863 if (!strcasecmp(service, "HTTP")) flags |= SASL_NEED_HTTP;
864 #endif
865
866 saslresult=sasl_client_new(service,
867 serverFQDN,
868 localip,
869 remoteip,
870 NULL,
871 flags,
872 &conn);
873
874 if (saslresult!=SASL_OK) return IMTEST_FAIL;
875
876 /* create a security structure and give it to sasl */
877 secprops = make_secprops(minssf, maxssf);
878 if (secprops != NULL)
879 {
880 sasl_setprop(conn, SASL_SEC_PROPS, secprops);
881 free(secprops);
882 }
883
884 return IMTEST_OK;
885 }
886
887 #define BUFSIZE 16384
888
getauthline(struct sasl_cmd_t * sasl_cmd,char ** line,int * linelen)889 static imt_stat getauthline(struct sasl_cmd_t *sasl_cmd, char **line, int *linelen)
890 {
891 char buf[BUFSIZE];
892 int saslresult;
893 unsigned len;
894 char *str=(char *) buf;
895 int ret = STAT_CONT;
896
897 *line = NULL;
898 *linelen = 0;
899
900 do {
901 str = prot_fgets(str, BUFSIZE, pin);
902 if (str == NULL) imtest_fatal("prot layer failure");
903 printf("S: %s",str);
904 } while(str[0] == '*'); /* Ignore potential untagged responses */
905
906 if (!strncasecmp(str, sasl_cmd->ok, strlen(sasl_cmd->ok))) {
907 if (sasl_cmd->parse_success) {
908 str = sasl_cmd->parse_success(str);
909 if (!str) return STAT_OK;
910
911 ret = STAT_OK;
912 }
913 else {
914 return STAT_OK;
915 }
916 }
917 else if (!strncasecmp(str, sasl_cmd->fail, strlen(sasl_cmd->fail))) {
918 return STAT_NO;
919 }
920 else if (sasl_cmd->cont) {
921 str += strlen(sasl_cmd->cont); /* jump past the continuation */
922 }
923 else {
924 /* literal */
925 len = atoi(str+1);
926
927 str = prot_fgets(str, BUFSIZE, pin);
928 if (str == NULL || strlen(str) < len)
929 imtest_fatal("prot layer failure");
930 printf("S: %s", str);
931 }
932
933 if (*str != '\r') {
934 /* trim CRLF */
935 char *p = str + strlen(str) - 1;
936 if (p >= str && *p == '\n') *p-- = '\0';
937 if (p >= str && *p == '\r') *p-- = '\0';
938
939 /* alloc space for decoded response */
940 len = strlen(str) + 1;
941 *line = malloc(len);
942 if ((*line) == NULL) {
943 return STAT_NO;
944 }
945
946 /* decode this line */
947 saslresult = sasl_decode64(str, strlen(str),
948 *line, len, (unsigned *) linelen);
949 if (saslresult != SASL_OK && saslresult != SASL_CONTINUE) {
950 printf("base64 decoding error\n");
951 return STAT_NO;
952 }
953 } else {
954 /* this is a blank */
955 *line = NULL;
956 *linelen = 0;
957 }
958
959 return ret;
960 }
961
interaction(int id,const char * challenge,const char * prompt,char ** tresult,unsigned int * tlen)962 static void interaction (int id, const char *challenge, const char *prompt,
963 char **tresult, unsigned int *tlen)
964 {
965 char *s;
966 char result[1024];
967
968 if (id==SASL_CB_PASS && !cmdline_password) {
969 printf("%s: ", prompt);
970 s = xstrdup(cyrus_getpass(""));
971 strarray_appendm(&stashed_strings, s);
972 *tlen = strlen(s);
973 *tresult = s;
974 return;
975 } else if (id==SASL_CB_PASS && cmdline_password) {
976 strcpy(result, cmdline_password);
977 } else if (id==SASL_CB_USER) {
978 if (username != NULL) {
979 strcpy(result, username);
980 } else {
981 strcpy(result, "");
982 }
983 } else if (id==SASL_CB_AUTHNAME) {
984 if (authname != NULL) {
985 strcpy(result, authname);
986 } else {
987 strcpy(result, getpwuid(getuid())->pw_name);
988 }
989 #ifdef SASL_CB_GETREALM
990 } else if ((id==SASL_CB_GETREALM) && (realm != NULL)) {
991 strcpy(result, realm);
992 #endif
993 } else {
994 int c;
995
996 if (((id==SASL_CB_ECHOPROMPT) || (id==SASL_CB_NOECHOPROMPT)) &&
997 (challenge != NULL)) {
998 printf("Server challenge: %s\n", challenge);
999 }
1000 printf("%s: ",prompt);
1001 if (id==SASL_CB_NOECHOPROMPT) {
1002 strcpy(result, cyrus_getpass(""));
1003 } else {
1004 result[0] = '\0';
1005 if (fgets(result, sizeof(result) - 1, stdin) != NULL) {
1006 c = strlen(result);
1007 result[c - 1] = '\0';
1008 }
1009 }
1010 }
1011
1012 s = xstrdup(result);
1013 strarray_appendm(&stashed_strings, s);
1014 *tlen = strlen(s);
1015 *tresult = s;
1016 }
1017
fillin_interactions(sasl_interact_t * tlist)1018 static void fillin_interactions(sasl_interact_t *tlist)
1019 {
1020 while (tlist->id!=SASL_CB_LIST_END)
1021 {
1022 interaction(tlist->id, tlist->challenge, tlist->prompt,
1023 (void *) &(tlist->result),
1024 &(tlist->len));
1025 tlist++;
1026 }
1027
1028 }
1029
waitfor(char * tag,char * tag2,int echo)1030 static char *waitfor(char *tag, char *tag2, int echo)
1031 {
1032 static char str[1024];
1033
1034 do {
1035 if (prot_fgets(str, sizeof(str), pin) == NULL) {
1036 imtest_fatal("prot layer failure");
1037 }
1038 if(echo) printf("S: %s", str);
1039 } while (strncmp(str, tag, strlen(tag)) &&
1040 (tag2 ? strncmp(str, tag2, strlen(tag2)) : 1));
1041
1042 if (echo) {
1043 /* pipe any responses remaining after tagged response */
1044 char str2[1024] = "";
1045
1046 prot_NONBLOCK(pin);
1047 while (prot_fgets(str2, sizeof(str2), pin)) {
1048 printf("S: %s", str2);
1049 }
1050 prot_BLOCK(pin);
1051 }
1052
1053 return str;
1054 }
1055
auth_sasl(struct sasl_cmd_t * sasl_cmd,const char * mechlist)1056 static int auth_sasl(struct sasl_cmd_t *sasl_cmd, const char *mechlist)
1057 {
1058 sasl_interact_t *client_interact = NULL;
1059 int saslresult;
1060 const char *out = NULL;
1061 unsigned int outlen = 0;
1062 char *in;
1063 int inlen;
1064 const char *mechusing;
1065 char inbase64[4096];
1066 int inbase64len;
1067 char cmdbuf[40];
1068 int sendliteral;
1069 int initial_response = 1;
1070 imt_stat status;
1071
1072 if (!sasl_cmd || !sasl_cmd->cmd) return IMTEST_FAIL;
1073 sendliteral = sasl_cmd->quote;
1074
1075 do { /* start authentication */
1076 saslresult = sasl_client_start(conn, mechlist, &client_interact,
1077 /* do we support initial response? */
1078 sasl_cmd->maxlen ? &out : NULL,
1079 &outlen, &mechusing);
1080
1081 if (saslresult == SASL_INTERACT)
1082 fillin_interactions(client_interact); /* fill in prompts */
1083 } while (saslresult == SASL_INTERACT);
1084
1085 if ((saslresult != SASL_OK) && (saslresult != SASL_CONTINUE)) {
1086 return saslresult;
1087 }
1088
1089 /* build the auth command */
1090 if (sasl_cmd->quote) {
1091 sprintf(cmdbuf, "%s \"%s\"", sasl_cmd->cmd, mechusing);
1092 }
1093 else {
1094 sprintf(cmdbuf, "%s %s", sasl_cmd->cmd, mechusing);
1095 }
1096 printf("C: %s", cmdbuf);
1097 prot_printf(pout, "%s", cmdbuf);
1098
1099 if (out) { /* initial response */
1100 if (!outlen) { /* empty initial response */
1101 printf(" =");
1102 prot_printf(pout, " =");
1103
1104 out = NULL;
1105 }
1106 else if (!sendliteral &&
1107 ((int) (strlen(cmdbuf) + outlen + 3) > sasl_cmd->maxlen)) {
1108 /* initial response is too long for auth command,
1109 so wait for a server challenge before sending it */
1110 goto noinitresp;
1111 }
1112 else { /* full response -- encoded below */
1113 printf(" ");
1114 prot_printf(pout, " ");
1115 }
1116 } else {
1117 goto noinitresp;
1118 }
1119
1120 do {
1121 if (out) { /* response */
1122 /* convert to base64 */
1123 saslresult = sasl_encode64(out, outlen, inbase64, sizeof(inbase64),
1124 (unsigned *) &inbase64len);
1125 if (saslresult != SASL_OK) return saslresult;
1126
1127 /* send to server */
1128 if (sendliteral) {
1129 printf("%s{%d+}\r\n",
1130 initial_response ? "" : "C: ", inbase64len);
1131 prot_printf(pout, "{%d+}\r\n", inbase64len);
1132 prot_flush(pout);
1133 }
1134 printf("%s%s", initial_response ? "" : "C: ", inbase64);
1135 prot_write(pout, inbase64, inbase64len);
1136
1137 out = NULL;
1138 } else if (sendliteral) {
1139 /* If we had no response, we still need to send the
1140 empty literal in this case */
1141 printf("{0+}\r\nC: ");
1142 prot_printf(pout, "{0+}\r\n");
1143 } else if (!initial_response) {
1144 printf("C: ");
1145 }
1146 noinitresp:
1147 initial_response = 0;
1148
1149 printf("\r\n");
1150 prot_printf(pout, "\r\n");
1151 prot_flush(pout);
1152
1153 /* get challenge/reply from the server */
1154 status = getauthline(sasl_cmd, &in, &inlen);
1155
1156 if ((status == STAT_CONT || (status == STAT_OK && in)) &&
1157 (inlen || !out)) { /* no delayed initial response */
1158 do { /* do the next step */
1159 saslresult = sasl_client_step(conn, in, inlen,
1160 &client_interact,
1161 &out, &outlen);
1162
1163 if (saslresult == SASL_INTERACT)
1164 fillin_interactions(client_interact); /* fill in prompts */
1165 } while (saslresult == SASL_INTERACT);
1166
1167 if (in) free(in);
1168 }
1169
1170 if ((saslresult != SASL_OK) && (saslresult != SASL_CONTINUE)) {
1171 /* cancel the exchange */
1172 printf("C: %s\r\n", sasl_cmd->cancel);
1173 prot_printf(pout, "%s\r\n", sasl_cmd->cancel);
1174 prot_flush(pout);
1175
1176 return saslresult;
1177 }
1178
1179 sendliteral = !sasl_cmd->cont;
1180
1181 } while (status == STAT_CONT);
1182
1183 return (status == STAT_OK) ? IMTEST_OK : IMTEST_FAIL;
1184 }
1185
1186 /* initialize the network */
init_net(char * serverFQDN,char * port)1187 static int init_net(char *serverFQDN, char *port)
1188 {
1189 struct addrinfo hints, *res0 = NULL, *res;
1190 int err;
1191
1192 memset(&hints, 0, sizeof(hints));
1193 hints.ai_family = PF_UNSPEC;
1194 hints.ai_socktype = SOCK_STREAM;
1195 hints.ai_flags = AI_CANONNAME;
1196 if ((err = getaddrinfo(serverFQDN, port, &hints, &res0)) != 0) {
1197 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err));
1198 return IMTEST_FAIL;
1199 }
1200
1201 if (res0->ai_canonname)
1202 strncpy(serverFQDN, res0->ai_canonname, 1023);
1203 for (res = res0; res; res = res->ai_next) {
1204 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1205 if (sock < 0)
1206 continue;
1207 if (connect(sock, res->ai_addr, res->ai_addrlen) >= 0)
1208 break;
1209 close(sock);
1210 sock = -1;
1211 }
1212
1213 freeaddrinfo(res0);
1214 if(sock < 0) {
1215 perror("connect");
1216 return IMTEST_FAIL;
1217 }
1218
1219 return IMTEST_OK;
1220 }
1221
logout(struct logout_cmd_t * logout_cmd,int wait)1222 static void logout(struct logout_cmd_t *logout_cmd, int wait)
1223 {
1224 if (!logout_cmd->cmd) return;
1225
1226 printf("C: %s\r\n", logout_cmd->cmd);
1227 prot_printf(pout, "%s\r\n", logout_cmd->cmd);
1228 prot_flush(pout);
1229
1230 /* only wait if we are explicitly told to */
1231 if(wait) waitfor(logout_cmd->resp, NULL, 1);
1232 }
1233
1234 static int gotsigint = 0;
1235
sigint_handler(int sig)1236 static void sigint_handler(int sig __attribute__((unused)))
1237 {
1238 gotsigint = 1;
1239 }
1240
haveinput(struct protstream * s)1241 static int haveinput(struct protstream *s)
1242 {
1243 /* Is something currently pending in our protstream's buffer? */
1244 #ifdef HAVE_SSL
1245 if (s->cnt == 0 && s->tls_conn != NULL) {
1246 /* Maybe there's data pending in the SSL buffer? */
1247 int n = SSL_pending(s->tls_conn);
1248 if (verbose) printf("SSL_pending=%d\n", n);
1249 return n;
1250 }
1251 #endif
1252 return s->cnt;
1253 }
1254
1255 /* This needs to support 3 modes:
1256 *
1257 * 1. Terminal Interface Only
1258 * 2. File input
1259 * 3. Redirect to a unix socket - This mode needs to be sure that the
1260 * IMAP session is in an unselected state whenever the unix socket is
1261 * disconnected.
1262 *
1263 */
interactive(struct protocol_t * protocol,char * filename)1264 static void interactive(struct protocol_t *protocol, char *filename)
1265 {
1266 char buf[2048];
1267 fd_set read_set, rset;
1268 fd_set write_set, wset;
1269 fd_set accept_set, aset;
1270 int nfds;
1271 int nfound;
1272 int count;
1273 int fd = 0, fd_out = 1, listen_sock = -1;
1274 void *rock = NULL;
1275 int donewritingfile = 0;
1276
1277 struct sockaddr_un sunsock;
1278 int salen;
1279
1280 /* open the file if available */
1281 if (filename != NULL) {
1282 if ((fd = open(filename, O_RDONLY)) == -1) {
1283 fprintf(stderr,"Unable to open file: %s:", filename);
1284 perror("");
1285 exit(1);
1286 }
1287 } else if(output_socket) {
1288 struct timeval tv;
1289 struct stat sbuf;
1290
1291 /* can't have this and a file for input */
1292 sunsock.sun_family = AF_UNIX;
1293 strlcpy(sunsock.sun_path, output_socket, sizeof(sunsock.sun_path));
1294 unlink(output_socket);
1295
1296 listen_sock = socket(AF_UNIX, SOCK_STREAM, 0);
1297 if(listen_sock < 0) imtest_fatal("could not create output socket");
1298
1299 salen = sizeof(sunsock.sun_family) + strlen(sunsock.sun_path) + 1;
1300
1301 if((bind(listen_sock, (struct sockaddr *)&sunsock, salen)) < 0) {
1302 imtest_fatal("could not bind output socket");
1303 }
1304
1305 if((listen(listen_sock, 5)) < 0) {
1306 imtest_fatal("could not listen to output socket");
1307 }
1308
1309 if(stat(output_socket, &sbuf) == -1) {
1310 imtest_fatal("could not stat output socket");
1311 }
1312
1313 output_socket_opened = 1;
1314 output_socket_ino = sbuf.st_ino;
1315
1316 FD_ZERO(&accept_set);
1317 FD_SET(listen_sock, &accept_set);
1318
1319 accept_again:
1320 if(rock) {
1321 free(rock);
1322 rock = NULL;
1323 }
1324
1325 tv.tv_sec = 600; /* 10 minute timeout - xxx protocol specific? */
1326 tv.tv_usec = 0;
1327
1328 aset = accept_set;
1329
1330 /* Have the separate select so that signals will wake us up
1331 * and we get a timeout to use on our own imap connection */
1332 if(select(listen_sock + 1, &aset, NULL, NULL, &tv) <= 0) {
1333 /* either we timed out or had an error */
1334 goto cleanup;
1335 }
1336
1337 fd = fd_out = accept(listen_sock, NULL, NULL);
1338 if(fd < 0) imtest_fatal("accept failure");
1339
1340 if(protocol->init_conn) rock = protocol->init_conn();
1341 }
1342
1343 FD_ZERO(&read_set);
1344 FD_SET(fd, &read_set); /* In the terminal case fd == 0 */
1345 FD_SET(sock, &read_set);
1346
1347 FD_ZERO(&write_set);
1348 FD_SET(fd_out, &write_set);
1349 FD_SET(sock, &write_set);
1350
1351 nfds = fd;
1352 if (nfds < sock) nfds = sock;
1353 if (nfds < fd_out) nfds = fd_out;
1354 nfds++;
1355
1356 if (filename != NULL) {
1357 donewritingfile = 0;
1358 }
1359
1360 /* add handler for SIGINT */
1361 signal(SIGINT, sigint_handler);
1362
1363 /* loop reading from network and from stdin as applicable */
1364 int unauth = 0;
1365 while (1) {
1366 rset = read_set;
1367 wset = write_set;
1368 nfound = select(nfds, &rset, &wset, NULL, NULL);
1369 if (nfound < 0) {
1370 perror("select");
1371 imtest_fatal("select");
1372 }
1373
1374 if (!output_socket &&
1375 (FD_ISSET(0, &rset)) && (FD_ISSET(sock, &wset))) {
1376 /* There is explicit terminal input -- note this is only possible
1377 * if fd is 0 (and we are in terminal mode!).
1378 * We need to use stream API for this, which is why it
1379 * is different */
1380 if (fgets(buf, sizeof (buf) - 1, stdin) == NULL) {
1381 logout(&protocol->logout_cmd, 0);
1382 FD_CLR(0, &read_set);
1383 } else {
1384 count = strlen(buf);
1385 /* If we read a full line, translate the newline
1386 * if necessary. */
1387 if (buf[count - 1] == '\n' && (count < 2 || buf[count - 2] != '\r')) {
1388 buf[count - 1] = '\r';
1389 buf[count] = '\n';
1390 buf[count + 1] = '\0';
1391 count++;
1392 }
1393 prot_write(pout, buf, count);
1394
1395 if (protocol->unauth_cmd) {
1396 /* Check if unauthenticate command was sent */
1397 char *p = stristr(buf, protocol->unauth_cmd);
1398
1399 if (p && !strcmp("\r\n", p + strlen(protocol->unauth_cmd)))
1400 unauth = 1;
1401 }
1402 }
1403 prot_flush(pout);
1404 } else if (FD_ISSET(sock, &rset) && (FD_ISSET(fd_out, &wset))) {
1405 /* This does input from remote for all modes */
1406 do {
1407 count = prot_read(pin, buf, sizeof (buf) - 1);
1408 if (count == 0) {
1409 const char *str = prot_error(pin);
1410 if (str && strcmp(str, PROT_EOF_STRING)) {
1411 printf("Protection error: %s\n", prot_error(pin));
1412 }
1413 close(sock);
1414 printf("Connection closed.\n");
1415 return;
1416 }
1417 if (count < 0) {
1418 perror("read");
1419 imtest_fatal("prot_read");
1420 }
1421 if(output_socket)
1422 retry_write(fd_out, buf, count);
1423 else {
1424 /* use the stream API */
1425 buf[count] = '\0';
1426 printf("%s", buf);
1427 }
1428
1429 if (unauth) {
1430 /* Reset auth and connection state (other than TLS) */
1431 sasl_dispose(&conn);
1432 if (init_sasl(protocol->service, NULL,
1433 0, 128, 0) != IMTEST_OK) {
1434 imtest_fatal("SASL initialization");
1435 }
1436 unauth = 0;
1437
1438 #ifdef HAVE_ZLIB
1439 prot_unsetcompress(pout);
1440 prot_unsetcompress(pin);
1441 #endif
1442 prot_unsetsasl(pout);
1443 prot_unsetsasl(pin);
1444 }
1445 } while (haveinput(pin) > 0);
1446 } else if ((FD_ISSET(fd, &rset)) && (FD_ISSET(sock, &wset))
1447 && (donewritingfile == 0)) {
1448 /* This does input for both socket and file modes */
1449 int numr = read(fd, buf, sizeof(buf));
1450
1451 /* and send out over wire */
1452 if (numr < 0)
1453 {
1454 perror("read");
1455 imtest_fatal("read");
1456 } else if (numr==0) {
1457 if(output_socket) {
1458 if(protocol->reset) {
1459 if(protocol->reset(rock) != IMTEST_OK)
1460 goto cleanup;
1461 } else
1462 /* no protocol->reset, we're done */
1463 goto cleanup;
1464
1465 close(fd);
1466 fd = 0;
1467 goto accept_again;
1468 } else {
1469 /* we're done, cleanup */
1470 donewritingfile = 1;
1471
1472 FD_CLR(fd,&read_set);
1473
1474 /* send LOGOUT */
1475 logout(&protocol->logout_cmd, 0);
1476 }
1477 } else {
1478 if (!output_socket) {
1479 /* echo for the user - if not in socket mode */
1480 retry_write(1, buf, numr);
1481 }
1482
1483 if (output_socket && protocol->pipe) {
1484 if(protocol->pipe(buf, numr, rock) == IMTEST_CLOSEME) {
1485 if(protocol->reset) {
1486 if(protocol->reset(rock) != IMTEST_OK)
1487 goto cleanup;
1488 } else
1489 /* no protocol->reset, we're done */
1490 goto cleanup;
1491
1492 close(fd);
1493 fd = 0;
1494 fd_out = 1;
1495 goto accept_again;
1496 }
1497 } else {
1498 /* echo to remote */
1499 prot_write(pout, buf, numr);
1500 prot_flush(pout);
1501 }
1502 }
1503 } else {
1504 /* if can't do anything else sleep */
1505 usleep(1000);
1506 }
1507
1508 /* received interrupt signal, logout */
1509 if (gotsigint) goto cleanup;
1510 }
1511
1512 cleanup:
1513 if(rock) free(rock);
1514
1515 if(output_socket && output_socket_opened) {
1516 struct stat sbuf;
1517
1518 close(fd);
1519 if (listen_sock != -1) close(listen_sock);
1520
1521 if(stat(output_socket, &sbuf) != -1
1522 && sbuf.st_ino == output_socket_ino) {
1523 unlink(output_socket);
1524 }
1525 }
1526
1527 logout(&protocol->logout_cmd, 0);
1528 close(sock);
1529
1530 printf("Connection closed.\n");
1531
1532 /* remove handler for SIGINT */
1533 signal(SIGINT, SIG_DFL);
1534 return;
1535 }
1536
1537 enum {
1538 AUTO_BANNER = -1,
1539 AUTO_NO = 0,
1540 AUTO_YES = 1
1541 };
1542
1543 enum {
1544 CAPA_LOGIN = (1 << 0),
1545 CAPA_STARTTLS = (1 << 1),
1546 CAPA_COMPRESS = (1 << 2)
1547 };
1548
print_command(const char * cmd,const char * arg)1549 static void print_command(const char *cmd, const char *arg)
1550 {
1551 static struct buf buf = BUF_INITIALIZER;
1552
1553 buf_reset(&buf);
1554 buf_printf(&buf, cmd, arg);
1555 buf_replace_all(&buf, "\r\n", "\r\nC: ");
1556
1557 printf("C: %s", buf_cstring(&buf));
1558 }
1559
ask_capability(struct protocol_t * prot,const char * servername,unsigned long * capabilities,int automatic)1560 static struct buf *ask_capability(struct protocol_t *prot,
1561 const char *servername,
1562 unsigned long *capabilities, int automatic)
1563 {
1564 char str[1024] = "";
1565 char *tmp, *resp;
1566 static struct buf ret = BUF_INITIALIZER;
1567
1568 buf_reset(&ret);
1569
1570 /* default state of login command unless toggled by capabilities */
1571 *capabilities = prot->login_enabled;
1572
1573 resp = (automatic == AUTO_BANNER) ? prot->banner.resp : prot->capa_cmd.resp;
1574
1575 if (!automatic) {
1576 /* no capability command */
1577 if (!prot->capa_cmd.cmd) return NULL;
1578
1579 /* request capabilities of server */
1580 print_command(prot->capa_cmd.cmd, servername);
1581 printf("\r\n");
1582
1583 prot_printf(pout, prot->capa_cmd.cmd, servername);
1584 prot_puts(pout, "\r\n");
1585 prot_flush(pout);
1586 }
1587
1588 do { /* look for the end of the capabilities */
1589 if (prot_fgets(str, sizeof(str), pin) == NULL) {
1590 if (!*str) imtest_fatal("prot layer failure");
1591 else break;
1592 }
1593 printf("S: %s", str);
1594
1595 /* check for login - toggles existing state */
1596 if (prot->capa_cmd.login &&
1597 strstr(str, prot->capa_cmd.login) != NULL) {
1598 *capabilities ^= CAPA_LOGIN;
1599 }
1600
1601 /* check for starttls */
1602 if (prot->capa_cmd.tls &&
1603 strstr(str, prot->capa_cmd.tls) != NULL) {
1604 *capabilities |= CAPA_STARTTLS;
1605 }
1606
1607 /* check for compress */
1608 if (prot->capa_cmd.compress &&
1609 strstr(str, prot->capa_cmd.compress) != NULL) {
1610 *capabilities |= CAPA_COMPRESS;
1611 }
1612
1613 /* check for auth */
1614 if (prot->capa_cmd.auth &&
1615 (tmp = strstr(str, prot->capa_cmd.auth)) != NULL) {
1616
1617 if (prot->capa_cmd.parse_mechlist)
1618 prot->capa_cmd.parse_mechlist(&ret, str, prot, capabilities);
1619 else
1620 buf_setcstr(&ret, tmp+strlen(prot->capa_cmd.auth));
1621 }
1622
1623 if (!resp) {
1624 /* multiline response with no distinct end (IMAP banner) */
1625 prot_NONBLOCK(pin);
1626 }
1627
1628 /* look for the end of the capabilities */
1629 } while (!resp || strncasecmp(str, resp, strlen(resp)));
1630
1631 prot_BLOCK(pin);
1632 return &ret;
1633 }
1634
1635 /* generic pipe functionality - break it into one line at a time, and
1636 * pass that into a per-protocol pipe function. */
1637 struct generic_context_t
1638 {
1639 int (*pipe_oneline)(char *buf, int len, void *rock);
1640 void *rock;
1641
1642 /* Deal with half-finished lines */
1643 char *midLine;
1644 size_t midLineLen;
1645 };
1646
generic_pipe(char * buf,int len,void * rock)1647 static int generic_pipe(char *buf, int len, void *rock)
1648 {
1649 struct generic_context_t *text = (struct generic_context_t *)rock;
1650 char *toWrite = NULL, *toSend = NULL;
1651 int toWriteLen = 0;
1652 char *lineEnd = NULL;
1653 int ret = IMTEST_OK;
1654
1655 /* do we have leftovers? -- if so, we append the new stuff */
1656 if(text->midLine) {
1657 text->midLine =
1658 (char *)xrealloc(text->midLine, text->midLineLen+len+1);
1659 memcpy(text->midLine+text->midLineLen, buf, len);
1660 text->midLineLen += len;
1661 text->midLine[text->midLineLen] = '\0';
1662
1663 toWrite = text->midLine;
1664 toWriteLen = text->midLineLen;
1665 } else {
1666 toWrite = buf;
1667 toWriteLen = len;
1668 }
1669
1670 /* one line at a time now */
1671 while(toWrite && (lineEnd = memchr(toWrite, '\n', toWriteLen)) != NULL) {
1672 size_t len_todo;
1673
1674 len_todo = lineEnd - toWrite + 1; /* +1 is to include the newline! */
1675
1676 toSend = (char *)xrealloc(toSend, len_todo + 1);
1677
1678 memcpy(toSend, toWrite, len_todo);
1679 toSend[len_todo] = '\0';
1680
1681 ret = text->pipe_oneline(toSend, len_todo, text->rock);
1682 if(ret != IMTEST_OK) break;
1683
1684 toWrite = lineEnd+1; /* +1 is to skip the newline! */
1685 toWriteLen -= len_todo;
1686
1687 if(toWriteLen <= 0) toWrite = NULL;
1688
1689 }
1690
1691 if(toWrite && ret == IMTEST_OK) {
1692 char *newMidLine;
1693 /* we need to save the leftover for next time */
1694 newMidLine = (char *)xmalloc(toWriteLen);
1695 memcpy(newMidLine, toWrite, toWriteLen);
1696 if(text->midLine) free(text->midLine);
1697 text->midLine = newMidLine;
1698 text->midLineLen = toWriteLen;
1699 } else if (text->midLine || ret != IMTEST_OK) {
1700 free(text->midLine);
1701 text->midLine = NULL;
1702 text->midLineLen = 0;
1703 }
1704
1705 free(toSend);
1706 return ret;
1707 }
1708
1709 /*********************************** IMAP ************************************/
1710
1711 /*
1712 * Parse a mech list of the form: ... AUTH=foo AUTH=bar ...
1713 *
1714 * Return: string with mechs separated by spaces
1715 *
1716 */
1717
imap_parse_mechlist(struct buf * ret,const char * str,struct protocol_t * prot,unsigned long * capabilities)1718 static void imap_parse_mechlist(struct buf *ret, const char *str,
1719 struct protocol_t *prot,
1720 unsigned long *capabilities __attribute__((unused)))
1721 {
1722 char *tmp;
1723 int num = 0;
1724
1725 if (strstr(str, " SASL-IR")) {
1726 /* server supports initial response in AUTHENTICATE command */
1727 prot->sasl_cmd.maxlen = USHRT_MAX;
1728 }
1729
1730 while ((tmp = strstr(str, " AUTH="))) {
1731 char *end = (tmp += 6);
1732
1733 while((*end != ' ') && (*end != '\0')) end++;
1734
1735 /* add entry to list */
1736 if (num++ > 0) buf_putc(ret, ' ');
1737 buf_appendmap(ret, tmp, (end - tmp) + 1);
1738
1739 /* reset the string */
1740 str = end;
1741 }
1742 }
1743
auth_imap(void)1744 static int auth_imap(void)
1745 {
1746 char str[1024];
1747 /* we need username and password to do "login" */
1748 char *username;
1749 unsigned int userlen;
1750 char *pass;
1751 unsigned int passlen;
1752 char *tag = "L01 ";
1753
1754 str[0] = '\0';
1755
1756 interaction(SASL_CB_AUTHNAME, NULL, "Authname", &username, &userlen);
1757 interaction(SASL_CB_PASS, NULL, "Please enter your password",
1758 &pass, &passlen);
1759
1760 printf("C: %sLOGIN %s {%d}\r\n", tag, username, passlen);
1761 prot_printf(pout,"%sLOGIN %s {%d}\r\n", tag, username, passlen);
1762 prot_flush(pout);
1763
1764 if (!strncmp(waitfor("+", tag, 1), "+", 1)) {
1765 printf("C: <omitted>\r\n");
1766 prot_printf(pout,"%s\r\n", pass);
1767 prot_flush(pout);
1768
1769 do {
1770 if (prot_fgets(str, sizeof(str), pin) == NULL) {
1771 imtest_fatal("prot layer failure");
1772 }
1773 printf("S: %s", str);
1774 } while (strncmp(str, tag, strlen(tag)));
1775 }
1776
1777 if (!strncasecmp(str+strlen(tag), "OK", 2)) {
1778 return IMTEST_OK;
1779 } else {
1780 return IMTEST_FAIL;
1781 }
1782 }
1783
imap_do_auth(struct sasl_cmd_t * sasl_cmd,void * rock,int login_enabled,char * mech,const char * mechlist)1784 static int imap_do_auth(struct sasl_cmd_t *sasl_cmd,
1785 void *rock __attribute__((unused)),
1786 int login_enabled,
1787 char *mech, const char *mechlist)
1788 {
1789 int result = IMTEST_FAIL;
1790
1791 if (mech) {
1792 if (!strcasecmp(mech, "login")) {
1793 if (!login_enabled) {
1794 printf("[Server advertised LOGINDISABLED]\n");
1795 } else {
1796 result = auth_imap();
1797 }
1798 } else if (!mechlist || !stristr(mechlist, mech)) {
1799 printf("[Server did not advertise AUTH=%s]\n", ucase(mech));
1800 } else {
1801 result = auth_sasl(sasl_cmd, mech);
1802 }
1803 } else {
1804 if (mechlist) {
1805 result = auth_sasl(sasl_cmd, mechlist);
1806 } else if (login_enabled) {
1807 result = auth_imap();
1808 }
1809 }
1810
1811 return result;
1812 }
1813
1814 struct imap_context_t
1815 {
1816 int inLiteral;
1817 };
1818
imap_pipe_oneline(char * buf,int len,void * rock)1819 static int imap_pipe_oneline(char *buf, int len, void *rock) {
1820 struct imap_context_t *text = (struct imap_context_t *)rock;
1821 int add_crlf = 0; /* hack for terminals */
1822
1823 if(text->inLiteral) {
1824 if(len <= text->inLiteral) {
1825 text->inLiteral -= len;
1826 } else {
1827 prot_write(pout, buf, text->inLiteral);
1828 buf += text->inLiteral;
1829 len -= text->inLiteral;
1830 text->inLiteral = 0;
1831 }
1832 }
1833
1834 if(!text->inLiteral) {
1835 char *tag, *cmd, *tmp, *sparebuf = (char *)xstrdup(buf);
1836 int c, i;
1837 tmp = sparebuf;
1838
1839 if(len > 4 &&
1840 buf[len-1] == '\n' && buf[len-1] == '\r' && buf[len-2] == '}') {
1841 /* possible literal, with \r */
1842 i = len-4;
1843 while(i > 0 && buf[i] != '{' && Uisdigit(buf[i])) i--;
1844 if(buf[i] == '{') text->inLiteral = atoi(buf + i + 1);
1845 } else if(len > 3 && buf[len-1] == '\n' && buf[len-2] == '}') {
1846 /* possible literal, no \r -- hack for terminals*/
1847 i = len-3;
1848 while(i > 0 && buf[i] != '{' && Uisdigit(buf[i])) i--;
1849 if(buf[i] == '{') text->inLiteral = atoi(buf + i + 1);
1850 }
1851
1852 /* We could still have another special case! */
1853 c = imparse_word(&tmp, &tag);
1854 if(c == ' ') {
1855 c = imparse_word(&tmp, &cmd);
1856 if(c == '\n' || (c == '\r' && *tmp == '\n')){
1857 /* Are we logging out? */
1858 if(!strncasecmp(cmd, "LOGOUT", 6)) {
1859 free(sparebuf);
1860 return IMTEST_CLOSEME;
1861 }
1862 }
1863 }
1864
1865 free(sparebuf);
1866
1867 /* If the remote is sending only \n, clean it up for them */
1868 if((len == 1 && buf[0] == '\n') ||
1869 (len >= 2 && buf[len-2] != '\r')) {
1870 len -= 1; /* truncate \n */
1871 add_crlf = 1;
1872 }
1873 }
1874
1875 prot_write(pout, buf, len);
1876 if(add_crlf) prot_write(pout, "\r\n", 2);
1877 prot_flush(pout);
1878
1879 return IMTEST_OK;
1880 }
1881
imap_init_conn(void)1882 static void * imap_init_conn(void)
1883 {
1884 struct generic_context_t *ret;
1885
1886 ret =
1887 (void *)xmalloc(sizeof(struct generic_context_t));
1888 memset(ret, 0, sizeof(struct generic_context_t));
1889
1890 ret->rock =
1891 (void *)xmalloc(sizeof(struct imap_context_t));
1892 memset(ret->rock, 0, sizeof(struct imap_context_t));
1893
1894 ret->pipe_oneline = &imap_pipe_oneline;
1895
1896 return ret;
1897 }
1898
imap_reset(void * rock)1899 static int imap_reset(void *rock)
1900 {
1901 struct generic_context_t *gentext = (struct generic_context_t *)rock;
1902 struct imap_context_t *text = (struct imap_context_t *)gentext->rock;
1903 char tag[64];
1904 static int i=0;
1905
1906 if(text->inLiteral || gentext->midLine) return IMTEST_FAIL;
1907
1908 snprintf(tag, sizeof(tag) - 1, "UN%d", i);
1909 prot_printf(pout, "%s UNSELECT\r\n", tag);
1910 prot_flush(pout);
1911 waitfor(tag, NULL, 0);
1912
1913 return IMTEST_OK;
1914 }
1915
1916 #define HEADERS "Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)\r\n \
1917 From: Fred Foobar <foobar@Blurdybloop.COM>\r\n \
1918 Subject: afternoon meeting\r\n \
1919 To: mooch@owatagu.siam.edu\r\n \
1920 Message-Id: <B27397-0100000@Blurdybloop.COM>\r\n \
1921 MIME-Version: 1.0\r\n \
1922 Content-Type: TEXT/PLAIN; CHARSET=US-ASCII\r\n\r\n"
1923
append_msg(char * mbox,int size)1924 static int append_msg(char *mbox, int size)
1925 {
1926 int lup;
1927
1928 prot_printf(pout,"A003 APPEND %s (\\Seen) {" SIZE_T_FMT "}\r\n",
1929 mbox,size+strlen(HEADERS));
1930 /* do normal header foo */
1931 prot_printf(pout,HEADERS);
1932
1933 for (lup=0;lup<size/10;lup++)
1934 prot_printf(pout,"0123456789");
1935 prot_printf(pout,"\r\n");
1936
1937 prot_flush(pout);
1938
1939 waitfor("A003", NULL, 1);
1940
1941 return IMTEST_OK;
1942 }
1943
1944 /**************
1945 *
1946 * This tests throughput of IMAP server
1947 *
1948 * Steps:
1949 * Creat mailbox
1950 * Append message of 200 bytes, 2000 bytes, 20k, 200k, 2M
1951 * Delete mailbox
1952 *
1953 *************/
1954
1955
send_recv_test(void)1956 static void send_recv_test(void)
1957 {
1958 char *mboxname="inbox.imtest";
1959 time_t start, end;
1960 int lup;
1961
1962 start=time(NULL);
1963
1964 for (lup=0;lup<10;lup++)
1965 {
1966 prot_printf(pout,"C01 CREATE %s\r\n",mboxname);
1967 prot_flush(pout);
1968 waitfor("C01", NULL, 1);
1969
1970 append_msg(mboxname,200);
1971 append_msg(mboxname,2000);
1972 append_msg(mboxname,20000);
1973 append_msg(mboxname,200000);
1974 append_msg(mboxname,2000000);
1975
1976 prot_printf(pout,"D01 DELETE %s\r\n",mboxname);
1977 prot_flush(pout);
1978 waitfor("D01", NULL, 1);
1979 }
1980
1981 end=time(NULL);
1982
1983 printf("took " TIME_T_FMT " seconds\n", end - start);
1984 }
1985
1986 /*********************************** POP3 ************************************/
1987
pop3_parse_banner(char * str)1988 static void *pop3_parse_banner(char *str)
1989 {
1990 char *cp, *start;
1991 char *chal = NULL;
1992
1993 /* look for APOP challenge in banner '<...@...>' */
1994 cp = str+3;
1995 while (cp && (start = strchr(cp, '<'))) {
1996 cp = start + 1;
1997 while (*cp && *cp != '@' && *cp != '<' && *cp != '>') cp++;
1998 if (*cp != '@') continue;
1999 while (*cp && *cp != '<' && *cp != '>') cp++;
2000 if (*cp == '>') {
2001 *(++cp) = '\0';
2002 chal = strdup(start);
2003 if (!chal) imtest_fatal("memory error");
2004 break;
2005 }
2006 }
2007
2008 return chal;
2009 }
2010
auth_pop(void)2011 static int auth_pop(void)
2012 {
2013 char str[1024];
2014 /* we need username and password to do USER/PASS */
2015 char *username;
2016 unsigned int userlen;
2017 char *pass;
2018 unsigned int passlen;
2019
2020 interaction(SASL_CB_AUTHNAME, NULL, "Authname", &username, &userlen);
2021
2022 printf("C: USER %s\r\n", username);
2023 prot_printf(pout,"USER %s\r\n", username);
2024 prot_flush(pout);
2025
2026 if (prot_fgets(str, 1024, pin) == NULL) {
2027 imtest_fatal("prot layer failure");
2028 }
2029
2030 printf("S: %s", str);
2031
2032 if (strncasecmp(str, "+OK", 3)) return IMTEST_FAIL;
2033
2034 interaction(SASL_CB_PASS, NULL, "Please enter your password",
2035 &pass, &passlen);
2036
2037 printf("C: PASS <omitted>\r\n");
2038 prot_printf(pout,"PASS %s\r\n",pass);
2039 prot_flush(pout);
2040
2041 if (prot_fgets(str, 1024, pin) == NULL) {
2042 imtest_fatal("prot layer failure");
2043 }
2044
2045 printf("S: %s", str);
2046
2047 if (!strncasecmp(str, "+OK", 3)) {
2048 return IMTEST_OK;
2049 } else {
2050 return IMTEST_FAIL;
2051 }
2052 }
2053
auth_apop(char * apop_chal)2054 static int auth_apop(char *apop_chal)
2055 {
2056 char str[1024];
2057 /* we need username and password to do "APOP" */
2058 char *username;
2059 unsigned int userlen;
2060 char *pass;
2061 unsigned int passlen;
2062 int i;
2063 MD5_CTX ctx;
2064 unsigned char digest[MD5_DIGEST_LENGTH];
2065 char digeststr[2*MD5_DIGEST_LENGTH+1];
2066
2067 interaction(SASL_CB_AUTHNAME, NULL, "Authname", &username, &userlen);
2068 interaction(SASL_CB_PASS,NULL, "Please enter your password",
2069 &pass, &passlen);
2070
2071 MD5Init(&ctx);
2072 MD5Update(&ctx,apop_chal,strlen(apop_chal));
2073 MD5Update(&ctx,pass,passlen);
2074 MD5Final(digest, &ctx);
2075
2076 /* convert digest from binary to ASCII hex */
2077 for (i = 0; i < 16; i++)
2078 sprintf(digeststr + (i*2), "%02x", digest[i]);
2079
2080 printf("C: APOP %s %s\r\n", username, digeststr);
2081 prot_printf(pout,"APOP %s %s\r\n", username, digeststr);
2082 prot_flush(pout);
2083
2084 if(prot_fgets(str, 1024, pin) == NULL) {
2085 imtest_fatal("prot layer failure");
2086 }
2087
2088 printf("S: %s", str);
2089
2090 if (!strncasecmp(str, "+OK", 3)) {
2091 return IMTEST_OK;
2092 } else {
2093 return IMTEST_FAIL;
2094 }
2095 }
2096
pop3_do_auth(struct sasl_cmd_t * sasl_cmd,void * apop_chal,int user_enabled,char * mech,const char * mechlist)2097 static int pop3_do_auth(struct sasl_cmd_t *sasl_cmd, void *apop_chal,
2098 int user_enabled, char *mech, const char *mechlist)
2099 {
2100 int result = IMTEST_FAIL;
2101
2102 if (mech) {
2103 if (!strcasecmp(mech, "apop")) {
2104 if (!apop_chal) {
2105 printf("[Server did not advertise APOP challenge]\n");
2106 } else {
2107 result = auth_apop((char *) apop_chal);
2108 }
2109 } else if (!strcasecmp(mech, "user")) {
2110 if (!user_enabled) {
2111 printf("[Server did not advertise USER]\n");
2112 } else {
2113 result = auth_pop();
2114 }
2115 } else if (!mechlist || !stristr(mechlist, mech)) {
2116 printf("[Server did not advertise SASL %s]\n", ucase(mech));
2117 } else {
2118 result = auth_sasl(sasl_cmd, mech);
2119 }
2120 } else {
2121 if (mechlist) {
2122 result = auth_sasl(sasl_cmd, mechlist);
2123 } else if (apop_chal) {
2124 result = auth_apop((char *) apop_chal);
2125 } else if (user_enabled) {
2126 result = auth_pop();
2127 }
2128 }
2129
2130 return result;
2131 }
2132
2133 /********************************** NNTP *************************************/
2134
auth_nntp()2135 static int auth_nntp()
2136 {
2137 char str[1024];
2138 /* we need username and password to do AUTHINFO USER/PASS */
2139 char *username;
2140 unsigned int userlen;
2141 char *pass;
2142 unsigned int passlen;
2143
2144 interaction(SASL_CB_AUTHNAME, NULL, "Authname", &username, &userlen);
2145
2146 printf("C: AUTHINFO USER %s\r\n", username);
2147 prot_printf(pout,"AUTHINFO USER %s\r\n", username);
2148 prot_flush(pout);
2149
2150 if (prot_fgets(str, 1024, pin) == NULL) {
2151 imtest_fatal("prot layer failure");
2152 }
2153
2154 printf("S: %s", str);
2155
2156 if (!strncmp(str, "381", 3)) {
2157 interaction(SASL_CB_PASS, NULL, "Please enter your password",
2158 &pass, &passlen);
2159
2160 printf("C: AUTHINFO PASS <omitted>\r\n");
2161 prot_printf(pout,"AUTHINFO PASS %s\r\n",pass);
2162 prot_flush(pout);
2163
2164 if (prot_fgets(str, 1024, pin) == NULL) {
2165 imtest_fatal("prot layer failure");
2166 }
2167
2168 printf("S: %s", str);
2169 }
2170
2171 if (!strncmp(str, "281", 3)) {
2172 return IMTEST_OK;
2173 } else {
2174 return IMTEST_FAIL;
2175 }
2176 }
2177
nntp_do_auth(struct sasl_cmd_t * sasl_cmd,void * rock,int user_enabled,char * mech,const char * mechlist)2178 static int nntp_do_auth(struct sasl_cmd_t *sasl_cmd,
2179 void *rock __attribute__((unused)),
2180 int user_enabled, char *mech, const char *mechlist)
2181 {
2182 int result = IMTEST_OK;
2183
2184 if (mech) {
2185 if (!strcasecmp(mech, "user")) {
2186 if (!user_enabled) {
2187 printf("[Server did not advertise AUTHINFO USER]\n");
2188 result = IMTEST_FAIL;
2189 } else {
2190 result = auth_nntp();
2191 }
2192 } else if (!mechlist || !stristr(mechlist, mech)) {
2193 printf("[Server did not advertise SASL %s]\n", ucase(mech));
2194 result = IMTEST_FAIL;
2195 } else {
2196 result = auth_sasl(sasl_cmd, mech);
2197 }
2198 } else {
2199 if (mechlist) {
2200 result = auth_sasl(sasl_cmd, mechlist);
2201 } else if (user_enabled) {
2202 result = auth_nntp();
2203 }
2204 }
2205
2206 return result;
2207 }
2208
nntp_parse_success(char * str)2209 static char *nntp_parse_success(char *str)
2210 {
2211 char *success = NULL, *tmp;
2212
2213 if (!strncmp(str, "283 ", 4)) {
2214 success = str+4;
2215 if ((tmp = strchr(success, ' ')))
2216 *tmp = '\0'; /* clip trailing comment */
2217 }
2218
2219 return success;
2220 }
2221
2222 /******************************** LMTP/SMTP **********************************/
2223
xmtp_do_auth(struct sasl_cmd_t * sasl_cmd,void * rock,int login_enabled,char * mech,const char * mechlist)2224 static int xmtp_do_auth(struct sasl_cmd_t *sasl_cmd,
2225 void *rock __attribute__((unused)),
2226 int login_enabled __attribute__((unused)),
2227 char *mech, const char *mechlist)
2228 {
2229 int result = IMTEST_OK;
2230
2231 if (mech) {
2232 result = auth_sasl(sasl_cmd, mech);
2233 } else if (mechlist) {
2234 result = auth_sasl(sasl_cmd, mechlist);
2235 }
2236
2237 return result;
2238 }
2239
2240 struct xmtp_context_t
2241 {
2242 int inData;
2243 };
2244
2245 /* This takes a NUL-terminated full line (including any trailing \r\n) */
xmtp_pipe_oneline(char * buf,int len,void * rock)2246 static int xmtp_pipe_oneline(char *buf, int len, void *rock) {
2247 struct xmtp_context_t *text = (struct xmtp_context_t *)rock;
2248
2249 if(text->inData && len <= 3) {
2250 if(buf[0] == '.' &&
2251 (buf[1] == '\n' || (buf[1] == '\r' && buf[2] == '\n'))) {
2252 text->inData = 0;
2253 }
2254 } else if(!text->inData && len > 4 && len <= 6) {
2255 if(!strncasecmp(buf, "DATA", 4) &&
2256 (buf[4] == '\n' || (buf[4] == '\r' && buf[5] == '\n'))) {
2257 text->inData = 1;
2258 } else if(!strncasecmp(buf, "QUIT", 4) &&
2259 (buf[4] == '\n' || (buf[4] == '\r' && buf[5] == '\n'))) {
2260 return IMTEST_CLOSEME;
2261 }
2262 }
2263
2264 prot_write(pout, buf, len);
2265 prot_flush(pout);
2266
2267 return IMTEST_OK;
2268 }
2269
xmtp_init_conn(void)2270 static void *xmtp_init_conn(void)
2271 {
2272 struct generic_context_t *ret;
2273
2274 ret =
2275 (void *)xmalloc(sizeof(struct generic_context_t));
2276 memset(ret, 0, sizeof(struct generic_context_t));
2277
2278 ret->rock =
2279 (void *)xmalloc(sizeof(struct xmtp_context_t));
2280 memset(ret->rock, 0, sizeof(struct xmtp_context_t));
2281
2282 ret->pipe_oneline = &xmtp_pipe_oneline;
2283
2284 return ret;
2285 }
2286
xmtp_reset(void * rock)2287 static int xmtp_reset(void *rock)
2288 {
2289 struct generic_context_t *gentext = (struct generic_context_t *)rock;
2290 struct xmtp_context_t *text = (struct xmtp_context_t *)gentext->rock;
2291
2292 if(text->inData || gentext->midLine) return IMTEST_FAIL;
2293
2294 prot_printf(pout, "RSET\r\n");
2295 prot_flush(pout);
2296 waitfor("250", NULL, 1);
2297
2298 return IMTEST_OK;
2299 }
2300
2301
2302 /******************************** MUPDATE ************************************/
2303
2304
2305 /********************************* SIEVE *************************************/
2306
sieve_parse_success(char * str)2307 static char *sieve_parse_success(char *str)
2308 {
2309 char *success = NULL, *tmp;
2310
2311 if (!strncmp(str, "OK (", 4) &&
2312 (tmp = strstr(str+4, "SASL \"")) != NULL) {
2313 success = tmp+6; /* skip SASL " */
2314 tmp = strstr(success, "\"");
2315 if (tmp) *tmp = '\0'; /* clip " */
2316 }
2317
2318 return success;
2319 }
2320
2321 /********************************** HTTP *************************************/
2322
2323 #define HTTP_CAPA_TLS "Upgrade: TLS/1.2"
2324 #define HTTP_CAPA_AUTH "WWW-Authenticate:"
2325 #define HTTP_AUTH_INFO "Authentication-Info:"
2326
2327 #define HTTP_OPTIONS \
2328 "OPTIONS * HTTP/1.1\r\n" \
2329 "Host: %s\r\n" \
2330 "User-Agent: httptest\r\n"
2331
2332 #define HTTP_STARTTLS \
2333 HTTP_OPTIONS \
2334 "Connection: Upgrade\r\n" \
2335 HTTP_CAPA_TLS "\r\n"
2336
2337 #define HTTP_101 "HTTP/1.1 101"
2338 #define HTTP_200 "HTTP/1.1 200"
2339 #define HTTP_401 "HTTP/1.1 401"
2340 #define HTTP_5xx "HTTP/1.1 5"
2341
http_parse_mechlist(struct buf * ret,const char * str,struct protocol_t * prot,unsigned long * capabilities)2342 static void http_parse_mechlist(struct buf *ret, const char *str,
2343 struct protocol_t *prot __attribute__((unused)),
2344 unsigned long *capabilities)
2345 {
2346 char *scheme;
2347 size_t len;
2348
2349 scheme = strchr(str, ':');
2350 while (strchr(" \t", *++scheme)); /* trim options whitespace */
2351 len = strcspn(scheme, " \t\r\n"); /* end of scheme name */
2352
2353 if (len == 5 && !strncmp(scheme, "Basic", len)) {
2354 *capabilities |= CAPA_LOGIN;
2355 return;
2356 }
2357 else if (len == 6 && !strncmp(scheme, "Digest", len)) {
2358 scheme = "DIGEST-MD5";
2359 len = strlen(scheme);
2360 }
2361 else if (len == 9 && !strncmp(scheme, "Negotiate", len)) {
2362 scheme = "GSS-SPNEGO";
2363 len = strlen(scheme);
2364 }
2365
2366 /* add entry to list */
2367 if (buf_len(ret)) buf_putc(ret, ' ');
2368 buf_appendmap(ret, scheme, len);
2369 }
2370
auth_http_basic(const char * servername)2371 static int auth_http_basic(const char *servername)
2372 {
2373 char str[1024];
2374 /* we need username and password to do HTTP Basic */
2375 char *authname;
2376 unsigned int authlen;
2377 char *username;
2378 unsigned int userlen;
2379 char *pass;
2380 unsigned int passlen;
2381 char creds[4096];
2382 int credslen;
2383 char *resp;
2384
2385 interaction(SASL_CB_AUTHNAME, NULL, "Authname", &authname, &authlen);
2386 interaction(SASL_CB_USER, NULL, "Username", &username, &userlen);
2387 interaction(SASL_CB_PASS, NULL, "Please enter your password",
2388 &pass, &passlen);
2389
2390 snprintf(str, sizeof(str), "%s:%s", authname, pass);
2391 if (sasl_encode64(str, strlen(str), creds, sizeof(creds),
2392 (unsigned *) &credslen) != SASL_OK) {
2393 return IMTEST_FAIL;
2394 }
2395
2396 print_command(HTTP_OPTIONS, servername);
2397 if (username && *username) printf("Authorize-As: %s\r\nC: ", username);
2398 printf("Authorization: Basic %.*s\r\nC: \r\n", credslen, creds);
2399
2400 prot_printf(pout, HTTP_OPTIONS, servername);
2401 if (username && *username)
2402 prot_printf(pout, "Authorize-As: %s\r\n", username);
2403 prot_printf(pout, "Authorization: Basic %.*s\r\n", credslen, creds);
2404 prot_puts(pout, "\r\n");
2405 prot_flush(pout);
2406
2407 resp = waitfor("HTTP/1.1 ", NULL, 1);
2408
2409 if (!strncmp(resp, HTTP_200, strlen(HTTP_200))) {
2410 return IMTEST_OK;
2411 } else {
2412 return IMTEST_FAIL;
2413 }
2414 }
2415
2416 #define BASE64_BUF_SIZE 21848 /* per RFC 2222bis: ((16K / 3) + 1) * 4 */
2417
auth_http_sasl(const char * servername,const char * mechlist)2418 static int auth_http_sasl(const char *servername, const char *mechlist)
2419 {
2420 sasl_interact_t *client_interact = NULL;
2421 int saslresult;
2422 const char *out = NULL;
2423 unsigned int outlen = 0;
2424 char *in, *sid = NULL;
2425 int inlen;
2426 const char *mechusing;
2427 char buf[BASE64_BUF_SIZE+1], *base64 = buf;
2428 int initial_response = 1, do_base64 = 1, use_params = 0;;
2429 imt_stat status;
2430 char *username;
2431 unsigned int userlen;
2432
2433 #ifdef SASL_HTTP_REQUEST
2434 /* Set HTTP request (REQUIRED) */
2435 sasl_http_request_t httpreq = { "OPTIONS", /* Method */
2436 "*", /* URI */
2437 (u_char *) "", /* Empty body */
2438 0, /* Zero-length body */
2439 1 }; /* Persistent cxn? */
2440 sasl_setprop(conn, SASL_HTTP_REQUEST, &httpreq);
2441 #endif
2442
2443 interaction(SASL_CB_USER, NULL, "Username", &username, &userlen);
2444
2445 do { /* start authentication */
2446 saslresult = sasl_client_start(conn, mechlist, &client_interact,
2447 &out, &outlen, &mechusing);
2448
2449 if (saslresult == SASL_INTERACT)
2450 fillin_interactions(client_interact); /* fill in prompts */
2451 } while (saslresult == SASL_INTERACT);
2452
2453 if ((saslresult != SASL_OK) && (saslresult != SASL_CONTINUE)) {
2454 return saslresult;
2455 }
2456
2457 if (!strcmp(mechusing, "DIGEST-MD5")) {
2458 mechusing = "Digest";
2459 do_base64 = 0;
2460 }
2461 else if (!strcmp(mechusing, "GSS-SPNEGO")) {
2462 mechusing = "Negotiate";
2463 }
2464 else if (!strncmp(mechusing, "SCRAM-", 6)) {
2465 use_params = 1;
2466 }
2467
2468 do {
2469 /* build the auth command */
2470 print_command(HTTP_OPTIONS, servername);
2471 if (username && *username) printf("Authorize-As: %s\r\nC: ", username);
2472 printf("Authorization: %s", mechusing);
2473
2474 prot_printf(pout, HTTP_OPTIONS, servername);
2475 if (username && *username)
2476 prot_printf(pout, "Authorize-As: %s\r\n", username);
2477 prot_printf(pout, "Authorization: %s", mechusing);
2478
2479 if (out) { /* response */
2480 printf(" ");
2481 prot_putc(' ', pout);
2482
2483 if (initial_response && !outlen) {
2484 /* empty initial response */
2485 printf("=");
2486 prot_putc('=', pout);
2487 }
2488 else {
2489 if (outlen && do_base64) {
2490 /* convert to base64 */
2491 saslresult = sasl_encode64(out, outlen,
2492 base64, BASE64_BUF_SIZE,
2493 (unsigned *) &outlen);
2494 if (saslresult != SASL_OK) return saslresult;
2495
2496 out = base64;
2497 }
2498
2499 /* send response to server */
2500 if (use_params) {
2501 if (sid) {
2502 printf("sid=%s,", sid);
2503 prot_printf(pout, "sid=%s,", sid);
2504 }
2505 printf("data=");
2506 prot_puts(pout, "data=");
2507 }
2508 printf("%.*s", outlen, out);
2509 prot_write(pout, out, outlen);
2510 }
2511 }
2512
2513 out = NULL;
2514 initial_response = 0;
2515
2516 printf("\r\nC: \r\n");
2517 prot_puts(pout, "\r\n\r\n");
2518 prot_flush(pout);
2519
2520 /* get status line from the server */
2521 if (prot_fgets(buf, sizeof(buf), pin) == NULL) {
2522 imtest_fatal("prot layer failure");
2523 }
2524 printf("S: %s", buf);
2525
2526 if (!strncmp(buf, HTTP_200, strlen(HTTP_200)))
2527 status = STAT_OK;
2528 else if (!strncmp(buf, HTTP_401, strlen(HTTP_401)))
2529 status = STAT_CONT;
2530 else status = STAT_NO;
2531
2532 /* get challenge/reply from the server */
2533 in = NULL;
2534 inlen = 0;
2535 prot_NONBLOCK(pin);
2536 while (prot_fgets(buf, sizeof(buf), pin)) {
2537 printf("S: %s", buf);
2538
2539 if ((status == STAT_OK &&
2540 !strncmp(buf, HTTP_AUTH_INFO, strlen(HTTP_AUTH_INFO))) ||
2541 !strncmp(buf, HTTP_CAPA_AUTH, strlen(HTTP_CAPA_AUTH))) {
2542 char *scheme;
2543 size_t len;
2544
2545 scheme = strchr(buf, ':');
2546 while (strchr(" \t", *++scheme)); /* trim optional whitespace */
2547 len = strcspn(scheme, " \t\r\n"); /* end of scheme name */
2548
2549 if (len == strlen(mechusing) &&
2550 !strncmp(scheme, mechusing, len)) {
2551 in = scheme + len;
2552 while (strchr(" \t", *++in)); /* trim optional whitespace */
2553
2554 if (use_params) {
2555 /* Parse parameters */
2556 const char *token = in;
2557
2558 in = NULL;
2559 while (token && *token) {
2560 size_t tok_len, val_len;
2561 char *value;
2562
2563 /* Trim leading and trailing BWS */
2564 while (strchr(", \t", *token)) token++;
2565 tok_len = strcspn(token, "= \t");
2566
2567 /* Find value */
2568 value = strchr(token + tok_len, '=');
2569 if (!value) {
2570 printf("Missing value for '%.*s'"
2571 " parameter in challenge\n",
2572 (int) tok_len, token);
2573 return IMTEST_FAIL;
2574 }
2575
2576 /* Trim leading and trailing BWS */
2577 while (strchr(" \t", *++value));
2578 val_len = strcspn(value, ", \t");
2579
2580 /* Check known parameters */
2581 if (!strncmp("sid", token, tok_len)) {
2582 if (!sid) sid = xstrndup(value, val_len);
2583 else if (val_len != strlen(sid) ||
2584 strncmp(sid, value, val_len)) {
2585 printf("Incorrect session ID parameter\n");
2586 return IMTEST_FAIL;
2587 }
2588 }
2589 else if (!strncmp("data", token, tok_len)) {
2590 in = value;
2591 inlen = val_len;
2592 }
2593
2594 /* Find next token */
2595 token = strchr(value + val_len, ',');
2596 }
2597 if (!in) {
2598 printf("Missing 'data' parameter in challenge\n");
2599 return IMTEST_FAIL;
2600 }
2601 }
2602 else {
2603 /* token68 */
2604 inlen = strcspn(in, " \t\r\n"); /* end of challenge */
2605 }
2606
2607 in = xstrndup(in, inlen);
2608 }
2609 }
2610 }
2611 prot_BLOCK(pin);
2612
2613 if ((status == STAT_CONT || (status == STAT_OK && in))) {
2614 if (do_base64) {
2615 /* decode this line */
2616 saslresult = sasl_decode64(in, inlen,
2617 base64, BASE64_BUF_SIZE,
2618 (unsigned *) &inlen);
2619
2620 free(in);
2621
2622 if (saslresult != SASL_OK && saslresult != SASL_CONTINUE) {
2623 printf("base64 decoding error: %d\n", saslresult);
2624 return IMTEST_FAIL;
2625 }
2626
2627 in = base64;
2628 }
2629
2630 do { /* do the next step */
2631 saslresult = sasl_client_step(conn, in, inlen,
2632 &client_interact,
2633 &out, &outlen);
2634
2635 if (saslresult == SASL_INTERACT)
2636 fillin_interactions(client_interact); /* fill in prompts */
2637 } while (saslresult == SASL_INTERACT);
2638 }
2639
2640 if ((saslresult != SASL_OK) && (saslresult != SASL_CONTINUE)) {
2641 /* cancel the exchange */
2642 return IMTEST_FAIL;
2643 }
2644 } while (out);
2645
2646 free(sid);
2647
2648 return (status == STAT_OK) ? IMTEST_OK : IMTEST_FAIL;
2649 }
2650
http_do_auth(struct sasl_cmd_t * sasl_cmd,void * servername,int basic_enabled,char * mech,const char * mechlist)2651 static int http_do_auth(struct sasl_cmd_t *sasl_cmd __attribute__((unused)),
2652 void *servername, int basic_enabled,
2653 char *mech, const char *mechlist)
2654 {
2655 int result = IMTEST_OK;
2656
2657 if (mech) {
2658 if (!strcasecmp(mech, "basic") || !strcasecmp(mech, "plain")) {
2659 if (!basic_enabled) {
2660 printf("[Server did not advertise HTTP Basic]\n");
2661 result = IMTEST_FAIL;
2662 } else {
2663 result = auth_http_basic(servername);
2664 }
2665 } else {
2666 if (!strcasecmp(mech, "digest")) mech = "DIGEST-MD5";
2667 else if (!strcasecmp(mech, "negotiate")) mech = "GSS-SPNEGO";
2668
2669 if (!mechlist || !stristr(mechlist, mech)) {
2670 printf("[Server did not advertise HTTP %s]\n", ucase(mech));
2671 result = IMTEST_FAIL;
2672 } else {
2673 result = auth_http_sasl(servername, mech);
2674 }
2675 }
2676 } else {
2677 if (mechlist) {
2678 result = auth_http_sasl(servername, mechlist);
2679 } else if (basic_enabled) {
2680 result = auth_http_basic(servername);
2681 }
2682 }
2683
2684 return result;
2685 }
2686
2687 /*****************************************************************************/
2688
2689 /* didn't give correct parameters; let's exit */
usage(char * prog,char * prot)2690 static void usage(char *prog, char *prot)
2691 {
2692 printf("Usage: %s [options] hostname\n", prog);
2693 printf(" -p port : port to use (default=standard port for protocol)\n");
2694 if (!strcasecmp(prot, "imap"))
2695 printf(" -z : timing test\n");
2696 printf(" -k # : minimum protection layer required\n");
2697 printf(" -l # : max protection layer (0=none; 1=integrity; etc)\n");
2698 printf(" -u user : authorization name to use\n");
2699 printf(" -a user : authentication name to use\n");
2700 printf(" -w pass : password to use (if not supplied, we will prompt)\n");
2701 printf(" -v : verbose\n");
2702 printf(" -m mech : SASL mechanism to use\n");
2703 if (!strcasecmp(prot, "imap"))
2704 printf(" (\"login\" for IMAP LOGIN)\n");
2705 else if (!strcasecmp(prot, "pop3"))
2706 printf(" (\"user\" for USER/PASS, \"apop\" for APOP)\n");
2707 else if (!strcasecmp(prot, "nntp"))
2708 printf(" (\"user\" for AUTHINFO USER/PASS\n");
2709 else if (!strcasecmp(prot, "http"))
2710 printf(" (\"basic\", \"digest\", \"negotiate\", \"ntlm\")\n");
2711 printf(" -f file : pipe file into connection after authentication\n");
2712 printf(" -r realm : realm\n");
2713 #ifdef HAVE_SSL
2714 if (!strcasecmp(prot, "imap") || !strcasecmp(prot, "pop3") ||
2715 !strcasecmp(prot, "nntp") || !strcasecmp(prot, "smtp") || !strcasecmp(prot, "http"))
2716 printf(" -s : Enable %s over SSL (%ss)\n", prot, prot);
2717 if (strcasecmp(prot, "mupdate"))
2718 printf(" -t file : Enable TLS. file has the TLS public and private keys\n"
2719 " (specify \"\" to not use TLS for authentication)\n");
2720 #endif /* HAVE_SSL */
2721 #ifdef HAVE_ZLIB
2722 if (!strcasecmp(prot, "imap") || !strcasecmp(prot, "nntp") ||
2723 !strcasecmp(prot, "mupdate") || !strcasecmp(prot, "csync")) {
2724 printf(" -q : Enable %s COMPRESSion (after authentication)\n",
2725 prot);
2726 }
2727 #endif /* HAVE_ZLIB */
2728 printf(" -c : enable challenge prompt callbacks\n"
2729 " (enter one-time password instead of secret pass-phrase)\n");
2730 printf(" -n : number of auth attempts (default=1)\n");
2731 printf(" -I file : output my PID to (file) (useful with -X)\n");
2732 printf(" -x file : open the named socket for the interactive portion\n");
2733 printf(" -X file : same as -X, except close all file descriptors & daemonize\n");
2734
2735 exit(1);
2736 }
2737
2738
2739 static struct protocol_t protocols[] = {
2740 { "imap", "imaps", "imap", 1, /* LOGIN available until LOGINDISABLED */
2741 { 1, NULL, NULL },
2742 { "C01 CAPABILITY", "C01 ", " STARTTLS", " LOGINDISABLED", " AUTH=",
2743 " COMPRESS=DEFLATE", &imap_parse_mechlist },
2744 { "S01 STARTTLS", "S01 OK", "S01 NO", 0 },
2745 { "A01 AUTHENTICATE", 0, /* no init resp until SASL-IR advertised */
2746 0, "A01 OK", "A01 NO", "+ ", "*", NULL, 0 },
2747 { "Z01 COMPRESS DEFLATE", "Z01 OK", "Z01 NO" },
2748 &imap_do_auth, { "Q01 LOGOUT", "Q01 " }, " UNAUTHENTICATE",
2749 &imap_init_conn, &generic_pipe, &imap_reset
2750 },
2751 { "pop3", "pop3s", "pop", 0, /* USER unavailable until advertised */
2752 { 0, "+OK", &pop3_parse_banner },
2753 { "CAPA", ".", "STLS", "USER", "SASL ", NULL, NULL },
2754 { "STLS", "+OK", "-ERR", 0 },
2755 { "AUTH", 255, 0, "+OK", "-ERR", "+ ", "*", NULL, 0 },
2756 { NULL, NULL, NULL, },
2757 &pop3_do_auth, { "QUIT", "+OK" }, NULL, NULL, NULL, NULL
2758 },
2759 { "nntp", "nntps", "nntp", 0, /* AUTHINFO USER unavail until advertised */
2760 { 0, "20", NULL },
2761 { "CAPABILITIES", ".", "STARTTLS", "AUTHINFO USER", "SASL ",
2762 "COMPRESS DEFLATE", NULL },
2763 { "STARTTLS", "382", "580", 0 },
2764 { "AUTHINFO SASL", 512, 0, "28", "48", "383 ", "*",
2765 &nntp_parse_success, 0 },
2766 { "COMPRESS DEFLATE", "206", "403", },
2767 &nntp_do_auth, { "QUIT", "205" }, NULL, NULL, NULL, NULL
2768 },
2769 { "lmtp", NULL, "lmtp", 0,
2770 { 0, "220 ", NULL },
2771 { "LHLO lmtptest", "250 ", "STARTTLS", NULL, "AUTH ", NULL, NULL },
2772 { "STARTTLS", "220", "454", 0 },
2773 { "AUTH", 512, 0, "235", "5", "334 ", "*", NULL, 0 },
2774 { NULL, NULL, NULL, },
2775 &xmtp_do_auth, { "QUIT", "221" }, NULL,
2776 &xmtp_init_conn, &generic_pipe, &xmtp_reset
2777 },
2778 { "smtp", "smtps", "smtp", 0,
2779 { 0, "220 ", NULL },
2780 { "EHLO smtptest", "250 ", "STARTTLS", NULL, "AUTH ", NULL, NULL },
2781 { "STARTTLS", "220", "454", 0 },
2782 { "AUTH", 512, 0, "235", "5", "334 ", "*", NULL, 0 },
2783 { NULL, NULL, NULL, },
2784 &xmtp_do_auth, { "QUIT", "221" }, NULL,
2785 &xmtp_init_conn, &generic_pipe, &xmtp_reset
2786 },
2787 { "mupdate", NULL, "mupdate", 0,
2788 { 1, "* OK", NULL },
2789 { NULL , "* OK", "* STARTTLS", NULL, "* AUTH ", "* COMPRESS \"DEFLATE\"", NULL },
2790 { "S01 STARTTLS", "S01 OK", "S01 NO", 1 },
2791 { "A01 AUTHENTICATE", USHRT_MAX, 1, "A01 OK", "A01 NO", "", "*", NULL, 0 },
2792 { "Z01 COMPRESS \"DEFLATE\"", "Z01 OK", "Z01 NO" },
2793 NULL, { "Q01 LOGOUT", "Q01 " }, NULL, NULL, NULL, NULL
2794 },
2795 { "sieve", NULL, SIEVE_SERVICE_NAME, 0,
2796 { 1, "OK", NULL },
2797 { "CAPABILITY", "OK", "\"STARTTLS\"", NULL, "\"SASL\" ", NULL, NULL },
2798 { "STARTTLS", "OK", "NO", 1 },
2799 { "AUTHENTICATE", USHRT_MAX, 1, "OK", "NO", NULL, "*",
2800 &sieve_parse_success, 1 },
2801 { NULL, NULL, NULL, },
2802 NULL, { "LOGOUT", "OK" }, "UNAUTHENTICATE", NULL, NULL, NULL
2803 },
2804 { "csync", NULL, "csync", 0,
2805 { 1, "* OK", NULL },
2806 { NULL , "* OK", "* STARTTLS", NULL, "* SASL ", "* COMPRESS DEFLATE", NULL },
2807 { "STARTTLS", "OK", "NO", 1 },
2808 { "AUTHENTICATE", USHRT_MAX, 0, "OK", "NO", "+ ", "*", NULL, 0 },
2809 { "COMPRESS DEFLATE", "OK", "NO" },
2810 NULL, { "EXIT", "OK" }, NULL, NULL, NULL, NULL
2811 },
2812 { "http", "https", "HTTP", 0, /* Basic unavail until advertised */
2813 { 0, NULL, NULL },
2814 { HTTP_OPTIONS, NULL, HTTP_CAPA_TLS, NULL,
2815 HTTP_CAPA_AUTH, NULL, &http_parse_mechlist },
2816 { HTTP_STARTTLS, HTTP_101, HTTP_5xx, 1 },
2817 { NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, 0 },
2818 { NULL, NULL, NULL, },
2819 &http_do_auth, { NULL, NULL }, NULL, NULL, NULL, NULL
2820 },
2821 { NULL, NULL, NULL, 0,
2822 { 0, NULL, NULL },
2823 { NULL, NULL, NULL, NULL, NULL, NULL, NULL },
2824 { NULL, NULL, NULL, 0 },
2825 { NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, 0 },
2826 { NULL, NULL, NULL, },
2827 NULL, { NULL, NULL }, NULL, NULL, NULL, NULL
2828 }
2829 };
2830
main(int argc,char ** argv)2831 int main(int argc, char **argv)
2832 {
2833 struct protocol_t *protocol;
2834 char *mechanism = NULL;
2835 char servername[1024];
2836 char *filename=NULL;
2837
2838 struct buf *mechlist = NULL;
2839 unsigned ext_ssf = 0;
2840 const void *ssfp;
2841 sasl_ssf_t ssf;
2842 int maxssf = 128;
2843 int minssf = 0;
2844 int c;
2845 int result;
2846 int errflg = 0;
2847
2848 #ifdef HAVE_SSL
2849 #define WITH_SSL_ONLY /**/
2850 #else
2851 #define WITH_SSL_ONLY __attribute__((unused))
2852 #endif
2853 #ifdef HAVE_ZLIB
2854 #define WITH_ZLIB_ONLY /**/
2855 #else
2856 #define WITH_ZLIB_ONLY __attribute__((unused))
2857 #endif
2858
2859 char *prog;
2860 char *tls_keyfile WITH_SSL_ONLY = "";
2861 char *port = "", *prot = "";
2862 int run_stress_test=0;
2863 int dotls WITH_SSL_ONLY = 0, dossl = 0, docompress WITH_ZLIB_ONLY = 0;
2864 unsigned long capabilities = 0;
2865 char str[1024];
2866 const char *pidfile = NULL;
2867 void *rock = NULL;
2868 int reauth = 1;
2869 int dochallenge = 0, noinitresp = 0;
2870 char *val;
2871
2872 #undef WITH_SSL_ONLY
2873
2874 if (!construct_hash_table(&confighash, CONFIGHASHSIZE, 1)) {
2875 imtest_fatal("could not construct config hash table");
2876 }
2877
2878 /* do not buffer */
2879 setbuf(stdin, NULL);
2880 setbuf(stdout, NULL);
2881 setbuf(stderr, NULL);
2882
2883 prog = strrchr(argv[0], '/') ? strrchr(argv[0], '/')+1 : argv[0];
2884
2885 /* look at all the extra args */
2886 while ((c = getopt(argc, argv, "P:qscizvk:l:p:u:a:m:f:r:t:n:I:x:X:w:o:?h")) != EOF)
2887 switch (c) {
2888 case 'P':
2889 prot = optarg;
2890 break;
2891 case 'q':
2892 #ifdef HAVE_ZLIB
2893 docompress=1;
2894 #else
2895 imtest_fatal("imtest was not compiled with zlib support\n");
2896 #endif
2897 break;
2898 case 's':
2899 #ifdef HAVE_SSL
2900 dossl=1;
2901 #else
2902 imtest_fatal("imtest was not compiled with SSL/TLS support\n");
2903 #endif
2904 break;
2905 case 'c':
2906 dochallenge=1;
2907 break;
2908 case 'i':
2909 noinitresp=1;
2910 break;
2911 case 'z':
2912 run_stress_test=1;
2913 break;
2914 case 'v':
2915 verbose=1;
2916 break;
2917 case 'k':
2918 minssf=atoi(optarg);
2919 break;
2920 case 'l':
2921 maxssf=atoi(optarg);
2922 break;
2923 case 'p':
2924 port = optarg;
2925 break;
2926 case 'u':
2927 username = optarg;
2928 break;
2929 case 'a':
2930 authname = optarg;
2931 break;
2932 case 'w':
2933 cmdline_password = optarg;
2934 break;
2935 case 'm':
2936 mechanism=optarg;
2937 break;
2938 case 'f':
2939 if(output_socket)
2940 imtest_fatal("cannot pipe a file when using unix domain socket output");
2941 filename=optarg;
2942 break;
2943 case 'r':
2944 realm=optarg;
2945 break;
2946 case 't':
2947 #ifdef HAVE_SSL
2948 dotls=1;
2949 tls_keyfile=optarg;
2950 #else
2951 imtest_fatal("imtest was not compiled with SSL/TLS support\n");
2952 #endif
2953 break;
2954 case 'n':
2955 reauth = atoi(optarg);
2956 if (reauth <= 0)
2957 imtest_fatal("number of auth attempts must be > 0\n");
2958 break;
2959 case 'I':
2960 pidfile = optarg;
2961 break;
2962 case 'X':
2963 case 'x':
2964 if(filename)
2965 imtest_fatal("cannot pipe a file when using unix domain socket output");
2966 if(output_socket)
2967 imtest_fatal("cannot specify both -X and -x");
2968
2969 output_socket = optarg;
2970
2971 if(c == 'X'){
2972 /* close all already-open file descriptors that are
2973 * not stdin/stdout/stderr */
2974 int i, dsize = getdtablesize();
2975
2976 /* close all file descriptors */
2977 for(i=0; i<dsize; i++) close(i);
2978
2979 /* background ourselves and lose the process group info */
2980 for(i=0;i<3;i++) if(fork()) exit(0);
2981 }
2982
2983 break;
2984
2985 case 'o':
2986 /* parse the opt=val string. if no value is given, assume '1' */
2987 if ((val = strchr(optarg, '=')))
2988 *val++ = '\0';
2989 else
2990 val = "1";
2991
2992 /* insert the opt/val pair into the hash table */
2993 hash_insert(optarg, xstrdup(val), &confighash);
2994 break;
2995
2996 case 'h':
2997 case '?':
2998 default:
2999 errflg = 1;
3000 break;
3001 }
3002
3003 if (!*prot) {
3004 if (!strcasecmp(prog, "imtest"))
3005 prot = "imap";
3006 else if (!strcasecmp(prog, "pop3test"))
3007 prot = "pop3";
3008 else if (!strcasecmp(prog, "nntptest"))
3009 prot = "nntp";
3010 else if (!strcasecmp(prog, "lmtptest"))
3011 prot = "lmtp";
3012 else if (!strcasecmp(prog, "smtptest"))
3013 prot = "smtp";
3014 else if (!strcasecmp(prog, "mupdatetest"))
3015 prot = "mupdate";
3016 else if (!strcasecmp(prog, "sivtest"))
3017 prot = "sieve";
3018 else if (!strcasecmp(prog, "synctest"))
3019 prot = "csync";
3020 else if (!strcasecmp(prog, "httptest"))
3021 prot = "http";
3022 }
3023
3024 protocol = protocols;
3025 while (protocol->protocol && strcasecmp(prot, protocol->protocol))
3026 protocol++;
3027
3028 if (!protocol->protocol)
3029 imtest_fatal("unknown protocol\n");
3030
3031 if (dossl && !protocol->sprotocol)
3032 imtest_fatal("protocol cannot be SSL-wrapped\n");
3033
3034 if (run_stress_test && strcmp(protocol->protocol, "imap"))
3035 imtest_fatal("stress test can only be run for IMAP\n");
3036
3037 if (errflg) {
3038 usage(prog, protocol->protocol);
3039 }
3040
3041 if (!*port) {
3042 if (dossl) {
3043 port=protocol->sprotocol;
3044 } else {
3045 port=protocol->protocol;
3046 }
3047 }
3048
3049 /* last arg is server name */
3050 if (optind < argc)
3051 strncpy(servername, argv[optind], 1023);
3052 else {
3053 fprintf(stderr, "WARNING: no hostname supplied, assuming localhost\n\n");
3054 strncpy(servername, "localhost", 1023);
3055 }
3056
3057 if(pidfile) {
3058 FILE *pf;
3059 pf = fopen(pidfile, "w");
3060 if(!pf) {
3061 fprintf(stderr, "could not open %s for writing\n",pidfile);
3062 perror("error");
3063 exit(1);
3064 }
3065 fprintf(pf, "%d", getpid());
3066 fclose(pf);
3067 }
3068
3069 /* attempt to start sasl */
3070 if (sasl_client_init(callbacks+(!dochallenge ? 2 : 0)) != IMTEST_OK) {
3071 imtest_fatal("SASL initialization");
3072 }
3073
3074 conn = NULL;
3075 do {
3076 unsigned flags = 0;
3077
3078 if (conn) {
3079 /* send LOGOUT */
3080 logout(&protocol->logout_cmd, 1);
3081 printf("Connection closed.\n\n");
3082
3083 prot_free(pin);
3084 prot_free(pout);
3085
3086 #ifdef HAVE_SSL
3087 /* Properly shutdown TLS so that session can be reused */
3088 if (tls_conn) {
3089 SSL_shutdown(tls_conn);
3090 SSL_set_shutdown(tls_conn, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
3091 }
3092 #endif
3093
3094 close(sock);
3095
3096 sasl_dispose(&conn);
3097 }
3098
3099 if (init_net(servername, port) != IMTEST_OK) {
3100 imtest_fatal("Network initialization - cannot connect to %s:%s",
3101 servername, port);
3102 }
3103
3104 if (username && strcmpnull(authname, username)) flags += SASL_NEED_PROXY;
3105 if (protocol->sasl_cmd.parse_success) flags += SASL_SUCCESS_DATA;
3106
3107 if (init_sasl(protocol->service, servername,
3108 minssf, maxssf, flags) != IMTEST_OK) {
3109 imtest_fatal("SASL initialization");
3110 }
3111
3112 /* set up the prot layer */
3113 pin = prot_new(sock, 0);
3114 pout = prot_new(sock, 1);
3115
3116 #ifdef HAVE_SSL
3117 if (dossl==1) {
3118 do_starttls(1, "", &ext_ssf);
3119 }
3120 #endif /* HAVE_SSL */
3121
3122 if (protocol->banner.is_capa) {
3123 /* try to get the capabilities from the banner */
3124 mechlist = ask_capability(protocol, servername,
3125 &capabilities, AUTO_BANNER);
3126 if ((!mechlist || !buf_len(mechlist)) && !(capabilities & CAPA_STARTTLS)) {
3127 /* found no capabilities in banner -> get them explicitly */
3128 protocol->banner.is_capa = 0;
3129 }
3130 }
3131 else if (protocol->banner.resp) {
3132 do { /* look for the banner response */
3133 if (prot_fgets(str, sizeof(str), pin) == NULL) {
3134 imtest_fatal("prot layer failure");
3135 }
3136 printf("S: %s", str);
3137
3138 /* parse it if need be */
3139 if (protocol->banner.parse_banner)
3140 rock = protocol->banner.parse_banner(str);
3141 } while (strncasecmp(str, protocol->banner.resp,
3142 strlen(protocol->banner.resp)));
3143 }
3144 if (!protocol->banner.is_capa) {
3145 mechlist = ask_capability(protocol, servername,
3146 &capabilities, AUTO_NO);
3147 }
3148
3149 #ifdef HAVE_SSL
3150 if ((dossl==0) && (dotls==1) && (capabilities & CAPA_STARTTLS)) {
3151 char *resp;
3152
3153 print_command(protocol->tls_cmd.cmd, servername);
3154 printf("\r\n");
3155
3156 prot_printf(pout, protocol->tls_cmd.cmd, servername);
3157 prot_puts(pout, "\r\n");
3158 prot_flush(pout);
3159
3160 resp = waitfor(protocol->tls_cmd.ok, protocol->tls_cmd.fail, 1);
3161
3162 if (!strncasecmp(resp, protocol->tls_cmd.ok,
3163 strlen(protocol->tls_cmd.ok))) {
3164
3165 do_starttls(0, tls_keyfile, &ext_ssf);
3166
3167 /* ask for the capabilities again */
3168 if (verbose==1)
3169 printf("Asking for capabilities again "
3170 "since they might have changed\n");
3171 mechlist = ask_capability(protocol, servername, &capabilities,
3172 protocol->tls_cmd.auto_capa);
3173 }
3174
3175 } else if ((dotls==1) && !(capabilities & CAPA_STARTTLS)) {
3176 imtest_fatal("STARTTLS not supported by the server!\n");
3177 }
3178 #endif /* HAVE_SSL */
3179
3180 if (noinitresp) {
3181 /* don't use an initial response, even if its supported */
3182 protocol->sasl_cmd.maxlen = 0;
3183 }
3184
3185 if (protocol->do_auth)
3186 result = protocol->do_auth(&protocol->sasl_cmd,
3187 rock ? rock : servername,
3188 capabilities & CAPA_LOGIN,
3189 mechanism, buf_cstring(mechlist));
3190 else {
3191 if (mechanism) {
3192 result = auth_sasl(&protocol->sasl_cmd, mechanism);
3193 } else if (mechlist) {
3194 result = auth_sasl(&protocol->sasl_cmd, buf_cstring(mechlist));
3195 } else {
3196 result = IMTEST_FAIL;
3197 }
3198 }
3199
3200 if (rock) free(rock);
3201
3202 if (result == IMTEST_OK) {
3203 printf("Authenticated.\n");
3204
3205 /* turn on layer if need be */
3206 prot_setsasl(pin, conn);
3207 prot_setsasl(pout, conn);
3208 } else {
3209 const char *s = sasl_errstring(result, NULL, NULL);
3210
3211 printf("Authentication failed. %s\n", s);
3212 }
3213
3214 result = sasl_getprop(conn, SASL_SSF, &ssfp);
3215 ssf = *((sasl_ssf_t *) ssfp);
3216 if (result != SASL_OK) {
3217 printf("SSF: unable to determine (SASL ERROR %d)\n", result);
3218 } else {
3219 printf("Security strength factor: %d\n", ext_ssf + ssf);
3220
3221 if (ssf) {
3222 /* ask for the capabilities again */
3223 struct buf orig_mechlist = BUF_INITIALIZER;
3224
3225 if (verbose==1)
3226 printf("Asking for capabilities again "
3227 "since they might have changed\n");
3228 if (!strcmp(protocol->protocol, "sieve")) {
3229 /* XXX Hack to handle ManageSieve servers.
3230 * No way to tell from protocol if server will
3231 * automatically send capabilities, so we treat it
3232 * as optional.
3233 */
3234 char ch;
3235
3236 /* wait and probe for possible auto-capability response*/
3237 usleep(250000);
3238 prot_NONBLOCK(pin);
3239 if ((ch = prot_getc(pin)) != EOF) {
3240 prot_ungetc(ch, pin);
3241 } else {
3242 protocol->sasl_cmd.auto_capa = 0;
3243 }
3244 prot_BLOCK(pin);
3245 }
3246
3247 buf_copy(&orig_mechlist, mechlist);
3248
3249 mechlist = ask_capability(protocol, servername, &capabilities,
3250 protocol->sasl_cmd.auto_capa);
3251 if (mechlist &&
3252 strcmp(buf_cstring(mechlist), buf_cstring(&orig_mechlist))) {
3253 printf("WARNING: possible MITM attack: "
3254 "list of available SASL mechanisms changed\n");
3255 }
3256
3257 buf_free(&orig_mechlist);
3258 }
3259
3260 }
3261
3262 } while (--reauth);
3263
3264 #ifdef HAVE_ZLIB
3265 if ((docompress==1) && (capabilities & CAPA_COMPRESS)) {
3266 char *resp;
3267
3268 printf("C: %s\r\n", protocol->compress_cmd.cmd);
3269 prot_printf(pout, "%s\r\n", protocol->compress_cmd.cmd);
3270 prot_flush(pout);
3271
3272 resp = waitfor(protocol->compress_cmd.ok, protocol->compress_cmd.fail, 1);
3273
3274 if (!strncasecmp(resp, protocol->compress_cmd.ok,
3275 strlen(protocol->compress_cmd.ok))) {
3276 prot_setcompress(pin);
3277 prot_setcompress(pout);
3278 }
3279 }
3280 #endif /* HAVE_ZLIB */
3281
3282 if (run_stress_test == 1) {
3283 send_recv_test();
3284 } else {
3285 /* else run in interactive mode or
3286 pipe in a filename if applicable */
3287 interactive(protocol, filename);
3288 }
3289
3290 while (stashed_strings.count)
3291 free(strarray_pop(&stashed_strings));
3292 free_hash_table(&confighash, free);
3293
3294 exit(0);
3295 }
3296