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