Commit f25ebf2f f25ebf2f2f634e731a7b5ffd5828bec325e0926f by Alain Magloire

Support in POP3 maibox client for APOP authentication.

	This should work, now:
	#MAIL='pop://alain;AUTH=+APOP@localhost' ./frm
	Pop passwd: secret  <--- apop secret
	...

	* mailbox/folder_pop.c (folder_pop_get_authority):
	Enable APOP code.
	* mailbx/mbx_pop.c: Add md5-rsa.h.
	(_pop_apop): New function to do APOP authentication.
	(_pop_user): Factor some code in pop_get_user() and
	pop_get_passwd() to share with _pop_apop().
	(pop_get_user): New function, get the user login.
	(pop_get_passwd): New function, get the user passwd.
	(pop_get_md5): New function get timestamp.
	* mailbox/url_pop.c: Add 2001 in Copyright.
	* pop3d/apop.c: Remove trailing spaces.

	* frm/frm.c: New option -d, --debug.
	To put the mailbox_t in debug mode, very practicle
	to watch the traffic.
1 parent 5a79e104
2001-11-12 Alain Magloire
Support in POP3 maibox client for APOP authentication.
This should work, now:
#MAIL='pop://alain;AUTH=+APOP@localhost' ./frm
Pop passwd: secret <--- apop secret
...
* mailbox/folder_pop.c (folder_pop_get_authority):
Enable APOP code.
* mailbx/mbx_pop.c: Add md5-rsa.h.
(_pop_apop): New function to do APOP authentication.
(_pop_user): Factor some code in pop_get_user() and
pop_get_passwd() to share with _pop_apop().
(pop_get_user): New function, get the user login.
(pop_get_passwd): New function, get the user passwd.
(pop_get_md5): New function get timestamp.
* mailbox/url_pop.c: Add 2001 in Copyright.
* pop3d/apop.c: Remove trailing spaces.
* frm/frm.c: New option -d, --debug.
To put the mailbox_t in debug mode, very practicle
to watch the traffic.
2001-11-11 Alain Magloire
Most daemon will share the same code to daemonize(), we use
......
......@@ -37,6 +37,7 @@ static void usage (const char *argv);
static struct option long_options[] =
{
{"debug", no_argument, 0, 'd'},
{"help", no_argument, 0, 'h'},
{"field", required_argument, 0, 'f'},
{"to", no_argument, 0, 'l'},
......@@ -50,7 +51,7 @@ static struct option long_options[] =
{0, 0, 0, 0}
};
const char *short_options ="hf:lnQqSs:tv";
const char *short_options ="dhf:lnQqSs:tv";
static char* show_field;
static int show_to;
......@@ -62,6 +63,7 @@ static int be_quiet;
static int align = 1;
static int show_query;
static int have_new_mail;
static int dbug;
#define IS_READ 0x001
#define IS_OLD 0x010
......@@ -199,6 +201,7 @@ usage (const char *argv)
{
printf ("GNU Mailutils.\n");
printf ("Usage: %s [OPTIONS]\n\n", argv);
printf (" -d, --debug display debuging information\n");
printf (" -h, --help display this help and exit\n");
printf (" -f, --field=string header field to display\n");
printf (" -l, --to include the To: information\n");
......@@ -235,6 +238,10 @@ main(int argc, char **argv)
{
switch (c)
{
case 'd':
dbug++;
break;
case 'h':
usage (argv[0]);
break;
......@@ -325,13 +332,27 @@ main(int argc, char **argv)
observer_t observer;
observable_t observable;
if ((status = mailbox_create_default (&mbox, mailbox_name) != 0)
|| (status = mailbox_open (mbox, MU_STREAM_READ) != 0))
status = mailbox_create_default (&mbox, mailbox_name);
if (status != 0)
{
fprintf (stderr, "could not open mailbox\n");
fprintf (stderr, "could not create mailbox object\n");
exit (3);
}
if (dbug)
{
mu_debug_t debug;
mailbox_get_debug (mbox, &debug);
mu_debug_set_level (debug, MU_DEBUG_TRACE|MU_DEBUG_PROT);
}
status = mailbox_open (mbox, MU_STREAM_READ);
if (status != 0)
{
fprintf (stderr, "could not open mailbox\n");
exit (4);
}
if (! be_quiet)
{
observer_create (&observer, mbox);
......
......@@ -50,6 +50,7 @@ static int folder_pop_open __P ((folder_t, int));
static int folder_pop_close __P ((folder_t));
static int folder_pop_get_authority __P ((folder_t, authority_t *));
extern int _pop_user __P ((authority_t));
extern int _pop_apop __P ((authority_t));
/* XXX: The way, the POP folder is handle is not clean at all.
the I/O functions should have been here on folder, not in mbx_pop.c */
......@@ -102,11 +103,15 @@ folder_pop_get_authority (folder_t folder, authority_t *pauth)
authority_set_authenticate (folder->authority, _pop_user, folder);
}
/*
else...
"+apop" could be supported.
Anything else starting with "+" is an extension mechanism.
Without a "+" it's a SASL mechanism.
*/
else if (strcasecmp (folder->url->auth, "+APOP") == 0)
{
status = authority_create (&folder->authority, NULL, folder);
authority_set_authenticate (folder->authority, _pop_apop, folder);
}
else
{
status = ENOSYS;
......
......@@ -38,6 +38,8 @@
# include <strings.h>
#endif
#include <md5-rsa.h>
#include <mailutils/stream.h>
#include <mailutils/body.h>
#include <mailutils/message.h>
......@@ -100,6 +102,7 @@ static int pop_is_updated __P ((mailbox_t));
/* The implementation of message_t */
int _pop_user __P ((authority_t));
int _pop_apop __P ((authority_t));
static int pop_get_size __P ((mailbox_t, off_t *));
/* We use pop_top for retreiving headers. */
/* static int pop_header_read (header_t, char *, size_t, off_t, size_t *); */
......@@ -124,6 +127,10 @@ static int pop_readline __P ((pop_data_t));
static int pop_read_ack __P ((pop_data_t));
static int pop_writeline __P ((pop_data_t, const char *, ...));
static int pop_write __P ((pop_data_t));
static int pop_get_user __P ((authority_t));
static int pop_get_passwd __P ((authority_t));
static char *pop_get_timestamp __P ((pop_data_t));
static int pop_get_md5 __P ((pop_data_t));
/* This structure holds the info for a message. The pop_message_t
type, will serve as the owner of the message_t and contains the command to
......@@ -371,38 +378,23 @@ _pop_user (authority_t auth)
folder_t folder = authority_get_owner (auth);
mailbox_t mbox = folder->data;
pop_data_t mpd = mbox->data;
ticket_t ticket;
int status;
switch (mpd->state)
{
case POP_AUTH:
{
/* Fetch the user from them. */
size_t n = 0;
authority_get_ticket (auth, &ticket);
if (mpd->user)
free (mpd->user);
/* Was it in the URL? */
status = url_get_user (mbox->url, NULL, 0, &n);
if (status != 0 || n == 0)
ticket_pop (ticket, "Pop User: ", &mpd->user);
else
{
mpd->user = calloc (1, n + 1);
url_get_user (mbox->url, mpd->user, n + 1, NULL);
}
if (mpd->user == NULL || mpd->user[0] == '\0')
{
CHECK_ERROR_CLOSE (mbox, mpd, EINVAL);
}
status = pop_writeline (mpd, "USER %s\r\n", mpd->user);
CHECK_ERROR_CLOSE(mbox, mpd, status);
MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
free (mpd->user);
mpd->user = NULL;
mpd->state = POP_AUTH_USER;
}
/* Fetch the user from them. */
status = pop_get_user (auth);
if (status != 0 || mpd->user == NULL || mpd->user[0] == '\0')
{
CHECK_ERROR_CLOSE (mbox, mpd, EINVAL);
}
status = pop_writeline (mpd, "USER %s\r\n", mpd->user);
CHECK_ERROR_CLOSE(mbox, mpd, status);
MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
free (mpd->user);
mpd->user = NULL;
mpd->state = POP_AUTH_USER;
case POP_AUTH_USER:
/* Send username. */
......@@ -423,30 +415,14 @@ _pop_user (authority_t auth)
observable_notify (observable, MU_EVT_AUTHORITY_FAILED);
CHECK_ERROR_CLOSE (mbox, mpd, EACCES);
}
if (mpd->passwd)
{
free (mpd->passwd);
mpd->passwd = NULL;
}
/* Was it in the URL? */
{
size_t n = 0;
status = url_get_passwd (mbox->url, NULL, 0, &n);
if (status != 0 || n == 0)
ticket_pop (ticket, "Pop Passwd: ", &mpd->passwd);
else
{
mpd->passwd = calloc (1, n + 1);
url_get_passwd (mbox->url, mpd->passwd, n + 1, NULL);
}
}
if (mpd->passwd == NULL || mpd->passwd[0] == '\0')
status = pop_get_passwd (auth);
if (status != 0 || mpd->passwd == NULL || mpd->passwd[0] == '\0')
{
CHECK_ERROR_CLOSE (mbox, mpd, EINVAL);
}
status = pop_writeline (mpd, "PASS %s\r\n", mpd->passwd);
MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
/* We have to nuke the passwd. */
/* Leave not trail of the passwd. */
memset (mpd->passwd, '\0', strlen (mpd->passwd));
free (mpd->passwd);
mpd->passwd = NULL;
......@@ -484,6 +460,79 @@ _pop_user (authority_t auth)
return 0;
}
int
_pop_apop (authority_t auth)
{
folder_t folder = authority_get_owner (auth);
mailbox_t mbox = folder->data;
pop_data_t mpd = mbox->data;
int status;
switch (mpd->state)
{
case POP_AUTH:
/* Fetch the user from them. */
status = pop_get_user (auth);
if (status != 0 || mpd->user == NULL || mpd->user[0] == '\0')
{
CHECK_ERROR_CLOSE (mbox, mpd, EINVAL);
}
/* Fetch the secret from them. */
status = pop_get_passwd (auth);
if (status != 0 || mpd->passwd == NULL || mpd->passwd[0] == '\0')
{
CHECK_ERROR_CLOSE (mbox, mpd, EINVAL);
}
/* Make the MD5 digest string. */
status = pop_get_md5 (mpd);
if (status != 0)
{
CHECK_ERROR_CLOSE (mbox, mpd, status);
}
status = pop_writeline (mpd, "APOP %s %s\r\n", mpd->user, mpd->passwd);
MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
/* We have to obscure the md5 string. */
memset (mpd->passwd, '\0', strlen (mpd->passwd));
free (mpd->user);
free (mpd->passwd);
mpd->user = NULL;
mpd->passwd = NULL;
CHECK_ERROR_CLOSE (mbox, mpd, status);
mpd->state = POP_APOP;
case POP_APOP:
/* Send apop. */
status = pop_write (mpd);
CHECK_EAGAIN (mpd, status);
/* Clear the buffer it contains the md5. */
memset (mpd->buffer, '\0', mpd->buflen);
mpd->state = POP_APOP_ACK;
case POP_APOP_ACK:
status = pop_read_ack (mpd);
CHECK_EAGAIN (mpd, status);
MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
if (strncasecmp (mpd->buffer, "+OK", 3) != 0)
{
observable_t observable = NULL;
mailbox_get_observable (mbox, &observable);
CLEAR_STATE (mpd);
observable_notify (observable, MU_EVT_AUTHORITY_FAILED);
CHECK_ERROR_CLOSE (mbox, mpd, EACCES);
}
mpd->state = POP_AUTH_DONE;
break; /* We're outta here. */
default:
break;
}
CLEAR_STATE (mpd);
return 0;
}
/* Open the connection to the sever, and send the authentication.
FIXME: Should also send the CAPA command to detect for example the suport
for TOP, APOP, ... and DTRT(Do The Right Thing). */
......@@ -585,6 +634,8 @@ pop_open (mailbox_t mbox, int flags)
case POP_AUTH_USER_ACK:
case POP_AUTH_PASS:
case POP_AUTH_PASS_ACK:
case POP_APOP:
case POP_APOP_ACK:
/* Authenticate. */
status = authority_authenticate (mbox->folder->authority);
CHECK_EAGAIN (mpd, status);
......@@ -1786,6 +1837,121 @@ pop_retr (pop_message_t mpm, char *buffer, size_t buflen, off_t offset,
return 0;
}
/* Extract the User from the URL or the ticket. */
static int
pop_get_user (authority_t auth)
{
folder_t folder = authority_get_owner (auth);
mailbox_t mbox = folder->data;
pop_data_t mpd = mbox->data;
ticket_t ticket = NULL;
int status;
/* Fetch the user from them. */
size_t n = 0;
authority_get_ticket (auth, &ticket);
if (mpd->user)
{
free (mpd->user);
mpd->user = NULL;
}
/* Was it in the URL? */
status = url_get_user (mbox->url, NULL, 0, &n);
if (status != 0 || n == 0)
ticket_pop (ticket, "Pop User: ", &mpd->user);
else
{
mpd->user = calloc (1, n + 1);
url_get_user (mbox->url, mpd->user, n + 1, NULL);
}
return 0;
}
/* Extract the User from the URL or the ticket. */
static int
pop_get_passwd (authority_t auth)
{
folder_t folder = authority_get_owner (auth);
mailbox_t mbox = folder->data;
pop_data_t mpd = mbox->data;
ticket_t ticket = NULL;
int status;
/* Fetch the user from them. */
size_t n = 0;
authority_get_ticket (auth, &ticket);
if (mpd->passwd)
{
free (mpd->passwd);
mpd->passwd = NULL;
}
/* Was it in the URL? */
status = url_get_passwd (mbox->url, NULL, 0, &n);
if (status != 0 || n == 0)
ticket_pop (ticket, "Pop Passwd: ", &mpd->passwd);
else
{
mpd->passwd = calloc (1, n + 1);
url_get_passwd (mbox->url, mpd->passwd, n + 1, NULL);
}
return 0;
}
static char *
pop_get_timestamp (pop_data_t mpd)
{
char *right, *left;
char *timestamp = NULL;
size_t len;
len = strlen (mpd->buffer);
right = memchr (mpd->buffer, '<', len);
if (right)
{
len = len - (right - mpd->buffer);
left = memchr (right, '>', len);
if (left)
{
len = left - right + 1;
timestamp = calloc (len + 1, 1);
if (timestamp != NULL)
{
memcpy (timestamp, right, len);
}
}
}
return timestamp;
}
/* Make the MD5 string. */
static int
pop_get_md5 (pop_data_t mpd)
{
MD5_CTX md5context;
unsigned char md5digest[16];
char digest[64]; /* Really it just has to be 32 + 1(null). */
char *tmp;
size_t n;
char *timestamp;
timestamp = pop_get_timestamp (mpd);
if (timestamp == NULL)
return EINVAL;
MD5Init (&md5context);
MD5Update (&md5context, (unsigned char *)timestamp, strlen (timestamp));
MD5Update (&md5context, (unsigned char *)mpd->passwd, strlen (mpd->passwd));
MD5Final (md5digest, &md5context);
for (tmp = digest, n = 0; n < 16; n++, tmp += 2)
sprintf (tmp, "%02x", md5digest[n]);
*tmp = '\0';
free (timestamp);
free (mpd->passwd);
mpd->passwd = strdup (digest);
return 0;
}
/* GRRRRR!! We can not use sleep in the library since this we'll
muck up any alarm() done by the user. */
static int
......
/* GNU mailutils - a suite of utilities for electronic mail
Copyright (C) 1999, 2000 Free Software Foundation, Inc.
Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License as published by
......@@ -67,7 +67,6 @@ _url_pop_init (url_t url)
if (url->port == 0)
url->port = MU_POP_PORT;
return status;
}
......
......@@ -48,7 +48,7 @@ pop3d_apopuser (const char *user)
char *password;
int rc;
char buf[POP_MAXCMDLEN];
#ifdef USE_DBM
{
DBM_FILE db;
......@@ -64,7 +64,7 @@ pop3d_apopuser (const char *user)
strerror (rc));
return NULL;
}
memset (&key, 0, sizeof key);
memset (&data, 0, sizeof data);
......@@ -107,7 +107,7 @@ pop3d_apopuser (const char *user)
strerror (errno));
return NULL;
}
password = calloc (APOP_DIGEST, sizeof (*password));
if (password == NULL)
{
......@@ -143,7 +143,7 @@ pop3d_apopuser (const char *user)
return password;
}
#endif
#endif
}
int
......