Logo Search packages:      
Sourcecode: cyrus-sasl2 version File versions  Download package

testsuite.c

/* testsuite.c -- Stress the library a little
 * Rob Siemborski
 * Tim Martin
 * $Id: testsuite.c,v 1.38 2004/06/30 19:40:27 rjs3 Exp $
 */
/* 
 * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The name "Carnegie Mellon University" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For permission or any other legal
 *    details, please contact  
 *      Office of Technology Transfer
 *      Carnegie Mellon University
 *      5000 Forbes Avenue
 *      Pittsburgh, PA  15213-3890
 *      (412) 268-4387, fax: (412) 268-7395
 *      tech-transfer@andrew.cmu.edu
 *
 * 4. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by Computing Services
 *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
 *
 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * To create a krb5 srvtab file given a krb4 srvtab
 *
 * ~/> ktutil
 * ktutil:  rst /etc/srvtab
 * ktutil:  wkt /etc/krb5.keytab
 * ktutil:  q
 */

/*
 * TODO [FIXME]:
 *  put in alloc() routines that fail occasionally.
 */

#include <config.h>

#include <stdio.h>
#include <stdlib.h>

#include <sasl.h>
#include <saslutil.h>
#include <prop.h>
#include <md5global.h>
#include <md5.h>
#include <hmac-md5.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <time.h>
#include <string.h>
#include <ctype.h>
#ifndef WIN32
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/file.h>
#endif

#ifdef WIN32
__declspec(dllimport) char *optarg;
__declspec(dllimport) int optind;
__declspec(dllimport) int getsubopt(char **optionp, char * const *tokens, char **valuep);
#endif

char myhostname[1024+1];
#define MAX_STEPS 7 /* maximum steps any mechanism takes */

#define CLIENT_TO_SERVER "Hello. Here is some stuff"

#define REALLY_LONG_LENGTH  32000
#define REALLY_LONG_BACKOFF  2000

const char *username = "rjs3";
const char *nonexistant_username = "ABCDEFGHIJ";
const char *authname = "rjs3";
const char *proxyasname = "rjs3proxy";
const char *password = "1234";
const char *cu_plugin = "INTERNAL";
char other_result[1024];

int proxyflag = 0;

static const char *gssapi_service = "host";

/* our types of failures */
typedef enum {
    NOTHING = 0,
    ONEBYTE_RANDOM,           /* replace one byte with something random */
    ONEBYTE_NULL,       /* replace one byte with a null */
    ONEBYTE_QUOTES,           /* replace one byte with a double quote 
                           (try to fuck with digest-md5) */
    ONLY_ONE_BYTE,            /* send only one byte */
    ADDSOME,                  /* add some random bytes onto the end */
    SHORTEN,                  /* shorten the string some */
    REASONABLE_RANDOM,        /* send same size but random */
    REALLYBIG,                /* send something absurdly large all random */
    NEGATIVE_LENGTH,          /* send negative length */
    CORRUPT_SIZE        /* keep this one last */
} corrupt_type_t;

const char *corrupt_types[] = {
    "NOTHING",
    "ONEBYTE_RANDOM",
    "ONEBYTE_NULL",
    "ONEBYTE_QUOTES",
    "ONLY_ONE_BYTE",
    "ADDSOME",
    "SHORTEN",
    "REASONABLE_RANDOM",
    "REALLYBIG",
    "NEGATIVE_LENGTH",
    "CORRUPT_SIZE"
};

void fatal(char *str)
{
    printf("Failed with: %s\n",str);
    exit(3);
}

/* interactions we support */
static sasl_callback_t client_interactions[] = {
  {
    SASL_CB_GETREALM, NULL, NULL
  }, {
    SASL_CB_USER, NULL, NULL
  }, {
    SASL_CB_AUTHNAME, NULL, NULL
  }, {
    SASL_CB_PASS, NULL, NULL    
  }, {
    SASL_CB_LIST_END, NULL, NULL
  }
};

int test_getrealm(void *context __attribute__((unused)), int id,
              const char **availrealms __attribute__((unused)),
              const char **result) 
{
    if(id != SASL_CB_GETREALM) fatal("test_getrealm not looking for realm");
    if(!result) return SASL_BADPARAM;
    *result = myhostname;
    return SASL_OK;
}

/* yeah, yeah, this leaks memory, but we don't really care in
 * the testsuite */
int test_getsecret(sasl_conn_t *conn __attribute__((unused)),
               void *context __attribute__((unused)), int id,
               sasl_secret_t **psecret) 
{
    if(id != SASL_CB_PASS) fatal("test_getsecret not looking for pass");
    if(!psecret) return SASL_BADPARAM;

    *psecret = malloc(sizeof(sasl_secret_t) + strlen(password));
    (*psecret)->len = strlen(password);
    strcpy((*psecret)->data, password);

    return SASL_OK;
}

int test_getsimple(void *context __attribute__((unused)), int id,
               const char **result, unsigned *len) 
{
    if(!result) return SASL_BADPARAM;
    
    if (id==SASL_CB_USER && proxyflag == 0) {
      *result=(char *) username;
    } else if (id==SASL_CB_USER && proxyflag == 1) {
      *result=(char *) proxyasname;
    } else if (id==SASL_CB_AUTHNAME) {
      *result=(char *) authname;
    } else {
      printf("I want %d\n", id);
      fatal("unknown callback in test_getsimple");
    }

    if(len) *len = strlen(*result);
    return SASL_OK;
}

/* callbacks we support */
static sasl_callback_t client_callbacks[] = {
  {
    SASL_CB_GETREALM, test_getrealm, NULL
  }, {
    SASL_CB_USER, test_getsimple, NULL
  }, {
    SASL_CB_AUTHNAME, test_getsimple, NULL
  }, {
    SASL_CB_PASS, test_getsecret, NULL    
  }, {
    SASL_CB_LIST_END, NULL, NULL
  }
};

typedef void *foreach_t(char *mech, void *rock);

typedef struct tosend_s {
    corrupt_type_t type; /* type of corruption to make */
    int step; /* step it should send bogus data on */
    sasl_callback_t *client_callbacks; /* which client callbacks to use */
} tosend_t;

typedef struct mem_info 
{
    void *addr;
    size_t size;
    struct mem_info *next;
} mem_info_t;

int DETAILED_MEMORY_DEBUGGING = 0;

mem_info_t *head = NULL;

#ifndef WITH_DMALLOC

void *test_malloc(size_t size)
{
    void *out;
    mem_info_t *new_data;
    
    out = malloc(size);

    if(DETAILED_MEMORY_DEBUGGING)
      fprintf(stderr, "  %X = malloc(%d)\n", (unsigned)out, size);
    
    if(out) {
      new_data = malloc(sizeof(mem_info_t));
      if(!new_data) return out;

      new_data->addr = out;
      new_data->size = size;
      new_data->next = head;
      head = new_data;
    }

    return out;
}

void *test_realloc(void *ptr, size_t size)
{
    void *out;
    mem_info_t **prev, *cur;
    
    out = realloc(ptr, size);
    
    if(DETAILED_MEMORY_DEBUGGING)
      fprintf(stderr, "  %X = realloc(%X,%d)\n",
            (unsigned)out, (unsigned)ptr, size);

    prev = &head; cur = head;
    
    while(cur) {
      if(cur->addr == ptr) {
          cur->addr = out;
          cur->size = size;
          return out;
      }
      
      prev = &cur->next;
      cur = cur->next;
    }
    
    if(DETAILED_MEMORY_DEBUGGING && cur == NULL) {
      fprintf(stderr,
            "  MEM WARNING: reallocing something we never allocated!\n");

      cur = malloc(sizeof(mem_info_t));
      if(!cur) return out;

      cur->addr = out;
      cur->size = size;
      cur->next = head;
      head = cur;
    }

    return out;
}

void *test_calloc(size_t nmemb, size_t size)
{
    void *out;
    mem_info_t *new_data;
    
    out = calloc(nmemb, size);

    if(DETAILED_MEMORY_DEBUGGING)    
      fprintf(stderr, "  %X = calloc(%d, %d)\n",
            (unsigned)out, nmemb, size);

    if(out) {
      new_data = malloc(sizeof(mem_info_t));
      if(!new_data) return out;

      new_data->addr = out;
      new_data->size = size;
      new_data->next = head;
      head = new_data;
    }
    
    return out;
}


void test_free(void *ptr)
{
    mem_info_t **prev, *cur;

    if(DETAILED_MEMORY_DEBUGGING)
      fprintf(stderr, "  free(%X)\n",
            (unsigned)ptr);

    prev = &head; cur = head;
    
    while(cur) {
      if(cur->addr == ptr) {
          *prev = cur->next;
          free(cur);
          break;
      }
      
      prev = &cur->next;
      cur = cur->next;
    }

    if(DETAILED_MEMORY_DEBUGGING && cur == NULL) {
      fprintf(stderr,
            "  MEM WARNING: Freeing something we never allocated!\n");
    }

    free(ptr);
}

#endif /* WITH_DMALLOC */

int mem_stat() 
{
#ifndef WITH_DMALLOC
    mem_info_t *cur;
    size_t n;
    unsigned char *data;

    if(!head) {
      fprintf(stderr, "  All memory accounted for!\n");
      return SASL_OK;
    }
    
    fprintf(stderr, "  Currently Still Allocated:\n");
    for(cur = head; cur; cur = cur->next) {
      fprintf(stderr, "    %X (%5d)\t", (unsigned)cur->addr, cur->size);
      for(data = (unsigned char *) cur->addr,
            n = 0; n < (cur->size > 12 ? 12 : cur->size); n++) {
          if (isprint((int) data[n]))
            fprintf(stderr, "'%c' ", (char) data[n]);
          else
            fprintf(stderr, "%02X  ", data[n] & 0xff);
      }
      if (n < cur->size)
          fprintf(stderr, "...");
      fprintf(stderr, "\n");
    }
    return SASL_FAIL;
#else
    return SASL_OK;
#endif /* WITH_DMALLOC */
}


/************* End Memory Allocation functions ******/

/* my mutex functions */
int g_mutex_cnt = 0;

typedef struct my_mutex_s {

    int num;
    int val;
    
} my_mutex_t;

void *my_mutex_new(void)
{
    my_mutex_t *ret = (my_mutex_t *)malloc(sizeof(my_mutex_t));
    ret->num = g_mutex_cnt;
    g_mutex_cnt++;

    ret->val = 0;

    return ret;
}

int my_mutex_lock(my_mutex_t *m)
{
    if (m->val != 0)
    {
      fatal("Trying to lock a mutex already locked [single-threaded app]");
    }

    m->val = 1;
    return SASL_OK;
}

int my_mutex_unlock(my_mutex_t *m)
{
    if (m->val != 1)
    {
      fatal("Unlocking mutex that isn't locked");
    }

    m->val = 0;

    return SASL_OK;
}

void my_mutex_dispose(my_mutex_t *m)
{
    if (m==NULL) return;

    free(m);

    return;
}

