1#if HAVE_CONFIG_H
2#include <config.h>
3#endif
4#include <signal.h>
5#include <sys/wait.h>
6#include <sys/mman.h>
7#include <assert.h>
8#include "cunit/cunit.h"
9#include "imap/saslclient.h"
10#include <sasl/saslutil.h>
11#include <sasl/saslplug.h>
12#include "xmalloc.h"
13#include "imap/mutex.h"
14#include "prot.h"
15#include "imap/backend.h"
16
17struct server_config {
18    int sasl_plain;
19    int sasl_login;
20    int sasl_digestmd5;
21    int starttls;
22    int deflate;
23    int caps_one_per_line;
24};
25
26/*
27 * This is a useful hack.  All the test server's state is
28 * stored using this structure in an anonymous page which
29 * is shared between the client and server.  This lets the
30 * test code in the client reach into the server state and
31 * either tweak the behaviour via the config or do asserts
32 * on the per-connection state (which makes integrating
33 * with CUnit a whole lot easier, as CU asserts in a child
34 * process are really not helpful).
35 */
36struct server_state {
37    /* dynamic configuration */
38    struct server_config config;
39
40    /* global state */
41    int rend_sock;
42
43    /* per-connection state */
44    int is_connected;
45    int is_authenticated;
46    int is_tls;
47    sasl_conn_t *saslconn;          /* the sasl connection context */
48#ifdef HAVE_SSL
49    SSL *tls_conn;
50#endif
51    struct protstream *in;
52    struct protstream *out;
53};
54
55
56#define HOST            "localhost"
57#define SASLSERVICE     "vorpal"
58#define USERID          "fbloggs"
59#define PASSWORD        "shibboleth"
60extern int verbose;
61
62static sasl_callback_t *callbacks;
63static struct server_state *server_state;
64static const struct server_config default_server_config = {
65    .sasl_plain = 1,
66    .sasl_login = 0,
67    .sasl_digestmd5 = 0,
68    .starttls = 0,
69    .deflate = 0,
70    .caps_one_per_line = 1
71};
72static const struct capa_t default_capa[] = {
73    { "CCSASL", CAPA_AUTH },
74    { "CCSTARTTLS", CAPA_STARTTLS },
75    { "CCCOMPRESS=DEFLATE", CAPA_COMPRESS },
76    { NULL, 0 }
77};
78static char default_service[32];
79
80static int init_sasl(int isclient);
81
82static struct protocol_t test_prot =
83{
84    /* .service is setup in default_conditions() */
85    .sasl_service = SASLSERVICE,
86    .type = TYPE_STD,
87    .u.std = {
88        .banner = {
89            .auto_capa = 1,
90            .resp = "OK"
91        },
92        .capa_cmd = {
93            .cmd = "XXCAPABILITY",
94            .arg = NULL,
95            .resp = "OK",
96            .postcapability = NULL,
97            .formatflags = CAPAF_ONE_PER_LINE|CAPAF_SKIP_FIRST_WORD,
98            /* .capa is setup in default_conditions() */
99        },
100        .tls_cmd = {
101            .cmd = "XXSTARTTLS",
102            .ok = "OK",
103            .fail = "NO",
104            .auto_capa = 0
105        },
106        .sasl_cmd = {
107            .cmd = "XXAUTHENTICATE",
108            .maxlen = USHRT_MAX,
109            .quote = 0,
110            .ok = "OK",
111            .fail = "NO",
112            .cont = "+ ",
113            .cancel = "*",
114            .parse_success = NULL,
115            .auto_capa = 0
116        },
117        .compress_cmd = {
118            .cmd = NULL,
119            .unsol = NULL,
120            .ok = NULL
121        },
122        .ping_cmd = {
123            .cmd = "XXNOOP",
124            .unsol = NULL,
125            .ok = "OK"
126        },
127        .logout_cmd = {
128            .cmd = "XXLOGOUT",
129            .unsol = NULL,
130            .ok = "OK"
131        }
132    }
133};
134
135
136/*
137 * Setup default test conditions, on both the client and server.
138 */
139static void default_conditions(void)
140{
141    server_state->config = default_server_config;
142
143    test_prot.service = default_service;
144    memcpy(&test_prot.u.std.capa_cmd.capa, default_capa, sizeof(default_capa));
145    test_prot.u.std.capa_cmd.formatflags = CAPAF_ONE_PER_LINE|CAPAF_SKIP_FIRST_WORD;
146}
147
148/* ====================================================================== */
149
150/*
151 * Test connecting to a host which doesn't exist.
152 */
153static void test_badhost(void)
154{
155    struct backend *be;
156    const char *auth_status = NULL;
157
158    default_conditions();
159
160    be = backend_connect(NULL, "nonexistanthost", &test_prot,
161                         USERID, callbacks, &auth_status, /*fd*/-1);
162    CU_ASSERT_PTR_NULL(be);
163    CU_ASSERT_EQUAL(server_state->is_connected, 0);
164}
165
166/*
167 * Test connecting to the wrong port on the right host.
168 */
169static void test_badservice(void)
170{
171    struct backend *be;
172    const char *auth_status = NULL;
173
174    default_conditions();
175    test_prot.service = "nonexistantservice";
176
177    be = backend_connect(NULL, HOST, &test_prot,
178                         USERID, callbacks, &auth_status, /*fd*/-1);
179    CU_ASSERT_PTR_NULL(be);
180    CU_ASSERT_EQUAL(server_state->is_connected, 0);
181}
182
183/*
184 * Test authenticating with the PLAIN mechanism.
185 */
186static void test_sasl_plain(void)
187{
188    struct backend *be;
189    const char *auth_status = NULL;
190    char *mechs;
191    int r;
192
193    default_conditions();
194    server_state->config.sasl_plain = 1;
195
196    be = backend_connect(NULL, HOST, &test_prot,
197                         USERID, callbacks, &auth_status, /*fd*/-1);
198    CU_ASSERT_PTR_NOT_NULL_FATAL(be);
199    CU_ASSERT_EQUAL(server_state->is_connected, 1);
200    CU_ASSERT_EQUAL(server_state->is_authenticated, 1);
201    CU_ASSERT_EQUAL(server_state->is_tls, 0);
202
203    mechs = backend_get_cap_params(be, CAPA_AUTH);
204    CU_ASSERT_STRING_EQUAL(mechs, "PLAIN");
205    free(mechs);
206
207    r = backend_ping(be, NULL);
208    CU_ASSERT_EQUAL(r, 0);
209
210    backend_disconnect(be);
211    free(be);
212}
213
214#if 0
215/*
216 * Test authenticating with the LOGIN mechanism.
217 * This test doesn't work for me.  I have NFI why - gnb.
218 */
219static void not_test_sasl_login(void)
220{
221    struct backend *be;
222    const char *auth_status = NULL;
223    char *mechs;
224    int r;
225
226    default_conditions();
227    server_state->config.sasl_plain = 0;
228    server_state->config.sasl_login = 1;
229
230    be = backend_connect(NULL, HOST, &test_prot,
231                         USERID, callbacks, &auth_status, /*fd*/-1);
232    CU_ASSERT_PTR_NOT_NULL_FATAL(be);
233    CU_ASSERT_EQUAL(server_state->is_connected, 1);
234    CU_ASSERT_EQUAL(server_state->is_authenticated, 1);
235    CU_ASSERT_EQUAL(server_state->is_tls, 0);
236
237    mechs = backend_get_cap_params(be, CAPA_AUTH);
238    CU_ASSERT_STRING_EQUAL(mechs, "LOGIN");
239    free(mechs);
240
241    r = backend_ping(be);
242    CU_ASSERT_EQUAL(r, 0);
243
244    backend_disconnect(be);
245    free(be);
246}
247#endif
248
249/*
250 * Test authenticating with the DIGEST-MD5 mechanism.
251 */
252static void test_sasl_digestmd5(void)
253{
254    struct backend *be;
255    const char *auth_status = NULL;
256    char *mechs;
257    int r;
258
259    default_conditions();
260    server_state->config.sasl_plain = 0;
261    server_state->config.sasl_digestmd5 = 1;
262
263    be = backend_connect(NULL, HOST, &test_prot,
264                         USERID, callbacks, &auth_status, /*fd*/-1);
265    CU_ASSERT_PTR_NOT_NULL_FATAL(be);
266    CU_ASSERT_EQUAL(server_state->is_connected, 1);
267    CU_ASSERT_EQUAL(server_state->is_authenticated, 1);
268    CU_ASSERT_EQUAL(server_state->is_tls, 0);
269
270    mechs = backend_get_cap_params(be, CAPA_AUTH);
271    CU_ASSERT_STRING_EQUAL(mechs, "DIGEST-MD5");
272    free(mechs);
273
274    r = backend_ping(be, NULL);
275    CU_ASSERT_EQUAL(r, 0);
276
277    backend_disconnect(be);
278    free(be);
279}
280
281/* Common routine to test the semantics of capabilities */
282static void caps_common(void)
283{
284#define CAPA_FOO        (1<<(CAPA_COMPRESS+1))
285#define CAPA_BAZ        (1<<(CAPA_COMPRESS+2))
286#define CAPA_QUUX       (1<<(CAPA_COMPRESS+3))
287#define CAPA_FNORD      (1<<(CAPA_COMPRESS+4))
288#define CAPA_CAPITALS   (1<<(CAPA_COMPRESS+5))
289#define CAPA_MIA        (1<<(CAPA_COMPRESS+6))
290    struct backend *be;
291    const char *auth_status = NULL;
292    char *params;
293    int r;
294    int n;
295    static const struct capa_t extra_capa[] = {
296        { "FOO", CAPA_FOO },
297        { "BAZ", CAPA_BAZ },
298        { "QUUX", CAPA_QUUX },
299        { "FNORD=BOO", CAPA_FNORD },
300        { "CAPITALS", CAPA_CAPITALS },
301        { "MIA", CAPA_MIA },
302        { NULL, 0 }
303        /* No more room in the fixed size array, dammit */
304    };
305
306    /* append some extra capabilities */
307    n = sizeof(default_capa)/sizeof(default_capa[0]) - 1;
308    CU_ASSERT_FATAL(n * sizeof(struct capa_t) +
309                    sizeof(extra_capa) <= sizeof(test_prot.u.std.capa_cmd.capa));
310    memcpy(&test_prot.u.std.capa_cmd.capa[n], extra_capa, sizeof(extra_capa));
311
312    be = backend_connect(NULL, HOST, &test_prot,
313                         USERID, callbacks, &auth_status, /*fd*/-1);
314    CU_ASSERT_PTR_NOT_NULL_FATAL(be);
315    CU_ASSERT_EQUAL(server_state->is_connected, 1);
316    CU_ASSERT_EQUAL(server_state->is_authenticated, 1);
317    CU_ASSERT_EQUAL(server_state->is_tls, 0);
318
319    /* FOO is present, its parameter is BAR */
320    CU_ASSERT_EQUAL(!!CAPA(be, CAPA_FOO), 1);
321    params = backend_get_cap_params(be, CAPA_FOO);
322    CU_ASSERT_STRING_EQUAL(params, "BAR");
323    free(params);
324
325    /* BAZ is present, it has no parameters */
326    CU_ASSERT_EQUAL(!!CAPA(be, CAPA_BAZ), 1);
327    params = backend_get_cap_params(be, CAPA_BAZ);
328    CU_ASSERT_PTR_NULL(params);
329
330    /* QUUX is present, its parameters are FOONLY and FMEH */
331    CU_ASSERT_EQUAL(!!CAPA(be, CAPA_QUUX), 1);
332    params = backend_get_cap_params(be, CAPA_QUUX);
333    CU_ASSERT_STRING_EQUAL(params, "FOONLY FMEH");
334    free(params);
335
336    /* FNORD is present, its parameters are BOO and GNERGH,
337     * but we asked specifically for FNORD=BOO so CAPA()
338     * should succeed but we should see no params. */
339    CU_ASSERT_EQUAL(!!CAPA(be, CAPA_FNORD), 1);
340    params = backend_get_cap_params(be, CAPA_FNORD);
341    CU_ASSERT_PTR_NULL(params);
342
343    /* CAPITALS is present, its parameter is Oslo.  Note the
344     * name is matched case-insensitive but the parameters
345     * are returned as seen on the wire. */
346    CU_ASSERT_EQUAL(!!CAPA(be, CAPA_CAPITALS), 1);
347    params = backend_get_cap_params(be, CAPA_CAPITALS);
348    CU_ASSERT_STRING_EQUAL(params, "Oslo");
349    free(params);
350
351    /* MIA is missing in action */
352    CU_ASSERT_EQUAL(!!CAPA(be, CAPA_MIA), 0);
353    params = backend_get_cap_params(be, CAPA_MIA);
354    CU_ASSERT_PTR_NULL(params);
355
356    r = backend_ping(be, NULL);
357    CU_ASSERT_EQUAL(r, 0);
358
359    backend_disconnect(be);
360    free(be);
361#undef CAPA_FOO
362#undef CAPA_BAZ
363#undef CAPA_QUUX
364#undef CAPA_FNORD
365#undef CAPA_CAPITALS
366#undef CAPA_MIA
367}
368
369/*
370 * Test parsing capabilities in multi-line format
371 */
372static void test_multiline_caps(void)
373{
374    default_conditions();
375    server_state->config.caps_one_per_line = 1;
376    test_prot.u.std.capa_cmd.formatflags = CAPAF_ONE_PER_LINE|CAPAF_SKIP_FIRST_WORD;
377    caps_common();
378}
379
380/*
381 * Test parsing capabilities in one-line format
382 */
383static void test_oneline_caps(void)
384{
385    default_conditions();
386    server_state->config.caps_one_per_line = 0;
387    test_prot.u.std.capa_cmd.formatflags = CAPAF_MANY_PER_LINE;
388    caps_common();
389}
390
391#ifdef HAVE_SSL
392/*
393 * Test STARTTLS
394 */
395static void test_starttls(void)
396{
397    struct backend *be;
398    const char *auth_status = NULL;
399    char *mechs;
400    int r;
401
402    default_conditions();
403    server_state->config.sasl_plain = 1;
404    server_state->config.starttls = 1;
405
406    be = backend_connect(NULL, HOST, &test_prot,
407                         USERID, callbacks, &auth_status, /*fd*/-1);
408    CU_ASSERT_PTR_NOT_NULL_FATAL(be);
409    CU_ASSERT_EQUAL(server_state->is_connected, 1);
410    CU_ASSERT_EQUAL(server_state->is_authenticated, 1);
411    CU_ASSERT_EQUAL(server_state->is_tls, 1);
412
413    mechs = backend_get_cap_params(be, CAPA_AUTH);
414    CU_ASSERT_STRING_EQUAL(mechs, "PLAIN");
415    free(mechs);
416
417    CU_ASSERT(CAPA(be, CAPA_STARTTLS))
418
419    r = backend_ping(be, NULL);
420    CU_ASSERT_EQUAL(r, 0);
421
422    backend_disconnect(be);
423    free(be);
424}
425#else
426/*
427 * cunit.pl doesn't process C macros, so it expects this to exist
428 * regardless of the state of HAVE_SSL
429 */
430static void test_starttls(void) { }
431#endif
432
433/* TODO: test UNIX socket comms too */
434/* TODO: test IPv6 socket comms too */
435/* TODO: test connect() timeout */
436
437/* ====================================================================== */
438
439/*
440 * Allocate, and return a mapped pointer to, an anonymous page in
441 * memory (a non-heap non-text page with no backing file) which will be
442 * shared across fork().  The page is assumed to be zeroed.
443 */
444static void *get_anonymous_page(void)
445{
446    void *page;
447    size_t pagesize = getpagesize();
448
449#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
450    /* FreeBSD */
451#define MAP_ANONYMOUS MAP_ANON
452#endif
453
454#ifdef MAP_ANONYMOUS
455    /* Linux and Solaris */
456    page = mmap(NULL, pagesize, PROT_READ|PROT_WRITE,
457                MAP_ANONYMOUS|MAP_SHARED, -1, 0);
458    if (page == MAP_FAILED) {
459        perror("mmap");
460        return NULL;
461    }
462#else
463    int fd;
464
465    fd = open("/dev/zero", O_RDWR);
466    if (fd < 0) {
467        perror("/dev/zero");
468        return NULL;
469    }
470    page = mmap(NULL, pagesize, PROT_READ|PROT_WRITE,
471                MAP_SHARED, fd, 0);
472    if (page == MAP_FAILED) {
473        perror("mmap");
474        page = NULL;
475    }
476    close(fd);
477#endif
478
479    return page;
480}
481
482/*
483 * Free an anonymous page returned from get_anonymous_page()
484 */
485static void free_anonymous_page(void *page)
486{
487    munmap(page, getpagesize());
488}
489
490/*
491 * Create a bound and listening TCP4 server socket, bound to the IPv4
492 * loopback address and a port chosen by the kernel.
493 *
494 * Returns: socket, or -1 on error.  *@portp is filled with the port
495 * number.
496 */
497static int create_server_socket(int *portp)
498{
499    int sock;
500    struct sockaddr_in sin;
501    int r;
502
503    sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
504    if (sock < 0) {
505        perror("socket(TCP)");
506        return -1;
507    }
508
509    memset(&sin, 0, sizeof(sin));
510    sin.sin_family = AF_INET;
511    sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
512    if (*portp)
513        sin.sin_port = htons(*portp);
514
515    r = bind(sock, (struct sockaddr *)&sin, sizeof(sin));
516    if (r < 0) {
517        perror("bind");
518        goto error;
519    }
520
521    r = listen(sock, 5);
522    if (r < 0) {
523        perror("listen");
524        goto error;
525    }
526
527    if (!*portp) {
528        socklen_t len = sizeof(sin);
529        r = getsockname(sock, (struct sockaddr *)&sin, &len);
530        if (r < 0) {
531            perror("getsockname");
532            goto error;
533        }
534        if (len != sizeof(sin) || sin.sin_family != AF_INET) {
535            fprintf(stderr, "Bad address from getsockname()\n");
536            goto error;
537        }
538        *portp = ntohs(sin.sin_port);
539    }
540
541    return sock;
542
543error:
544    close(sock);
545    return -1;
546}
547
548/*
549 * Block until an incoming connection is received, then establish the
550 * connection and initialise per-connection state.  On success, set
551 * @state->is_connected.
552 *
553 * Returns 0 on success, -1 on failure (errors indicate some resource
554 * limitation in the server, not any behaviour of the client, and
555 * should be fatal).
556 */
557static int server_accept(struct server_state *state)
558{
559    int conn_sock;
560    int r;
561
562    if (verbose > 1)
563        fprintf(stderr, "Server waiting for connection\n");
564
565    conn_sock = accept(state->rend_sock, NULL, NULL);
566    if (conn_sock < 0) {
567        perror("accept");
568        return -1;
569    }
570    if (verbose > 1)
571        fprintf(stderr, "Server accepted connection\n");
572
573    state->in = prot_new(conn_sock, /*read*/0);
574    state->out = prot_new(dup(conn_sock), /*write*/1);
575    if (!state->in || !state->out) {
576        perror("prot_new");
577        return -1;
578    }
579
580    r = sasl_server_new(SASLSERVICE, HOST,
581                        /*user_realm*/NULL, /*iplocalport*/NULL,
582                        /*ipremoteport*/NULL, /*callbacks*/NULL,
583                        /*flags*/0, &state->saslconn);
584    if (r != SASL_OK) {
585        fprintf(stderr, "sasl_server_new() failed with error %d\n", r);
586        return -1;
587    }
588
589    state->is_authenticated = 0;
590    state->is_connected = 1;
591    state->is_tls = 0;
592#ifdef HAVE_SSL
593    state->tls_conn = NULL;
594#endif
595
596    return 0;
597}
598
599/*
600 * Handle the end of a connection, by cleaning up all the per-connection
601 * state.  In particular, clears @state->is_connected.
602 */
603static void server_unaccept(struct server_state *state)
604{
605    prot_free(state->in);
606    state->in = NULL;
607    prot_free(state->out);
608    state->out = NULL;
609    sasl_dispose(&state->saslconn);
610    state->is_connected = 0;
611    state->is_authenticated = 0;
612    state->is_tls = 0;
613#ifdef HAVE_SSL
614    if (state->tls_conn) {
615        tls_reset_servertls(&state->tls_conn);
616        state->tls_conn = NULL;
617    }
618#endif
619}
620
621/*
622 * Main routine for pushing text back to the client.
623 */
624static void server_printf(struct server_state *, const char *fmt, ...)
625    __attribute__((format(printf,2,3)));
626
627static void server_printf(struct server_state *state, const char *fmt, ...)
628{
629    va_list args;
630    int r;
631    char buf[2048];
632
633    if (verbose > 1) {
634        va_start(args, fmt);
635        fprintf(stderr, "S: ");
636        vfprintf(stderr, fmt, args);
637        va_end(args);
638    }
639
640    va_start(args, fmt);
641    r = vsnprintf(buf, sizeof(buf), fmt, args);
642    assert(r < (int)sizeof(buf));
643    va_end(args);
644
645    r = prot_write(state->out, buf, strlen(buf));
646    assert(r >= 0);
647}
648
649/*
650 * Flush any pending output back to the client.
651 */
652static void server_flush(struct server_state *state)
653{
654    prot_flush(state->out);
655}
656
657/*
658 * Get a line of text from the client.  Blocks until
659 * a while line is available.
660 *
661 * Returns: 0 on success, -1 on error or end of file.
662 */
663static int server_getline(struct server_state *state,
664                          char *buf, int maxlen)
665{
666    if (!prot_fgets(buf, maxlen, state->in))
667        return -1;
668    if (verbose > 1)
669        fprintf(stderr, "C: %s\n", buf);
670    return 0;
671}
672
673/*
674 * Emit to the client a banner including the server's capability
675 * strings.  This might be in response to the client connecting, or to
676 * the client issuing the XXCAPABILITY command.  Various flags in
677 * @state->config control which of the magical caps known by the
678 * backend.c client code are reported.  Some other test capabilities are
679 * always reported.  Depending on @state->config.caps_one_per_line, the
680 * caps are listed in a multi-line format, one cap per line (LMTP, POP3,
681 * ManageSieve, and sync style) or a single-line, many caps per line
682 * format (IMAP style).
683 */
684static void server_emit_caps(struct server_state *state)
685{
686    const char *saslmechs = NULL;
687    char *p;
688    char *b;
689    int n = 0;
690    const char *name;
691    const char *words[256];     /* array of:
692                                 * NAME, VALUE, VALUE, NULL,
693                                 * NAME, VALUE, NULL,
694                                 * NULL */
695    char line[1024];
696    static const char banner[] = "Toy Test server v0.0.1";
697
698    /*
699     * The ccSASL cap reports a list of SASL mechanism names.
700     * Note that we suppress them all if STARTTLS is enabled.
701     */
702    if (!state->config.starttls || state->is_tls) {
703        int got_login = 0;
704        int got_plain = 0;
705        int got_digestmd5 = 0;
706
707        /* First see what mechanisms SASL has; no point reporting
708         * mechanisms which aren't actually available. */
709        sasl_listmech(state->saslconn, /*user*/NULL, /*prefix*/"",
710                      /*sep*/" ", /*suffix*/"", &saslmechs, /*plen*/NULL,
711                      /*pcount*/NULL);
712        b = xstrdup(saslmechs);
713        /* Build our own list of mechanisms, which is the intersection
714         * of the ones configured in SASL and the ones our server config
715         * allows us. */
716        words[n++] = "ccSASL";
717        for (p = strtok(b, " ") ; p ; p = strtok(NULL, " ")) {
718            if (!strcasecmp(p, "LOGIN") && state->config.sasl_login) {
719                words[n++] = "LOGIN";
720                got_login = 1;
721            }
722            if (!strcasecmp(p, "LOGIN") && state->config.sasl_plain) {
723                words[n++] = "PLAIN";
724                got_plain = 1;
725            }
726            if (!strcasecmp(p, "DIGEST-MD5") && state->config.sasl_digestmd5) {
727                words[n++] = "DIGEST-MD5";
728                got_digestmd5 = 1;
729            }
730        }
731        words[n++] = NULL;
732        free(b);
733
734        if (state->config.sasl_login && !got_login)
735            fprintf(stderr, "Server failed to find requested "
736                            "SASL mechanism \"LOGIN\"\n");
737        if (state->config.sasl_plain && !got_plain)
738            fprintf(stderr, "Server failed to find requested "
739                            "SASL mechanism \"PLAIN\"\n");
740        if (state->config.sasl_digestmd5 && !got_digestmd5)
741            fprintf(stderr, "Server failed to find requested "
742                            "SASL mechanism \"DIGEST-MD5\"\n");
743    }
744
745    /*
746     * The ccSTARTTLS cap reports the ability to do STARTTLS
747     */
748    if (state->config.starttls) {
749        words[n++] = "ccSTARTTLS";
750        words[n++] = NULL;
751    }
752
753    /*
754     * The ccCOMPRESS=DEFLATE cap reports the ability to do Deflate
755     * compression.
756     */
757    if (state->config.deflate) {
758        words[n++] = "ccCOMPRESS";
759        words[n++] = "DEFLATE";
760        words[n++] = NULL;
761    }
762
763    /*
764     * Various test capabilities; the test code in the client knows what
765     * to expect for these.
766     */
767    words[n++] = "FOO";
768    words[n++] = "BAR";
769    words[n++] = NULL;
770
771    words[n++] = "BAZ";
772    words[n++] = NULL;
773
774    words[n++] = "QUUX";
775    words[n++] = "FOONLY";
776    words[n++] = "FMEH";
777    words[n++] = NULL;
778
779    words[n++] = "FNORD";
780    words[n++] = "BOO";
781    words[n++] = "GNERGH";
782    words[n++] = NULL;
783
784    words[n++] = "cApItAls";
785    words[n++] = "Oslo";
786    words[n++] = NULL;
787
788    words[n++] = NULL;
789
790    /*
791     * Finally we have all the capability names and values, now
792     * pull them out of words[] and emit lines to the client.
793     */
794    n = 0;
795    if (state->config.caps_one_per_line) {
796        /* Multi-line, one cap and all its values on each line (LMTP,
797         * ManageSieve, POP3 and sync style) */
798        while ((name = words[n++])) {
799            line[0] = '\0';
800            for ( ; words[n] ; n++) {
801                strcat(line, " ");
802                strcat(line, words[n]);
803            }
804            n++;
805            server_printf(state, "* %s%s\r\n", name, line);
806        }
807        server_printf(state, "OK %s\r\n", banner);
808    } else {
809        /* One-line, NAME=VALUE pairs inside IMAP-style response code */
810        line[0] = '\0';
811        while ((name = words[n++])) {
812            if (words[n]) {
813                for ( ; words[n] ; n++) {
814                    strcat(line, " ");
815                    strcat(line, name);
816                    strcat(line, "=");
817                    strcat(line, words[n]);
818                }
819            } else {
820                strcat(line, " ");
821                strcat(line, name);
822            }
823            n++;
824        }
825        server_printf(state, "OK [CAPABILITY%s] %s\r\n", line, banner);
826    }
827    server_flush(state);
828}
829
830/*
831 * Handle the XXAUTHENTICATE command from the client.  The 1st and 2nd
832 * arguments to the command on wire are passed as @mech and
833 * @initial_in_b64.  Handles any SASL conversation with the client (e.g.
834 * server challenges and client responses), and telling the client the
835 * final status.  If successful, sets @state->is_authenticated.
836 */
837static void cmd_authenticate(struct server_state *state,
838                             const char *mech, const char *initial_in_b64)
839{
840    char *word;
841    const char *server_out;
842    unsigned int server_out_len;
843    int r;
844    unsigned int buflen = 0;
845    char buf[21848];
846    static const char sep[] = " \t\r\n";
847
848    if (!mech)
849        goto badsyntax;
850
851    if (initial_in_b64) {
852        r = sasl_decode64(initial_in_b64, strlen(initial_in_b64),
853                          buf, sizeof(buf), &buflen);
854        if (r != SASL_OK)
855            goto badsyntax;
856    } else {
857        buflen = 0;
858    }
859
860    server_out = NULL;
861    server_out_len = 0;
862    r = sasl_server_start(state->saslconn, mech,
863                          buf, buflen,
864                          &server_out, &server_out_len);
865
866    while (r == SASL_CONTINUE) {
867        if (server_out_len)
868            sasl_encode64(server_out, server_out_len, buf, sizeof(buf), NULL);
869        else
870            strcpy(buf, "=");
871        server_printf(state, "+ %s\n", buf);
872        server_flush(state);
873
874        if (server_getline(state, buf, sizeof(buf)) < 0) {
875            fprintf(stderr, "No response to AUTH challenge\n");
876            return;
877        }
878        word = strtok(buf, sep);
879        if (word) {
880            r = sasl_decode64(word, strlen(word), buf, sizeof(buf), &buflen);
881            if (r != SASL_OK)
882                goto badsyntax;
883        } else {
884            buflen = 0;
885        }
886
887        server_out = NULL;
888        server_out_len = 0;
889        r = sasl_server_step(state->saslconn, buf, buflen,
890                             &server_out, &server_out_len);
891    }
892
893    if (r != SASL_OK) {
894        server_printf(state, "BAD sasl error code %d\r\n", r);
895        server_flush(state);
896        return;
897    }
898
899    state->is_authenticated = 1;
900    server_printf(state, "OK\r\n");
901    server_flush(state);
902    return;
903
904badsyntax:
905    server_printf(state, "BAD syntax\r\n");
906    server_flush(state);
907}
908
909/*
910 * Handle the XXSTARTTLS command from the client.
911 * If successful, sets @state->is_tls.
912 */
913static void cmd_starttls(struct server_state *state)
914{
915#ifdef HAVE_SSL
916    int r;
917    char *auth_id = NULL;
918    SSL *tls_conn = NULL;
919    sasl_ssf_t ssf;
920
921    r = tls_init_serverengine("backend_test", /*verifydepth*/5,
922                              /*askcert*/1, NULL);
923    if (r < 0) {
924        server_printf(state, "BAD error initializing TLS\r\n");
925        server_flush(state);
926        return;
927    }
928
929    server_printf(state, "OK Begin TLS negotiation now\r\n");
930    /* must flush our buffers before starting tls */
931    server_flush(state);
932
933    r = tls_start_servertls(/*readfd*/state->in->fd,
934                            /*writefd*/state->out->fd,
935                            /*timeout_sec*/3000,
936                            (int *)&ssf, &auth_id, &tls_conn);
937    if (r < 0) {
938        server_printf(state, "BAD STARTTLS negotiation failed\r\n");
939        server_flush(state);
940        return;
941    }
942
943    /* tell SASL about the negotiated layer */
944    r = sasl_setprop(state->saslconn, SASL_SSF_EXTERNAL, &ssf);
945    if (r != SASL_OK) {
946        server_printf(state, "BAD sasl_setprop(SASL_SSF_EXTERNAL) "
947                              "failed: cmd_starttls()\r\n");
948        server_flush(state);
949        return;
950    }
951
952    r = sasl_setprop(state->saslconn, SASL_AUTH_EXTERNAL, auth_id);
953    if (r != SASL_OK) {
954        server_printf(state, "BAD sasl_setprop(SASL_AUTH_EXTERNAL) "
955                             "failed: cmd_starttls()\r\n");
956        server_flush(state);
957        return;
958    }
959
960    /* tell the prot layer about our new layers */
961    prot_settls(state->in, tls_conn);
962    prot_settls(state->out, tls_conn);
963    state->tls_conn = tls_conn;
964    state->is_tls = 1;
965#else
966    server_printf(state, "BAD this server is not built with SSL\r\n");
967    server_flush(state);
968#endif
969}
970
971/*
972 * Server main command loop for a single connection.  Blocks waiting for
973 * commands from the client, and handles each command.  Returns when the
974 * client sends a XXLOGOUT command or drops the connection.
975 */
976static void server_cmdloop(struct server_state *state)
977{
978    char *command;
979    char buf[1024];
980    static const char sep[] = " \t\r\n";
981
982    /* Emit the connection banner */
983    server_emit_caps(state);
984
985    while (server_getline(state, buf, sizeof(buf)) == 0) {
986        command = strtok(buf, sep);
987
988        if (!command) {
989            server_printf(state, "BAD command\r\n");
990            server_flush(state);
991            continue;
992        }
993
994        if (!strcasecmp(command, "XXLOGOUT")) {
995            /* graceful disconnect */
996            server_printf(state, "OK\r\n");
997            server_flush(state);
998            break;
999        } else if (!strcasecmp(command, "XXNOOP")) {
1000            server_printf(state, "OK\r\n");
1001            server_flush(state);
1002        } else if (!strcasecmp(command, "XXCAPABILITY")) {
1003            server_emit_caps(state);
1004        } else if (!strcasecmp(command, "XXAUTHENTICATE")) {
1005            char *mech = strtok(NULL, sep);
1006            char *initial_in = strtok(NULL, sep);
1007            cmd_authenticate(state, mech, initial_in);
1008        } else if (!strcasecmp(command, "XXSTARTTLS")) {
1009            cmd_starttls(state);
1010        } else {
1011            server_printf(state, "BAD command\r\n");
1012            server_flush(state);
1013        }
1014    }
1015}
1016
1017/*
1018 * Start the server.  Forks a child process, which does some once-off
1019 * server initialisation and then enters a service loop.  The service
1020 * loop is very dumb: it serves a single connection at a time, with no
1021 * forking or threading or async request handling.
1022 *
1023 * Returns: pid of the child process, or -1 on error.
1024 */
1025static pid_t server_start(struct server_state *state)
1026{
1027    pid_t pid;
1028
1029    pid = fork();
1030    if (pid < 0) {
1031        perror("fork");
1032        return -1;
1033    }
1034    if (pid) {
1035        /* We are the parent process.  We don't need to wait for the
1036         * child to finish starting, as we have a bound socket we can
1037         * connect immediately (it just might take the child a little
1038         * while to respond to the first command). */
1039        close(state->rend_sock);
1040        return pid;
1041    }
1042    /* We are the child process. */
1043
1044    if (init_sasl(/*client*/0) < 0)
1045        exit(1);
1046
1047    /* Main connection loop.  This is a toy, single-threaded server
1048     * which handles one connection at a time. */
1049    for (;;)
1050    {
1051        /* Wait for a connection */
1052        if (server_accept(state) < 0)
1053            exit(1);
1054
1055        /* Run the main command loop for this connection */
1056        server_cmdloop(state);
1057
1058        /* Forget the per-connection state */
1059        server_unaccept(state);
1060    }
1061}
1062
1063/*
1064 * Shut down the server, given it's PID.  It's pretty simple and brutal:
1065 * we just send it SIGTERM and wait to reap it.
1066 */
1067static void server_shutdown(pid_t pid)
1068{
1069    int status;
1070    pid_t r;
1071
1072    kill(pid, SIGTERM);
1073    for (;;) {
1074        r = waitpid(pid, &status, 0);
1075        if (r < 0) {
1076            if (errno == EINTR)
1077                continue;
1078            if (errno != ESRCH)
1079                perror("waitpid");
1080            return;
1081        }
1082        if (r == pid)
1083            return; /* ok, killed it */
1084        fprintf(stderr, "WTF? waitpid(%d) = %d?\n", pid, r);
1085    }
1086}
1087
1088/*
1089 * Callback which we use to fakes server-side password checking for
1090 * those mechanisms where a plaintext password from the client is
1091 * available to the SASL server code, i.e. PLAIN and LOGIN.
1092 */
1093static int server_checkpass(sasl_conn_t *conn __attribute__((unused)),
1094                            void *context __attribute__((unused)),
1095                            const char *user, const char *pass,
1096                            unsigned passlen,
1097                            struct propctx *propctx __attribute__((unused)))
1098{
1099    if (strcmp(user, USERID))
1100        return SASL_NOUSER;
1101    if (passlen != strlen(PASSWORD) ||
1102        strncmp(pass, PASSWORD, passlen))
1103        return SASL_BADAUTH;
1104    return SASL_OK;
1105}
1106
1107/* The auxprop API changed in commit a321326, Oct 2008 */
1108#if SASL_AUXPROP_PLUG_VERSION > 4
1109#   define AUXPROP_RTYPE int
1110#   define AUXPROP_RET 0
1111#else
1112#   define AUXPROP_RTYPE void
1113#   define AUXPROP_RET
1114#endif
1115
1116/*
1117 * Callback which we use to fake out server-side password checking for
1118 * those mechanisms where a plaintext password from the client is *NOT*
1119 * available, and so the SASL server code needs to retrieve the password
1120 * from an auxprop plugin.
1121 */
1122static AUXPROP_RTYPE server_auxprop_lookup(void *glob_context __attribute__((unused)),
1123                                 sasl_server_params_t *sparams,
1124                                 unsigned flags __attribute__((unused)),
1125                                 const char *user,
1126                                 unsigned ulen)
1127{
1128    const struct propval *prop;
1129
1130    if (ulen != strlen(USERID) ||
1131        strncmp(user, USERID, ulen))
1132        return AUXPROP_RET;
1133
1134    prop = sparams->utils->prop_get(sparams->propctx);
1135    if (!prop)
1136        return AUXPROP_RET;
1137    for ( ; prop->name ; prop++) {
1138        if (!strcmp(prop->name, "*userPassword") ||
1139            !strcmp(prop->name, "*cmusaslsecretDIGEST-MD5")) {
1140            if (prop->values)
1141                sparams->utils->prop_erase(sparams->propctx, prop->name);
1142            sparams->utils->prop_set(sparams->propctx, prop->name,
1143                                     PASSWORD, strlen(PASSWORD));
1144        }
1145    }
1146
1147    return AUXPROP_RET;
1148}
1149
1150/*
1151 * Helps create a fake "auxiliary propert plugin" for the SASL library,
1152 * which is how we hook into the DIGEST-MD5 mechanism when it wants to
1153 * get a plaintext password to check aginst the hash received from the
1154 * client.
1155 */
1156static int server_auxprop_init(const sasl_utils_t *utils __attribute__((unused)),
1157                               int max_version,
1158                               int *out_version,
1159                               sasl_auxprop_plug_t **plugp,
1160                               const char *plugname __attribute__((unused)))
1161{
1162    static sasl_auxprop_plug_t plug = {
1163        .features = 0,
1164        .auxprop_lookup = server_auxprop_lookup,
1165        .name = "testserver-hack"
1166    };
1167
1168    *out_version = max_version;
1169    *plugp = &plug;
1170    return SASL_OK;
1171}
1172
1173/*
1174 * Initialise the SASL library, client or server.
1175 *
1176 * Returns: 0 on success, -ve on error.
1177 */
1178static int init_sasl(int isclient)
1179{
1180    int r;
1181
1182    /* set the SASL allocation functions */
1183    sasl_set_alloc((sasl_malloc_t *)xmalloc,
1184                   (sasl_calloc_t *)xcalloc,
1185                   (sasl_realloc_t *)xrealloc,
1186                   (sasl_free_t *)free);
1187
1188    /* set the SASL mutex functions */
1189    sasl_set_mutex(cyrus_mutex_alloc, cyrus_mutex_lock,
1190                   cyrus_mutex_unlock, cyrus_mutex_free);
1191
1192    if (isclient) {
1193        static const struct sasl_callback client_cb[] = {
1194            { SASL_CB_LIST_END, NULL, NULL }
1195        };
1196
1197        /* load the SASL client plugins */
1198        if (sasl_client_init(client_cb)) {
1199            fprintf(stderr, "could not init sasl client\n");
1200            return -1;
1201        }
1202
1203        callbacks = mysasl_callbacks(USERID, USERID, NULL, PASSWORD);
1204        if (!callbacks)
1205            return -1;
1206    } else {
1207        static const struct sasl_callback server_cb[] = {
1208            { SASL_CB_SERVER_USERDB_CHECKPASS, (void *)&server_checkpass, NULL },
1209            { SASL_CB_LIST_END, NULL, NULL }
1210        };
1211
1212        /* load the SASL server plugins */
1213        r = sasl_server_init(server_cb, "testserver");
1214        if (r != SASL_OK) {
1215            fprintf(stderr, "sasl_server_init() failed: error %d\n", r);
1216            return -1;
1217        }
1218
1219        sasl_auxprop_add_plugin("testserver", server_auxprop_init);
1220    }
1221
1222    return 0;
1223}
1224
1225/* ====================================================================== */
1226
1227static struct server_state *server_state;
1228static pid_t server_pid;
1229static int sasl_initialised;
1230static int old_session_timeout;
1231static char *old_config_dir;
1232static char *old_tls_ca_file;
1233static char *old_tls_cert_file;
1234static char *old_tls_key_file;
1235
1236/*
1237 * Test suite setup function.  Sets up the global
1238 * server_state page.  Forks a server process which listens
1239 * on a TCP port, and writes the port number into default_service[]
1240 * so that the client code can connect to it.
1241 */
1242static int set_up(void)
1243{
1244    int rend_sock, port = 0;
1245    char *sr;
1246    static char cwd[PATH_MAX];
1247
1248    if (verbose > 1)
1249        fprintf(stderr, "Starting server!\n");
1250
1251    sr = getcwd(cwd, sizeof(cwd));
1252    if (!sr) {
1253        fprintf(stderr, "getcwd() failed: %s\n", strerror(errno));
1254        return -1;
1255    }
1256
1257    old_config_dir = (char *)config_dir;
1258    config_dir = xstrdup(cwd);
1259
1260    old_tls_ca_file = (char *)imapopts[IMAPOPT_TLS_SERVER_CA_FILE].val.s;
1261    imapopts[IMAPOPT_TLS_SERVER_CA_FILE].val.s =
1262            strconcat(cwd, "/cacert.pem", (char *)NULL);
1263
1264    old_tls_cert_file = (char *)imapopts[IMAPOPT_TLS_SERVER_CERT].val.s;
1265    imapopts[IMAPOPT_TLS_SERVER_CERT].val.s =
1266            strconcat(cwd, "/cert.pem", (char *)NULL);
1267
1268    old_tls_key_file = (char *)imapopts[IMAPOPT_TLS_SERVER_KEY].val.s;
1269    imapopts[IMAPOPT_TLS_SERVER_KEY].val.s =
1270            strconcat(cwd, "/key.pem", (char *)NULL);
1271
1272    /* disable SSL session caching */
1273    old_session_timeout = imapopts[IMAPOPT_TLS_SESSION_TIMEOUT].val.i;
1274    imapopts[IMAPOPT_TLS_SESSION_TIMEOUT].val.i = 0;
1275
1276    rend_sock = create_server_socket(&port);
1277    if (rend_sock < 0)
1278        return -1;
1279
1280    server_state = get_anonymous_page();
1281    if (!server_state) {
1282        close(rend_sock);
1283        return -1;
1284    }
1285    server_state->rend_sock = rend_sock;
1286
1287    if (verbose > 1)
1288        fprintf(stderr, "Bound to port tcp/%u\n", port);
1289    snprintf(default_service, sizeof(default_service), "%d", port);
1290
1291    server_pid = server_start(server_state);
1292    if (server_pid < 0)
1293        return -1;
1294
1295    /*
1296     * Initialise the SASL library in the client process.  This init
1297     * function can actually be called multiple times in the lifetime of
1298     * a test runner process, which is perhaps surprising, but we have
1299     * to deal with it.  We do however seem to be able to get away with
1300     * initialising SASL multiple times as long as we call sasl_done()
1301     * after we're done.
1302     */
1303    if (init_sasl(/*client*/1) < 0)
1304        return -1;
1305    sasl_initialised = 1;
1306
1307    return 0;
1308}
1309
1310/*
1311 * Test suite teardown function.  Shuts down the server and
1312 * removes the global server_state page.
1313 */
1314static int tear_down(void)
1315{
1316    if (verbose > 1)
1317        fprintf(stderr, "Cleaning up server! NOT.\n");
1318    if (server_pid > 1)
1319        server_shutdown(server_pid);
1320    if (server_state)
1321        free_anonymous_page(server_state);
1322    if (callbacks) {
1323        free_callbacks(callbacks);
1324        callbacks = NULL;
1325    }
1326    if (sasl_initialised) {
1327        sasl_done();
1328        sasl_initialised = 0;
1329    }
1330
1331    free((char *)config_dir);
1332    config_dir = old_config_dir;
1333
1334    imapopts[IMAPOPT_TLS_SESSION_TIMEOUT].val.i = old_session_timeout;
1335
1336    free((char *)imapopts[IMAPOPT_TLS_SERVER_CA_FILE].val.s);
1337    imapopts[IMAPOPT_TLS_SERVER_CA_FILE].val.s = old_tls_ca_file;
1338
1339    free((char *)imapopts[IMAPOPT_TLS_SERVER_CERT].val.s);
1340    imapopts[IMAPOPT_TLS_SERVER_CERT].val.s = old_tls_cert_file;
1341
1342    free((char *)imapopts[IMAPOPT_TLS_SERVER_KEY].val.s);
1343    imapopts[IMAPOPT_TLS_SERVER_KEY].val.s = old_tls_key_file;
1344
1345    return 0;
1346}
1347
1348/* ====================================================================== */
1349/* vim: set ft=c: */
1350