927 lines
19 KiB
C
Executable File
927 lines
19 KiB
C
Executable File
/*
|
|
** various utility routines for mailsend
|
|
**
|
|
**
|
|
** Development History:
|
|
** who when why
|
|
** muquit@muquit.com Mar-26-2001 first cut
|
|
*/
|
|
|
|
#include "mailsend.h"
|
|
#include "copyright.h"
|
|
|
|
#define DAY_MIN (24 * HOUR_MIN) /* minutes in a day */
|
|
#define HOUR_MIN 60 /* minutes in an hour */
|
|
#define MIN_SEC 60 /* seconds in a minute */
|
|
|
|
static struct _MimeType
|
|
{
|
|
char
|
|
*ext,
|
|
*val;
|
|
} s_mime_type[] =
|
|
{
|
|
#include "mime_types.h"
|
|
};
|
|
|
|
|
|
/*
|
|
** returns a positive number if the file descriptor is connected to
|
|
** console. 0 if not
|
|
*/
|
|
int isInConsole(int fd)
|
|
{
|
|
int
|
|
rc;
|
|
rc=_isatty(fd);
|
|
return (rc);
|
|
}
|
|
|
|
int isInteractive(void)
|
|
{
|
|
#ifdef WINNT
|
|
if (isInConsole(_fileno(stdout)))
|
|
return(1);
|
|
else
|
|
return(0);
|
|
#else
|
|
if (isatty(fileno(stdout)))
|
|
return(1);
|
|
else
|
|
return(0);
|
|
#endif /* ! WINNT */
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
/*
|
|
** arg: type
|
|
** at this time valid types are: "base64", "none"
|
|
*/
|
|
int get_encoding_type(const char *type)
|
|
{
|
|
if (type == NULL || *type == '\0')
|
|
{
|
|
return ENCODE_BASE64;
|
|
}
|
|
if (strncmp("base64", type, 6) == 0)
|
|
{
|
|
return ENCODE_BASE64;
|
|
}
|
|
else if (strncmp("none", type, 4) == 0)
|
|
{
|
|
return ENCODE_NONE;
|
|
}
|
|
|
|
return ENCODE_BASE64;
|
|
}
|
|
|
|
int get_content_disposition(const char *disposition)
|
|
{
|
|
if (disposition == NULL || *disposition == '\0')
|
|
{
|
|
return CONTENT_DISPOSITION_ATTACHMENT;
|
|
}
|
|
if (strncmp("attachment", disposition, 10) == 0)
|
|
{
|
|
return CONTENT_DISPOSITION_ATTACHMENT;
|
|
}
|
|
else if (strncmp("inline", disposition, 6) == 0)
|
|
{
|
|
return CONTENT_DISPOSITION_INLINE;
|
|
}
|
|
return CONTENT_DISPOSITION_ATTACHMENT;
|
|
}
|
|
|
|
/*
|
|
** duplicate a string. exits on failure
|
|
*/
|
|
char *xStrdup (const char *string)
|
|
{
|
|
char
|
|
*tmp;
|
|
|
|
if (string == (char *) NULL || *string == '\0')
|
|
return ((char *) NULL);
|
|
|
|
/* malloc twice as much memory. */
|
|
tmp = (char *) malloc ((int) strlen(string)+1);
|
|
|
|
if (tmp == (char *) NULL)
|
|
{
|
|
(void) fprintf(stderr,"Error: mystrdup(): memory allocation problem\n");
|
|
exit_error();
|
|
}
|
|
/* it's safe to copy this way */
|
|
(void) strcpy(tmp, string);
|
|
return (tmp);
|
|
}
|
|
|
|
void log_info(const char *fmt, ...)
|
|
{
|
|
va_list
|
|
args;
|
|
|
|
va_start(args, fmt);
|
|
va_end(args);
|
|
}
|
|
|
|
void log_debug(const char *fmt, ...)
|
|
{
|
|
}
|
|
|
|
void log_error(const char *fmt, ...)
|
|
{
|
|
}
|
|
|
|
void log_fatal(const char *fmt, ...)
|
|
{
|
|
}
|
|
|
|
void errorMsg(char *format,...)
|
|
{
|
|
va_list
|
|
args;
|
|
|
|
va_start(args,format);
|
|
(void) fprintf (stderr,"Error: ");
|
|
vfprintf(stderr,format,args);
|
|
(void) fprintf(stderr,"\n");
|
|
(void) fflush(stderr);
|
|
|
|
if (g_log_fp != NULL)
|
|
{
|
|
MutilsTime
|
|
mt;
|
|
char
|
|
timebuf[64];
|
|
mutils_time_now(&mt);
|
|
mutils_time_fmt(&mt,timebuf,sizeof(timebuf));
|
|
(void) fprintf (g_log_fp,"%s: Error: ",timebuf);
|
|
vfprintf(g_log_fp,format,args);
|
|
(void) fprintf(g_log_fp,"\n");
|
|
(void) fflush(g_log_fp);
|
|
}
|
|
va_end(args);
|
|
}
|
|
|
|
void showVerbose(char *format,...)
|
|
{
|
|
va_list
|
|
args;
|
|
|
|
if (g_quiet)
|
|
return;
|
|
|
|
if (g_verbose == 1)
|
|
{
|
|
if (isInConsole(_fileno(stdout)))
|
|
{
|
|
va_start(args,format);
|
|
vfprintf(stdout,format,args);
|
|
(void) fflush(stdout);
|
|
va_end(args);
|
|
}
|
|
|
|
if (g_log_fp != NULL)
|
|
{
|
|
MutilsTime
|
|
mt;
|
|
char
|
|
timebuf[64];
|
|
mutils_time_now(&mt);
|
|
mutils_time_fmt(&mt,timebuf,sizeof(timebuf));
|
|
(void) fprintf(g_log_fp,"%s: " ,timebuf);
|
|
va_start(args,format);
|
|
vfprintf(g_log_fp,format,args);
|
|
(void) fflush(g_log_fp);
|
|
va_end(args);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void print_info(char *format,...)
|
|
{
|
|
va_list
|
|
args;
|
|
if (g_quiet)
|
|
return;
|
|
|
|
va_start(args,format);
|
|
vfprintf(stdout,format,args);
|
|
(void) fflush(stdout);
|
|
va_end(args);
|
|
}
|
|
|
|
void write_log(char *format,...)
|
|
{
|
|
va_list
|
|
args;
|
|
|
|
MutilsTime
|
|
mt;
|
|
char
|
|
timebuf[64];
|
|
|
|
if (g_log_fp == NULL)
|
|
return;
|
|
|
|
mutils_time_now(&mt);
|
|
mutils_time_fmt(&mt,timebuf,sizeof(timebuf));
|
|
|
|
va_start(args,format);
|
|
(void) fprintf(g_log_fp,"%s: " ,timebuf);
|
|
vfprintf(g_log_fp,format,args);
|
|
(void) fflush(g_log_fp);
|
|
va_end(args);
|
|
}
|
|
|
|
void close_log(void)
|
|
{
|
|
if (g_log_fp != NULL)
|
|
{
|
|
(void) fclose(g_log_fp);
|
|
g_log_fp = NULL;
|
|
}
|
|
}
|
|
|
|
void exit_ok(void)
|
|
{
|
|
close_log();
|
|
exit(0);
|
|
}
|
|
|
|
void exit_error(void)
|
|
{
|
|
close_log();
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/*
|
|
** Initialize TheMail structure. returns pointer to the TheMail sturcture on
|
|
** success, NULL on failure.
|
|
*/
|
|
TheMail *newTheMail(void)
|
|
{
|
|
TheMail
|
|
*the_mail=(TheMail *) NULL;
|
|
|
|
the_mail=(TheMail *) malloc(sizeof(TheMail));
|
|
if (the_mail == (TheMail *) NULL)
|
|
{
|
|
(void) fprintf(stderr,"initTheMail(): malloc failed\n");
|
|
return ((TheMail *) NULL);
|
|
}
|
|
memset(the_mail,0,sizeof(TheMail));
|
|
|
|
return (the_mail);
|
|
}
|
|
|
|
Address *newAddress(void)
|
|
{
|
|
Address
|
|
*a;
|
|
|
|
a=(Address *) malloc(sizeof(Address));
|
|
if (a == (Address *) NULL)
|
|
{
|
|
(void) fprintf(stderr," newAddress() malloc failed\n");
|
|
}
|
|
memset(a,0,sizeof(Address));
|
|
|
|
return (a);
|
|
}
|
|
|
|
|
|
int validateMusts(char *from,char *to,char *smtp_server,char *helo_domain)
|
|
{
|
|
int
|
|
err=0;
|
|
|
|
if (getAddressList() == NULL)
|
|
{
|
|
if (to == (char *) NULL)
|
|
{
|
|
errorMsg("No To address/es specified. Speicfy with: -t to,to,..");
|
|
err++;
|
|
}
|
|
}
|
|
|
|
if (from == (char *) NULL)
|
|
{
|
|
errorMsg("No From address specified. Specify with: -f");
|
|
err++;
|
|
}
|
|
|
|
|
|
if (smtp_server == (char *) NULL)
|
|
{
|
|
errorMsg("No SMTP server address or IP specified. Specify with -smtp");
|
|
err++;
|
|
}
|
|
|
|
|
|
if (helo_domain == (char *) NULL)
|
|
{
|
|
errorMsg("No domain specified. Specify with: -d");
|
|
err++;
|
|
}
|
|
|
|
if (err)
|
|
return (-1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
char *askFor(char *buf,int buflen,char *label,int ask)
|
|
{
|
|
char *s;
|
|
|
|
if (label == NULL || *label == '\0')
|
|
return (NULL);
|
|
|
|
again:
|
|
if (isInConsole(_fileno(stdin)))
|
|
{
|
|
(void) fprintf(stdout,"%s",label);
|
|
(void) fflush(stdout);
|
|
(void) fflush(stderr);
|
|
}
|
|
|
|
s = fgets(buf,buflen,stdin);
|
|
if (s == NULL || *buf == '\0' || *buf == '\n')
|
|
{
|
|
if (ask == EMPTY_NOT_OK)
|
|
goto again;
|
|
}
|
|
|
|
mutilsChopNL(buf);
|
|
|
|
return (buf);
|
|
}
|
|
|
|
/*
|
|
** remote mailto from To
|
|
** remote mailto: from the address
|
|
** mailto:foo => foo
|
|
** mailto: => mailto:
|
|
** mailto:x => "x"
|
|
** mailto: x => " x"
|
|
*/
|
|
char *fix_to(char *to)
|
|
{
|
|
char
|
|
*p=to;
|
|
|
|
if (to == NULL || *to == '\0')
|
|
return(p);
|
|
|
|
/* mailto:foo */
|
|
if (strlen(to) > 7 && mutilsStrncasecmp(to,"mailto:",7) == 0)
|
|
{
|
|
p=to;
|
|
p=p+7;
|
|
return(p);
|
|
}
|
|
|
|
return(p);
|
|
}
|
|
|
|
/**
|
|
* return filepath and mime_type
|
|
* @param str String of the form "file,mime_type"
|
|
* example: "c:\usr\local\foo.txt,text/plain"
|
|
* "/usr/local/foo.html,text/html"
|
|
* @param filepath returns
|
|
* @param fp_size size of the buffer filepath
|
|
* @param mime_type returns
|
|
* @param mt_size size of the buffer mime_type
|
|
*
|
|
* @return 0 on success, -1 otherwise
|
|
*
|
|
* Note: it will not expand "~ $" etc in the filepath
|
|
* This function is used when a body is attached with -m flag
|
|
*
|
|
*/
|
|
int get_filepath_mimetype(char *str,char *filepath,int fp_size,char *mype_type,int mt_size)
|
|
{
|
|
|
|
int
|
|
separator,
|
|
rc=0;
|
|
char
|
|
*fp,
|
|
*mt;
|
|
separator = *g_attach_sep;
|
|
if ((mt=strchr(str,separator)))
|
|
{
|
|
*mt++='\0';
|
|
}
|
|
else
|
|
{
|
|
errorMsg("Could not determine mime-type from input: %s\n",str);
|
|
return(-1);
|
|
}
|
|
mutilsSafeStrcpy(mype_type,mt,mt_size-1);
|
|
|
|
/* get the filepath out */
|
|
fp=str;
|
|
mutilsSafeStrcpy(filepath,fp,fp_size-1);
|
|
return(rc);
|
|
}
|
|
|
|
void initialize_openssl(char *cipher)
|
|
{
|
|
#ifdef HAVE_OPENSSL
|
|
static const char rnd_seed[]="my huge entropy for rng.. blah";
|
|
SSL_CTX *ssl_ctx=(SSL_CTX *) NULL;
|
|
SSL *ssl=NULL;
|
|
SSL_library_init();
|
|
SSL_load_error_strings();
|
|
RAND_seed(rnd_seed,sizeof(rnd_seed));
|
|
OpenSSL_add_all_algorithms();
|
|
ssl_ctx=SSL_CTX_new(SSLv23_client_method());
|
|
if (ssl_ctx == NULL)
|
|
{
|
|
errorMsg("Could not create SSL context\n");
|
|
return;
|
|
}
|
|
if (cipher)
|
|
{
|
|
if (!SSL_CTX_set_cipher_list(ssl_ctx,cipher))
|
|
{
|
|
errorMsg("Could not set cipher list %s\n",cipher);
|
|
return;
|
|
}
|
|
}
|
|
ssl=SSL_new(ssl_ctx);
|
|
if (ssl == NULL)
|
|
{
|
|
errorMsg("SSL_new() failed\n");
|
|
return;
|
|
}
|
|
/* set ssl to msock's static */
|
|
msock_set_ssl(ssl);
|
|
|
|
#endif /* HAVE_OPENSSL */
|
|
}
|
|
|
|
/**
|
|
* Calculate Date for RFC822 Date: header
|
|
*
|
|
* @param when time since unix epoch
|
|
* @param datebuf returns NULL terminated date string.
|
|
* Example: Wed, 17 May 2006 13:55:35 -0400
|
|
* @param bufsiz size of buffer dates. It's callers responsibily to make sure
|
|
* datebuf contains enough space. It must be at least 64 bytes.
|
|
* Usually it'll contain 31 bytes.
|
|
*
|
|
* returns 0 on success, -1 on failure
|
|
*
|
|
* The code is adapted from postfix.
|
|
*
|
|
* Changes I made:
|
|
* - I use fixed size buffers instead of dynamic ones.
|
|
* - I don't use %e to calcuate day of the week, instead I use %d. Windows strftime
|
|
* does not have %e.
|
|
* - I don't add timzone name.
|
|
*
|
|
* Reference: http://cr.yp.to/immhf/date.html
|
|
*/
|
|
int rfc822_date(time_t when,char *datebuf,int bufsiz)
|
|
{
|
|
char
|
|
ts1[32],
|
|
ts2[32];
|
|
|
|
struct tm
|
|
*lt;
|
|
|
|
struct tm
|
|
gmt;
|
|
|
|
int
|
|
gmtoff;
|
|
|
|
if (bufsiz < 64)
|
|
{
|
|
(void) fprintf(stderr,"buffer size of date must be > 31, it is %d\n",bufsiz);
|
|
return(-1);
|
|
}
|
|
|
|
memset(datebuf,0,bufsiz);
|
|
memset(ts1,0,sizeof(ts1));
|
|
memset(ts2,0,sizeof(ts2));
|
|
|
|
gmt=*gmtime(&when);
|
|
lt=localtime(&when);
|
|
|
|
gmtoff = (lt->tm_hour - gmt.tm_hour) * HOUR_MIN + lt->tm_min - gmt.tm_min;
|
|
if (lt->tm_year < gmt.tm_year)
|
|
gmtoff -= DAY_MIN;
|
|
else if (lt->tm_year > gmt.tm_year)
|
|
gmtoff += DAY_MIN;
|
|
else if (lt->tm_yday < gmt.tm_yday)
|
|
gmtoff -= DAY_MIN;
|
|
else if (lt->tm_yday > gmt.tm_yday)
|
|
gmtoff += DAY_MIN;
|
|
if (lt->tm_sec <= gmt.tm_sec - MIN_SEC)
|
|
gmtoff -= 1;
|
|
else if (lt->tm_sec >= gmt.tm_sec + MIN_SEC)
|
|
gmtoff += 1;
|
|
|
|
/*
|
|
** windows strftime does not have %e, so I'll use %d instead.
|
|
** day 1 - 9 can be written as 01 - 09
|
|
*/
|
|
(void) strftime(ts1,sizeof(ts1)-1,"%a, %d %b %Y %H:%M:%S ",lt);
|
|
if (gmtoff < -DAY_MIN || gmtoff > DAY_MIN)
|
|
{
|
|
(void) fprintf(stderr,"UTC time offset %d is larger than one day",gmtoff);
|
|
return(-1);
|
|
}
|
|
|
|
(void) snprintf(ts2,sizeof(ts2)-1,"%+03d%02d",
|
|
(int) (gmtoff / HOUR_MIN),(int) (abs(gmtoff) % HOUR_MIN));
|
|
|
|
/* put everything in the buffer. it's usually 31 bytes */
|
|
(void) snprintf(datebuf,bufsiz,"%s%s",ts1,ts2);
|
|
|
|
/* I will not add timezone name, it's not required */
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
** return 0 on success, -1 on failure
|
|
** super simple naive funtion to detect if a file is
|
|
** binary or not.. it's just to prevent attaching binary file
|
|
** if no encoding type is specified. It's not a simple task
|
|
** to detect if a file is binary or not correctly.
|
|
*/
|
|
int guess_file_type(char *file,unsigned int *flag)
|
|
{
|
|
char
|
|
buf[513];
|
|
|
|
int
|
|
i,
|
|
c,
|
|
cr_found=0,
|
|
nbytes,
|
|
buflen;
|
|
|
|
FILE
|
|
*fp=NULL;
|
|
|
|
*flag=0;
|
|
|
|
buflen=sizeof(buf)-1;
|
|
fp=fopen(file,"rb");
|
|
if (fp == NULL)
|
|
{
|
|
errorMsg("Could not open file %s for reading (%s)\n",
|
|
file,ERR_STR);
|
|
return(-1);
|
|
}
|
|
while(!feof(fp))
|
|
{
|
|
nbytes=fread(buf,1,buflen,fp);
|
|
if ((nbytes != buflen) && ferror(fp))
|
|
{
|
|
errorMsg("Read error in guess_file_type\n");
|
|
goto ExitProcessing;
|
|
}
|
|
for (i=0; i < nbytes; i++)
|
|
{
|
|
c=buf[i];
|
|
if (cr_found)
|
|
{
|
|
cr_found=0;
|
|
if (c == '\n')
|
|
continue;
|
|
}
|
|
if (c == '\r')
|
|
{
|
|
cr_found=1;
|
|
*flag |= FILE_TYPE_DOS;
|
|
}
|
|
else if (c == '\n')
|
|
{
|
|
*flag |= FILE_TYPE_UNIX;
|
|
}
|
|
else if ((c < 32 || c > 126) && c != '\t')
|
|
{
|
|
*flag |= FILE_TYPE_BINARY;
|
|
(void) fclose(fp);
|
|
return(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fp)
|
|
(void) fclose(fp);
|
|
return(0);
|
|
ExitProcessing:
|
|
if (fp)
|
|
(void) fclose(fp);
|
|
return(-1);
|
|
|
|
}
|
|
|
|
/*
|
|
static int unix2dos(FILE *ifp,FILE *ofp)
|
|
{
|
|
int
|
|
n,
|
|
j,
|
|
k,
|
|
c,
|
|
c2;
|
|
char
|
|
buf2[BUFSIZ+1],
|
|
buf[BUFSIZ+1];
|
|
if (ifp == NULL || ofp == NULL)
|
|
return(-1);
|
|
#ifdef WINNT
|
|
_setmode(_fileno(ofp),_O_BINARY);
|
|
#endif
|
|
while(!feof(ifp))
|
|
{
|
|
n=fread(buf,1,BUFSIZ,ifp);
|
|
(void) fprintf(stderr,"n=\"%d\"\n",n);
|
|
k=0;
|
|
for (j=0; j < n; j++)
|
|
{
|
|
c=buf[j];
|
|
if (c == '\n')
|
|
{
|
|
(void) fprintf(stderr,"j=%d\n",j);
|
|
buf2[k++]='\r';
|
|
}
|
|
buf2[k++]=c;
|
|
}
|
|
c2=fwrite(buf2,sizeof(char),k,ofp);
|
|
if (c2 != k)
|
|
{
|
|
perror("fwrite");
|
|
return(1);
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
void print_copyright(void)
|
|
{
|
|
char
|
|
**p;
|
|
|
|
for (p = mailsend_copyright; *p != NULL; p++)
|
|
{
|
|
(void) fprintf(stdout,"%s\n",*p);
|
|
}
|
|
(void) fflush(stdout);
|
|
}
|
|
|
|
/*
|
|
** Return the MIME type of the file
|
|
** Parameters:
|
|
** path - path of the file
|
|
** Return Values:
|
|
** mime type
|
|
** Side Effects:
|
|
** none
|
|
** Comments:
|
|
** If no extension is found, "text/plain" will be returned
|
|
** Development History:
|
|
*/
|
|
char *get_mime_type(char *path)
|
|
{
|
|
char
|
|
*npath = NULL,
|
|
*ext = NULL;
|
|
|
|
char
|
|
*text_plain = "text/plain";
|
|
int
|
|
i;
|
|
|
|
if (path == NULL || *path == '\0')
|
|
{
|
|
return(text_plain);
|
|
}
|
|
|
|
/* Issue #140. path was modified */
|
|
npath = xStrdup(path);
|
|
ext = mutilsExtensionLower(npath);
|
|
for (i=0; i < sizeof(s_mime_type)/sizeof(*s_mime_type); i++)
|
|
{
|
|
if (strcmp(s_mime_type[i].ext, ext) == 0)
|
|
{
|
|
if (npath != NULL)
|
|
{
|
|
(void) free((char *) npath);
|
|
}
|
|
return (s_mime_type[i].val);
|
|
}
|
|
}
|
|
if (npath != NULL)
|
|
{
|
|
(void) free((char *) npath);
|
|
}
|
|
|
|
return(text_plain);
|
|
}
|
|
|
|
void show_mime_types()
|
|
{
|
|
int
|
|
i;
|
|
for (i=0; i < sizeof(s_mime_type)/sizeof(*s_mime_type); i++)
|
|
{
|
|
(void) fprintf(stdout,"%s\t%s\n",
|
|
s_mime_type[i].val,
|
|
s_mime_type[i].ext);
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef HAVE_OPENSSL
|
|
void print_cert_info(SSL *ssl)
|
|
{
|
|
X509
|
|
*cert=NULL;
|
|
X509_NAME
|
|
*sub=NULL,
|
|
*issuer=NULL;
|
|
char
|
|
buf[BUFSIZ];
|
|
|
|
if (!g_verbose)
|
|
return;
|
|
|
|
print_info("Cipher: %s\n",SSL_get_cipher(ssl));
|
|
(void) fprintf(stdout,"Certificate information:\n");
|
|
cert=SSL_get_peer_certificate(ssl);
|
|
if (cert == NULL)
|
|
{
|
|
errorMsg("No peer certificate found\n");
|
|
return;
|
|
}
|
|
/* get subject name */
|
|
sub=X509_get_subject_name(cert);
|
|
if (sub == NULL)
|
|
{
|
|
errorMsg("Could not get subject name in certificate\n");
|
|
}
|
|
X509_NAME_oneline(sub,buf,sizeof(buf)-1);
|
|
(void) fprintf(stdout,"Subject: %s\n",buf);
|
|
|
|
issuer=X509_get_issuer_name(cert);
|
|
if (issuer == NULL)
|
|
{
|
|
errorMsg("Could not get issuer name in certificate\n");
|
|
}
|
|
X509_NAME_oneline(issuer,buf,sizeof(buf)-1);
|
|
(void) fprintf(stdout,"Issuer: %s\n",buf);
|
|
(void) fflush(stdout);
|
|
}
|
|
|
|
/*
|
|
** params:
|
|
** challenge: base64 encoded challenge
|
|
** user username
|
|
** seceret password
|
|
**
|
|
** Returns: NULL terminated base64 encoded CRAM-MD5 material on success,
|
|
** NULL on failure
|
|
*/
|
|
char *encode_cram_md5(char *challenge,char *user,char *secret)
|
|
{
|
|
unsigned char
|
|
*data;
|
|
|
|
unsigned long
|
|
data_len;
|
|
|
|
unsigned char
|
|
hmac_md5[16];
|
|
|
|
#ifdef LIBRESSL_VERSION_NUMBER
|
|
HMAC_CTX
|
|
ctx;
|
|
#else
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
HMAC_CTX
|
|
ctx;
|
|
#else
|
|
/* OpenSSL 1.1.x*/
|
|
HMAC_CTX
|
|
*ctx;
|
|
#endif
|
|
#endif
|
|
|
|
const EVP_MD
|
|
*md5=NULL;
|
|
|
|
unsigned int
|
|
hmac_len;
|
|
|
|
int
|
|
i;
|
|
|
|
char
|
|
*b64;
|
|
|
|
/*
|
|
unsigned long
|
|
b64len=0;
|
|
*/
|
|
char
|
|
hex[33],
|
|
buf[BUFSIZ];
|
|
if (challenge == NULL || *challenge == '\0' ||
|
|
user == NULL || *user == '\0' ||
|
|
secret == NULL || *secret == '\0')
|
|
return(NULL);
|
|
|
|
showVerbose("Server Challenge: %s\n",challenge);
|
|
OpenSSL_add_all_digests();
|
|
|
|
/* decode the challenge */
|
|
data=mutils_decode_base64((unsigned char *) challenge,strlen(challenge),&data_len);
|
|
if (data == NULL)
|
|
{
|
|
errorMsg("Could not base64 decode CRAM-MD5 challenge: %s",challenge);
|
|
return(NULL);
|
|
}
|
|
showVerbose("Challenge After decoding: %s\n",data);
|
|
|
|
/* take HMAC-MD5 of the challenge*/
|
|
|
|
#ifdef LIBRESSL_VERSION_NUMBER
|
|
md5=EVP_get_digestbyname("md5");
|
|
HMAC_CTX_init(&ctx);
|
|
HMAC_Init(&ctx,secret,strlen(secret),md5);
|
|
HMAC_Update(&ctx,data,data_len);
|
|
HMAC_Final(&ctx,hmac_md5,&hmac_len);
|
|
#else
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
md5=EVP_get_digestbyname("md5");
|
|
HMAC_CTX_init(&ctx);
|
|
HMAC_Init(&ctx,secret,strlen(secret),md5);
|
|
HMAC_Update(&ctx,data,data_len);
|
|
HMAC_Final(&ctx,hmac_md5,&hmac_len);
|
|
#else
|
|
/* OpenSSL 1.1.x */
|
|
ctx = HMAC_CTX_new();
|
|
HMAC_Init_ex(ctx,secret,strlen(secret),EVP_md5(),NULL);
|
|
HMAC_Update(ctx,data,data_len);
|
|
HMAC_Final(ctx,hmac_md5,&hmac_len);
|
|
#endif
|
|
#endif /* LIBRESSL_VERSION_NUMBER */
|
|
|
|
/* convert the digest to hex */
|
|
memset(hex,0,sizeof(hex));
|
|
for (i=0; i < 16; i++)
|
|
{
|
|
(void) sprintf(hex+2*i,"%02x",hmac_md5[i]);
|
|
}
|
|
showVerbose("HMAC-MD5 of challenge: %s\n",hex);
|
|
|
|
(void) snprintf(buf,sizeof(buf)-1,"%s %s",user,hex);
|
|
showVerbose("base64 encode: %s\n",buf);
|
|
showVerbose("Taking base64 of \"%s\"\n",buf);
|
|
/* base64 encode "user hex_digest" */
|
|
#if 0
|
|
b64=mutils_encode_base64((unsigned char *) buf,strlen(buf),&b64len);
|
|
if (b64len <= 0)
|
|
return(NULL);
|
|
/* mutils_encode_base64 adds CRLF */
|
|
if (b64len > 2)
|
|
b64[b64len-2]='\0';
|
|
#endif
|
|
b64 = mutils_encode_base64_noformat(buf,strlen(buf));
|
|
if (b64 == NULL)
|
|
{
|
|
errorMsg("Could not base64 encode: %s",buf);
|
|
return (NULL);
|
|
}
|
|
showVerbose("base64: %s\n",b64);
|
|
|
|
return(b64);
|
|
}
|
|
#else
|
|
|
|
char *encode_cram_md5(char *challenge,char *user,char *secret)
|
|
{
|
|
errorMsg("Must be compiled with OpenSSL in order to get CRAM-MD5 support\n");
|
|
return(NULL);
|
|
}
|
|
#endif /* HAVE_OPENSSL */
|
|
|