int good_getopt(void *context __attribute__((unused)), 
            const char *plugin_name __attribute__((unused)), 
            const char *option,
            const char **result,
            unsigned *len)
{
    if (strcmp(option,"pwcheck_method")==0)
    {
      *result = "auxprop";
      if (len)
          *len = strlen("auxprop");
      return SASL_OK;
    } else if (!strcmp(option, "auxprop_plugin")) {
      *result = "sasldb";
      if (len)
          *len = strlen("sasldb");
      return SASL_OK;
    } else if (!strcmp(option, "sasldb_path")) {
      *result = "./sasldb";
      if (len)
          *len = strlen("./sasldb");
      return SASL_OK;
    } else if (!strcmp(option, "canon_user_plugin")) {
      *result = cu_plugin;
      if (len)
          *len = strlen(*result);
      return SASL_OK;
    }

    return SASL_FAIL;
}

static struct sasl_callback goodsasl_cb[] = {
    { SASL_CB_GETOPT, &good_getopt, NULL },
    { SASL_CB_LIST_END, NULL, NULL }
};

int givebadpath(void * context __attribute__((unused)), 
            char ** path)
{
    int lup;
    *path = malloc(10000);    
    strcpy(*path,"/tmp/is/not/valid/path/");

    for (lup = 0;lup<1000;lup++)
      strcat(*path,"a/");

    return SASL_OK;
}

static struct sasl_callback withbadpathsasl_cb[] = {
    { SASL_CB_GETPATH, &givebadpath, NULL },
    { SASL_CB_LIST_END, NULL, NULL }
};

int giveokpath(void * context __attribute__((unused)), 
            const char ** path)
{
    *path = "/tmp/";

    return SASL_OK;
}

static struct sasl_callback withokpathsasl_cb[] = {
    { SASL_CB_GETPATH, &giveokpath, NULL },
    { SASL_CB_LIST_END, NULL, NULL }
};

static struct sasl_callback emptysasl_cb[] = {
    { SASL_CB_LIST_END, NULL, NULL }
};

static int proxy_authproc(sasl_conn_t *conn,
                     void *context __attribute__((unused)),
                     const char *requested_user,
                     unsigned rlen __attribute__((unused)),
                     const char *auth_identity,
                     unsigned alen __attribute__((unused)),
                     const char *def_realm __attribute__((unused)),
                     unsigned urlen __attribute__((unused)),
                     struct propctx *propctx __attribute__((unused)))
{
    if(!strcmp(auth_identity, authname)
       && !strcmp(requested_user, proxyasname)) return SASL_OK;

    if(!strcmp(auth_identity, requested_user)) {
      printf("Warning: Authenticated name but DID NOT proxy (%s/%s)\n",
             requested_user, auth_identity);
      return SASL_OK;
    }

    sasl_seterror(conn, SASL_NOLOG, "authorization failed: %s by %s",
              requested_user, auth_identity);
    return SASL_BADAUTH;
}

static struct sasl_callback goodsaslproxy_cb[] = {
    { SASL_CB_PROXY_POLICY, &proxy_authproc, NULL },
    { SASL_CB_GETOPT, &good_getopt, NULL },
    { SASL_CB_LIST_END, NULL, NULL }
};

char really_long_string[REALLY_LONG_LENGTH];

/*
 * Setup some things for test
 */
void init(unsigned int seed)
{
    int lup;
    int result;

    srand(seed);    

    for (lup=0;lup<REALLY_LONG_LENGTH;lup++)
      really_long_string[lup] = '0' + (rand() % 10);

    really_long_string[REALLY_LONG_LENGTH - rand() % REALLY_LONG_BACKOFF] = '\0';

    result = gethostname(myhostname, sizeof(myhostname)-1);
    if (result == -1) fatal("gethostname");

    sasl_set_mutex((sasl_mutex_alloc_t *) &my_mutex_new,
               (sasl_mutex_lock_t *) &my_mutex_lock,
               (sasl_mutex_unlock_t *) &my_mutex_unlock,
               (sasl_mutex_free_t *) &my_mutex_dispose);

#ifndef WITH_DMALLOC
    sasl_set_alloc((sasl_malloc_t *)test_malloc,
               (sasl_calloc_t *)test_calloc,
               (sasl_realloc_t *)test_realloc,
               (sasl_free_t *)test_free);
#endif

}

/*
 * Tests for sasl_server_init
 */

void test_init(void)
{
    int result;

    /* sasl_done() before anything */
    sasl_done();
    if(mem_stat() != SASL_OK) fatal("memory error after sasl_done test");

    /* Try passing appname a really long string (just see if it crashes it)*/

    result = sasl_server_init(NULL,really_long_string);
    sasl_done();
    if(mem_stat() != SASL_OK) fatal("memory error after long appname test");

    /* this calls sasl_done when it wasn't inited */
    sasl_done();
    if(mem_stat() != SASL_OK) fatal("memory error after null appname test");

    /* try giving it a different path for where the plugins are */
    result = sasl_server_init(withokpathsasl_cb, "Tester");
    if (result!=SASL_OK) fatal("Didn't deal with ok callback path very well");
    sasl_done();
    if(mem_stat() != SASL_OK) fatal("memory error after callback path test");

    /* and the client */
    result = sasl_client_init(withokpathsasl_cb);

    if (result!=SASL_OK)
      fatal("Client didn't deal with ok callback path very well");
    sasl_done();
    if(mem_stat() != SASL_OK) fatal("memory error after client test");

#if defined(DO_DLOPEN) && (defined(PIC) || (!defined(PIC) && defined(TRY_DLOPEN_WHEN_STATIC)))
    /* try giving it an invalid path for where the plugins are */
    result = sasl_server_init(withbadpathsasl_cb, NULL);
    if (result==SASL_OK) fatal("Allowed invalid path");
    sasl_done();
    if(mem_stat() != SASL_OK) fatal("memory error after bad path test");
#endif

    /* and the client - xxx is this necessary?*/
#if 0
    result = sasl_client_init(withbadpathsasl_cb);

    if (result==SASL_OK)
      fatal("Client allowed invalid path");
    sasl_done();
#endif

    /* Now try to break all the sasl_server_* functions for not returning
       SASL_NOTINIT */

    if(sasl_global_listmech())
      fatal("sasl_global_listmech did not return NULL with no library initialized");

    if(sasl_server_new(NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL)
       != SASL_NOTINIT)
      fatal("sasl_server_new did not return SASL_NOTINIT");

/* Can't check this validly without a server conn, so this would be
   a hard one to tickle anyway */
#if 0    
    if(sasl_listmech(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)
       != SASL_NOTINIT)
      fatal("sasl_listmech did not return SASL_NOTINIT");
#endif

    if(sasl_server_start(NULL, NULL, NULL, 0, NULL, NULL)
       != SASL_NOTINIT)
      fatal("sasl_server_start did not return SASL_NOTINIT");

    if(sasl_server_step(NULL, NULL, 0, NULL, NULL)
       != SASL_NOTINIT)
      fatal("sasl_server_step did not return SASL_NOTINIT");
    
#ifdef DO_SASL_CHECKAPOP
    if(sasl_checkapop(NULL, NULL, 0, NULL, 0)
       != SASL_NOTINIT)
      fatal("sasl_checkapop did not return SASL_NOTINIT");
#endif    

    if(sasl_checkpass(NULL, NULL, 0, NULL, 0)
       != SASL_NOTINIT)
      fatal("sasl_checkpass did not return SASL_NOTINIT");
    
    if(sasl_user_exists(NULL, NULL, NULL, NULL)
       != SASL_NOTINIT)
      fatal("sasl_user_exists did not return SASL_NOTINIT");

    if(sasl_setpass(NULL, NULL, NULL, 0, NULL, 0, 0)
       != SASL_NOTINIT)
      fatal("sasl_setpass did not return SASL_NOTINIT");

    /* And sasl_client_*... */

    if(sasl_client_new(NULL, NULL, NULL, NULL, NULL, 0, NULL)
       != SASL_NOTINIT)
      fatal("sasl_client_new did not return SASL_NOTINIT");

    if(sasl_client_start(NULL, NULL, NULL, NULL, NULL, NULL)
       != SASL_NOTINIT)
      fatal("sasl_client_start did not return SASL_NOTINIT");

    if(sasl_client_step(NULL, NULL, 0, NULL, NULL, NULL)
       != SASL_NOTINIT)
      fatal("sasl_client_step did not return SASL_NOTINIT");

}


/* 
 * Tests sasl_listmech command
 */

void test_listmech(void)
{
    sasl_conn_t *saslconn, *cconn;
    int result;
    const char *str = NULL;
    unsigned int plen;
    unsigned lup, flag, pcount;
    const char **list;

    /* test without initializing library */
    result = sasl_listmech(NULL, /* conn */
                     NULL,
                     "[",
                     "-",
                     "]",
                     &str,
                     NULL,
                     NULL);

    /*    printf("List mech without library initialized: %s\n",sasl_errstring(result,NULL,NULL));*/
    if (result == SASL_OK) fatal("Failed sasl_listmech() with NULL saslconn");

    if (sasl_server_init(emptysasl_cb,"TestSuite")!=SASL_OK)
      fatal("can't sasl_server_init");
    if (sasl_client_init(client_interactions)!=SASL_OK)
      fatal("can't sasl_client_init");

    if (sasl_server_new("rcmd", myhostname,
                  NULL, NULL, NULL, NULL, 0, 
                  &saslconn) != SASL_OK)
      fatal("can't sasl_server_new");

    if (sasl_setprop(saslconn, SASL_AUTH_EXTERNAL, authname)!=SASL_OK)
      fatal("sasl_setprop(SASL_AUTH_EXTERNAL) failed");

    /* client new connection */
    if (sasl_client_new("rcmd",
                  myhostname,
                  NULL, NULL, NULL,
                  0,
                  &cconn)!= SASL_OK)
      fatal("sasl_client_new() failure");

    if (sasl_setprop(cconn, SASL_AUTH_EXTERNAL, authname)!=SASL_OK)
      fatal("sasl_setprop(SASL_AUTH_EXTERNAL) failed");

    /* try both sides */
    list = sasl_global_listmech();
    if(!list) fatal("sasl_global_listmech failure");

    printf(" [");
    flag = 0;
    for(lup = 0; list[lup]; lup++) {
      if(flag) printf(",");
      else flag++;
      printf("%s",list[lup]);
    }
    printf("]\n");

    /* try client side */
    result = sasl_listmech(cconn,
                     NULL,
                     " [",
                     ",",
                     "]",
                     &str,
                     NULL,
                     NULL);
    if(result == SASL_OK) {
      printf("Client mechlist:\n%s\n", str);
    } else {
      fatal("client side sasl_listmech failed");
    }

    /* Test with really long user */

    result = sasl_listmech(saslconn,
                     really_long_string,
                     "[",
                     "-",
                     "]",
                     &str,
                     NULL,
                     NULL);

    if (result != SASL_OK) fatal("Failed sasl_listmech() with long user");

    if (str[0]!='[') fatal("Failed sasl_listmech() with long user (didn't start with '['");

    result = sasl_listmech(saslconn,
                     really_long_string,
                     "[",
                     ",",
                     "]",
                     &str,
                     NULL,
                     NULL);

    if (result != SASL_OK) fatal("Failed sasl_listmech() with different params");

    printf("We have the following mechs:\n %s\n",str);

    /* Test with really long prefix */

    result = sasl_listmech(saslconn,
                     NULL,
                     really_long_string,
                     "-",
                     "]",
                     &str,
                     NULL,
                     NULL);

    if (result != SASL_OK) fatal("failed sasl_listmech() with long prefix");

    if (str[0]!=really_long_string[0]) fatal("failed sasl_listmech() with long prefix (str is suspect)");

    /* Test with really long suffix */

    result = sasl_listmech(saslconn,
                     NULL,
                     "[",
                     "-",
                     really_long_string,
                     &str,
                     NULL,
                     NULL);

    if (result != SASL_OK) fatal("Failed sasl_listmech() with long suffix");

    /* Test with really long seperator */

    result = sasl_listmech(saslconn,
                     NULL,
                     "[",
                     really_long_string,
                     "]",
                     &str,
                     NULL,
                     NULL);

    if (result != SASL_OK) fatal("Failed sasl_listmech() with long seperator");

    /* Test contents of output string is accurate */
    result = sasl_listmech(saslconn,
                     NULL,
                     "",
                     "%",
                     "",
                     &str,
                     &plen,
                     &pcount);

    if (result != SASL_OK) fatal("Failed sasl_listmech()");

    if (strlen(str)!=plen) fatal("Length of string doesn't match what we were told");
    
    for (lup=0;lup<plen;lup++)
      if (str[lup]=='%')
          pcount--;

    pcount--;
    if (pcount != 0)
    {
      printf("mechanism string = %s\n",str);
      printf("Mechs left = %d\n",pcount);
      fatal("Number of mechs received doesn't match what we were told");
    }

    /* Call sasl done then make sure listmech doesn't work anymore */
    sasl_dispose(&saslconn);
    sasl_dispose(&cconn);
    sasl_done();

    result = sasl_listmech(saslconn,
                     NULL,
                     "[",
                     "-",
                     "]",
                     &str,
                     NULL,
                     NULL);

    if (result == SASL_OK) fatal("Called sasl_done but listmech still works\n");

}

