Page MenuHomePhorge

imtest.c
No OneTemporary

Authored By
Unknown
Size
29 KB
Referenced Files
None
Subscribers
None

imtest.c

/* imtest.c -- imap test client
* Tim Martin (SASL implementation)
* $Id: imtest.c,v 1.43 2000/01/14 02:10:20 leg Exp $
*
* Copyright 1999 Carnegie Mellon University
*
* No warranties, either expressed or implied, are made regarding the
* operation, use, or results of the software.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted for non-commercial purposes only
* provided that this copyright notice appears in all copies and in
* supporting documentation.
*
* Permission is also granted to Internet Service Providers and others
* entities to use the software for internal purposes.
*
* The distribution, modification or sale of a product which uses or is
* based on the software, in whole or in part, for commercial purposes or
* benefits requires specific, additional permission from:
*
* 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
*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sasl.h>
#include <saslutil.h>
#include <pwd.h>
#include "prot.h"
#ifdef HAVE_SSL
static SSL_CTX *tls_ctx = NULL;
static SSL *tls_conn = NULL;
#endif /* HAVE_SSL */
#define IMTEST_OK 0
#define IMTEST_FAIL -1
typedef enum {
STAT_CONT = 0,
STAT_NO = 1,
STAT_OK = 2
} imt_stat;
/* global vars */
sasl_conn_t *conn;
int sock; /* socket descriptor */
int verbose=0;
struct protstream *pout, *pin;
static char *authname = NULL;
static char *username = NULL;
static char *realm = NULL;
extern int _sasl_debug;
extern char *optarg;
/* callbacks we support */
static sasl_callback_t callbacks[] = {
{
#ifdef SASL_CB_GETREALM
SASL_CB_GETREALM, NULL, NULL
}, {
#endif
SASL_CB_USER, NULL, NULL
}, {
SASL_CB_AUTHNAME, NULL, NULL
}, {
SASL_CB_PASS, NULL, NULL
}, {
SASL_CB_LIST_END, NULL, NULL
}
};
void imtest_fatal(char *msg)
{
if (msg != NULL) {
printf("failure: %s\n",msg);
}
exit(1);
}
/* libcyrus makes us define this */
void fatal(void)
{
exit(1);
}
#ifdef HAVE_SSL
static int verify_depth;
static int verify_error = X509_V_OK;
static int do_dump = 0;
#define CCERT_BUFSIZ 256
static char peer_subject[CCERT_BUFSIZ];
static char peer_issuer[CCERT_BUFSIZ];
static char peer_CN[CCERT_BUFSIZ];
static char issuer_CN[CCERT_BUFSIZ];
static unsigned char md[EVP_MAX_MD_SIZE];
static char fingerprint[EVP_MAX_MD_SIZE * 3];
char *tls_peer_CN = NULL;
char *tls_issuer_CN = NULL;
char *tls_protocol = NULL;
const char *tls_cipher_name = NULL;
int tls_cipher_usebits = 0;
int tls_cipher_algbits = 0;
/*
* Set up the cert things on the server side. We do need both the
* private key (in key_file) and the cert (in cert_file).
* Both files may be identical.
*
* This function is taken from OpenSSL apps/s_cb.c
*/
static int set_cert_stuff(SSL_CTX * ctx, char *cert_file, char *key_file)
{
if (cert_file != NULL) {
if (SSL_CTX_use_certificate_file(ctx, cert_file,
SSL_FILETYPE_PEM) <= 0) {
printf("unable to get certificate from '%s'\n", cert_file);
return (0);
}
if (key_file == NULL)
key_file = cert_file;
if (SSL_CTX_use_PrivateKey_file(ctx, key_file,
SSL_FILETYPE_PEM) <= 0) {
printf("unable to get private key from '%s'\n", key_file);
return (0);
}
/* Now we know that a key and cert have been set against
* the SSL context */
if (!SSL_CTX_check_private_key(ctx)) {
printf("Private key does not match the certificate public key\n");
return (0);
}
}
return (1);
}
/* taken from OpenSSL apps/s_cb.c */
static int verify_callback(int ok, X509_STORE_CTX * ctx)
{
char buf[256];
X509 *err_cert;
int err;
int depth;
err_cert = X509_STORE_CTX_get_current_cert(ctx);
err = X509_STORE_CTX_get_error(ctx);
depth = X509_STORE_CTX_get_error_depth(ctx);
X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
if (verbose==1)
printf("Peer cert verify depth=%d %s\n", depth, buf);
if (!ok) {
printf("verify error:num=%d:%s\n", err,
X509_verify_cert_error_string(err));
if (verify_depth >= depth) {
ok = 1;
verify_error = X509_V_OK;
} else {
ok = 0;
verify_error = X509_V_ERR_CERT_CHAIN_TOO_LONG;
}
}
switch (ctx->error) {
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
printf("issuer= %s\n", buf);
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
printf("cert not yet valid\n");
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
printf("cert has expired\n");
break;
}
if (verbose==1)
printf("verify return:%d\n", ok);
return (ok);
}
/* taken from OpenSSL apps/s_cb.c */
static RSA *tmp_rsa_cb(SSL * s, int export, int keylength)
{
static RSA *rsa_tmp = NULL;
if (rsa_tmp == NULL) {
rsa_tmp = RSA_generate_key(keylength, RSA_F4, NULL, NULL);
}
return (rsa_tmp);
}
/* taken from OpenSSL apps/s_cb.c
* tim - this seems to just be giving logging messages
*/
static void apps_ssl_info_callback(SSL * s, int where, int ret)
{
char *str;
int w;
if (verbose==0) return;
w = where & ~SSL_ST_MASK;
if (w & SSL_ST_CONNECT)
str = "SSL_connect";
else if (w & SSL_ST_ACCEPT)
str = "SSL_accept";
else
str = "undefined";
if (where & SSL_CB_LOOP) {
printf("%s:%s\n", str, SSL_state_string_long(s));
} else if (where & SSL_CB_ALERT) {
str = (where & SSL_CB_READ) ? "read" : "write";
if ((ret & 0xff) != SSL3_AD_CLOSE_NOTIFY)
printf("SSL3 alert %s:%s:%s\n", str,
SSL_alert_type_string_long(ret),
SSL_alert_desc_string_long(ret));
} else if (where & SSL_CB_EXIT) {
if (ret == 0)
printf("%s:failed in %s\n",
str, SSL_state_string_long(s));
else if (ret < 0) {
printf("%s:error in %s %i\n",
str, SSL_state_string_long(s),ret);
}
}
}
char *var_tls_CAfile="";
char *var_tls_CApath="";
/*
* This is the setup routine for the SSL client.
*
* The skeleton of this function is taken from OpenSSL apps/s_client.c.
*/
static int tls_init_clientengine(int verifydepth, char *var_tls_cert_file, char *var_tls_key_file)
{
int off = 0;
int verify_flags = SSL_VERIFY_NONE;
char *CApath;
char *CAfile;
char *c_cert_file;
char *c_key_file;
if (verbose==1)
printf("starting TLS engine\n");
SSL_load_error_strings();
SSLeay_add_ssl_algorithms();
tls_ctx = SSL_CTX_new(SSLv23_client_method());
if (tls_ctx == NULL) {
return IMTEST_FAIL;
};
off |= SSL_OP_ALL; /* Work around all known bugs */
SSL_CTX_set_options(tls_ctx, off);
SSL_CTX_set_info_callback(tls_ctx, apps_ssl_info_callback);
if (strlen(var_tls_CAfile) == 0)
CAfile = NULL;
else
CAfile = var_tls_CAfile;
if (strlen(var_tls_CApath) == 0)
CApath = NULL;
else
CApath = var_tls_CApath;
if (CAfile || CApath)
if ((!SSL_CTX_load_verify_locations(tls_ctx, CAfile, CApath)) ||
(!SSL_CTX_set_default_verify_paths(tls_ctx))) {
printf("TLS engine: cannot load CA data\n");
return IMTEST_FAIL;
}
if (strlen(var_tls_cert_file) == 0)
c_cert_file = NULL;
else
c_cert_file = var_tls_cert_file;
if (strlen(var_tls_key_file) == 0)
c_key_file = NULL;
else
c_key_file = var_tls_key_file;
if (c_cert_file || c_key_file)
if (!set_cert_stuff(tls_ctx, c_cert_file, c_key_file)) {
printf("TLS engine: cannot load cert/key data\n");
return IMTEST_FAIL;
}
SSL_CTX_set_tmp_rsa_callback(tls_ctx, tmp_rsa_cb);
verify_depth = verifydepth;
SSL_CTX_set_verify(tls_ctx, verify_flags, verify_callback);
return IMTEST_OK;
}
/*
* taken from OpenSSL crypto/bio/b_dump.c, modified to save a lot of strcpy
* and strcat by Matti Aarnio.
*/
#define TRUNCATE
#define DUMP_WIDTH 16
static int tls_dump(const char *s, int len)
{
int ret = 0;
char buf[160 + 1];
char *ss;
int i;
int j;
int rows;
int trunc;
unsigned char ch;
trunc = 0;
#ifdef TRUNCATE
for (; (len > 0) && ((s[len - 1] == ' ') || (s[len - 1] == '\0')); len--)
trunc++;
#endif
rows = (len / DUMP_WIDTH);
if ((rows * DUMP_WIDTH) < len)
rows++;
for (i = 0; i < rows; i++) {
buf[0] = '\0'; /* start with empty string */
ss = buf;
sprintf(ss, "%04x ", i * DUMP_WIDTH);
ss += strlen(ss);
for (j = 0; j < DUMP_WIDTH; j++) {
if (((i * DUMP_WIDTH) + j) >= len) {
strcpy(ss, " ");
} else {
ch = ((unsigned char) *((char *) (s) + i * DUMP_WIDTH + j))
& 0xff;
sprintf(ss, "%02x%c", ch, j == 7 ? '|' : ' ');
ss += 3;
}
}
ss += strlen(ss);
*ss+= ' ';
for (j = 0; j < DUMP_WIDTH; j++) {
if (((i * DUMP_WIDTH) + j) >= len)
break;
ch = ((unsigned char) *((char *) (s) + i * DUMP_WIDTH + j)) & 0xff;
*ss+= (((ch >= ' ') && (ch <= '~')) ? ch : '.');
if (j == 7) *ss+= ' ';
}
*ss = 0;
/*
* if this is the last call then update the ddt_dump thing so that
* we will move the selection point in the debug window
*/
printf("%s\n", buf);
ret += strlen(buf);
}
#ifdef TRUNCATE
if (trunc > 0) {
sprintf(buf, "%04x - <SPACES/NULS>\n", len+ trunc);
printf("%s\n", buf);
ret += strlen(buf);
}
#endif
return (ret);
}
/* taken from OpenSSL apps/s_cb.c */
static long bio_dump_cb(BIO * bio, int cmd, const char *argp, int argi,
long argl, long ret)
{
if (!do_dump)
return (ret);
if (cmd == (BIO_CB_READ | BIO_CB_RETURN)) {
printf("read from %08X [%08lX] (%d bytes => %ld (0x%X))\n", bio, argp,
argi, ret, ret);
tls_dump(argp, (int) ret);
return (ret);
} else if (cmd == (BIO_CB_WRITE | BIO_CB_RETURN)) {
printf("write to %08X [%08lX] (%d bytes => %ld (0x%X))\n", bio, argp,
argi, ret, ret);
tls_dump(argp, (int) ret);
}
return (ret);
}
int tls_start_clienttls(int *layer, char **authid)
{
int sts;
int j;
unsigned int n;
SSL_SESSION *session;
SSL_CIPHER *cipher;
X509 *peer;
if (verbose==1)
printf("setting up TLS connection\n");
if (tls_conn == NULL) {
tls_conn = (SSL *) SSL_new(tls_ctx);
}
if (tls_conn == NULL) {
printf("Could not allocate 'con' with SSL_new()\n");
return IMTEST_FAIL;
}
SSL_clear(tls_conn);
if (!SSL_set_fd(tls_conn, sock)) {
printf("SSL_set_fd failed\n");
return IMTEST_FAIL;
}
/*
* This is the actual handshake routine. It will do all the negotiations
* and will check the client cert etc.
*/
SSL_set_connect_state(tls_conn);
/*
* We do have an SSL_set_fd() and now suddenly a BIO_ routine is called?
* Well there is a BIO below the SSL routines that is automatically
* created for us, so we can use it for debugging purposes.
*/
if (verbose==1)
BIO_set_callback(SSL_get_rbio(tls_conn), bio_dump_cb);
/* Dump the negotiation for loglevels 3 and 4 */
if (verbose==1)
do_dump = 1;
if ((sts = SSL_connect(tls_conn)) <= 0) {
printf("SSL_connect error %d\n", sts);
session = SSL_get_session(tls_conn);
if (session) {
SSL_CTX_remove_session(tls_ctx, session);
printf("SSL session removed\n");
}
if (tls_conn!=NULL)
SSL_free(tls_conn);
tls_conn = NULL;
return IMTEST_FAIL;
}
/*
* Lets see, whether a peer certificate is availabe and what is
* the actual information. We want to save it for later use.
*/
peer = SSL_get_peer_certificate(tls_conn);
if (peer != NULL) {
X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
NID_commonName, peer_CN, CCERT_BUFSIZ);
tls_peer_CN = peer_CN;
X509_NAME_get_text_by_NID(X509_get_issuer_name(peer),
NID_commonName, issuer_CN, CCERT_BUFSIZ);
if (verbose==1)
printf("subject_CN=%s, issuer_CN=%s\n", peer_CN, issuer_CN);
tls_issuer_CN = issuer_CN;
}
tls_protocol = SSL_get_version(tls_conn);
cipher = SSL_get_current_cipher(tls_conn);
tls_cipher_name = SSL_CIPHER_get_name(cipher);
tls_cipher_usebits = SSL_CIPHER_get_bits(cipher,
&tls_cipher_algbits);
if (layer!=NULL)
*layer = tls_cipher_usebits;
if (authid!=NULL)
*authid = tls_peer_CN;
printf("TLS connection established: %s with cipher %s (%d/%d bits)\n",
tls_protocol, tls_cipher_name,
tls_cipher_usebits, tls_cipher_algbits);
return IMTEST_OK;
}
#endif /* HAVE_SSL */
static sasl_security_properties_t *make_secprops(int min,int max)
{
sasl_security_properties_t *ret=(sasl_security_properties_t *)
malloc(sizeof(sasl_security_properties_t));
ret->maxbufsize=1024;
ret->min_ssf=min;
ret->max_ssf=max;
ret->security_flags=0;
ret->property_names=NULL;
ret->property_values=NULL;
return ret;
}
/*
* Initialize SASL and set necessary options
*/
static int init_sasl(char *serverFQDN, int port, int minssf, int maxssf)
{
int saslresult;
sasl_security_properties_t *secprops=NULL;
int addrsize=sizeof(struct sockaddr_in);
struct sockaddr_in *saddr_l=malloc(sizeof(struct sockaddr_in));
struct sockaddr_in *saddr_r=malloc(sizeof(struct sockaddr_in));
/* attempt to start sasl */
saslresult=sasl_client_init(callbacks);
if (saslresult!=SASL_OK) return IMTEST_FAIL;
/* client new connection */
saslresult=sasl_client_new("imap",
serverFQDN,
NULL,
0,
&conn);
if (saslresult!=SASL_OK) return IMTEST_FAIL;
/* create a security structure and give it to sasl */
secprops = make_secprops(minssf, maxssf);
if (secprops != NULL)
{
sasl_setprop(conn, SASL_SEC_PROPS, secprops);
free(secprops);
}
if (getpeername(sock,(struct sockaddr *)saddr_r,&addrsize)!=0)
return IMTEST_FAIL;
if (sasl_setprop(conn, SASL_IP_REMOTE, saddr_r)!=SASL_OK)
return IMTEST_FAIL;
addrsize=sizeof(struct sockaddr_in);
if (getsockname(sock,(struct sockaddr *)saddr_l,&addrsize)!=0)
return IMTEST_FAIL;
if (sasl_setprop(conn, SASL_IP_LOCAL, saddr_l)!=SASL_OK)
return IMTEST_FAIL;
/* should be freed */
free(saddr_l);
free(saddr_r);
return IMTEST_OK;
}
#define BUFSIZE 16384
imt_stat getauthline(char **line, int *linelen)
{
char buf[BUFSIZE];
int saslresult;
char *str=(char *) buf;
str = prot_fgets(str, BUFSIZE, pin);
if (str == NULL) imtest_fatal("prot layer failure");
printf("S: %s",str);
if (!strncasecmp(str, "A01 OK ", 7)) { return STAT_OK; }
if (!strncasecmp(str, "A01 NO ", 7)) { return STAT_NO; }
str += 2; /* jump past the "+ " */
*line = malloc(strlen(str)+1);
if ((*line)==NULL) {
return STAT_NO;
}
if (*str != '\r') {
/* decode this line */
saslresult = sasl_decode64(str, strlen(str),
*line, (unsigned *) linelen);
if (saslresult != SASL_OK) {
printf("base64 decoding error\n");
return STAT_NO;
}
} else {
/* this is a blank */
*line = NULL;
*linelen = 0;
}
return STAT_CONT;
}
void interaction (int id, const char *prompt,
char **tresult, unsigned int *tlen)
{
char result[1024];
if (id==SASL_CB_PASS) {
printf("%s: ", prompt);
*tresult=strdup(getpass(""));
*tlen=strlen(*tresult);
return;
} else if (id==SASL_CB_USER) {
if (username != NULL) {
strcpy(result, username);
} else {
strcpy(result, getpwuid(getuid())->pw_name);
}
} else if (id==SASL_CB_AUTHNAME) {
if (authname != NULL) {
strcpy(result, authname);
} else {
strcpy(result, getpwuid(getuid())->pw_name);
}
#ifdef SASL_CB_GETREALM
} else if ((id==SASL_CB_GETREALM) && (realm != NULL)) {
strcpy(result, realm);
#endif
} else {
int c;
printf("%s: ",prompt);
fgets(result, 1023, stdin);
c = strlen(result);
result[c - 1] = '\0';
}
*tlen = strlen(result);
*tresult = (char *) malloc(*tlen+1);
memset(*tresult, 0, *tlen+1);
memcpy((char *) *tresult, result, *tlen);
}
void fillin_interactions(sasl_interact_t *tlist)
{
while (tlist->id!=SASL_CB_LIST_END)
{
interaction(tlist->id, tlist->prompt,
(void *) &(tlist->result),
&(tlist->len));
tlist++;
}
}
static int waitfor(char *tag)
{
char *str=malloc(301);
char *ptr;
do {
str=prot_fgets(str,300,pin);
if (str == NULL) {
imtest_fatal("prot layer failure");
}
printf("%s", str);
} while (strncmp(str, tag, strlen(tag)));
free(str);
return 0;
}
static int auth_login(void)
{
/* we need username and password to do "login" */
char *username;
unsigned int userlen;
char *pass;
unsigned int passlen;
interaction(SASL_CB_AUTHNAME,"Userid",&username,&userlen);
interaction(SASL_CB_PASS,"Password",&pass,&passlen);
prot_printf(pout,"L01 LOGIN %s {%d}\r\n",username,passlen);
prot_flush(pout);
waitfor("+");
prot_printf(pout,"%s\r\n",pass);
prot_flush(pout);
waitfor("L01");
return IMTEST_OK;
}
int auth_sasl(char *mechlist)
{
sasl_interact_t *client_interact=NULL;
int saslresult=SASL_INTERACT;
char *out;
unsigned int outlen;
char *in;
int inlen;
const char *mechusing;
char inbase64[4096];
int inbase64len;
imt_stat status = STAT_CONT;
/* call sasl client start */
while (saslresult==SASL_INTERACT)
{
saslresult=sasl_client_start(conn, mechlist,
NULL, &client_interact,
&out, &outlen,
&mechusing);
if (saslresult==SASL_INTERACT)
fillin_interactions(client_interact); /* fill in prompts */
}
if ((saslresult != SASL_OK) &&
(saslresult != SASL_CONTINUE)) {
return saslresult;
}
prot_printf(pout,"A01 AUTHENTICATE %s\r\n",mechusing);
prot_flush(pout);
printf("C: A01 AUTHENTICATE %s\r\n", mechusing);
inlen = 0;
status = getauthline(&in, &inlen);
while (status==STAT_CONT)
{
saslresult=SASL_INTERACT;
while (saslresult==SASL_INTERACT)
{
saslresult=sasl_client_step(conn,
in,
inlen,
&client_interact,
&out,
&outlen);
if (saslresult==SASL_INTERACT)
fillin_interactions(client_interact); /* fill in prompts */
}
/* check if sasl suceeded */
if (saslresult != SASL_OK && saslresult != SASL_CONTINUE) {
return saslresult;
}
/* convert to base64 */
saslresult = sasl_encode64(out, outlen,
inbase64, 2048, (unsigned *) &inbase64len);
if (saslresult != SASL_OK) return saslresult;
free(in);
if (out != NULL) free(out);
/* send to server */
printf("C: %s\n",inbase64);
prot_write(pout, inbase64, inbase64len);
prot_printf(pout,"\r\n");
prot_flush(pout);
/* get reply */
status=getauthline(&in,&inlen);
}
return (status == STAT_OK) ? IMTEST_OK : IMTEST_FAIL;
}
/* initialize the network */
int init_net(char *serverFQDN, int port)
{
struct sockaddr_in addr;
struct hostent *hp;
if ((hp = gethostbyname(serverFQDN)) == NULL) {
perror("gethostbyname");
return IMTEST_FAIL;
}
strncpy(serverFQDN, hp->h_name, 1023);
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
return IMTEST_FAIL;
}
addr.sin_family = AF_INET;
memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
addr.sin_port = htons(port);
if (connect(sock, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
perror("connect");
return IMTEST_FAIL;
}
return IMTEST_OK;
}
/***********************
* Parse a mech list of the form: ... AUTH=foo AUTH=bar ...
*
* Return: string with mechs seperated by spaces
*
***********************/
static char *parsemechlist(char *str)
{
char *tmp;
int num=0;
char *ret=malloc(strlen(str)+1);
if (ret==NULL) return NULL;
strcpy(ret,"");
while ((tmp=strstr(str,"AUTH="))!=NULL)
{
char *end=tmp+5;
tmp+=5;
while(((*end)!=' ') && ((*end)!='\0'))
end++;
(*end)='\0';
/* add entry to list */
if (num>0)
strcat(ret," ");
strcat(ret, tmp);
num++;
/* reset the string */
str=end+1;
}
return ret;
}
#define CAPATAG "C01"
#define CAPABILITY "C01 CAPABILITY\r\n"
static char *ask_capability(int *supports_starttls)
{
char *str=malloc(301);
char *ret;
/* request capabilities of server */
prot_printf(pout, CAPATAG " CAPABILITY\r\n");
prot_flush(pout);
printf("C: %s", CAPABILITY);
do { /* look for the * CAPABILITY response */
str = prot_fgets(str,300,pin);
if (str == NULL) imtest_fatal("prot layer failure");
printf("S: %s", str);
} while (strncasecmp(str, "* CAPABILITY", 12));
/* check for starttls */
if (strstr(str,"STARTTLS")!=NULL)
*supports_starttls=1;
else
*supports_starttls=0;
ret=parsemechlist(str);
do { /* look for TAG */
str = prot_fgets(str,300,pin);
if (str == NULL) imtest_fatal("prot layer failure");
printf("S: %s",str);
} while (strncmp(str, CAPATAG, strlen(CAPATAG)));
free(str);
return ret;
}
#define HEADERS "Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)\r\n \
From: Fred Foobar <foobar@Blurdybloop.COM>\r\n \
Subject: afternoon meeting\r\n \
To: mooch@owatagu.siam.edu\r\n \
Message-Id: <B27397-0100000@Blurdybloop.COM>\r\n \
MIME-Version: 1.0\r\n \
Content-Type: TEXT/PLAIN; CHARSET=US-ASCII\r\n\r\n"
static int append_msg(char *mbox, int size)
{
int lup;
prot_printf(pout,"A003 APPEND %s (\\Seen) {%u}\r\n",mbox,size+strlen(HEADERS));
/* do normal header foo */
prot_printf(pout,HEADERS);
for (lup=0;lup<size/10;lup++)
prot_printf(pout,"0123456789");
prot_printf(pout,"\r\n");
prot_flush(pout);
waitfor("A003");
return IMTEST_OK;
}
/**************
*
* This tests throughput of IMAP server
*
* Steps:
* Creat mailbox
* Append message of 200 bytes, 2000 bytes, 20k, 200k, 2M
* Delete mailbox
*
*************/
static void send_recv_test(void)
{
char *mboxname="inbox.imtest";
time_t start, end;
int lup;
start=time(NULL);
for (lup=0;lup<10;lup++)
{
prot_printf(pout,"C01 CREATE %s\r\n",mboxname);
prot_flush(pout);
waitfor("C01");
append_msg(mboxname,200);
append_msg(mboxname,2000);
append_msg(mboxname,20000);
append_msg(mboxname,200000);
append_msg(mboxname,2000000);
prot_printf(pout,"D01 DELETE %s\r\n",mboxname);
prot_flush(pout);
waitfor("D01");
}
end=time(NULL);
printf("Took: %i seconds\n",(int) end-start);
}
#define LOGOUT "L01 LOGOUT\r\n"
void interactive(char *filename)
{
char buf[2048];
fd_set read_set, rset;
fd_set write_set, wset;
int nfds;
int nfound;
int count;
int fd = 0;
int atend = 0;
int donewritingfile = 0;
/* open the file if available */
if (filename != NULL) {
if ((fd = open(filename, O_RDONLY)) == -1) {
fprintf(stderr,"Unable to open file: %s:", filename);
perror("");
exit(1);
}
}
FD_ZERO(&read_set);
FD_SET(fd, &read_set);
FD_SET(sock, &read_set);
FD_ZERO(&write_set);
FD_SET(sock, &write_set);
nfds = getdtablesize();
if (filename != NULL) {
donewritingfile = 0;
}
/* loop reading from network and from stdin if applicable */
while(1) {
rset = read_set;
wset = write_set;
nfound = select(nfds, &rset, &wset, NULL, NULL);
if (nfound < 0) {
perror("select");
imtest_fatal("select");
}
if ((FD_ISSET(0, &rset)) && (FD_ISSET(sock, &wset))) {
if (fgets(buf, sizeof (buf) - 1, stdin) == NULL) {
printf(LOGOUT);
prot_write(pout, LOGOUT, sizeof (LOGOUT));
FD_CLR(0, &read_set);
} else {
count = strlen(buf);
buf[count - 1] = '\r';
buf[count] = '\n';
buf[count + 1] = '\0';
prot_write(pout, buf, count + 1);
}
prot_flush(pout);
} else if (FD_ISSET(sock, &rset)) {
do {
count = prot_read(pin, buf, sizeof (buf) - 1);
if (count == 0) {
if (prot_error(pin)) {
printf("Protection error: %s\n", prot_error(pin));
}
close(sock);
printf("Connection closed.\n");
return;
}
if (count < 0) {
perror("read");
imtest_fatal("prot_read");
}
buf[count] = '\0';
printf("%s", buf);
} while (pin->cnt > 0);
} else if ((FD_ISSET(fd, &rset)) && (FD_ISSET(sock, &wset))
&& (donewritingfile == 0)) {
/* read from disk */
int numr = read(fd, buf, sizeof(buf));
/* and send out over wire */
if (numr < 0)
{
perror("read");
imtest_fatal("read");
} else if (numr==0) {
donewritingfile = 1;
FD_CLR(fd,&read_set);
/* send LOGOUT */
printf(LOGOUT);
prot_write(pout, LOGOUT, sizeof (LOGOUT));
prot_flush(pout);
} else {
prot_write(pout, buf, numr);
prot_flush(pout);
}
} else {
/* if can't do anything else sleep */
usleep(1000);
}
}
}
/* didn't give correct parameters; let's exit */
void usage(void)
{
printf("Usage: imtest [options] hostname\n");
printf(" -p port : port to use\n");
printf(" -z : timing test\n");
printf(" -k # : minimum protection layer required\n");
printf(" -l # : max protection layer (0=none; 1=integrity; etc)\n");
printf(" -u user : authorization name to use\n");
printf(" -a user : authentication name to use\n");
printf(" -v : verbose\n");
printf(" -m mech : SASL mechanism to use (\"login\" for LOGIN)\n");
printf(" -f file : pipe file into connection after authentication\n");
printf(" -r realm : realm\n");
#ifdef HAVE_SSL
printf(" -t file : Enable TLS. file has the TLS public and private keys (specify \"\" not to use TLS for authentication)\n");
#endif /* HAVE_SSL */
exit(1);
}
int main(int argc, char **argv)
{
char *mechanism=NULL;
char servername[1024];
char *filename=NULL;
char *mechlist;
int *ssfp;
int maxssf = 128;
int minssf = 0;
char c;
int result;
int errflg = 0;
char *tls_keyfile="";
char *port = "imap";
struct servent *serv;
int servport;
int run_stress_test=0;
int dotls=0;
int server_supports_tls;
/* look at all the extra args */
while ((c = getopt(argc, argv, "zvk:l:p:u:a:m:f:t:")) != EOF)
switch (c) {
case 'z':
run_stress_test=1;
break;
case 'v':
verbose=1;
break;
case 'k':
minssf=atoi(optarg);
break;
case 'l':
maxssf=atoi(optarg);
break;
case 'p':
port = optarg;
break;
case 'u':
username = optarg;
break;
case 'a':
authname = optarg;
break;
case 'm':
mechanism=optarg;
break;
case 'f':
filename=optarg;
break;
case 'r':
realm=optarg;
break;
case 't':
dotls=1;
tls_keyfile=optarg;
break;
case '?':
default:
errflg = 1;
break;
}
if (optind != argc - 1) {
errflg = 1;
}
if (errflg) {
usage();
}
/* last arg is server name */
strncpy(servername, argv[optind], 1023);
/* map port -> num */
serv = getservbyname(port, "tcp");
if (serv == NULL) {
servport = atoi(port);
} else {
servport = ntohs(serv->s_port);
}
if (init_net(servername, servport) != IMTEST_OK) {
imtest_fatal("Network initialization");
}
if (init_sasl(servername, servport, minssf, maxssf) != IMTEST_OK) {
imtest_fatal("SASL initialization");
}
/* set up the prot layer */
pin = prot_new(sock, 0);
pout = prot_new(sock, 1);
mechlist=ask_capability(&server_supports_tls); /* get the * line also */
#ifdef HAVE_SSL
if ((dotls==1) && (server_supports_tls==1))
{
sasl_external_properties_t externalprop;
prot_printf(pout,"S01 STARTTLS\r\n");
prot_flush(pout);
waitfor("S01");
result=tls_init_clientengine(10, tls_keyfile, tls_keyfile);
if (result!=IMTEST_OK)
{
printf("Start TLS engine failed\n");
} else {
result=tls_start_clienttls(&externalprop.ssf, &externalprop.auth_id);
if (result!=IMTEST_OK)
printf("TLS negotiation failed!\n");
}
/* TLS negotiation suceeded */
/* tell SASL about the negotiated layer */
result=sasl_setprop(conn,
SASL_SSF_EXTERNAL,
&externalprop);
if (result!=SASL_OK) imtest_fatal("Error setting SASL property");
prot_settls (pin, tls_conn);
prot_settls (pout, tls_conn);
/* ask for the capabilities again */
if (verbose==1) printf("Asking for capabilities again since they might have changed\n");
mechlist=ask_capability(&server_supports_tls);
} else if ((dotls==1) && (server_supports_tls!=1)) {
imtest_fatal("STARTTLS not supported by the server!\n");
}
#endif /* HAVE_SSL */
if (mechanism) {
if (!strcasecmp(mechanism, "login")) {
result = auth_login();
} else {
result = auth_sasl(mechanism);
}
} else {
result = auth_sasl(mechlist);
}
if (result == IMTEST_OK) {
printf("Authenticated.\n");
/* turn on layer if need be */
prot_setsasl(pin, conn);
prot_setsasl(pout, conn);
} else {
const char *s = sasl_errstring(result, NULL, NULL);
printf("Authentication failed. %s\n", s);
}
result = sasl_getprop(conn, SASL_SSF, (void **)&ssfp);
if (result != SASL_OK) {
printf("SSF: unable to determine (SASL ERROR %d)\n", result);
} else {
printf("Security strength factor: %d\n", *ssfp);
}
if (run_stress_test == 1) {
send_recv_test();
} else {
/* else run in interactive mode or
pipe in a filename if applicable */
interactive(filename);
}
exit(0);
}

File Metadata

Mime Type
text/x-c
Expires
Fri, Apr 24, 1:35 PM (6 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18861814
Default Alt Text
imtest.c (29 KB)

Event Timeline