#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.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/sasl.h>
#include <sasl/saslutil.h>
#include <pwd.h>
#include "prot.h"
#include "lex.h"
#include "request.h"
#define IMTEST_OK 0
#define IMTEST_FAIL -1
typedef enum {
STAT_CONT = 0,
STAT_NO = 1,
STAT_OK = 2
} imt_stat;
char *authname=NULL;
char *username=NULL;
char *realm=NULL;
char *password=NULL;
sasl_conn_t *conn;
int sock;
struct protstream *pout, *pin;
static int version;
void imtest_fatal(char *msg)
{
if (msg!=NULL)
printf("failure: %s\n",msg);
exit(1);
}
static int
getsecret(sasl_conn_t *conn,
void *context __attribute__((unused)),
int id,
sasl_secret_t **psecret)
{
char *password;
size_t len;
if (! conn || ! psecret || id != SASL_CB_PASS)
return SASL_BADPARAM;
printf("xxx todo\n");
return -1;
len = strlen(password);
*psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len);
if (! *psecret) {
memset(password, 0, len);
return SASL_NOMEM;
}
(*psecret)->len = len;
strcpy((*psecret)->data, password);
memset(password, 0, len);
return SASL_OK;
}
static int
simple(void *context,
int id,
const char **result,
unsigned *len)
{
const char *value = (const char *)context;
int l;
printf("asking for %d\n",id);
if (! result)
return SASL_BADPARAM;
switch (id) {
case SASL_CB_USER:
printf("Username: ");
*result = xmalloc(1025);
fgets(*result, 1024, stdin);
l = strlen(*result);
(*result)[l - 1] = '\0';
if (len)
*len = strlen(*result);
break;
case SASL_CB_AUTHNAME:
*result = value;
if (len)
*len = value ? strlen(value) : 0;
break;
case SASL_CB_LANGUAGE:
*result = NULL;
if (len)
*len = 0;
break;
default:
return SASL_BADPARAM;
}
return SASL_OK;
}
void interaction (int id, const char *prompt,
char **tresult, unsigned int *tlen)
{
char result[1024];
if (id==SASL_CB_PASS) {
if (password!=NULL)
{
*tresult = strdup(password);
*tlen=strlen(*tresult);
memset(password, '\0', strlen(password));
} else {
printf("%s: ", prompt);
*tresult=strdup(getpass(""));
*tlen=strlen(*tresult);
}
return;
} else if ((id==SASL_CB_USER) || (id==SASL_CB_AUTHNAME)) {
if ((id==SASL_CB_USER) && (username!=NULL))
{
strcpy(result, username);
} else if (authname) {
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 sasl_callback_t callbacks[] = {
{
SASL_CB_GETREALM, &simple, NULL
}, {
SASL_CB_USER, &simple, NULL
}, {
SASL_CB_AUTHNAME, &simple, NULL
}, {
SASL_CB_PASS, &getsecret, NULL
}, {
SASL_CB_LIST_END, NULL, NULL
}
};
void fatal(const char *s, int code)
{
printf("Error: %s\n",s);
exit(code);
}
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;
}
static int init_sasl(char *serverFQDN, int port, int ssf)
{
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));
saslresult=sasl_client_init(callbacks);
if (saslresult!=SASL_OK) return IMTEST_FAIL;
saslresult=sasl_client_new(SIEVE_SERVICE_NAME,
serverFQDN,
NULL,
0,
&conn);
if (saslresult!=SASL_OK) return IMTEST_FAIL;
secprops = make_secprops(0, ssf);
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;
saddr_l->sin_port = htons(port);
if (sasl_setprop(conn, SASL_IP_LOCAL, saddr_l)!=SASL_OK)
return IMTEST_FAIL;
free(saddr_l);
free(saddr_r);
return IMTEST_OK;
}
int getauthline(char **line, unsigned int *linelen)
{
lexstate_t state;
int res;
int ret;
mystring_t *errstr;
res=yylex(&state, pin);
if (res!=STRING)
{
ret = handle_response(res,version,
pin, &errstr);
if (res==TOKEN_OK) {
return STAT_OK;
} else {
printf("Authentication failed with: \"%s\"\n",string_DATAPTR(errstr));
return STAT_NO;
}
}
*line=(char *) malloc(state.str->len*2+1);
sasl_decode64(string_DATAPTR(state.str), state.str->len,
*line, linelen);
if (yylex(&state, pin)!=EOL)
parseerror("EOL");
return STAT_CONT;
}
int auth_sasl(int version, char *mechlist)
{
sasl_interact_t *client_interact=NULL;
int saslresult=SASL_INTERACT;
char *out;
unsigned int outlen;
char *in;
unsigned int inlen;
const char *mechusing;
char inbase64[2048];
unsigned int inbase64len;
imt_stat status = STAT_CONT;
while (saslresult==SASL_INTERACT)
{
saslresult=sasl_client_start(conn, mechlist,
NULL, &client_interact,
&out, &outlen,
&mechusing);
if (saslresult==SASL_INTERACT)
fillin_interactions(client_interact);
}
if ((saslresult!=SASL_OK) && (saslresult!=SASL_CONTINUE)) return saslresult;
if (out!=NULL)
{
prot_printf(pout,"AUTHENTICATE \"%s\" ",mechusing);
sasl_encode64(out, outlen,
inbase64, sizeof(inbase64), &inbase64len);
prot_printf(pout, "{%d+}\r\n",inbase64len);
prot_write(pout,inbase64,inbase64len);
prot_printf(pout,"\r\n");
} else {
prot_printf(pout,"AUTHENTICATE \"%s\"\r\n",mechusing);
}
prot_flush(pout);
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);
}
if (saslresult<SASL_OK)
{
printf("sasl result = %s\n",sasl_errstring(saslresult,NULL,NULL));
return saslresult;
}
sasl_encode64(out, outlen,
inbase64, sizeof(inbase64), &inbase64len);
prot_printf(pout, "{%d+}\r\n",inbase64len);
prot_flush(pout);
prot_write(pout,inbase64,inbase64len);
prot_flush(pout);
prot_printf(pout,"\r\n");
prot_flush(pout);
status=getauthline(&in,&inlen);
}
return (status == STAT_OK) ? IMTEST_OK : IMTEST_FAIL;
}
int init_net(char *serverFQDN, int port)
{
struct sockaddr_in addr;
struct hostent *hp;
if ((hp = gethostbyname(serverFQDN)) == NULL) {
perror("gethostbyname");
return IMTEST_FAIL;
}
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;
}
char *read_capability(int *version)
{
lexstate_t state;
char *cap = NULL;
*version = NEW_VERSION;
while (yylex(&state,pin)==STRING)
{
char *attr = string_DATAPTR(state.str);
char *val = NULL;
if (yylex(&state,pin)==' ')
{
if (yylex(&state,pin)!=STRING)
{
parseerror("STRING");
}
val = string_DATAPTR(state.str);
if (yylex(&state,pin)!=EOL)
{
parseerror("EOL1");
}
}
if (strcasecmp(attr,"SASL")==0)
{
cap = val;
} else if (strcasecmp(attr,"SIEVE")==0) {
} else if (strcasecmp(attr,"IMPLEMENTATION")==0) {
} else if (strncmp(val,"SASL=",5)==0) {
*version = OLD_VERSION;
cap = (char *) malloc(strlen(val));
memset(cap, '\0', strlen(val));
memcpy(cap, val+6, strlen(val)-7);
return cap;
} else {
}
}
if (yylex(&state,pin)!=EOL)
{
parseerror("EOL2");
}
return cap;
}
void list_cb(char *name, int isactive)
{
printf("name = %s active = %d\n",name,isactive);
}
void usage(void)
{
printf("usage:\n");
printf(" installsieve [options] servername\n");
printf(" -v <name> view script\n");
printf(" -l list available scripts\n");
printf(" -p <port> port to connect to\n");
printf(" -i <file> filename of script to install\n");
printf(" -a <name> Set <name> as the active script\n");
printf(" -d <name> Delete <name> script from server\n");
printf(" -m <mech> Mechanism to use for authentication\n");
printf(" -g <name> Get script <name> and save to disk\n");
printf(" -u <user> Userid/Authname to use\n");
printf(" -t <user> Userid to use (for proxying)\n");
printf(" -w <passwd> Specify password (Should only be used for automated scripts)\n");
exit(1);
}
int main(int argc, char **argv)
{
char c;
int dolist = 0;
int deflist = 1;
char *portstr = "sieve";
int port;
struct servent *serv;
char *mechanism=NULL;
char *installfile=NULL;
char *viewfile=NULL;
char *servername;
char *setactive=NULL;
char *deletescript=NULL;
char *getscriptname=NULL;
int ssf=0;
char *mechlist=NULL;
int result;
while ((c = getopt(argc, argv, "a:d:g:lv:p:i:m:u:w:t:")) != EOF)
switch (c)
{
case 'a':
setactive=optarg;
deflist = 0;
break;
case 'd':
deletescript=optarg;
deflist = 0;
break;
case 'i':
installfile=optarg;
deflist = 0;
break;
case 'l':
dolist=1;
break;
case 'v':
viewfile=optarg;
deflist = 0;
break;
case 'p':
portstr=optarg;
break;
case 'm':
mechanism=optarg;
break;
case 'g':
getscriptname=optarg;
deflist = 0;
break;
case 'u':
authname = optarg;
break;
case 't':
username = optarg;
break;
case 'w':
password = optarg;
break;
default:
usage();
break;
}
if (optind != argc - 1) {
usage();
}
servername = argv[optind];
serv = getservbyname(portstr, "tcp");
if (serv == NULL) {
port = atoi(portstr);
} else {
port = ntohs(serv->s_port);
}
if (init_net(servername, port) != IMTEST_OK) {
imtest_fatal("Network initialization");
}
if (init_sasl(servername, port, ssf) != IMTEST_OK) {
imtest_fatal("SASL initialization");
}
pin = prot_new(sock, 0);
pout = prot_new(sock, 1);
mechlist=read_capability(&version);
if (mechanism!=NULL) {
result=auth_sasl(version,mechanism);
} else if (mechlist==NULL) {
printf("Error reading mechanism list from server\n");
exit(1);
} else {
result=auth_sasl(version,mechlist);
}
if (result!=IMTEST_OK) {
printf("Authentication failed.\n");
exit(1);
}
if (viewfile!=NULL)
{
getscript(version,pout,pin, viewfile,0);
}
if (installfile!=NULL)
{
installafile(version,pout,pin,installfile);
}
if (setactive!=NULL)
{
setscriptactive(version,pout,pin,setactive);
}
if (deletescript!=NULL)
{
deleteascript(version,pout, pin, deletescript);
}
if (getscriptname!=NULL)
{
getscript(version,pout,pin, getscriptname,1);
}
if (dolist || deflist) {
showlist(version,pout,pin);
}
return 0;
}