/*
 * Perform tests on the random utilities
 */

void test_random(void)
{
    sasl_rand_t *rpool;
    int lup;
    char buf[4096];

    /* make sure it works consistantly */

    for (lup = 0;lup<10;lup++)
    {
      if (sasl_randcreate(&rpool) != SASL_OK) fatal("sasl_randcreate failed");
      sasl_randfree(&rpool);
    }

    /* try seeding w/o calling rand_create first */
    rpool = NULL;
    sasl_randseed(rpool, "seed", 4);

    /* try seeding with bad values */
    sasl_randcreate(&rpool);
    sasl_randseed(rpool, "seed", 0);
    sasl_randseed(rpool, NULL, 0);
    sasl_randseed(rpool, NULL, 4);    
    sasl_randfree(&rpool);

    /* try churning with bad values */
    sasl_randcreate(&rpool);
    sasl_churn(rpool, "seed", 0);
    sasl_churn(rpool, NULL, 0);
    sasl_churn(rpool, NULL, 4);    
    sasl_randfree(&rpool);

    /* try seeding with a lot of crap */
    sasl_randcreate(&rpool);
    
    for (lup=0;lup<(int) sizeof(buf);lup++)
    {
      buf[lup] = (rand() % 256);    
    }
    sasl_randseed(rpool, buf, sizeof(buf));
    sasl_churn(rpool, buf, sizeof(buf));

    sasl_randfree(&rpool);
}

/*
 * Test SASL base64 conversion routines
 */

void test_64(void)
{
    char orig[4096];
    char enc[8192];
    unsigned encsize;
    int lup;

    /* make random crap and see if enc->dec produces same as original */
    for (lup=0;lup<(int) sizeof(orig);lup++)
      orig[lup] = (char) (rand() % 256);
    
    if (sasl_encode64(orig, sizeof(orig), enc, sizeof(enc), &encsize)!=SASL_OK) 
      fatal("encode64 failed when we didn't expect it to");
    
    if (sasl_decode64(enc, encsize, enc, 8192, &encsize)!=SASL_OK)
      fatal("decode failed when didn't expect");
    
    if (encsize != sizeof(orig)) fatal("Now has different size");
    
    for (lup=0;lup<(int) sizeof(orig);lup++)
      if (enc[lup] != orig[lup])
          fatal("enc64->dec64 doesn't match");

    /* try to get a SASL_BUFOVER */
    
    if (sasl_encode64(orig, sizeof(orig)-1, enc, 10, &encsize)!=SASL_BUFOVER)
      fatal("Expected SASL_BUFOVER");


    /* pass some bad params */
    if (sasl_encode64(NULL, 10, enc, sizeof(enc), &encsize)==SASL_OK)
      fatal("Said ok to null data");

    if (sasl_encode64(orig, sizeof(orig), enc, sizeof(enc), NULL)!=SASL_OK)
      fatal("Didn't allow null return size");
    
}

/* This isn't complete, but then, what in the testsuite is? */
void test_props(void) 
{
    int result;
    struct propval foobar[3];
    struct propctx *ctx, *dupctx;

    const char *requests[] = {
      "userPassword",
        "userName",
      "homeDirectory",
        "uidNumber",
        "gidNumber",
        NULL
    };

    const char *more_requests[] = {
      "a",
      "b",
      "c",
      "defghijklmnop"
    };

    const char *short_requests[] = {
      "userPassword",
      "userName",
      "BAD",
      NULL
    };

    ctx = prop_new(2);
    if(!ctx) {
      fatal("no new prop context");
    }

    if(prop_request(NULL, requests) == SASL_OK)
      fatal("prop_request w/NULL context succeeded");
    if(prop_request(ctx, NULL) == SASL_OK)
      fatal("prop_request w/NULL request list succeeded");
    
    result = prop_request(ctx, requests);
    if(result != SASL_OK)
      fatal("prop request failed");

    /* set some values */
    prop_set(ctx, "uidNumber", really_long_string, 0);
    prop_set(ctx, "userPassword", "pw1", 0);
    prop_set(ctx, "userPassword", "pw2", 0);
    prop_set(ctx, "userName", "rjs3", 0);
    prop_set(ctx, NULL, "tmartin", 0);

    /* and request some more (this resets values) */
    prop_request(ctx, more_requests);

    /* and set some more... */
    prop_set(ctx, "c", really_long_string, 0);
    prop_set(ctx, "b", really_long_string, 0);
    prop_set(ctx, "userPassword", "pw1b", 0);
    prop_set(ctx, "userPassword", "pw2b", 0);
    prop_set(ctx, "userName", "rjs3b", 0);
    prop_set(ctx, NULL, "tmartinagain", 0);

    if(prop_set(ctx, "gah", "ack", 0) == SASL_OK) {
      printf("setting bad property name succeeded\n");
      exit(1);
    }

    result = prop_getnames(ctx, short_requests, foobar);
    if(result < 0)
      fatal("prop_getnames failed");

    if(strcmp(foobar[0].name, short_requests[0]))
      fatal("prop_getnames item 0 wrong name");
    if(strcmp(foobar[1].name, short_requests[1]))
      fatal("prop_getnames item 1 wrong name");
    if(foobar[2].name)
      fatal("prop_getnames returned an item 2");

    if(strcmp(foobar[0].values[0], "pw1b"))
      fatal("prop_getnames item 1a wrong value");
    if(strcmp(foobar[0].values[1], "pw2b"))
      fatal("prop_getnames item 1b wrong value");
    if(strcmp(foobar[1].values[0], "rjs3b"))
      fatal("prop_getnames item 2a wrong value");
    if(strcmp(foobar[1].values[1], "tmartinagain"))
      fatal("prop_getnames item 2b wrong value");

    result = prop_dup(ctx, &dupctx);
    if(result != SASL_OK)
      fatal("could not duplicate");

    prop_clear(ctx, 1);
    
    result = prop_getnames(ctx, short_requests, foobar);
    if(result < 0)
      fatal("prop_getnames failed second time");

    if(foobar[0].name)
      fatal("it appears that prop_clear failed");
    
    result = prop_getnames(dupctx, short_requests, foobar);
    if(result < 0)
      fatal("prop_getnames failed second time");

    if(!foobar[0].name)
      fatal("prop_clear appears to have affected dup'd context");
    
    prop_clear(dupctx, 0);

    result = prop_getnames(dupctx, short_requests, foobar);
    if(result < 0)
      fatal("prop_getnames failed second time");

    if(!foobar[0].name || strcmp(foobar[0].name, short_requests[0]))
      fatal("prop_clear appears to have cleared too much");

    prop_dispose(&ctx);
    prop_dispose(&dupctx);
    if(ctx != NULL)
      fatal("ctx not null after prop_dispose");
}

void interaction (int id, const char *prompt,
              const char **tresult, unsigned int *tlen)
{
    if (id==SASL_CB_PASS) {
      *tresult=(char *) password;
    } else if (id==SASL_CB_USER && proxyflag == 0) {
      *tresult=(char *) username;
    } else if (id==SASL_CB_USER && proxyflag == 1) {
      *tresult=(char *) proxyasname;
    } else if (id==SASL_CB_AUTHNAME) {
      *tresult=(char *) authname;
    } else if ((id==SASL_CB_GETREALM)) {
      *tresult=(char *) myhostname;
    } else {
      int c;
      
      printf("%s: ",prompt);
      fgets(other_result, sizeof(other_result) - 1, stdin);
      c = strlen(other_result);
      other_result[c - 1] = '\0';
      *tresult=other_result;
    }

    *tlen=strlen(*tresult);
}

void fillin_correctly(sasl_interact_t *tlist)
{
  while (tlist->id!=SASL_CB_LIST_END)
  {
    interaction(tlist->id, tlist->prompt,
            (void *) &(tlist->result), 
            &(tlist->len));
    tlist++;
  }

}

const sasl_security_properties_t security_props = {
    0,
    256,
    8192,
    0,
    NULL,
    NULL        
};

void set_properties(sasl_conn_t *conn, const sasl_security_properties_t *props)
{
    if(!props) {
      if (sasl_setprop(conn, SASL_SEC_PROPS, &security_props) != SASL_OK)
          fatal("sasl_setprop() failed - default properties");
    } else {
            if (sasl_setprop(conn, SASL_SEC_PROPS, props) != SASL_OK)
          fatal("sasl_setprop() failed");
    }

    if (sasl_setprop(conn, SASL_AUTH_EXTERNAL, authname)!=SASL_OK)
      fatal("sasl_setprop(SASL_AUTH_EXTERNAL) failed");
}

/*
 * This corrupts the string for us
 */
void corrupt(corrupt_type_t type, char *in, int inlen,
           char **out, unsigned *outlen)
{
    unsigned lup;
    

    switch (type)
      {
      case NOTHING:
          *out = in;
          *outlen = inlen;
          break;
      case ONEBYTE_RANDOM: /* corrupt one byte */

          if (inlen>0)
            in[ (rand() % inlen) ] = (char) (rand() % 256);

          *out = in;
          *outlen = inlen;

          break;
      case ONEBYTE_NULL:
          if (inlen>0)
            in[ (rand() % inlen) ] = '\0';

          *out = in;
          *outlen = inlen;
          break;
      case ONEBYTE_QUOTES:
          if (inlen>0)
            in[ (rand() % inlen) ] = '"';

          *out = in;
          *outlen = inlen;
          break;
      case ONLY_ONE_BYTE:
          *out = (char *) malloc(1);
          (*out)[0] = (char) (rand() % 256);
          *outlen = 1;
          break;

      case ADDSOME:
          *outlen = inlen+ (rand() % 100);
          *out = (char *) malloc(*outlen);
          memcpy( *out, in, inlen);
          
          for (lup=inlen;lup<*outlen;lup++)
            (*out)[lup] = (char) (rand() %256);

          break;

      case SHORTEN:
          if (inlen > 0)
          {
            *outlen = 0;
            while(*outlen == 0)
                *outlen = (rand() % inlen);
            *out = (char *) malloc(*outlen);
            memcpy(*out, in, *outlen);
          } else {
            *outlen = inlen;
            *out = in;
          }
          break;
      case REASONABLE_RANDOM:
          *outlen = inlen;
          if(*outlen != 0)
            *out = (char *) malloc(*outlen);
          else
            *out = malloc(1);

          for (lup=0;lup<*outlen;lup++)
            (*out)[lup] = (char) (rand() % 256);

          break;
      case REALLYBIG:
          *outlen = rand() % 50000;
          *out = (char *) malloc( *outlen);
          
          for (lup=0;lup<*outlen;lup++)
            (*out)[lup] = (char) (rand() % 256);
          
          break;
      case NEGATIVE_LENGTH:

          *out = in;
          if (inlen == 0) inlen = 10;
          *outlen = -1 * (rand() % inlen);
          
          break;
      default:
          fatal("Invalid corruption type");
          break;
      }
}

void sendbadsecond(char *mech, void *rock)
{
    int result, need_another_client = 0;
    sasl_conn_t *saslconn;
    sasl_conn_t *clientconn;
    const char *out, *dec, *out2;
    char *tmp;
    unsigned outlen, declen, outlen2;
    sasl_interact_t *client_interact=NULL;
    const char *mechusing;
    const char *service = "rcmd";
    int mystep = 0; /* what step in the authentication are we on */
    int mayfail = 0; /* we did some corruption earlier so it's likely to fail now */
    
    tosend_t *send = (tosend_t *)rock;

    struct sockaddr_in addr;
    struct hostent *hp;
    char buf[8192];
    int reauth = 1;

    printf("%s --> start\n",mech);
    
    if (strcmp(mech,"GSSAPI")==0) service = gssapi_service;

    if (sasl_client_init(client_interactions)!=SASL_OK) fatal("Unable to init client");

    if (sasl_server_init(goodsasl_cb,"TestSuite")!=SASL_OK) fatal("unable to init server");

    if ((hp = gethostbyname(myhostname)) == NULL) {
      perror("gethostbyname");
      fatal("can't gethostbyname");
    }

    addr.sin_family = 0;
    memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
    addr.sin_port = htons(0);

 reauth: /* loop back for reauth testing */
    sprintf(buf,"%s;%d", inet_ntoa(addr.sin_addr), 23);

    /* client new connection */
    if (sasl_client_new(service,
                  myhostname,
                  buf, buf, NULL,
                  0,
                  &clientconn)!= SASL_OK) fatal("sasl_client_new() failure");

    set_properties(clientconn, NULL);

    if (sasl_server_new(service, myhostname, NULL,
                  buf, buf, NULL, 0, 
                  &saslconn) != SASL_OK) {
      fatal("can't sasl_server_new");
    }
    set_properties(saslconn, NULL);

    do {
      result = sasl_client_start(clientconn, mech,
                           &client_interact,
                           &out, &outlen,
                           &mechusing);

      if (result == SASL_INTERACT) fillin_correctly(client_interact);
      else if(result == SASL_CONTINUE) need_another_client = 1;
      else if(result == SASL_OK) need_another_client = 0;
    } while (result == SASL_INTERACT);
                         
    if (result < 0)
    {
      printf("%s - \n",sasl_errdetail(clientconn));
      fatal("sasl_client_start() error");
    }

    if (mystep == send->step && outlen)
    {
      memcpy(buf, out, outlen);
      corrupt(send->type, buf, outlen, &tmp, &outlen);
      out = tmp;
      mayfail = 1;
    }

    result = sasl_server_start(saslconn,
                         mech,
                         out,
                         outlen,
                         &out,
                         &outlen);

    if (mayfail)
    {
      if (result >= SASL_OK)
          printf("WARNING: We did a corruption but it still worked\n");
      else {
          goto done;
      }
    } else {
      if (result < 0) 
      {
          printf("%s\n",sasl_errstring(result,NULL,NULL));
          fatal("sasl_server_start() error");
      }
    }
    mystep++;

    while (result == SASL_CONTINUE) {

      if (mystep == send->step)
      {
          memcpy(buf,out,outlen);
          corrupt(send->type, buf, outlen, &tmp, &outlen);
          out = tmp;
          mayfail = 1;
      }

      do {
          result = sasl_client_step(clientconn,
                              out, outlen,
                              &client_interact,
                              &out2, &outlen2);
          
          if (result == SASL_INTERACT)
            fillin_correctly(client_interact);
          else if (result == SASL_CONTINUE)
            need_another_client = 1;
          else if (result == SASL_OK)
            need_another_client = 0;
      } while (result == SASL_INTERACT);

      if (mayfail == 1)
      {
          if (result >= 0)
            printf("WARNING: We did a corruption but it still worked\n");
          else {
            goto done;
          }
      } else {
          if (result < 0) 
          {
            printf("%s\n",sasl_errstring(result,NULL,NULL));
            fatal("sasl_client_step() error");
          }
      }
      out=out2;
      outlen=outlen2;
      mystep++;

      if (mystep == send->step)
      {
          memcpy(buf, out, outlen);
          corrupt(send->type, buf, outlen, &tmp, &outlen);
          out = tmp;
          mayfail = 1;
      }

      result = sasl_server_step(saslconn,
                          out,
                          outlen,
                          &out,
                          &outlen);
      
      if (mayfail == 1)
      {
          if (result >= 0)
            printf("WARNING: We did a corruption but it still worked\n");
          else {
            goto done;
          }
      } else {
          if (result < 0) 
          {
            printf("%s\n",sasl_errstring(result,NULL,NULL));
            fatal("sasl_server_step() error");
          }
      }
      mystep++;

    }

    if(need_another_client) {
      result = sasl_client_step(clientconn,
                          out, outlen,
                          &client_interact,
                          &out2, &outlen2);
      if(result != SASL_OK)
          fatal("client was not ok on last server step");
    }
    
    if (reauth) {
      sasl_dispose(&clientconn);
      sasl_dispose(&saslconn);
 
      reauth = 0;
      goto reauth;
    }

    /* client to server */
    result = sasl_encode(clientconn, CLIENT_TO_SERVER,
                   strlen(CLIENT_TO_SERVER), &out, &outlen);
    if (result != SASL_OK) fatal("Error encoding");

    if (mystep == send->step)
    {
      memcpy(buf, out, outlen);
      corrupt(send->type, buf, outlen, &tmp, &outlen);
      out = tmp;
      mayfail = 1;
    }

    result = sasl_decode(saslconn, out, outlen, &dec, &declen);

    if (mayfail == 1)
    {
      if (result >= 0)
          printf("WARNING: We did a corruption but it still worked\n");
      else {
          goto done;
      }
    } else {
      if (result < 0) 
      {
          printf("%s\n",sasl_errstring(result,NULL,NULL));
          fatal("sasl_decode() failure");
      }
    }
    mystep++;

    /* no need to do other direction since symetric */

    /* Just verify oparams */
    if(sasl_getprop(saslconn, SASL_USERNAME, (const void **)&out)
       != SASL_OK) {
      fatal("couldn't get server username");
      goto done;
    }
    if(sasl_getprop(clientconn, SASL_USERNAME, (const void **)&out2)
       != SASL_OK) {
      fatal("couldn't get client username");
      goto done;
    }
    if(strcmp(out,out2)) {
      fatal("client username does not match server username");
      goto done;
    }

    printf("%s --> %s (as %s)\n",mech,sasl_errstring(result,NULL,NULL),out);

 done:
    sasl_dispose(&clientconn);
    sasl_dispose(&saslconn);
    sasl_done();
}

/* Authenticate two sasl_conn_t's to eachother, validly.
 * used to test the security layer */
int doauth(char *mech, sasl_conn_t **server_conn, sasl_conn_t **client_conn,
           const sasl_security_properties_t *props,
         sasl_callback_t *c_calls, int fail_ok)
{
    int result, need_another_client = 0;
    sasl_conn_t *saslconn;
    sasl_conn_t *clientconn;
    const char *out, *out2;
    unsigned outlen, outlen2;
    sasl_interact_t *client_interact=NULL;
    const char *mechusing;
    const char *service = "rcmd";
    struct sockaddr_in addr;
    struct hostent *hp;
    char buf[8192];

    if(!server_conn || !client_conn) return SASL_BADPARAM;
    
    if (strcmp(mech,"GSSAPI")==0) service = gssapi_service;

    result = sasl_client_init((c_calls ? c_calls : client_interactions));
    if (result!=SASL_OK) {
      if(!fail_ok) fatal("Unable to init client");
      else return result;
    }

    if(proxyflag == 0) {
        result = sasl_server_init(goodsasl_cb,"TestSuite");
    } else {
        result = sasl_server_init(goodsaslproxy_cb,"TestSuite");
    }
    if(result != SASL_OK) {
       if(!fail_ok) fatal("unable to init server");
       else return result;
    }
          

    if ((hp = gethostbyname(myhostname)) == NULL) {
      perror("gethostbyname");
      if(!fail_ok) fatal("can't gethostbyname");
      else return SASL_FAIL;
    }

    addr.sin_family = 0;
    memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
    addr.sin_port = htons(0);

    sprintf(buf,"%s;%d", inet_ntoa(addr.sin_addr), 0);

    /* client new connection */
    result = sasl_client_new(service,
                       myhostname,
                       buf, buf, NULL,
                       0,
                       &clientconn);
    if(result != SASL_OK) {
      if(!fail_ok) fatal("sasl_client_new() failure");
      else return result;
    }

    /* Set the security properties */
    set_properties(clientconn, props);

    result = sasl_server_new(service, myhostname, NULL,
                       buf, buf, NULL, 0, 
                       &saslconn);
    if(result != SASL_OK) {
      if(!fail_ok) fatal("can't sasl_server_new");
      else return result;
    }
    set_properties(saslconn, props);

    do {
      result = sasl_client_start(clientconn, mech,
                           &client_interact,
                           &out, &outlen,
                           &mechusing);

      if (result == SASL_INTERACT) fillin_correctly(client_interact);
      else if(result == SASL_CONTINUE) need_another_client = 1;
      else if(result == SASL_OK) need_another_client = 0;
    } while (result == SASL_INTERACT);
                         
    if (result < 0)
    {
      if(!fail_ok) fatal("sasl_client_start() error");
      else return result;
    }

    result = sasl_server_start(saslconn,
                         mech,
                         out,
                         outlen,
                         &out,
                         &outlen);

    if (result < 0) 
    {
      if(!fail_ok) fatal("sasl_server_start() error");
      else return result;
    }

    while (result == SASL_CONTINUE) {
      do {
          result = sasl_client_step(clientconn,
                              out, outlen,
                              &client_interact,
                              &out2, &outlen2);
          
          if (result == SASL_INTERACT)
            fillin_correctly(client_interact);
          else if (result == SASL_CONTINUE)
            need_another_client = 1;
          else if (result == SASL_OK)
            need_another_client = 0;
      } while (result == SASL_INTERACT);

      if (result < 0) 
      {
          if(!fail_ok) fatal("sasl_client_step() error");
          else return result;
      }

      out=out2;
      outlen=outlen2;

      result = sasl_server_step(saslconn,
                          out,
                          outlen,
                          &out,
                          &outlen);
      
      if (result < 0) 
      {
          if(!fail_ok) fatal("sasl_server_step() error");
          else return result;
      }

    }

    if(need_another_client) {
      if(!fail_ok) fatal("server-last not allowed, but need another client call");
      else return SASL_BADPROT;
    }

    *server_conn = saslconn;
    *client_conn = clientconn;
    
    return SASL_OK;
}

/* Authenticate two sasl_conn_t's to eachother, validly.
 * without allowing client-send-first */
int doauth_noclientfirst(char *mech, sasl_conn_t **server_conn,
                   sasl_conn_t **client_conn,
                   const sasl_security_properties_t *props,
                   sasl_callback_t *c_calls)
{
    int result, need_another_client = 0;
    sasl_conn_t *saslconn;
    sasl_conn_t *clientconn;
    const char *out, *out2;
    unsigned outlen, outlen2;
    sasl_interact_t *client_interact=NULL;
    const char *mechusing;
    const char *service = "rcmd";

    struct sockaddr_in addr;
    struct hostent *hp;
    char buf[8192];

    if(!server_conn || !client_conn) return SASL_BADPARAM;
    
    if (strcmp(mech,"GSSAPI")==0) service = gssapi_service;


    if (sasl_client_init((c_calls ? c_calls : client_interactions))!=SASL_OK)
      fatal("Unable to init client");

    if (sasl_server_init(goodsasl_cb,"TestSuite")!=SASL_OK)
        fatal("unable to init server");  

    if ((hp = gethostbyname(myhostname)) == NULL) {
      perror("gethostbyname");
      fatal("can't gethostbyname");
    }

    addr.sin_family = 0;
    memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
    addr.sin_port = htons(0);

    sprintf(buf,"%s;%d", inet_ntoa(addr.sin_addr), 0);

    /* client new connection */
    if (sasl_client_new(service,
                  myhostname,
                  buf, buf, NULL,
                  0,
                  &clientconn)!= SASL_OK) fatal("sasl_client_new() failure");

    /* Set the security properties */
    set_properties(clientconn, props);

    if (sasl_server_new(service, myhostname, NULL,
                  buf, buf, NULL, 0, 
                  &saslconn) != SASL_OK) {
      fatal("can't sasl_server_new");
    }
    set_properties(saslconn, props);

    do {
      result = sasl_client_start(clientconn, mech,
                           &client_interact,
                           NULL, NULL,
                           &mechusing);

      if (result == SASL_INTERACT) fillin_correctly(client_interact);
      else if(result == SASL_CONTINUE) need_another_client = 1;
      else if(result == SASL_OK) need_another_client = 0;
    } while (result == SASL_INTERACT);

    if (result < 0)
    {
      fatal("sasl_client_start() error");
    } 

    result = sasl_server_start(saslconn,
                         mech,
                         NULL,
                         0,
                         &out,
                         &outlen);

    if (result < 0) 
    {
      fatal("sasl_server_start() error");
    }

    while (result == SASL_CONTINUE) {
      do {
          result = sasl_client_step(clientconn,
                              out, outlen,
                              &client_interact,
                              &out2, &outlen2);
          
          if (result == SASL_INTERACT)
            fillin_correctly(client_interact);
          else if (result == SASL_CONTINUE)
            need_another_client = 1;
          else if (result == SASL_OK)
            need_another_client = 0;
      } while (result == SASL_INTERACT);

      if (result < 0) 
      {
          fatal("sasl_client_step() error");
      }

      out=out2;
      outlen=outlen2;

      result = sasl_server_step(saslconn,
                          out,
                          outlen,
                          &out,
                          &outlen);
      
      if (result < 0) 
      {
          fatal("sasl_server_step() error");
      }

    }

    if(need_another_client) {
      fatal("server-last not allowed, but need another client call");
    }

    *server_conn = saslconn;
    *client_conn = clientconn;
    
    return SASL_OK;
}

/* Authenticate two sasl_conn_t's to eachother, validly.
 * used to test the security layer */
int doauth_serverlast(char *mech, sasl_conn_t **server_conn,
                  sasl_conn_t **client_conn,
                  const sasl_security_properties_t *props,
                  sasl_callback_t *c_calls)
{
    int result, need_another_client = 0;
    sasl_conn_t *saslconn;
    sasl_conn_t *clientconn;
    const char *out, *out2;
    unsigned outlen, outlen2;
    sasl_interact_t *client_interact=NULL;
    const char *mechusing;
    const char *service = "rcmd";

    struct sockaddr_in addr;
    struct hostent *hp;
    char buf[8192];

    if(!server_conn || !client_conn) return SASL_BADPARAM;
    
    if (strcmp(mech,"GSSAPI")==0) service = gssapi_service;

    if (sasl_client_init((c_calls ? c_calls : client_interactions))!=SASL_OK)
      fatal("unable to init client");

    if (sasl_server_init(goodsasl_cb,"TestSuite")!=SASL_OK)
      fatal("unable to init server");

    if ((hp = gethostbyname(myhostname)) == NULL) {
      perror("gethostbyname");
      fatal("can't gethostbyname");
    }

    addr.sin_family = 0;
    memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
    addr.sin_port = htons(0);

    sprintf(buf,"%s;%d", inet_ntoa(addr.sin_addr), 0);

    /* client new connection */
    if (sasl_client_new(service,
                  myhostname,
                  buf, buf, NULL,
                  SASL_SUCCESS_DATA,
                  &clientconn)!= SASL_OK) fatal("sasl_client_new() failure");

    /* Set the security properties */
    set_properties(clientconn, props);

    if (sasl_server_new(service, myhostname, NULL,
                  buf, buf, NULL, SASL_SUCCESS_DATA, 
                  &saslconn) != SASL_OK) {
      fatal("can't sasl_server_new");
    }
    set_properties(saslconn, props);

    do {
      result = sasl_client_start(clientconn, mech,
                           &client_interact,
                           &out, &outlen,
                           &mechusing);

      if (result == SASL_INTERACT) fillin_correctly(client_interact);
      else if(result == SASL_CONTINUE) need_another_client = 1;
      else if(result == SASL_OK) need_another_client = 0;
    } while (result == SASL_INTERACT);
      

    if (result < 0)
    {
      fatal("sasl_client_start() error");
    }                    

    result = sasl_server_start(saslconn,
                         mech,
                         out,
                         outlen,
                         &out,
                         &outlen);

    if (result < 0) 
    {
      fatal("sasl_server_start() error");
    }

    while (result == SASL_CONTINUE) {
      do {
          result = sasl_client_step(clientconn,
                              out, outlen,
                              &client_interact,
                              &out2, &outlen2);
          
          if (result == SASL_INTERACT)
            fillin_correctly(client_interact);
          else if (result == SASL_CONTINUE)
            need_another_client = 1;
          else if (result == SASL_OK)
            need_another_client = 0;
      } while (result == SASL_INTERACT);

      if (result < 0) 
      {
          fatal("sasl_client_step() error");
      }

      out=out2;
      outlen=outlen2;

      result = sasl_server_step(saslconn,
                          out,
                          outlen,
                          &out,
                          &outlen);
      
      if (result < 0) 
      {
          fatal("sasl_server_step() error");
      }

    }

    if(need_another_client) {
      result = sasl_client_step(clientconn,
                          out, outlen,
                          &client_interact,
                          &out2, &outlen2);
      if(result != SASL_OK)
          fatal("client was not ok on last server step");
    }

    *server_conn = saslconn;
    *client_conn = clientconn;
    
    return SASL_OK;
}

/* Authenticate two sasl_conn_t's to eachother, validly.
 * without allowing client-send-first */
int doauth_noclientfirst_andserverlast(char *mech, sasl_conn_t **server_conn,
                               sasl_conn_t **client_conn,
                               const sasl_security_properties_t *props,
                               sasl_callback_t *c_calls)
{
    int result, need_another_client = 0;
    sasl_conn_t *saslconn;
    sasl_conn_t *clientconn;
    const char *out, *out2;
    unsigned outlen, outlen2;
    sasl_interact_t *client_interact=NULL;
    const char *mechusing;
    const char *service = "rcmd";

    struct sockaddr_in addr;
    struct hostent *hp;
    char buf[8192];

    if(!server_conn || !client_conn) return SASL_BADPARAM;
    
    if (strcmp(mech,"GSSAPI")==0) service = gssapi_service;

    if (sasl_client_init((c_calls ? c_calls : client_interactions))!=SASL_OK)
      fatal("unable to init client");

    if (sasl_server_init(goodsasl_cb,"TestSuite")!=SASL_OK)
      fatal("unable to init server");

    if ((hp = gethostbyname(myhostname)) == NULL) {
      perror("gethostbyname");
      fatal("can't gethostbyname");
    }

    addr.sin_family = 0;
    memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
    addr.sin_port = htons(0);

    sprintf(buf,"%s;%d", inet_ntoa(addr.sin_addr), 0);

    /* client new connection */
    if (sasl_client_new(service,
                  myhostname,
                  buf, buf, NULL,
                  SASL_SUCCESS_DATA,
                  &clientconn)!= SASL_OK) fatal("sasl_client_new() failure");

    /* Set the security properties */
    set_properties(clientconn, props);

    if (sasl_server_new(service, myhostname, NULL,
                  buf, buf, NULL, SASL_SUCCESS_DATA, 
                  &saslconn) != SASL_OK) {
      fatal("can't sasl_server_new");
    }
    set_properties(saslconn, props);

    do {
      result = sasl_client_start(clientconn, mech,
                           &client_interact,
                           NULL, NULL,
                           &mechusing);

      if (result == SASL_INTERACT) fillin_correctly(client_interact);
      else if(result == SASL_CONTINUE) need_another_client = 1;
      else if(result == SASL_OK) need_another_client = 0;
    } while (result == SASL_INTERACT);

    if (result < 0)
    {
      fatal("sasl_client_start() error");
    }                          

    result = sasl_server_start(saslconn,
                         mech,
                         NULL,
                         0,
                         &out,
                         &outlen);

    if (result < 0) 
    {
      fatal("sasl_server_start() error");
    }

    while (result == SASL_CONTINUE) {
      do {
          result = sasl_client_step(clientconn,
                              out, outlen,
                              &client_interact,
                              &out2, &outlen2);
          
          if (result == SASL_INTERACT)
            fillin_correctly(client_interact);
          else if (result == SASL_CONTINUE)
            need_another_client = 1;
          else if (result == SASL_OK)
            need_another_client = 0;
      } while (result == SASL_INTERACT);

      if (result < 0) 
      {
          fatal("sasl_client_step() error");
      }

      out=out2;
      outlen=outlen2;

      result = sasl_server_step(saslconn,
                          out,
                          outlen,
                          &out,
                          &outlen);
      
      if (result < 0) 
      {
          fatal("sasl_server_step() error");
      }

    }

    if(need_another_client) {
      result = sasl_client_step(clientconn,
                          out, outlen,
                          &client_interact,
                          &out2, &outlen2);
      if(result != SASL_OK)
          fatal("client was not ok on last server step");
    }

    *server_conn = saslconn;
    *client_conn = clientconn;
    
    return SASL_OK;
}

void cleanup_auth(sasl_conn_t **client, sasl_conn_t **server) 
{
    sasl_dispose(client);
    sasl_dispose(server);
    sasl_done();
}


const sasl_security_properties_t int_only = {
    0,
    1,
    8192,
    0,
    NULL,
    NULL        
};

const sasl_security_properties_t force_des = {
    0,
    55,
    8192,
    0,
    NULL,
    NULL        
};

const sasl_security_properties_t force_rc4_56 = {
    0,
    56,
    8192,
    0,
    NULL,
    NULL        
};

const sasl_security_properties_t force_3des = {
    0,
    112,
    8192,
    0,
    NULL,
    NULL        
};


const sasl_security_properties_t no_int = {
    2,
    256,
    8192,
    0,
    NULL,
    NULL        
};

const sasl_security_properties_t disable_seclayer = {
    0,
    256,
    0,
    0,
    NULL,
    NULL        
};

void do_proxypolicy_test(char *mech, void *rock __attribute__((unused))) 
{
    sasl_conn_t *sconn, *cconn;
    const char *username;
    
    printf("%s --> start\n", mech);
    proxyflag = 1;
    if(doauth(mech, &sconn, &cconn, &security_props, NULL, 0) != SASL_OK) {
      fatal("doauth failed in do_proxypolicy_test");
    }

    if(sasl_getprop(sconn, SASL_USERNAME, (const void **)&username) != SASL_OK)
    {
      fatal("getprop failed in do_proxypolicy_test");
    }
    
    if(strcmp(username, proxyasname)) {
      printf("Warning: Server Authorization Name != proxyasuser\n");
    }

    cleanup_auth(&cconn, &sconn);
    proxyflag = 0;
    printf("%s --> successful result\n",mech);
}

void test_clientfirst(char *mech, void *rock) 
{
    sasl_conn_t *sconn, *cconn;
    tosend_t *tosend = (tosend_t *)rock;
    
    printf("%s --> start\n", mech);

    /* Basic crash-tests (none should cause a crash): */
    if(doauth(mech, &sconn, &cconn, &security_props, tosend->client_callbacks,
            0) != SASL_OK) {
      fatal("doauth failed in test_clientfirst");
    }

    cleanup_auth(&cconn, &sconn);

    printf("%s --> successful result\n", mech);
}

void test_noclientfirst(char *mech, void *rock) 
{
    sasl_conn_t *sconn, *cconn;
    tosend_t *tosend = (tosend_t *)rock;
    
    printf("%s --> start\n", mech);

    /* Basic crash-tests (none should cause a crash): */
    if(doauth_noclientfirst(mech, &sconn, &cconn, &security_props,
      tosend->client_callbacks) != SASL_OK) {
      fatal("doauth failed in test_noclientfirst");
    }

    cleanup_auth(&cconn, &sconn);

    printf("%s --> successful result\n", mech);
}

void test_serverlast(char *mech, void *rock) 
{
    sasl_conn_t *sconn, *cconn;
    tosend_t *tosend = (tosend_t *)rock;
    
    printf("%s --> start\n", mech);

    /* Basic crash-tests (none should cause a crash): */
    if(doauth_serverlast(mech, &sconn, &cconn, &security_props,
                   tosend->client_callbacks) != SASL_OK) {
      fatal("doauth failed in test_serverlast");
    }

    cleanup_auth(&cconn, &sconn);

    printf("%s --> successful result\n", mech);
}


void test_noclientfirst_andserverlast(char *mech, void *rock) 
{
    sasl_conn_t *sconn, *cconn;
    tosend_t *tosend = (tosend_t *)rock;
    
    printf("%s --> start\n", mech);

    /* Basic crash-tests (none should cause a crash): */
    if(doauth_noclientfirst_andserverlast(mech, &sconn, &cconn,
                                &security_props,
                                tosend->client_callbacks)
       != SASL_OK) {
      fatal("doauth failed in test_noclientfirst_andserverlast");
    }

    cleanup_auth(&cconn, &sconn);

    printf("%s --> successful result\n", mech);
}

void testseclayer(char *mech, void *rock __attribute__((unused))) 
{
    sasl_conn_t *sconn, *cconn;
    int result;
    char buf[8192], buf2[8192];
    const char *txstring = "THIS IS A TEST";
    const char *out, *out2;
    char *tmp;
    const sasl_security_properties_t *test_props[7] =
                                          { &security_props,
                                  &force_3des,
                                  &force_rc4_56,
                                  &force_des,
                                  &int_only,
                                  &no_int,
                                  &disable_seclayer };
    const unsigned num_properties = 7;
    unsigned i;
    const sasl_ssf_t *this_ssf;
    unsigned outlen = 0, outlen2 = 0, totlen = 0;
    
    printf("%s --> security layer start\n", mech);

    for(i=0; i<num_properties; i++) {
        
    /* Basic crash-tests (none should cause a crash): */
    result = doauth(mech, &sconn, &cconn, test_props[i], NULL, 1);
    if(result == SASL_NOMECH && test_props[i]->min_ssf > 0) {
      printf("  Testing SSF: SKIPPED (requested minimum > 0: %d)\n",
             test_props[i]->min_ssf);
      cleanup_auth(&sconn, &cconn);
      continue;
    } else if(result != SASL_OK) {
      fatal("doauth failed in testseclayer");
    }

    if(sasl_getprop(cconn, SASL_SSF, (const void **)&this_ssf) != SASL_OK) {
      fatal("sasl_getprop in testseclayer");
    }

    if(*this_ssf != 0 && !test_props[i]->maxbufsize) {
      fatal("got nonzero SSF with zero maxbufsize");
    }

    printf("  SUCCESS Testing SSF: %d (requested %d/%d with maxbufsize: %d)\n",
         (unsigned)(*this_ssf),
         test_props[i]->min_ssf, test_props[i]->max_ssf,
         test_props[i]->maxbufsize);

    if(!test_props[i]->maxbufsize) {
      result = sasl_encode(cconn, txstring, strlen(txstring), &out, &outlen);
      if(result == SASL_OK) {
          fatal("got OK when encoding with zero maxbufsize");
      }
      result = sasl_decode(sconn, "foo", 3, &out, &outlen);
      if(result == SASL_OK) {
          fatal("got OK when decoding with zero maxbufsize");
      }
      cleanup_auth(&sconn, &cconn);
      continue;
    }
    
    sasl_encode(NULL, txstring, strlen(txstring), &out, &outlen);
    sasl_encode(cconn, NULL, strlen(txstring), &out, &outlen);
    sasl_encode(cconn, txstring, 0, &out, &outlen);
    sasl_encode(cconn, txstring, (unsigned)-1, &out, &outlen);
    sasl_encode(cconn, txstring, strlen(txstring), NULL, &outlen);
    sasl_encode(cconn, txstring, strlen(txstring), &out, NULL);
    
    sasl_decode(NULL, txstring, strlen(txstring), &out, &outlen);
    sasl_decode(cconn, NULL, strlen(txstring), &out, &outlen);
    sasl_decode(cconn, txstring, 0, &out, &outlen);
    sasl_decode(cconn, txstring, (unsigned)-1, &out, &outlen);
    sasl_decode(cconn, txstring, strlen(txstring), NULL, &outlen);
    sasl_decode(cconn, txstring, strlen(txstring), &out, NULL);
    
    cleanup_auth(&sconn, &cconn);

    /* Basic I/O Test */
    if(doauth(mech, &sconn, &cconn, test_props[i], NULL, 0) != SASL_OK) {
      fatal("doauth failed in testseclayer");
    }

    result = sasl_encode(cconn, txstring, strlen(txstring), &out, &outlen);
    if(result != SASL_OK) {
      fatal("basic sasl_encode failure");
    }

    result = sasl_decode(sconn, out, outlen, &out, &outlen);
    if(result != SASL_OK) {
      fatal("basic sasl_decode failure");
    }    
    
    cleanup_auth(&sconn, &cconn);

    /* Split one block and reassemble */
    if(doauth(mech, &sconn, &cconn, test_props[i], NULL, 0) != SASL_OK) {
      fatal("doauth failed in testseclayer");
    }

    result = sasl_encode(cconn, txstring, strlen(txstring), &out, &outlen);
    if(result != SASL_OK) {
      fatal("basic sasl_encode failure (2)");
    }

    memcpy(buf, out, 5);
    buf[5] = '\0';
    
    out += 5;

    result = sasl_decode(sconn, buf, 5, &out2, &outlen2);
    if(result != SASL_OK) {
      printf("Failed with: %s\n", sasl_errstring(result, NULL, NULL));
      fatal("sasl_decode failure part 1/2");
    }    

    memset(buf2, 0, 8192);
    if(outlen2) 
        memcpy(buf2, out2, outlen2);

    result = sasl_decode(sconn, out, outlen - 5, &out, &outlen);
    if(result != SASL_OK) {
      fatal("sasl_decode failure part 2/2");
    }

    strcat(buf2, out);
    if(strcmp(buf2, txstring)) {
      printf("Exptected '%s' but got '%s'\n", txstring, buf2);
      fatal("did not get correct string back after 2 sasl_decodes");
    }

    cleanup_auth(&sconn, &cconn);

    /* Combine 2 blocks */
    if(doauth(mech, &sconn, &cconn, test_props[i], NULL, 0) != SASL_OK) {
      fatal("doauth failed in testseclayer");
    }

    result = sasl_encode(cconn, txstring, strlen(txstring), &out, &outlen);
    if(result != SASL_OK) {
      fatal("basic sasl_encode failure (3)");
    }

    memcpy(buf, out, outlen);

    tmp = buf + outlen;
    totlen = outlen;
    
    result = sasl_encode(cconn, txstring, strlen(txstring), &out, &outlen);
    if(result != SASL_OK) {
      fatal("basic sasl_encode failure (4)");
    }

    memcpy(tmp, out, outlen);
    totlen += outlen;

    result = sasl_decode(sconn, buf, totlen, &out, &outlen);
    if(result != SASL_OK) {
      printf("Failed with: %s\n", sasl_errstring(result, NULL, NULL));
      fatal("sasl_decode failure (2 blocks)");
    }    

    sprintf(buf2, "%s%s", txstring, txstring);

    if(strcmp(out, buf2)) {
      fatal("did not get correct string back (2 blocks)");
    }

    cleanup_auth(&sconn, &cconn);

    /* Combine 2 blocks with 1 split */
    if(doauth(mech, &sconn, &cconn, test_props[i], NULL, 0) != SASL_OK) {
      fatal("doauth failed in testseclayer");
    }

    result = sasl_encode(cconn, txstring, strlen(txstring), &out, &outlen);
    if(result != SASL_OK) {
      fatal("basic sasl_encode failure (3)");
    }

    memcpy(buf, out, outlen);

    tmp = buf + outlen;

    result = sasl_encode(cconn, txstring, strlen(txstring), &out2, &outlen2);
    if(result != SASL_OK) {
      fatal("basic sasl_encode failure (4)");
    }

    memcpy(tmp, out2, 5);
    tmp[5] = '\0';
    outlen += 5;

    outlen2 -= 5;
    out2 += 5;

    result = sasl_decode(sconn, buf, outlen, &out, &outlen);
    if(result != SASL_OK) {
      printf("Failed with: %s\n", sasl_errstring(result, NULL, NULL));
      fatal("sasl_decode failure 1/2 (2 blocks, 1 split)");
    }    

    memset(buf2, 0, 8192);
    memcpy(buf2, out, outlen);

    tmp = buf2 + outlen;

    result = sasl_decode(sconn, out2, outlen2, &out, &outlen);
    if(result != SASL_OK) {
      printf("Failed with: %s\n", sasl_errstring(result, NULL, NULL));
      fatal("sasl_decode failure 2/2 (2 blocks, 1 split)");
    }

    memcpy(tmp, out, outlen);

    sprintf(buf, "%s%s", txstring, txstring);
    if(strcmp(buf, buf2)) {
      fatal("did not get correct string back (2 blocks, 1 split)");
    }

    cleanup_auth(&sconn, &cconn);
    
    } /* for each properties type we want to test */
     
    printf("%s --> security layer OK\n", mech);
    
}


/*
 * Apply the given function to each machanism 
 */

void foreach_mechanism(foreach_t *func, void *rock)
{
    const char *out;
    char *str, *start;
    sasl_conn_t *saslconn;
    int result;
    struct sockaddr_in addr;
    struct hostent *hp;
    unsigned len;
    char buf[8192];

    /* Get the list of mechanisms */
    sasl_done();

    if (sasl_server_init(emptysasl_cb,"TestSuite")!=SASL_OK)
      fatal("sasl_server_init failed in foreach_mechanism");

    if ((hp = gethostbyname(myhostname)) == NULL) {
        perror("gethostbyname");
        fatal("can't gethostbyname");
    }

    addr.sin_family = 0;
    memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
    addr.sin_port = htons(0);

    sprintf(buf,"%s;%d", inet_ntoa(addr.sin_addr), 0);

    if (sasl_server_new("rcmd", myhostname, NULL,
                  buf, buf, NULL, 0,
                  &saslconn) != SASL_OK) {
      fatal("sasl_server_new in foreach_mechanism");
    }

    if (sasl_setprop(saslconn, SASL_AUTH_EXTERNAL, authname)!=SASL_OK)
      fatal("sasl_setprop(SASL_AUTH_EXTERNAL) failed");

    result = sasl_listmech(saslconn,
                     NULL,
                     "",
                     "\n",
                     "",
                     &out,
                     &len,
                     NULL);

    if(result != SASL_OK) {
      fatal("sasl_listmech in foreach_mechanism");
    }
    
    memcpy(buf, out, len + 1);

    sasl_dispose(&saslconn);
    sasl_done();

    /* call the function for each mechanism */
    start = str = buf;
    while (*start != '\0')
    {
      while ((*str != '\n') && (*str != '\0'))
          str++;

      if (*str == '\n')
      {
          *str = '\0';
          str++;
      }

      func(start, rock);

      start = str;
    }
}

void test_serverstart()
{
    int result;
    sasl_conn_t *saslconn;
    const char *out;
    unsigned outlen;
    struct sockaddr_in addr;
    struct hostent *hp;
    char buf[8192];

    if (sasl_server_init(emptysasl_cb,"TestSuite")!=SASL_OK)
      fatal("can't sasl_server_init in test_serverstart");

    if ((hp = gethostbyname(myhostname)) == NULL) {
        perror("gethostbyname");
        fatal("can't gethostbyname in test_serverstart");
    }

    addr.sin_family = 0;
    memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
    addr.sin_port = htons(0);

    sprintf(buf,"%s;%d", inet_ntoa(addr.sin_addr), 0);

    if (sasl_server_new("rcmd", myhostname, NULL,
                  buf, buf, NULL, 0, 
                  &saslconn) != SASL_OK) {
      fatal("can't sasl_server_new in test_serverstart");
    }


    /* Test null connection */
    result = sasl_server_start(NULL,
                         "foobar",
                         NULL,
                         0,
                         NULL,
                         NULL);
    
    if (result == SASL_OK) fatal("Said ok to null sasl_conn_t in sasl_server_start()");

    /* send plausible but invalid mechanism */
    result = sasl_server_start(saslconn,
                         "foobar",
                         NULL,
                         0,
                         &out,
                         &outlen);

    if (result == SASL_OK) fatal("Said ok to invalid mechanism");

    /* send really long and invalid mechanism */
    result = sasl_server_start(saslconn,
                         really_long_string,
                         NULL,
                         0,
                         &out,
                         &outlen);

    if (result == SASL_OK) fatal("Said ok to invalid mechanism");

    sasl_dispose(&saslconn);
    sasl_done();
}

void test_rand_corrupt(unsigned steps) 
{
    unsigned lup;
    tosend_t tosend;
    
    for (lup=0;lup<steps;lup++)
    {
      tosend.type = rand() % CORRUPT_SIZE;
      tosend.step = lup % MAX_STEPS;
      tosend.client_callbacks = NULL;

      printf("RANDOM TEST: (%s in step %d) (%d of %d)\n",corrupt_types[tosend.type],tosend.step,lup+1,steps);
      foreach_mechanism((foreach_t *) &sendbadsecond,&tosend);
    }
}

void test_proxypolicy() 
{
    foreach_mechanism((foreach_t *) &do_proxypolicy_test,NULL);
}

void test_all_corrupt() 
{
    tosend_t tosend;
    tosend.client_callbacks = NULL;

    /* Start just beyond NOTHING */
    for(tosend.type=1; tosend.type<CORRUPT_SIZE; tosend.type++) {
      for(tosend.step=0; tosend.step<MAX_STEPS; tosend.step++) {
          printf("TEST: %s in step %d:\n", corrupt_types[tosend.type],
               tosend.step);
          foreach_mechanism((foreach_t *) &sendbadsecond, &tosend);
      }
    }
}

void test_seclayer() 
{
    foreach_mechanism((foreach_t *) &testseclayer, NULL);
}

void create_ids(void)
{
    sasl_conn_t *saslconn;
    int result;
    struct sockaddr_in addr;
    struct hostent *hp;
    char buf[8192];
#ifdef DO_SASL_CHECKAPOP
    int i;
    const char challenge[] = "<1896.697170952@cyrus.andrew.cmu.edu>";
    MD5_CTX ctx;
    unsigned char digest[16];
    char digeststr[32];
#endif

    if (sasl_server_init(goodsasl_cb,"TestSuite")!=SASL_OK)
      fatal("can't sasl_server_init in create_ids");

    if ((hp = gethostbyname(myhostname)) == NULL) {
        perror("gethostbyname");
        fatal("can't gethostbyname in create_ids");
    }

    addr.sin_family = 0;
    memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
    addr.sin_port = htons(0);

    sprintf(buf,"%s;%d", inet_ntoa(addr.sin_addr), 0);

    if (sasl_server_new("rcmd", myhostname, NULL,
                  buf, buf, NULL, 0,
                  &saslconn) != SASL_OK)
      fatal("can't sasl_server_new in create_ids");
    
    /* Try to set password then check it */

    result = sasl_setpass(saslconn, username, password, strlen(password),
                    NULL, 0, SASL_SET_CREATE);
    if (result != SASL_OK) {
      printf("error was %s (%d)\n",sasl_errstring(result,NULL,NULL),result);
      fatal("Error setting password. Do we have write access to sasldb?");
    }    
    
    result = sasl_checkpass(saslconn, username, strlen(username),
                      password, strlen(password));
    if (result != SASL_OK) {
      fprintf(stderr, "%s\n", sasl_errdetail(saslconn));
      fatal("Unable to verify password we just set");
    }
    result = sasl_user_exists(saslconn, "imap", NULL, username);
    if(result != SASL_OK)
      fatal("sasl_user_exists did not find user");

    result = sasl_user_exists(saslconn, "imap", NULL,
                        nonexistant_username);
    if(result == SASL_OK)
      fatal("sasl_user_exists found nonexistant username");

    /* Test sasl_checkapop */
#ifdef DO_SASL_CHECKAPOP
    _sasl_MD5Init(&ctx);
    _sasl_MD5Update(&ctx,challenge,strlen(challenge));
    _sasl_MD5Update(&ctx,password,strlen(password));
    _sasl_MD5Final(digest, &ctx);
                            
    /* convert digest from binary to ASCII hex */
    for (i = 0; i < 16; i++)
      sprintf(digeststr + (i*2), "%02x", digest[i]);

    sprintf(buf, "%s %s", username, digeststr);
    
    result = sasl_checkapop(saslconn,
                            challenge, strlen(challenge),
                            buf, strlen(buf));
    if(result != SASL_OK)
        fatal("Unable to checkapop password we just set");
    /* End checkapop test */
#else /* Just check that checkapop is really turned off */
    if(sasl_checkapop(saslconn, NULL, 0, NULL, 0) == SASL_OK)
      fatal("sasl_checkapop seems to work but was disabled at compile time");
#endif

    /* now delete user and make sure can't find him anymore */
    result = sasl_setpass(saslconn, username, password,
                    strlen(password), NULL, 0, SASL_SET_DISABLE);
    if (result != SASL_OK)
      fatal("Error disabling password. Do we have write access to sasldb?");

    result = sasl_checkpass(saslconn, username, strlen(username),
                      password, strlen(password));
    if (result == SASL_OK) {
      printf("\n  WARNING: sasl_checkpass got SASL_OK after disableing\n");
      printf("           This is generally ok, just an artifact of sasldb\n");
      printf("           being an external verifier\n");
    }

#ifdef DO_SASL_CHECKAPOP
    /* And checkapop... */
    result = sasl_checkapop(saslconn,
                            challenge, strlen(challenge), 
                            buf, strlen(buf));
    if (result == SASL_OK) {
      printf("\n  WARNING: sasl_checkapop got SASL_OK after disableing\n");
      printf("           This is generally ok, just an artifact of sasldb\n");
      printf("           being an external verifier\n");
    }
#endif

    /* try bad params */
    if (sasl_setpass(NULL,username, password, strlen(password), NULL, 0, SASL_SET_CREATE)==SASL_OK)
      fatal("Didn't specify saslconn");
    if (sasl_setpass(saslconn,username, password, 0, NULL, 0, SASL_SET_CREATE)==SASL_OK)
      fatal("Allowed password of zero length");
    if (sasl_setpass(saslconn,username, password, strlen(password), NULL, 0, 43)==SASL_OK)
      fatal("Gave weird code");

#ifndef SASL_NDBM
    if (sasl_setpass(saslconn,really_long_string, password, strlen(password), 
                 NULL, 0, SASL_SET_CREATE)!=SASL_OK)
      fatal("Didn't allow really long username");
#else
    printf("WARNING: skipping sasl_setpass() on really_long_string with NDBM\n");
#endif

    if (sasl_setpass(saslconn,"bob",really_long_string,
                 strlen(really_long_string),NULL, 0,
                 SASL_SET_CREATE)!=SASL_OK)
      fatal("Didn't allow really long password");

    result = sasl_setpass(saslconn,"frank" ,password, strlen(password), 
                    NULL, 0, SASL_SET_DISABLE);

    if ((result!=SASL_NOUSER) && (result!=SASL_OK))
      {
          printf("error = %d\n",result);
          fatal("Disabling non-existant didn't return SASL_NOUSER");
      }
    
    /* Now set the user again (we use for rest of program) */
    result = sasl_setpass(saslconn, username, password, strlen(password),
                    NULL, 0, SASL_SET_CREATE);
    if (result != SASL_OK)
      fatal("Error setting password. Do we have write access to sasldb?");

    /* cleanup */
    sasl_dispose(&saslconn);
    sasl_done();
}

/*
 * Test the checkpass routine
 */

void test_checkpass(void)
{
    sasl_conn_t *saslconn;

    /* try without initializing anything */
    if(sasl_checkpass(NULL, username, strlen(username),
                  password, strlen(password)) != SASL_NOTINIT) {
      fatal("sasl_checkpass() when library not initialized");
    }    

    if (sasl_server_init(goodsasl_cb,"TestSuite")!=SASL_OK)
      fatal("can't sasl_server_init in test_checkpass");

    if (sasl_server_new("rcmd", myhostname,
                  NULL, NULL, NULL, NULL, 0, 
                  &saslconn) != SASL_OK)
      fatal("can't sasl_server_new in test_checkpass");

    /* make sure works for general case */

    if (sasl_checkpass(saslconn, username, strlen(username),
                   password, strlen(password))!=SASL_OK)
      fatal("sasl_checkpass() failed on simple case");

    /* NULL saslconn */
    if (sasl_checkpass(NULL, username, strlen(username),
               password, strlen(password)) == SASL_OK)
      fatal("Suceeded with NULL saslconn");

    /* NULL username -- should be OK if sasl_checkpass enabled */
    if (sasl_checkpass(saslconn, NULL, strlen(username),
               password, strlen(password)) != SASL_OK)
      fatal("failed check if sasl_checkpass is enabled");

    /* NULL password */
    if (sasl_checkpass(saslconn, username, strlen(username),
               NULL, strlen(password)) == SASL_OK)
      fatal("Suceeded with NULL password");

    sasl_dispose(&saslconn);
    sasl_done();
}



void notes(void)
{
    printf("NOTE:\n");
    printf("-For KERBEROS_V4 must be able to read srvtab file (usually /etc/srvtab)\n");
    printf("-For GSSAPI must be able to read srvtab (/etc/krb5.keytab)\n");
    printf("-For both KERBEROS_V4 and GSSAPI you must have non-expired tickets\n");
    printf("-For OTP (w/OPIE) must be able to read/write opiekeys (/etc/opiekeys)\n");
    printf("-For OTP you must have a non-expired secret\n");
    printf("-Must be able to read sasldb, which needs to be setup with a\n");
    printf(" username and a password (see top of testsuite.c)\n");
    printf("\n\n");
}

void usage(void)
{
    printf("Usage:\n" \
           " testsuite [-g name] [-s seed] [-r tests] -a -M\n" \
           "    g -- gssapi service name to use (default: host)\n" \
         "    r -- # of random tests to do (default: 25)\n" \
         "    a -- do all corruption tests (and ignores random ones unless -r specified)\n" \
         "    n -- skip the initial \"do correctly\" tests\n"
         "    h -- show this screen\n" \
           "    s -- random seed to use\n" \
         "    M -- detailed memory debugging ON\n" \
           );
}

int main(int argc, char **argv)
{
    char c;
    int random_tests = -1;
    int do_all = 0;
    int skip_do_correct = 0;
    unsigned int seed = time(NULL);
#ifdef WIN32
  /* initialize winsock */
    int result;
    WSADATA wsaData;

    result = WSAStartup( MAKEWORD(2, 0), &wsaData );
    if ( result != 0) {
      fatal("Windows sockets initialization failure");
    }
#endif

    while ((c = getopt(argc, argv, "Ms:g:r:han")) != EOF)
      switch (c) {
      case 'M':
          DETAILED_MEMORY_DEBUGGING = 1;
          break;
      case 's':
          seed = atoi(optarg);
          break;
      case 'g':
          gssapi_service = optarg;
          break;
      case 'r':
          random_tests = atoi(optarg);
          break;
      case 'a':
          random_tests = 0;
          do_all = 1;
          break;
      case 'n':
          skip_do_correct = 1;
          break;
      case 'h':
          usage();
          exit(0);
          break;
      default:
          usage();
          fatal("Invalid parameter\n");
          break;
    }

    if(random_tests < 0) random_tests = 25;

    notes();

    init(seed);

#if 0 /* Disabled because it is borked */
    printf("Creating id's in mechanisms (not in sasldb)...\n");
    create_ids();
    if(mem_stat() != SASL_OK) fatal("memory error");
    printf("Creating id's in mechanisms (not in sasldb)... ok\n");
#endif

    printf("Checking plaintext passwords... ");
    test_checkpass();
    if(mem_stat() != SASL_OK) fatal("memory error");
    printf("ok\n");

    printf("Random number functions... ");
    test_random();
    if(mem_stat() != SASL_OK) fatal("memory error");
    printf("ok\n");

    printf("Testing base64 functions... ");
    test_64();
    if(mem_stat() != SASL_OK) fatal("memory error");
    printf("ok\n");

    printf("Testing auxprop functions... ");
    test_props();
    if(mem_stat() != SASL_OK) fatal("memory error");
    printf("ok\n");

    printf("Tests of sasl_{server|client}_init()... ");
    test_init();
    if(mem_stat() != SASL_OK) fatal("memory error");
    printf("ok\n");
    
    printf("Testing sasl_listmech()... \n");
    test_listmech();
    if(mem_stat() != SASL_OK) fatal("memory error");
    printf("Testing sasl_listmech()... ok\n");

    printf("Testing serverstart...");
    test_serverstart();
    if(mem_stat() != SASL_OK) fatal("memory error");
    printf("ok\n");

    if(!skip_do_correct) {
      tosend_t tosend;
      
      tosend.type = NOTHING;
      tosend.step = 500;
      tosend.client_callbacks = client_interactions;
      
      printf("Testing client-first/no-server-last correctly...\n");
      foreach_mechanism((foreach_t *) &test_clientfirst,&tosend);
      if(mem_stat() != SASL_OK) fatal("memory error");
      printf("Test of client-first/no-server-last...ok\n");

      printf("Testing no-client-first/no-server-last correctly...\n");
      foreach_mechanism((foreach_t *) &test_noclientfirst, &tosend);
      if(mem_stat() != SASL_OK) fatal("memory error");
      printf("Test of no-client-first/no-server-last...ok\n");
      
      printf("Testing no-client-first/server-last correctly...\n");
      foreach_mechanism((foreach_t *) &test_noclientfirst_andserverlast,
                    &tosend);
      if(mem_stat() != SASL_OK) fatal("memory error");
      printf("Test of no-client-first/server-last...ok\n");

      printf("Testing client-first/server-last correctly...\n");
      foreach_mechanism((foreach_t *) &test_serverlast, &tosend);
      if(mem_stat() != SASL_OK) fatal("memory error");
      printf("Test of client-first/server-last...ok\n");

      tosend.client_callbacks = client_callbacks;
      printf("-=-=-=-=- And now using the callbacks interface -=-=-=-=-\n");

      printf("Testing client-first/no-server-last correctly...\n");
      foreach_mechanism((foreach_t *) &test_clientfirst,&tosend);
      if(mem_stat() != SASL_OK) fatal("memory error");
      printf("Test of client-first/no-server-last...ok\n");

      printf("Testing no-client-first/no-server-last correctly...\n");
      foreach_mechanism((foreach_t *) &test_noclientfirst, &tosend);
      if(mem_stat() != SASL_OK) fatal("memory error");
      printf("Test of no-client-first/no-server-last...ok\n");
      
      printf("Testing no-client-first/server-last correctly...\n");
      foreach_mechanism((foreach_t *) &test_noclientfirst_andserverlast,
                    &tosend);
      if(mem_stat() != SASL_OK) fatal("memory error");
      printf("Test of no-client-first/server-last...ok\n");

      printf("Testing client-first/server-last correctly...\n");
      foreach_mechanism((foreach_t *) &test_serverlast, &tosend);
      if(mem_stat() != SASL_OK) fatal("memory error");
      printf("Test of client-first/server-last...ok\n");
    } else {
      printf("Testing client-first/no-server-last correctly...skipped\n");
      printf("Testing no-client-first/no-server-last correctly...skipped\n");
      printf("Testing no-client-first/server-last correctly...skipped\n");
      printf("Testing client-first/server-last correctly...skipped\n");
      printf("Above tests with callbacks interface...skipped\n");
    }
    
    /* FIXME: do memory tests below here on the things
     * that are MEANT to fail sometime. */
    if(do_all) {  
      printf("All corruption tests...\n");
      test_all_corrupt();
      printf("All corruption tests... ok\n");
    }
    
    if(random_tests) {
      printf("Random corruption tests...\n");
      test_rand_corrupt(random_tests);
      printf("Random tests... ok\n");
    } else {
      printf("Random tests... skipped\n");
    }

    printf("Testing Proxy Policy...\n");
    test_proxypolicy();
    printf("Tests of Proxy Policy...ok\n");

    printf("Testing security layer...\n");
    test_seclayer();
    printf("Tests of security layer... ok\n");

    printf("All tests seemed to go ok (i.e. we didn't crash)\n");

    exit(0);
}

Generated by  Doxygen 1.6.0   Back to index